Merge "Separate and add more SizeCompat tests; add Display Builder"
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e80c9b3..deb3246 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6171,8 +6171,7 @@
         // The rest of the condition is that only one side is smaller than the parent, but it still
         // needs to exclude the cases where the size is limited by the fixed aspect ratio.
         if (info.maxAspectRatio > 0) {
-            final float aspectRatio = (0.5f + Math.max(appWidth, appHeight))
-                    / Math.min(appWidth, appHeight);
+            final float aspectRatio = Math.max(appWidth, appHeight) / Math.min(appWidth, appHeight);
             if (aspectRatio >= info.maxAspectRatio) {
                 // The current size has reached the max aspect ratio.
                 return false;
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 9df7b45..864f946 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -38,12 +38,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
 
-import android.app.TaskStackListener;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
@@ -51,10 +46,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.ArrayList;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
-
 /**
  * Tests for the {@link ActivityDisplay} class.
  *
@@ -331,50 +322,4 @@
         verify(mSupervisor).removeTaskByIdLocked(eq(task1.mTaskId), anyBoolean(), anyBoolean(),
                 any());
     }
-
-    /**
-     * Ensures that {@link TaskStackListener} can receive callback about the activity in size
-     * compatibility mode.
-     */
-    @Test
-    public void testHandleActivitySizeCompatMode() throws Exception {
-        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
-        final ActivityRecord activity = createFullscreenStackWithSimpleActivityAt(
-                display).topRunningActivityLocked();
-        activity.setState(ActivityStack.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
-        when(activity.getRequestedOrientation()).thenReturn(
-                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-        activity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-        activity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-        activity.visible = true;
-        activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
-
-        final ArrayList<CompletableFuture<IBinder>> resultWrapper = new ArrayList<>();
-        mService.getTaskChangeNotificationController().registerTaskStackListener(
-                new TaskStackListener() {
-                    @Override
-                    public void onSizeCompatModeActivityChanged(int displayId,
-                            IBinder activityToken) {
-                        resultWrapper.get(0).complete(activityToken);
-                    }
-                });
-
-        resultWrapper.add(new CompletableFuture<>());
-
-        // resize the display to exercise size-compat mode
-        final DisplayContent displayContent = display.mDisplayContent;
-        displayContent.mBaseDisplayHeight = (int) (0.8f * displayContent.mBaseDisplayHeight);
-        Configuration c = new Configuration();
-        displayContent.computeScreenConfiguration(c);
-        display.onRequestedOverrideConfigurationChanged(c);
-
-        assertEquals(activity.appToken, resultWrapper.get(0).get(2, TimeUnit.SECONDS));
-
-        // 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);
-
-        assertNull(resultWrapper.get(0).get(2, TimeUnit.SECONDS));
-    }
 }
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 c51a46a..ac1da7c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -18,24 +18,18 @@
 
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.os.Process.NOBODY_UID;
 import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_90;
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
@@ -61,18 +55,13 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
 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 static org.mockito.Mockito.never;
-import static org.mockito.Mockito.when;
 
-import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
-import android.app.WindowConfiguration;
 import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.PauseActivityItem;
@@ -80,13 +69,11 @@
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.PersistableBundle;
 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;
@@ -219,23 +206,6 @@
     }
 
     @Test
-    public void testRestartProcessIfVisible() {
-        doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);
-        mActivity.visible = true;
-        mActivity.setSavedState(null /* savedState */);
-        mActivity.setState(ActivityStack.ActivityState.RESUMED, "testRestart");
-        prepareFixedAspectRatioUnresizableActivity();
-
-        final Rect originalOverrideBounds = new Rect(mActivity.getBounds());
-        setupDisplayAndParentSize(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());
-        assertNotEquals(originalOverrideBounds, mActivity.getBounds());
-    }
-
-    @Test
     public void testsApplyOptionsLocked() {
         ActivityOptions activityOptions = ActivityOptions.makeBasic();
 
@@ -484,214 +454,6 @@
     }
 
     @Test
-    public void testSizeCompatMode_KeepBoundsWhenChangingFromFreeformToFullscreen() {
-        setupDisplayContentForCompatDisplayInsets();
-
-        // put display in freeform mode
-        ActivityDisplay display = mActivity.getDisplay();
-        final Configuration c = new Configuration(display.getRequestedOverrideConfiguration());
-        c.windowConfiguration.setBounds(new Rect(0, 0, 2000, 1000));
-        c.densityDpi = 300;
-        c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
-        display.onRequestedOverrideConfigurationChanged(c);
-
-        // launch compat activity in freeform and store bounds
-        when(mActivity.getRequestedOrientation()).thenReturn(
-                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-        mTask.getRequestedOverrideConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
-        mTask.setBounds(100, 100, 400, 600);
-        mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-        mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-        mActivity.visible = true;
-        ensureActivityConfiguration();
-
-        final Rect bounds = new Rect(mActivity.getBounds());
-        final int density = mActivity.getConfiguration().densityDpi;
-
-        // change display configuration to fullscreen
-        c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
-        display.onRequestedOverrideConfigurationChanged(c);
-
-        // check if dimensions stay the same
-        assertTrue(mActivity.inSizeCompatMode());
-        assertEquals(bounds.width(), mActivity.getBounds().width());
-        assertEquals(bounds.height(), mActivity.getBounds().height());
-        assertEquals(density, mActivity.getConfiguration().densityDpi);
-        assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, mActivity.getWindowingMode());
-    }
-
-    @Test
-    public void testSizeCompatMode_FixedAspectRatioBoundsWithDecor() {
-        setupDisplayContentForCompatDisplayInsets();
-        final int decorHeight = 200; // e.g. The device has cutout.
-        final DisplayPolicy policy = setupDisplayAndParentSize(600, 800).getDisplayPolicy();
-        spyOn(policy);
-        doAnswer(invocationOnMock -> {
-            final int rotation = invocationOnMock.<Integer>getArgument(0);
-            final Rect insets = invocationOnMock.<Rect>getArgument(4);
-            if (rotation == ROTATION_0) {
-                insets.top = decorHeight;
-            } else if (rotation == ROTATION_90) {
-                insets.left = decorHeight;
-            }
-            return null;
-        }).when(policy).getNonDecorInsetsLw(anyInt() /* rotation */, anyInt() /* width */,
-                anyInt() /* height */, any() /* displayCutout */, any() /* outInsets */);
-        // set appBounds to incorporate decor
-        final Configuration c =
-                new Configuration(mStack.getDisplay().getRequestedOverrideConfiguration());
-        c.windowConfiguration.getAppBounds().top = decorHeight;
-        mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
-
-        doReturn(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
-                .when(mActivity).getRequestedOrientation();
-        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
-        mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = 1;
-        mActivity.visible = true;
-        ensureActivityConfiguration();
-        // The parent configuration doesn't change since the first resolved configuration, so the
-        // activity shouldn't be in the size compatibility mode.
-        assertFalse(mActivity.inSizeCompatMode());
-
-        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().windowConfiguration.setRotation(ROTATION_90);
-        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 Rect newDisplayBounds = new Rect(0, 0, 1000, 2000);
-        DisplayInfo info = new DisplayInfo();
-        mService.mContext.getDisplay().getDisplayInfo(info);
-        info.logicalWidth = newDisplayBounds.width();
-        info.logicalHeight = newDisplayBounds.height();
-        info.logicalDensityDpi = 300;
-
-        final ActivityDisplay newDisplay =
-                addNewActivityDisplayAt(info, ActivityDisplay.POSITION_TOP);
-
-        final Configuration c =
-                new Configuration(mStack.getDisplay().getRequestedOverrideConfiguration());
-        c.densityDpi = 200;
-        mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
-        mActivity = new ActivityBuilder(mService)
-                .setTask(mTask)
-                .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
-                .setMaxAspectRatio(1.5f)
-                .build();
-        mActivity.visible = true;
-
-        final Rect originalBounds = new Rect(mActivity.getBounds());
-        final int originalDpi = mActivity.getConfiguration().densityDpi;
-
-        // Move the non-resizable activity to the new display.
-        mStack.reparent(newDisplay.mDisplayContent, true /* onTop */);
-
-        assertEquals(originalBounds.width(),
-                mActivity.getWindowConfiguration().getBounds().width());
-        assertEquals(originalBounds.height(),
-                mActivity.getWindowConfiguration().getBounds().height());
-        assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
-        assertTrue(mActivity.inSizeCompatMode());
-    }
-
-    @Test
-    public void testSizeCompatMode_FixedScreenBoundsWhenDisplaySizeChanged() {
-        setupDisplayContentForCompatDisplayInsets();
-        when(mActivity.getRequestedOrientation()).thenReturn(
-                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-        mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
-        mTask.getRequestedOverrideConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
-        mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-        mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-        mActivity.visible = true;
-
-        ensureActivityConfiguration();
-        final Rect originalBounds = new Rect(mActivity.getBounds());
-
-        // Change the size of current display.
-        setupDisplayAndParentSize(1000, 2000);
-        ensureActivityConfiguration();
-
-        assertEquals(originalBounds.width(),
-                mActivity.getWindowConfiguration().getBounds().width());
-        assertEquals(originalBounds.height(),
-                mActivity.getWindowConfiguration().getBounds().height());
-        assertTrue(mActivity.inSizeCompatMode());
-    }
-
-    @Test
-    public void testSizeCompatMode_FixedScreenLayoutSizeBits() {
-        final int fixedScreenLayout = Configuration.SCREENLAYOUT_LONG_NO
-                | Configuration.SCREENLAYOUT_SIZE_NORMAL;
-        final int layoutMask = Configuration.SCREENLAYOUT_LONG_MASK
-                | Configuration.SCREENLAYOUT_SIZE_MASK
-                | Configuration.SCREENLAYOUT_LAYOUTDIR_MASK;
-        mTask.getRequestedOverrideConfiguration().screenLayout = fixedScreenLayout
-                | Configuration.SCREENLAYOUT_LAYOUTDIR_LTR;
-        prepareFixedAspectRatioUnresizableActivity();
-
-        // The initial configuration should inherit from parent.
-        assertEquals(mTask.getConfiguration().screenLayout & layoutMask,
-                mActivity.getConfiguration().screenLayout & layoutMask);
-
-        mTask.getConfiguration().screenLayout = Configuration.SCREENLAYOUT_LAYOUTDIR_RTL
-                | Configuration.SCREENLAYOUT_LONG_YES | Configuration.SCREENLAYOUT_SIZE_LARGE;
-        mActivity.onConfigurationChanged(mTask.getConfiguration());
-
-        // The size and aspect ratio bits don't change, but the layout direction should be updated.
-        assertEquals(fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_RTL,
-                mActivity.getConfiguration().screenLayout & layoutMask);
-    }
-
-    @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.
-        setupDisplayAndParentSize(600, 1200);
-
-        // Simulate the display changes orientation.
-        doReturn(ActivityInfo.CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION
-                | ActivityInfo.CONFIG_WINDOW_CONFIGURATION)
-                        .when(display).getLastOverrideConfigurationChanges();
-        mActivity.onConfigurationChanged(mTask.getConfiguration());
-        when(display.getLastOverrideConfigurationChanges()).thenCallRealMethod();
-        // The override configuration should not change so it is still in size compatibility mode.
-        assertTrue(mActivity.inSizeCompatMode());
-
-        // Change display density
-        final DisplayContent displayContent = mStack.getDisplay().mDisplayContent;
-        displayContent.mBaseDisplayDensity = (int) (0.7f * displayContent.mBaseDisplayDensity);
-        final Configuration c = new Configuration();
-        displayContent.computeScreenConfiguration(c);
-        mService.mAmInternal = mock(ActivityManagerInternal.class);
-        mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
-
-        // The override configuration should be reset and the activity's process will be killed.
-        assertFalse(mActivity.inSizeCompatMode());
-        verify(mActivity).restartProcessIfVisible();
-        waitHandlerIdle(mService.mH);
-        verify(mService.mAmInternal).killProcess(
-                eq(mActivity.app.mName), eq(mActivity.app.mUid), anyString());
-    }
-
-    @Test
     public void testTakeOptions() {
         ActivityOptions opts = ActivityOptions.makeRemoteAnimation(
                 new RemoteAnimationAdapter(new Stub() {
@@ -1340,33 +1102,4 @@
 
         verify(mActivity).removeFromHistory(anyString());
     }
-
-    /** Setup {@link #mActivity} as a size-compat-mode-able activity without fixed orientation. */
-    private void prepareFixedAspectRatioUnresizableActivity() {
-        setupDisplayContentForCompatDisplayInsets();
-        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
-        mActivity.info.maxAspectRatio = 1.5f;
-        mActivity.visible = true;
-        ensureActivityConfiguration();
-    }
-
-    private void setupDisplayContentForCompatDisplayInsets() {
-        final Rect displayBounds = mStack.getDisplay().getBounds();
-        setupDisplayAndParentSize(displayBounds.width(), displayBounds.height());
-    }
-
-    private DisplayContent setupDisplayAndParentSize(int width, int height) {
-        final DisplayContent displayContent = mStack.getDisplay().mDisplayContent;
-        displayContent.mBaseDisplayWidth = width;
-        displayContent.mBaseDisplayHeight = height;
-        final Configuration c =
-                new Configuration(mStack.getDisplay().getRequestedOverrideConfiguration());
-        c.windowConfiguration.setBounds(new Rect(0, 0, width, height));
-        c.windowConfiguration.setAppBounds(0, 0, width, height);
-        c.windowConfiguration.setRotation(ROTATION_0);
-        c.orientation = width > height
-                ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
-        mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
-        return displayContent;
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 9f4143f..21f6c3c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -715,14 +715,10 @@
 
         activity.setRequestedOrientation(newOrientation);
 
-        final ArgumentCaptor<Configuration> captor = ArgumentCaptor.forClass(Configuration.class);
-        verify(dc.mActivityDisplay).updateDisplayOverrideConfigurationLocked(captor.capture(),
-                same(activity), anyBoolean(), same(null));
-        final Configuration newDisplayConfig = captor.getValue();
         final int expectedOrientation = newOrientation == SCREEN_ORIENTATION_PORTRAIT
                 ? Configuration.ORIENTATION_PORTRAIT
                 : Configuration.ORIENTATION_LANDSCAPE;
-        assertEquals(expectedOrientation, newDisplayConfig.orientation);
+        assertEquals(expectedOrientation, dc.getConfiguration().orientation);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
new file mode 100644
index 0000000..c9331af
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.TaskStackListener;
+import android.app.WindowConfiguration;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for Size Compatibility mode.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:SizeCompatTests
+ */
+@MediumTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class SizeCompatTests extends ActivityTestsBase {
+    private ActivityStack mStack;
+    private Task mTask;
+    private ActivityRecord mActivity;
+
+    private void setUpApp(ActivityDisplay display) {
+        mStack = new StackBuilder(mRootActivityContainer).setDisplay(display).build();
+        mTask = mStack.getChildAt(0);
+        mActivity = mTask.getTopActivity();
+    }
+
+    private void ensureActivityConfiguration() {
+        mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+    }
+
+    @Test
+    public void testRestartProcessIfVisible() {
+        setUpApp(new TestActivityDisplay.Builder(mService, 1000, 2500).build());
+        doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);
+        mActivity.visible = true;
+        mActivity.setSavedState(null /* savedState */);
+        mActivity.setState(ActivityStack.ActivityState.RESUMED, "testRestart");
+        prepareUnresizable(1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+
+        final Rect originalOverrideBounds = new Rect(mActivity.getBounds());
+        resizeDisplay(mStack.getDisplay(), 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());
+        assertNotEquals(originalOverrideBounds, mActivity.getBounds());
+    }
+
+    @Test
+    public void testKeepBoundsWhenChangingFromFreeformToFullscreen() {
+        removeGlobalMinSizeRestriction();
+        // create freeform display and a freeform app
+        ActivityDisplay display = new TestActivityDisplay.Builder(mService, 2000, 1000)
+                .setCanRotate(false)
+                .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM).build();
+        setUpApp(display);
+
+        // Put app window into freeform and then make it a compat app.
+        mTask.setBounds(100, 100, 400, 600);
+        prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+
+        final Rect bounds = new Rect(mActivity.getBounds());
+        final int density = mActivity.getConfiguration().densityDpi;
+
+        // change display configuration to fullscreen
+        Configuration c = new Configuration(display.getRequestedOverrideConfiguration());
+        c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+        display.onRequestedOverrideConfigurationChanged(c);
+
+        // check if dimensions stay the same
+        assertTrue(mActivity.inSizeCompatMode());
+        assertEquals(bounds.width(), mActivity.getBounds().width());
+        assertEquals(bounds.height(), mActivity.getBounds().height());
+        assertEquals(density, mActivity.getConfiguration().densityDpi);
+    }
+
+    @Test
+    public void testFixedAspectRatioBoundsWithDecor() {
+        final int decorHeight = 200; // e.g. The device has cutout.
+        setUpApp(new TestActivityDisplay.Builder(mService, 600, 800)
+                .setNotch(decorHeight).build());
+
+        mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = 1;
+        prepareUnresizable(-1f, SCREEN_ORIENTATION_UNSPECIFIED);
+
+        // The parent configuration doesn't change since the first resolved configuration, so the
+        // activity shouldn't be in the size compatibility mode.
+        assertFalse(mActivity.inSizeCompatMode());
+
+        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().windowConfiguration.setRotation(ROTATION_90);
+        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 testFixedScreenConfigurationWhenMovingToDisplay() {
+        setUpApp(new TestActivityDisplay.Builder(mService, 1000, 2500).build());
+
+        // Make a new less-tall display with lower density
+        final ActivityDisplay newDisplay =
+                new TestActivityDisplay.Builder(mService, 1000, 2000)
+                        .setDensityDpi(200).build();
+
+        mActivity = new ActivityBuilder(mService)
+                .setTask(mTask)
+                .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+                .setMaxAspectRatio(1.5f)
+                .build();
+        mActivity.visible = true;
+
+        final Rect originalBounds = new Rect(mActivity.getBounds());
+        final int originalDpi = mActivity.getConfiguration().densityDpi;
+
+        // Move the non-resizable activity to the new display.
+        mStack.reparent(newDisplay.mDisplayContent, true /* onTop */);
+
+        assertEquals(originalBounds.width(), mActivity.getBounds().width());
+        assertEquals(originalBounds.height(), mActivity.getBounds().height());
+        assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
+        assertTrue(mActivity.inSizeCompatMode());
+    }
+
+    @Test
+    public void testFixedScreenBoundsWhenDisplaySizeChanged() {
+        setUpApp(new TestActivityDisplay.Builder(mService, 1000, 2500).build());
+        prepareUnresizable(-1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+        assertFalse(mActivity.inSizeCompatMode());
+
+        final Rect origBounds = new Rect(mActivity.getBounds());
+
+        // Change the size of current display.
+        resizeDisplay(mStack.getDisplay(), 1000, 2000);
+        ensureActivityConfiguration();
+
+        assertEquals(origBounds.width(), mActivity.getWindowConfiguration().getBounds().width());
+        assertEquals(origBounds.height(), mActivity.getWindowConfiguration().getBounds().height());
+        assertTrue(mActivity.inSizeCompatMode());
+
+        // Change display size to a different orientation
+        resizeDisplay(mStack.getDisplay(), 2000, 1000);
+        ensureActivityConfiguration();
+        assertEquals(origBounds.width(), mActivity.getWindowConfiguration().getBounds().width());
+        assertEquals(origBounds.height(), mActivity.getWindowConfiguration().getBounds().height());
+    }
+
+    @Test
+    public void testLetterboxFullscreenBounds() {
+        setUpApp(new TestActivityDisplay.Builder(mService, 1000, 2500).build());
+
+        // Fill out required fields on default display since WM-side is mocked out
+        prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
+        assertFalse(mActivity.inSizeCompatMode());
+        assertTrue(mActivity.getBounds().width() > mActivity.getBounds().height());
+    }
+
+    @Test
+    public void testMoveToDifferentOrientDisplay() {
+        setUpApp(new TestActivityDisplay.Builder(mService, 1000, 2500).build());
+
+        final ActivityDisplay newDisplay =
+                new TestActivityDisplay.Builder(mService, 2000, 1000)
+                        .setCanRotate(false).build();
+
+        prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+        assertFalse(mActivity.inSizeCompatMode());
+
+        final Rect origBounds = new Rect(mActivity.getBounds());
+
+        // Move the non-resizable activity to the new display.
+        mStack.reparent(newDisplay.mDisplayContent, true /* onTop */);
+        ensureActivityConfiguration();
+        assertEquals(origBounds.width(), mActivity.getWindowConfiguration().getBounds().width());
+        assertEquals(origBounds.height(), mActivity.getWindowConfiguration().getBounds().height());
+        assertTrue(mActivity.inSizeCompatMode());
+    }
+
+    @Test
+    public void testFixedOrientRotateCutoutDisplay() {
+        // Create a display with a notch/cutout
+        setUpApp(new TestActivityDisplay.Builder(mService, 1000, 2500).setNotch(60).build());
+        prepareUnresizable(1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+
+        final Rect origBounds = new Rect(mActivity.getBounds());
+        final Rect origAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
+
+        // Rotate the display
+        Configuration c = new Configuration();
+        mStack.getDisplay().mDisplayContent.getDisplayRotation().setRotation(ROTATION_270);
+        mStack.getDisplay().mDisplayContent.computeScreenConfiguration(c);
+        mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
+
+        // Make sure the app size is the same
+        assertEquals(ROTATION_270, mStack.getWindowConfiguration().getRotation());
+        assertEquals(origBounds.width(), mActivity.getWindowConfiguration().getBounds().width());
+        assertEquals(origBounds.height(), mActivity.getWindowConfiguration().getBounds().height());
+        assertEquals(origAppBounds.width(),
+                mActivity.getWindowConfiguration().getAppBounds().width());
+        assertEquals(origAppBounds.height(),
+                mActivity.getWindowConfiguration().getAppBounds().height());
+    }
+
+    @Test
+    public void testFixedAspOrientChangeOrient() {
+        setUpApp(new TestActivityDisplay.Builder(mService, 1000, 2500).build());
+
+        prepareUnresizable(1.4f /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
+        assertTrue(mActivity.inSizeCompatMode());
+
+        final Rect originalBounds = new Rect(mActivity.getBounds());
+        final Rect originalAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
+
+        // Change the fixed orientation
+        mActivity.mOrientation = SCREEN_ORIENTATION_PORTRAIT;
+        mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
+        // TaskRecord's configuration actually depends on the activity config right now for
+        // pillarboxing.
+        mActivity.getTask().onRequestedOverrideConfigurationChanged(
+                mActivity.getTask().getRequestedOverrideConfiguration());
+
+        assertEquals(originalBounds.width(), mActivity.getBounds().height());
+        assertEquals(originalBounds.height(), mActivity.getBounds().width());
+        assertEquals(originalAppBounds.width(),
+                mActivity.getWindowConfiguration().getAppBounds().height());
+        assertEquals(originalAppBounds.height(),
+                mActivity.getWindowConfiguration().getAppBounds().width());
+    }
+
+    @Test
+    public void testFixedScreenLayoutSizeBits() {
+        setUpApp(new TestActivityDisplay.Builder(mService, 1000, 2500).build());
+        final int fixedScreenLayout = Configuration.SCREENLAYOUT_LONG_NO
+                | Configuration.SCREENLAYOUT_SIZE_NORMAL;
+        final int layoutMask = Configuration.SCREENLAYOUT_LONG_MASK
+                | Configuration.SCREENLAYOUT_SIZE_MASK
+                | Configuration.SCREENLAYOUT_LAYOUTDIR_MASK;
+        Configuration c = new Configuration(mTask.getRequestedOverrideConfiguration());
+        c.screenLayout = fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_LTR;
+        mTask.onRequestedOverrideConfigurationChanged(c);
+        prepareUnresizable(1.5f, SCREEN_ORIENTATION_UNSPECIFIED);
+
+        // The initial configuration should inherit from parent.
+        assertEquals(fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_LTR,
+                mActivity.getConfiguration().screenLayout & layoutMask);
+
+        mTask.getConfiguration().screenLayout = Configuration.SCREENLAYOUT_LAYOUTDIR_RTL
+                | Configuration.SCREENLAYOUT_LONG_YES | Configuration.SCREENLAYOUT_SIZE_LARGE;
+        mActivity.onConfigurationChanged(mTask.getConfiguration());
+
+        // The size and aspect ratio bits don't change, but the layout direction should be updated.
+        assertEquals(fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_RTL,
+                mActivity.getConfiguration().screenLayout & layoutMask);
+    }
+
+    @Test
+    public void testResetNonVisibleActivity() {
+        setUpApp(new TestActivityDisplay.Builder(mService, 1000, 2500).build());
+        final ActivityDisplay display = mStack.getDisplay();
+        spyOn(display);
+
+        prepareUnresizable(1.5f, SCREEN_ORIENTATION_UNSPECIFIED);
+        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.
+        when(display.getLastOverrideConfigurationChanges()).thenReturn(
+                ActivityInfo.CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION
+                        | ActivityInfo.CONFIG_WINDOW_CONFIGURATION);
+        mActivity.onConfigurationChanged(mTask.getConfiguration());
+        when(display.getLastOverrideConfigurationChanges()).thenCallRealMethod();
+        // The override configuration should not change so it is still in size compatibility mode.
+        assertTrue(mActivity.inSizeCompatMode());
+
+        // Change display density
+        final DisplayContent displayContent = mStack.getDisplay().mDisplayContent;
+        displayContent.mBaseDisplayDensity = (int) (0.7f * displayContent.mBaseDisplayDensity);
+        final Configuration c = new Configuration();
+        displayContent.computeScreenConfiguration(c);
+        mService.mAmInternal = mock(ActivityManagerInternal.class);
+        mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
+
+        // The override configuration should be reset and the activity's process will be killed.
+        assertFalse(mActivity.inSizeCompatMode());
+        verify(mActivity).restartProcessIfVisible();
+        mLockRule.runWithScissors(mService.mH, () -> { }, TimeUnit.SECONDS.toMillis(3));
+        verify(mService.mAmInternal).killProcess(
+                eq(mActivity.app.mName), eq(mActivity.app.mUid), anyString());
+    }
+
+    /**
+     * Ensures that {@link TaskStackListener} can receive callback about the activity in size
+     * compatibility mode.
+     */
+    @Test
+    public void testHandleActivitySizeCompatMode() {
+        setUpApp(new TestActivityDisplay.Builder(mService, 1000, 2000).build());
+        ActivityRecord activity = mActivity;
+        activity.setState(ActivityStack.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
+        prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+        ensureActivityConfiguration();
+        assertFalse(mActivity.inSizeCompatMode());
+
+        final ArrayList<IBinder> compatTokens = new ArrayList<>();
+        mService.getTaskChangeNotificationController().registerTaskStackListener(
+                new TaskStackListener() {
+                    @Override
+                    public void onSizeCompatModeActivityChanged(int displayId,
+                            IBinder activityToken) {
+                        compatTokens.add(activityToken);
+                    }
+                });
+
+        // Resize the display so that the activity exercises size-compat mode.
+        resizeDisplay(mStack.getDisplay(), 1000, 2500);
+
+        // Expect the exact token when the activity is in size compatibility mode.
+        assertEquals(1, compatTokens.size());
+        assertEquals(activity.appToken, compatTokens.get(0));
+
+        compatTokens.clear();
+        // Make the activity resizable again by restarting it
+        activity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
+        activity.visible = true;
+        activity.restartProcessIfVisible();
+        // The full lifecycle isn't hooked up so manually set state to resumed
+        activity.setState(ActivityStack.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
+        mStack.getDisplay().handleActivitySizeCompatModeIfNeeded(activity);
+
+        // Expect null token when switching to non-size-compat mode activity.
+        assertEquals(1, compatTokens.size());
+        assertEquals(null, compatTokens.get(0));
+    }
+
+    /**
+     * Setup {@link #mActivity} as a size-compat-mode-able activity with fixed aspect and/or
+     * orientation.
+     */
+    private void prepareUnresizable(float maxAspect, int screenOrientation) {
+        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+        mActivity.visible = true;
+        if (maxAspect >= 0) {
+            mActivity.info.maxAspectRatio = maxAspect;
+        }
+        if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+            mActivity.mOrientation = screenOrientation;
+            mActivity.info.screenOrientation = screenOrientation;
+            // TaskRecord's configuration actually depends on the activity config right now for
+            // pillarboxing.
+            mActivity.getTask().onRequestedOverrideConfigurationChanged(
+                    mActivity.getTask().getRequestedOverrideConfiguration());
+        }
+        ensureActivityConfiguration();
+    }
+
+    private void resizeDisplay(ActivityDisplay display, int width, int height) {
+        final DisplayContent displayContent = display.mDisplayContent;
+        displayContent.mBaseDisplayWidth = width;
+        displayContent.mBaseDisplayHeight = height;
+        Configuration c = new Configuration();
+        displayContent.computeScreenConfiguration(c);
+        display.onRequestedOverrideConfigurationChanged(c);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java b/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
index 9c3ff65..48ec261 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
@@ -23,12 +23,17 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
 import static org.mockito.ArgumentMatchers.any;
 
+import android.content.res.Configuration;
+import android.graphics.Insets;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManagerGlobal;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 
 class TestActivityDisplay extends ActivityDisplay {
@@ -107,4 +112,94 @@
                 .setCreateActivity(false)
                 .build();
     }
+
+    public static class Builder {
+        private final DisplayInfo mInfo;
+        private boolean mCanRotate = true;
+        private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
+        private int mPosition = POSITION_TOP;
+        private final ActivityTaskManagerService mService;
+        private boolean mSystemDecorations = false;
+
+        Builder(ActivityTaskManagerService service, int width, int height) {
+            mService = service;
+            mInfo = new DisplayInfo();
+            mService.mContext.getDisplay().getDisplayInfo(mInfo);
+            mInfo.logicalWidth = width;
+            mInfo.logicalHeight = height;
+            mInfo.logicalDensityDpi = 300;
+            mInfo.displayCutout = null;
+        }
+        Builder(ActivityTaskManagerService service, DisplayInfo info) {
+            mService = service;
+            mInfo = info;
+        }
+        Builder setSystemDecorations(boolean yes) {
+            mSystemDecorations = yes;
+            return this;
+        }
+        Builder setPosition(int position) {
+            mPosition = position;
+            return this;
+        }
+        Builder setUniqueId(String uniqueId) {
+            mInfo.uniqueId = uniqueId;
+            return this;
+        }
+        Builder setType(int type) {
+            mInfo.type = type;
+            return this;
+        }
+        Builder setOwnerUid(int ownerUid) {
+            mInfo.ownerUid = ownerUid;
+            return this;
+        }
+        Builder setNotch(int height) {
+            mInfo.displayCutout = new DisplayCutout(
+                    Insets.of(0, height, 0, 0), null, new Rect(20, 0, 80, height), null, null);
+            return this;
+        }
+        Builder setCanRotate(boolean canRotate) {
+            mCanRotate = canRotate;
+            return this;
+        }
+        Builder setWindowingMode(int windowingMode) {
+            mWindowingMode = windowingMode;
+            return this;
+        }
+        Builder setDensityDpi(int dpi) {
+            mInfo.logicalDensityDpi = dpi;
+            return this;
+        }
+        TestActivityDisplay build() {
+            final int displayId = SystemServicesTestRule.sNextDisplayId++;
+            final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
+                    mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
+            final TestActivityDisplay newDisplay;
+            synchronized (mService.mGlobalLock) {
+                newDisplay = new TestActivityDisplay(mService.mStackSupervisor, display);
+                mService.mRootActivityContainer.addChild(newDisplay, mPosition);
+            }
+            // disable the normal system decorations
+            final DisplayPolicy displayPolicy = newDisplay.mDisplayContent.getDisplayPolicy();
+            spyOn(displayPolicy);
+            if (mSystemDecorations) {
+                doReturn(true).when(newDisplay).supportsSystemDecorations();
+            } else {
+                doReturn(false).when(displayPolicy).hasNavigationBar();
+                doReturn(false).when(displayPolicy).hasStatusBar();
+                doReturn(false).when(newDisplay).supportsSystemDecorations();
+            }
+            Configuration c = new Configuration();
+            newDisplay.mDisplayContent.computeScreenConfiguration(c);
+            c.windowConfiguration.setWindowingMode(mWindowingMode);
+            newDisplay.onRequestedOverrideConfigurationChanged(c);
+            // This is a rotating display
+            if (mCanRotate) {
+                doReturn(false).when(newDisplay.mDisplayContent)
+                        .handlesOrientationChangeFromDescendant();
+            }
+            return newDisplay;
+        }
+    }
 }