1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
20 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
22 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
23 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
24 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
25 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
26 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
27 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
28 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
29 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
30 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
31 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
32 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
33 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
34 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
35 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
36 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
37 import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
38 import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
39 import static android.view.Surface.ROTATION_0;
40 import static android.view.Surface.ROTATION_180;
41 import static android.view.Surface.ROTATION_270;
42 import static android.view.Surface.ROTATION_90;
43 import static android.view.WindowInsets.Type.navigationBars;
44 import static android.view.WindowInsets.Type.statusBars;
45 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
46 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
47 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
48 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
49 
50 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
51 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
52 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
53 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
54 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
55 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
56 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
57 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
58 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
59 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
60 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
61 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
62 import static com.android.server.wm.ActivityRecord.State.DESTROYED;
63 import static com.android.server.wm.ActivityRecord.State.PAUSED;
64 import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
65 import static com.android.server.wm.ActivityRecord.State.RESUMED;
66 import static com.android.server.wm.ActivityRecord.State.STOPPED;
67 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
68 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_POSITION_MULTIPLIER_CENTER;
69 import static com.android.server.wm.WindowContainer.POSITION_TOP;
70 
71 import static com.google.common.truth.Truth.assertThat;
72 
73 import static org.junit.Assert.assertEquals;
74 import static org.junit.Assert.assertFalse;
75 import static org.junit.Assert.assertNotEquals;
76 import static org.junit.Assert.assertNotNull;
77 import static org.junit.Assert.assertNull;
78 import static org.junit.Assert.assertTrue;
79 import static org.mockito.ArgumentMatchers.any;
80 import static org.mockito.ArgumentMatchers.anyBoolean;
81 import static org.mockito.ArgumentMatchers.anyInt;
82 import static org.mockito.ArgumentMatchers.anyString;
83 import static org.mockito.ArgumentMatchers.argThat;
84 import static org.mockito.ArgumentMatchers.isNull;
85 import static org.mockito.ArgumentMatchers.same;
86 import static org.mockito.Mockito.atLeastOnce;
87 import static org.mockito.Mockito.clearInvocations;
88 import static org.mockito.Mockito.doCallRealMethod;
89 import static org.mockito.Mockito.times;
90 
91 import android.annotation.Nullable;
92 import android.app.ActivityManager;
93 import android.app.ActivityManagerInternal;
94 import android.app.WindowConfiguration;
95 import android.compat.testing.PlatformCompatChangeRule;
96 import android.content.ComponentName;
97 import android.content.pm.ActivityInfo;
98 import android.content.pm.ActivityInfo.ScreenOrientation;
99 import android.content.pm.IPackageManager;
100 import android.content.pm.PackageManager;
101 import android.content.res.Configuration;
102 import android.graphics.Rect;
103 import android.os.Binder;
104 import android.os.RemoteException;
105 import android.os.UserHandle;
106 import android.platform.test.annotations.Presubmit;
107 import android.provider.DeviceConfig;
108 import android.provider.DeviceConfig.Properties;
109 import android.view.InsetsFrameProvider;
110 import android.view.InsetsSource;
111 import android.view.InsetsState;
112 import android.view.WindowInsets;
113 import android.view.WindowManager;
114 
115 import androidx.test.filters.MediumTest;
116 
117 import com.android.internal.policy.SystemBarUtils;
118 import com.android.internal.statusbar.LetterboxDetails;
119 import com.android.server.statusbar.StatusBarManagerInternal;
120 import com.android.server.wm.DeviceStateController.DeviceState;
121 
122 import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
123 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
124 
125 import org.junit.After;
126 import org.junit.Before;
127 import org.junit.Rule;
128 import org.junit.Test;
129 import org.junit.rules.TestRule;
130 import org.junit.runner.RunWith;
131 import org.mockito.ArgumentCaptor;
132 
133 import java.util.List;
134 import java.util.function.Consumer;
135 import java.util.function.Function;
136 
137 /**
138  * Tests for Size Compatibility mode.
139  *
140  * Build/Install/Run:
141  *  atest WmTests:SizeCompatTests
142  */
143 @MediumTest
144 @Presubmit
145 @RunWith(WindowTestRunner.class)
146 public class SizeCompatTests extends WindowTestsBase {
147     private static final String CONFIG_NEVER_CONSTRAIN_DISPLAY_APIS =
148             "never_constrain_display_apis";
149     private static final String CONFIG_ALWAYS_CONSTRAIN_DISPLAY_APIS =
150             "always_constrain_display_apis";
151     private static final String CONFIG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES =
152             "never_constrain_display_apis_all_packages";
153 
154     @Rule
155     public TestRule compatChangeRule = new PlatformCompatChangeRule();
156 
157     private Task mTask;
158     private ActivityRecord mActivity;
159     private ActivityMetricsLogger mActivityMetricsLogger;
160     private Properties mInitialConstrainDisplayApisFlags;
161 
162     @Before
setUp()163     public void setUp() throws Exception {
164         mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
165         clearInvocations(mActivityMetricsLogger);
166         doReturn(mActivityMetricsLogger).when(mAtm.mTaskSupervisor).getActivityMetricsLogger();
167         mInitialConstrainDisplayApisFlags = DeviceConfig.getProperties(
168                 NAMESPACE_CONSTRAIN_DISPLAY_APIS);
169         // Provide empty default values for the configs.
170         setNeverConstrainDisplayApisFlag("", true);
171         setNeverConstrainDisplayApisAllPackagesFlag(false, true);
172         setAlwaysConstrainDisplayApisFlag("", true);
173     }
174 
175     @After
tearDown()176     public void tearDown() throws Exception {
177         DeviceConfig.setProperties(mInitialConstrainDisplayApisFlags);
178     }
179 
setUpApp(DisplayContent display)180     private void setUpApp(DisplayContent display) {
181         mTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build();
182         mActivity = mTask.getTopNonFinishingActivity();
183     }
184 
setUpDisplaySizeWithApp(int dw, int dh)185     private void setUpDisplaySizeWithApp(int dw, int dh) {
186         final TestDisplayContent.Builder builder = new TestDisplayContent.Builder(mAtm, dw, dh);
187         setUpApp(builder.build());
188     }
189 
190     @Test
testCleanLetterboxConfigListenerWhenTranslucentIsDestroyed()191     public void testCleanLetterboxConfigListenerWhenTranslucentIsDestroyed() {
192         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
193         setUpDisplaySizeWithApp(2000, 1000);
194         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
195         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
196         // Translucent Activity
197         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
198                 .setLaunchedFromUid(mActivity.getUid())
199                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
200                 .build();
201         doReturn(false).when(translucentActivity).fillsParent();
202         mTask.addChild(translucentActivity);
203 
204         translucentActivity.setState(DESTROYED, "testing");
205         translucentActivity.removeImmediately();
206 
207         assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
208     }
209 
210     @Test
testHorizontalReachabilityEnabledForTranslucentActivities()211     public void testHorizontalReachabilityEnabledForTranslucentActivities() {
212         setUpDisplaySizeWithApp(2500, 1000);
213         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
214         final LetterboxConfiguration config = mWm.mLetterboxConfiguration;
215         config.setTranslucentLetterboxingOverrideEnabled(true);
216         config.setLetterboxHorizontalPositionMultiplier(0.5f);
217         config.setIsHorizontalReachabilityEnabled(true);
218 
219         // Opaque activity
220         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
221         addWindowToActivity(mActivity);
222         mActivity.mRootWindowContainer.performSurfacePlacement();
223 
224         // Translucent Activity
225         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
226                 .setLaunchedFromUid(mActivity.getUid())
227                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
228                 .build();
229         doReturn(false).when(translucentActivity).fillsParent();
230         mTask.addChild(translucentActivity);
231 
232         spyOn(translucentActivity.mLetterboxUiController);
233         doReturn(true).when(translucentActivity.mLetterboxUiController)
234                 .shouldShowLetterboxUi(any());
235 
236         addWindowToActivity(translucentActivity);
237         translucentActivity.mRootWindowContainer.performSurfacePlacement();
238 
239         final Function<ActivityRecord, Rect> innerBoundsOf =
240                 (ActivityRecord a) -> {
241                     final Rect bounds = new Rect();
242                     a.mLetterboxUiController.getLetterboxInnerBounds(bounds);
243                     return bounds;
244                 };
245         final Runnable checkLetterboxPositions = () -> assertEquals(innerBoundsOf.apply(mActivity),
246                 innerBoundsOf.apply(translucentActivity));
247         final Runnable checkIsLeft = () -> assertThat(
248                 innerBoundsOf.apply(translucentActivity).left).isEqualTo(0);
249         final Runnable checkIsRight = () -> assertThat(
250                 innerBoundsOf.apply(translucentActivity).right).isEqualTo(2500);
251         final Runnable checkIsCentered = () -> assertThat(
252                 innerBoundsOf.apply(translucentActivity).left > 0
253                         && innerBoundsOf.apply(translucentActivity).right < 2500).isTrue();
254 
255         final Consumer<Integer> doubleClick =
256                 (Integer x) -> {
257                     mActivity.mLetterboxUiController.handleHorizontalDoubleTap(x);
258                     mActivity.mRootWindowContainer.performSurfacePlacement();
259                 };
260 
261         // Initial state
262         checkIsCentered.run();
263 
264         // Double-click left
265         doubleClick.accept(/* x */ 10);
266         checkLetterboxPositions.run();
267         checkIsLeft.run();
268 
269         // Double-click right
270         doubleClick.accept(/* x */ 1990);
271         checkLetterboxPositions.run();
272         checkIsCentered.run();
273 
274         // Double-click right
275         doubleClick.accept(/* x */ 1990);
276         checkLetterboxPositions.run();
277         checkIsRight.run();
278 
279         // Double-click left
280         doubleClick.accept(/* x */ 10);
281         checkLetterboxPositions.run();
282         checkIsCentered.run();
283     }
284 
285     @Test
testVerticalReachabilityEnabledForTranslucentActivities()286     public void testVerticalReachabilityEnabledForTranslucentActivities() {
287         setUpDisplaySizeWithApp(1000, 2500);
288         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
289         final LetterboxConfiguration config = mWm.mLetterboxConfiguration;
290         config.setTranslucentLetterboxingOverrideEnabled(true);
291         config.setLetterboxVerticalPositionMultiplier(0.5f);
292         config.setIsVerticalReachabilityEnabled(true);
293 
294         // Opaque activity
295         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
296         addWindowToActivity(mActivity);
297         mActivity.mRootWindowContainer.performSurfacePlacement();
298 
299         // Translucent Activity
300         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
301                 .setLaunchedFromUid(mActivity.getUid())
302                 .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
303                 .build();
304         doReturn(false).when(translucentActivity).fillsParent();
305         mTask.addChild(translucentActivity);
306 
307         spyOn(translucentActivity.mLetterboxUiController);
308         doReturn(true).when(translucentActivity.mLetterboxUiController)
309                 .shouldShowLetterboxUi(any());
310 
311         addWindowToActivity(translucentActivity);
312         translucentActivity.mRootWindowContainer.performSurfacePlacement();
313 
314         final Function<ActivityRecord, Rect> innerBoundsOf =
315                 (ActivityRecord a) -> {
316                     final Rect bounds = new Rect();
317                     a.mLetterboxUiController.getLetterboxInnerBounds(bounds);
318                     return bounds;
319                 };
320         final Runnable checkLetterboxPositions = () -> assertEquals(innerBoundsOf.apply(mActivity),
321                 innerBoundsOf.apply(translucentActivity));
322         final Runnable checkIsTop = () -> assertThat(
323                 innerBoundsOf.apply(translucentActivity).top).isEqualTo(0);
324         final Runnable checkIsBottom = () -> assertThat(
325                 innerBoundsOf.apply(translucentActivity).bottom).isEqualTo(2500);
326         final Runnable checkIsCentered = () -> assertThat(
327                 innerBoundsOf.apply(translucentActivity).top > 0
328                         && innerBoundsOf.apply(translucentActivity).bottom < 2500).isTrue();
329 
330         final Consumer<Integer> doubleClick =
331                 (Integer y) -> {
332                     mActivity.mLetterboxUiController.handleVerticalDoubleTap(y);
333                     mActivity.mRootWindowContainer.performSurfacePlacement();
334                 };
335 
336         // Initial state
337         checkIsCentered.run();
338 
339         // Double-click top
340         doubleClick.accept(/* y */ 10);
341         checkLetterboxPositions.run();
342         checkIsTop.run();
343 
344         // Double-click bottom
345         doubleClick.accept(/* y */ 1990);
346         checkLetterboxPositions.run();
347         checkIsCentered.run();
348 
349         // Double-click bottom
350         doubleClick.accept(/* y */ 1990);
351         checkLetterboxPositions.run();
352         checkIsBottom.run();
353 
354         // Double-click top
355         doubleClick.accept(/* y */ 10);
356         checkLetterboxPositions.run();
357         checkIsCentered.run();
358     }
359 
360     @Test
testApplyStrategyAgainWhenOpaqueIsDestroyed()361     public void testApplyStrategyAgainWhenOpaqueIsDestroyed() {
362         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
363         setUpDisplaySizeWithApp(2000, 1000);
364         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
365         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
366         // Launch another opaque activity
367         final ActivityRecord opaqueActivity = new ActivityBuilder(mAtm)
368                 .setLaunchedFromUid(mActivity.getUid())
369                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
370                 .build();
371         mTask.addChild(opaqueActivity);
372         // Transparent activity strategy not applied
373         assertFalse(opaqueActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
374 
375         // Launch translucent Activity
376         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
377                 .setLaunchedFromUid(mActivity.getUid())
378                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
379                 .build();
380         doReturn(false).when(translucentActivity).fillsParent();
381         mTask.addChild(translucentActivity);
382         // Transparent strategy applied
383         assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
384 
385         spyOn(translucentActivity.mLetterboxUiController);
386         clearInvocations(translucentActivity.mLetterboxUiController);
387 
388         // We destroy the first opaque activity
389         opaqueActivity.setState(DESTROYED, "testing");
390         opaqueActivity.removeImmediately();
391 
392         // Check that updateInheritedLetterbox() is invoked again
393         verify(translucentActivity.mLetterboxUiController).updateInheritedLetterbox();
394     }
395 
396     @Test
testResetOpaqueReferenceWhenOpaqueIsDestroyed()397     public void testResetOpaqueReferenceWhenOpaqueIsDestroyed() {
398         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
399         setUpDisplaySizeWithApp(2000, 1000);
400         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
401         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
402 
403         // Launch translucent Activity
404         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
405                 .setLaunchedFromUid(mActivity.getUid())
406                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
407                 .build();
408         doReturn(false).when(translucentActivity).fillsParent();
409         mTask.addChild(translucentActivity);
410         // Transparent strategy applied
411         assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
412         assertNotNull(translucentActivity.mLetterboxUiController.mFirstOpaqueActivityBeneath);
413 
414         spyOn(translucentActivity.mLetterboxUiController);
415         clearInvocations(translucentActivity.mLetterboxUiController);
416 
417         // We destroy the first opaque activity
418         mActivity.removeImmediately();
419 
420         // Check that updateInheritedLetterbox() is invoked again
421         verify(translucentActivity.mLetterboxUiController).updateInheritedLetterbox();
422         assertNull(translucentActivity.mLetterboxUiController.mFirstOpaqueActivityBeneath);
423     }
424 
425     @Test
testNotApplyStrategyAgainWhenOpaqueIsNotDestroyed()426     public void testNotApplyStrategyAgainWhenOpaqueIsNotDestroyed() {
427         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
428         setUpDisplaySizeWithApp(2000, 1000);
429         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
430         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
431         // Launch another opaque activity
432         final ActivityRecord opaqueActivity = new ActivityBuilder(mAtm)
433                 .setLaunchedFromUid(mActivity.getUid())
434                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
435                 .build();
436         mTask.addChild(opaqueActivity);
437         // Transparent activity strategy not applied
438         assertFalse(opaqueActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
439 
440         // Launch translucent Activity
441         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
442                 .setLaunchedFromUid(mActivity.getUid())
443                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
444                 .build();
445         doReturn(false).when(translucentActivity).fillsParent();
446         mTask.addChild(translucentActivity);
447         // Transparent strategy applied
448         assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
449 
450         spyOn(translucentActivity.mLetterboxUiController);
451         clearInvocations(translucentActivity.mLetterboxUiController);
452 
453         // Check that updateInheritedLetterbox() is invoked again
454         verify(translucentActivity.mLetterboxUiController, never()).updateInheritedLetterbox();
455     }
456 
457     @Test
testApplyStrategyToTranslucentActivities()458     public void testApplyStrategyToTranslucentActivities() {
459         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
460         setUpDisplaySizeWithApp(2000, 1000);
461         prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
462         mActivity.info.setMinAspectRatio(1.2f);
463         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
464         // Translucent Activity
465         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
466                 .setLaunchedFromUid(mActivity.getUid())
467                 .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
468                 .setMinAspectRatio(1.1f)
469                 .setMaxAspectRatio(3f)
470                 .build();
471         doReturn(false).when(translucentActivity).fillsParent();
472         mTask.addChild(translucentActivity);
473         // We check bounds
474         final Rect opaqueBounds = mActivity.getConfiguration().windowConfiguration.getBounds();
475         final Rect translucentRequestedBounds = translucentActivity.getRequestedOverrideBounds();
476         assertEquals(opaqueBounds, translucentRequestedBounds);
477         // We check orientation
478         final int translucentOrientation =
479                 translucentActivity.getRequestedConfigurationOrientation();
480         assertEquals(ORIENTATION_PORTRAIT, translucentOrientation);
481         // We check aspect ratios
482         assertEquals(1.2f, translucentActivity.getMinAspectRatio(), 0.00001f);
483         assertEquals(1.5f, translucentActivity.getMaxAspectRatio(), 0.00001f);
484     }
485 
486     @Test
testApplyStrategyToTranslucentActivitiesRetainsWindowConfigurationProperties()487     public void testApplyStrategyToTranslucentActivitiesRetainsWindowConfigurationProperties() {
488         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
489         setUpDisplaySizeWithApp(2000, 1000);
490         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
491         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
492         // Translucent Activity
493         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
494                 .setLaunchedFromUid(mActivity.getUid())
495                 .build();
496         doReturn(false).when(translucentActivity).fillsParent();
497         final Configuration requestedConfig =
498                 translucentActivity.getRequestedOverrideConfiguration();
499         final WindowConfiguration translucentWinConf = requestedConfig.windowConfiguration;
500         translucentWinConf.setActivityType(ACTIVITY_TYPE_STANDARD);
501         translucentWinConf.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
502         translucentWinConf.setDisplayWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
503         translucentWinConf.setAlwaysOnTop(true);
504         translucentActivity.onRequestedOverrideConfigurationChanged(requestedConfig);
505 
506         mTask.addChild(translucentActivity);
507 
508         // The original override of WindowConfiguration should keep.
509         assertEquals(ACTIVITY_TYPE_STANDARD, translucentActivity.getActivityType());
510         assertEquals(WINDOWING_MODE_MULTI_WINDOW, translucentWinConf.getWindowingMode());
511         assertEquals(WINDOWING_MODE_MULTI_WINDOW, translucentWinConf.getDisplayWindowingMode());
512         assertTrue(translucentWinConf.isAlwaysOnTop());
513         // Unless display is going to be rotated, it should always inherit from parent.
514         assertEquals(ROTATION_UNDEFINED, translucentWinConf.getDisplayRotation());
515     }
516 
517     @Test
testApplyStrategyToMultipleTranslucentActivities()518     public void testApplyStrategyToMultipleTranslucentActivities() {
519         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
520         setUpDisplaySizeWithApp(2000, 1000);
521         prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
522         mActivity.info.setMinAspectRatio(1.2f);
523         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
524         // Translucent Activity
525         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
526                 .setLaunchedFromUid(mActivity.getUid())
527                 .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
528                 .setMinAspectRatio(1.1f)
529                 .setMaxAspectRatio(3f)
530                 .build();
531         doReturn(false).when(translucentActivity).fillsParent();
532         mTask.addChild(translucentActivity);
533         // We check bounds
534         final Rect opaqueBounds = mActivity.getConfiguration().windowConfiguration.getBounds();
535         final Rect translucentRequestedBounds = translucentActivity.getRequestedOverrideBounds();
536         assertEquals(opaqueBounds, translucentRequestedBounds);
537         // Launch another translucent activity
538         final ActivityRecord translucentActivity2 = new ActivityBuilder(mAtm)
539                 .setLaunchedFromUid(mActivity.getUid())
540                 .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
541                 .build();
542         doReturn(false).when(translucentActivity2).fillsParent();
543         mTask.addChild(translucentActivity2);
544         // We check bounds
545         final Rect translucent2RequestedBounds = translucentActivity2.getRequestedOverrideBounds();
546         assertEquals(opaqueBounds, translucent2RequestedBounds);
547     }
548 
549     @Test
testNotApplyStrategyToTranslucentActivitiesOverEmbeddedActivities()550     public void testNotApplyStrategyToTranslucentActivitiesOverEmbeddedActivities() {
551         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
552         setUpDisplaySizeWithApp(2000, 1000);
553         mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
554         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
555         // Mock the activity as embedded without additional TaskFragment layer in the task for
556         // simplicity.
557         doReturn(true).when(mActivity).isEmbedded();
558         // Translucent Activity
559         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm).build();
560         doReturn(false).when(translucentActivity).matchParentBounds();
561         doReturn(false).when(translucentActivity).fillsParent();
562         mTask.addChild(translucentActivity);
563         // Check the strategy has not being applied
564         assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
565     }
566 
567     @Test
testTranslucentActivitiesDontGoInSizeCompatMode()568     public void testTranslucentActivitiesDontGoInSizeCompatMode() {
569         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
570         setUpDisplaySizeWithApp(2800, 1400);
571         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
572         prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
573         // Rotate to put activity in size compat mode.
574         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
575         assertTrue(mActivity.inSizeCompatMode());
576         // Rotate back
577         rotateDisplay(mActivity.mDisplayContent, ROTATION_0);
578         assertFalse(mActivity.inSizeCompatMode());
579         // We launch a transparent activity
580         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
581                 .setLaunchedFromUid(mActivity.getUid())
582                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
583                 .build();
584         doReturn(false).when(translucentActivity).fillsParent();
585         mTask.addChild(translucentActivity);
586         // It should not be in SCM
587         assertFalse(translucentActivity.inSizeCompatMode());
588         // We rotate again
589         rotateDisplay(translucentActivity.mDisplayContent, ROTATION_90);
590         assertFalse(translucentActivity.inSizeCompatMode());
591     }
592 
593     @Test
testCheckOpaqueIsLetterboxedWhenStrategyIsApplied()594     public void testCheckOpaqueIsLetterboxedWhenStrategyIsApplied() {
595         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
596         setUpDisplaySizeWithApp(2000, 1000);
597         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
598         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
599         // Translucent Activity
600         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
601                 .setLaunchedFromUid(mActivity.getUid())
602                 .build();
603         doReturn(false).when(translucentActivity).fillsParent();
604         spyOn(mActivity);
605         mTask.addChild(translucentActivity);
606         verify(mActivity).isFinishing();
607     }
608 
609     @Test
testTranslucentActivitiesWhenUnfolding()610     public void testTranslucentActivitiesWhenUnfolding() {
611         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
612         setUpDisplaySizeWithApp(2800, 1400);
613         mActivity.mDisplayContent.setIgnoreOrientationRequest(
614                 true /* ignoreOrientationRequest */);
615         mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
616                 1.0f /*letterboxVerticalPositionMultiplier*/);
617         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
618         // We launch a transparent activity
619         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
620                 .setLaunchedFromUid(mActivity.getUid())
621                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
622                 .build();
623         doReturn(false).when(translucentActivity).fillsParent();
624         mTask.addChild(translucentActivity);
625         assertEquals(translucentActivity.getBounds(), mActivity.getBounds());
626 
627         mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
628         spyOn(mActivity);
629 
630         // Halffold
631         setFoldablePosture(translucentActivity, true /* isHalfFolded */,
632                 false /* isTabletop */);
633         verify(mActivity).recomputeConfiguration();
634         assertEquals(translucentActivity.getBounds(), mActivity.getBounds());
635         clearInvocations(mActivity);
636 
637         // Unfold
638         setFoldablePosture(translucentActivity, false /* isHalfFolded */,
639                 false /* isTabletop */);
640         verify(mActivity).recomputeConfiguration();
641         assertEquals(translucentActivity.getBounds(), mActivity.getBounds());
642     }
643 
644     @Test
testTranslucentActivity_clearSizeCompatMode_inheritedCompatDisplayInsetsCleared()645     public void testTranslucentActivity_clearSizeCompatMode_inheritedCompatDisplayInsetsCleared() {
646         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
647         setUpDisplaySizeWithApp(2800, 1400);
648         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
649         prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
650         // Rotate to put activity in size compat mode.
651         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
652         assertTrue(mActivity.inSizeCompatMode());
653 
654         // We launch a transparent activity
655         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
656                 .setLaunchedFromUid(mActivity.getUid())
657                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
658                 .build();
659         doReturn(false).when(translucentActivity).fillsParent();
660         mTask.addChild(translucentActivity);
661 
662         // The transparent activity inherits the compat display insets of the opaque activity
663         // beneath it
664         assertNotNull(translucentActivity.getCompatDisplayInsets());
665 
666         // Clearing SCM should also clear the inherited compat display insets
667         translucentActivity.clearSizeCompatMode();
668         assertNull(translucentActivity.getCompatDisplayInsets());
669     }
670 
671     @Test
testRestartProcessIfVisible()672     public void testRestartProcessIfVisible() {
673         setUpDisplaySizeWithApp(1000, 2500);
674         doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);
675         mActivity.setVisibleRequested(true);
676         mActivity.setSavedState(null /* savedState */);
677         mActivity.setState(RESUMED, "testRestart");
678         prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
679 
680         final Rect originalOverrideBounds = new Rect(mActivity.getBounds());
681         resizeDisplay(mTask.mDisplayContent, 600, 1200);
682         // The visible activity should recompute configuration according to the last parent bounds.
683         mAtm.mActivityClientController.restartActivityProcessIfVisible(mActivity.token);
684 
685         assertEquals(RESTARTING_PROCESS, mActivity.getState());
686         assertNotEquals(originalOverrideBounds, mActivity.getBounds());
687 
688         // Even if the state is changed (e.g. a floating activity on top is finished and make it
689         // resume), the restart procedure should recover the state and continue to kill the process.
690         mActivity.setState(RESUMED, "anyStateChange");
691         doReturn(true).when(mSupervisor).hasScheduledRestartTimeouts(mActivity);
692         mAtm.mActivityClientController.activityStopped(mActivity.token, null /* icicle */,
693                 null /* persistentState */, null /* description */);
694         assertEquals(RESTARTING_PROCESS, mActivity.getState());
695         verify(mSupervisor).removeRestartTimeouts(mActivity);
696     }
697 
698     @Test
testFixedAspectRatioBoundsWithDecorInSquareDisplay()699     public void testFixedAspectRatioBoundsWithDecorInSquareDisplay() {
700         final int notchHeight = 100;
701         setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800).setNotch(notchHeight).build());
702 
703         final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds();
704         final float aspectRatio = 1.2f;
705         mActivity.info.setMaxAspectRatio(aspectRatio);
706         mActivity.info.setMinAspectRatio(aspectRatio);
707         prepareUnresizable(mActivity, -1f, SCREEN_ORIENTATION_UNSPECIFIED);
708         final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
709 
710         // The parent configuration doesn't change since the first resolved configuration, so the
711         // activity should fit in the parent naturally (size=583x700, appBounds=[9, 100 - 592, 800],
712         // horizontal offset = round((600 - 583) / 2) = 9)).
713         assertFitted();
714         final int offsetX = (int) ((1f + displayBounds.width() - appBounds.width()) / 2);
715         // The bounds must be horizontal centered.
716         assertEquals(offsetX, appBounds.left);
717         assertEquals(appBounds.height(), displayBounds.height() - notchHeight);
718         // Ensure the app bounds keep the declared aspect ratio.
719         assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */);
720         // The decor height should be a part of the effective bounds.
721         assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight);
722         // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
723         assertActivityMaxBoundsSandboxed();
724         // Activity max bounds ignore notch, since an app can be shown past the notch (although app
725         // is currently limited by the notch).
726         assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
727                 .isEqualTo(displayBounds.height());
728 
729         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
730         assertFitted();
731 
732         // After the orientation of activity is changed, the display is rotated, the aspect
733         // ratio should be the same (bounds=[0, 0 - 800, 583], appBounds=[100, 0 - 800, 583]).
734         assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
735         // Activity max bounds are sandboxed.
736         assertActivityMaxBoundsSandboxed();
737 
738         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
739         assertFitted();
740         // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
741         assertActivityMaxBoundsSandboxed();
742         // Activity max bounds ignore notch, since an app can be shown past the notch (although app
743         // is currently limited by the notch).
744         assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
745                 .isEqualTo(displayBounds.height());
746     }
747 
748     @Test
testFixedScreenConfigurationWhenMovingToDisplay()749     public void testFixedScreenConfigurationWhenMovingToDisplay() {
750         setUpDisplaySizeWithApp(1000, 2500);
751 
752         // Make a new less-tall display with lower density
753         final DisplayContent newDisplay =
754                 new TestDisplayContent.Builder(mAtm, 1000, 2000)
755                         .setDensityDpi(200).build();
756 
757         prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
758 
759         final Rect originalBounds = new Rect(mActivity.getBounds());
760         final int originalDpi = mActivity.getConfiguration().densityDpi;
761 
762         // Move the non-resizable activity to the new display.
763         mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
764 
765         assertEquals(originalBounds.width(), mActivity.getBounds().width());
766         assertEquals(originalBounds.height(), mActivity.getBounds().height());
767         assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
768         // Activity is sandboxed; it is in size compat mode since it is not resizable and has a
769         // max aspect ratio.
770         assertActivityMaxBoundsSandboxed();
771         assertScaled();
772     }
773 
774     @Test
testFixedScreenBoundsWhenDisplaySizeChanged()775     public void testFixedScreenBoundsWhenDisplaySizeChanged() {
776         setUpDisplaySizeWithApp(1000, 2500);
777         prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
778         final DisplayContent display = mActivity.mDisplayContent;
779         assertFitted();
780         // Activity inherits bounds from TaskDisplayArea, since not sandboxed.
781         assertMaxBoundsInheritDisplayAreaBounds();
782 
783         final Rect origBounds = new Rect(mActivity.getBounds());
784         final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
785 
786         // Change the size of current display.
787         resizeDisplay(display, 1000, 2000);
788         // The bounds should be [100, 0 - 1100, 2500].
789         assertEquals(origBounds.width(), currentBounds.width());
790         assertEquals(origBounds.height(), currentBounds.height());
791         assertScaled();
792 
793         // The scale is 2000/2500=0.8. The horizontal centered offset is (1000-(1000*0.8))/2=100.
794         final float scale = (float) display.mBaseDisplayHeight / currentBounds.height();
795         final int offsetX = (int) (display.mBaseDisplayWidth - (origBounds.width() * scale)) / 2;
796         final int screenX = mActivity.getBounds().left;
797         assertEquals(offsetX, screenX);
798 
799         // The position of configuration bounds should be in app space.
800         assertEquals(screenX, (int) (currentBounds.left * scale + 0.5f));
801         // Activity is sandboxed to the offset size compat bounds.
802         assertActivityMaxBoundsSandboxed();
803 
804         // Change display size to a different orientation
805         resizeDisplay(display, 2000, 1000);
806         // The bounds should be [800, 0 - 1800, 2500].
807         assertEquals(origBounds.width(), currentBounds.width());
808         assertEquals(origBounds.height(), currentBounds.height());
809         assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
810         assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
811         // Activity is sandboxed to the offset size compat bounds.
812         assertActivityMaxBoundsSandboxed();
813 
814         // The previous resize operation doesn't consider the rotation change after size changed.
815         // These setups apply the requested orientation to rotation as real case that the top fixed
816         // portrait activity will determine the display rotation.
817         final DisplayRotation displayRotation = display.getDisplayRotation();
818         doCallRealMethod().when(displayRotation).updateRotationUnchecked(anyBoolean());
819         // Skip unrelated layout procedures.
820         mAtm.deferWindowLayout();
821         display.reconfigureDisplayLocked();
822         displayRotation.updateOrientation(display.getOrientation(), true /* forceUpdate */);
823         display.sendNewConfiguration();
824 
825         assertEquals(Configuration.ORIENTATION_PORTRAIT, display.getConfiguration().orientation);
826         assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
827         // The size should still be in portrait [100, 0 - 1100, 2500] = 1000x2500.
828         assertEquals(origBounds.width(), currentBounds.width());
829         assertEquals(origBounds.height(), currentBounds.height());
830         assertEquals(offsetX, mActivity.getBounds().left);
831         assertScaled();
832         // Activity is sandboxed due to size compat mode.
833         assertActivityMaxBoundsSandboxed();
834 
835         final WindowState appWindow = addWindowToActivity(mActivity);
836         assertTrue(mActivity.hasSizeCompatBounds());
837         assertEquals("App window must use size compat bounds for layout in screen space",
838                 mActivity.getBounds(), appWindow.getBounds());
839     }
840 
841     @Test
testLetterboxFullscreenBoundsAndNotImeAttachable()842     public void testLetterboxFullscreenBoundsAndNotImeAttachable() {
843         final int displayWidth = 2500;
844         setUpDisplaySizeWithApp(displayWidth, 1000);
845 
846         final float maxAspect = 1.5f;
847         prepareUnresizable(mActivity, maxAspect, SCREEN_ORIENTATION_LANDSCAPE);
848         assertFitted();
849 
850         final Rect bounds = mActivity.getBounds();
851         assertEquals(bounds.width(), bounds.height() * maxAspect, 0.0001f /* delta */);
852         // The position should be horizontal centered.
853         assertEquals((displayWidth - bounds.width()) / 2, bounds.left);
854         // Activity max bounds should be sandboxed since it is letterboxed.
855         assertActivityMaxBoundsSandboxed();
856 
857         mActivity.mDisplayContent.setImeLayeringTarget(addWindowToActivity(mActivity));
858         // Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
859         assertFalse(mActivity.mDisplayContent.shouldImeAttachedToApp());
860 
861         // Recompute the natural configuration without resolving size compat configuration.
862         mActivity.clearSizeCompatMode();
863         mActivity.onConfigurationChanged(mTask.getConfiguration());
864         // It should keep non-attachable because the resolved bounds will be computed according to
865         // the aspect ratio that won't match its parent bounds.
866         assertFalse(mActivity.mDisplayContent.shouldImeAttachedToApp());
867         // Activity max bounds should be sandboxed since it is letterboxed.
868         assertActivityMaxBoundsSandboxed();
869     }
870 
871     @Test
testIsLetterboxed_activityShowsWallpaper_returnsFalse()872     public void testIsLetterboxed_activityShowsWallpaper_returnsFalse() {
873         setUpDisplaySizeWithApp(1000, 2500);
874         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
875 
876         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
877         final WindowState window = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "window");
878 
879         assertEquals(window, mActivity.findMainWindow());
880 
881         spyOn(mActivity.mLetterboxUiController);
882         doReturn(true).when(mActivity.mLetterboxUiController)
883                 .isSurfaceReadyToShow(any());
884         doReturn(true).when(mActivity.mLetterboxUiController)
885                 .isSurfaceVisible(any());
886 
887         assertTrue(mActivity.mLetterboxUiController.shouldShowLetterboxUi(
888                 mActivity.findMainWindow()));
889 
890         window.mAttrs.flags |= FLAG_SHOW_WALLPAPER;
891 
892         assertFalse(mActivity.mLetterboxUiController.shouldShowLetterboxUi(
893                 mActivity.findMainWindow()));
894     }
895 
896     @Test
testAspectRatioMatchParentBoundsAndImeAttachable()897     public void testAspectRatioMatchParentBoundsAndImeAttachable() {
898         setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2000).build());
899         prepareUnresizable(mActivity, 2f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
900         assertFitted();
901 
902         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
903         mActivity.mDisplayContent.setImeLayeringTarget(addWindowToActivity(mActivity));
904         mActivity.mDisplayContent.setImeInputTarget(
905                 mActivity.mDisplayContent.getImeTarget(IME_TARGET_LAYERING).getWindow());
906         // Because the aspect ratio of display doesn't exceed the max aspect ratio of activity.
907         // The activity should still fill its parent container and IME can attach to the activity.
908         assertTrue(mActivity.matchParentBounds());
909         assertTrue(mActivity.mDisplayContent.shouldImeAttachedToApp());
910 
911         final Rect letterboxInnerBounds = new Rect();
912         mActivity.getLetterboxInnerBounds(letterboxInnerBounds);
913         // The activity should not have letterbox.
914         assertTrue(letterboxInnerBounds.isEmpty());
915     }
916 
917     @Test
testMoveToDifferentOrientationDisplay()918     public void testMoveToDifferentOrientationDisplay() {
919         setUpDisplaySizeWithApp(1000, 2500);
920         prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
921         assertFitted();
922 
923         final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
924         final Rect currentAppBounds = mActivity.getWindowConfiguration().getAppBounds();
925         final Rect originalBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
926 
927         final int notchHeight = 100;
928         final DisplayContent newDisplay = new TestDisplayContent.Builder(mAtm, 2000, 1000)
929                 .setCanRotate(false).setNotch(notchHeight).build();
930 
931         // Move the non-resizable activity to the new display.
932         mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
933         // The configuration bounds [820, 0 - 1820, 2500] should keep the same.
934         assertEquals(originalBounds.width(), currentBounds.width());
935         assertEquals(originalBounds.height(), currentBounds.height());
936         assertScaled();
937         // Activity max bounds are sandboxed due to size compat mode on the new display.
938         assertActivityMaxBoundsSandboxed();
939 
940         final Rect newDisplayBounds = newDisplay.getWindowConfiguration().getBounds();
941         // The scaled bounds should exclude notch area (1000 - 100 == 360 * 2500 / 1000 = 900).
942         assertEquals(newDisplayBounds.height() - notchHeight,
943                 (int) ((float) mActivity.getBounds().width() * originalBounds.height()
944                         / originalBounds.width()));
945 
946         // Recompute the natural configuration in the new display.
947         mActivity.clearSizeCompatMode();
948         mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
949         // Because the display cannot rotate, the portrait activity will fit the short side of
950         // display with keeping portrait bounds [200, 0 - 700, 1000] in center.
951         assertEquals(newDisplayBounds.height(), currentBounds.height());
952         assertEquals(currentAppBounds.height() * newDisplayBounds.height()
953                 / newDisplayBounds.width(), currentAppBounds.width());
954         assertFitted();
955         // The appBounds should be [200, 100 - 700, 1000].
956         final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
957         assertEquals(currentBounds.width(), appBounds.width());
958         assertEquals(currentBounds.height() - notchHeight, appBounds.height());
959         // Activity max bounds are sandboxed due to letterboxing from orientation mismatch with
960         // display.
961         assertActivityMaxBoundsSandboxed();
962     }
963 
964     @Test
testFixedOrientationRotateCutoutDisplay()965     public void testFixedOrientationRotateCutoutDisplay() {
966         // Create a display with a notch/cutout
967         final int notchHeight = 60;
968         final int width = 1000;
969         setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500)
970                 .setNotch(notchHeight).build());
971         // Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460].
972         final float maxAspect = 1.4f;
973         prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
974 
975         final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
976         final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
977         final Rect origBounds = new Rect(currentBounds);
978         final Rect origAppBounds = new Rect(appBounds);
979 
980         // Activity is sandboxed, and bounds include the area consumed by the notch.
981         assertActivityMaxBoundsSandboxed();
982         assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds().height())
983                 .isEqualTo(Math.round(width * maxAspect) + notchHeight);
984 
985         // Although the activity is fixed orientation, force rotate the display.
986         rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
987         assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation());
988 
989         assertEquals(origBounds.width(), currentBounds.width());
990         // Make sure the app size is the same
991         assertEquals(origAppBounds.width(), appBounds.width());
992         assertEquals(origAppBounds.height(), appBounds.height());
993         // The activity is 1000x1400 and the display is 2500x1000.
994         assertScaled();
995         final float scale = mActivity.getCompatScale();
996         // The position in configuration should be in app coordinates.
997         final Rect screenBounds = mActivity.getBounds();
998         assertEquals(screenBounds.left, (int) (currentBounds.left * scale + 0.5f));
999         assertEquals(screenBounds.top, (int) (currentBounds.top * scale + 0.5f));
1000 
1001         // Activity max bounds are sandboxed due to size compat mode.
1002         assertActivityMaxBoundsSandboxed();
1003     }
1004 
1005     @Test
testFixedAspectRatioOrientationChangeOrientation()1006     public void testFixedAspectRatioOrientationChangeOrientation() {
1007         setUpDisplaySizeWithApp(1000, 2500);
1008 
1009         final float maxAspect = 1.4f;
1010         prepareUnresizable(mActivity, maxAspect, SCREEN_ORIENTATION_PORTRAIT);
1011         // The display aspect ratio 2.5 > 1.4 (max of activity), so the size is fitted.
1012         assertFitted();
1013 
1014         final Rect originalBounds = new Rect(mActivity.getBounds());
1015         final Rect originalAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
1016 
1017         assertEquals((int) (originalBounds.width() * maxAspect), originalBounds.height());
1018         // Activity is sandboxed due to fixed aspect ratio.
1019         assertActivityMaxBoundsSandboxed();
1020 
1021         // Change the fixed orientation.
1022         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
1023 
1024         assertFitted();
1025         assertEquals(originalBounds.width(), mActivity.getBounds().height());
1026         assertEquals(originalBounds.height(), mActivity.getBounds().width());
1027         assertEquals(originalAppBounds.width(),
1028                 mActivity.getWindowConfiguration().getAppBounds().height());
1029         assertEquals(originalAppBounds.height(),
1030                 mActivity.getWindowConfiguration().getAppBounds().width());
1031         // Activity is sandboxed due to fixed aspect ratio.
1032         assertActivityMaxBoundsSandboxed();
1033     }
1034 
1035     @Test
testFixedScreenLayoutSizeBits()1036     public void testFixedScreenLayoutSizeBits() {
1037         setUpDisplaySizeWithApp(1000, 2500);
1038         final int fixedScreenLayout = Configuration.SCREENLAYOUT_LONG_NO
1039                 | Configuration.SCREENLAYOUT_SIZE_NORMAL
1040                 | Configuration.SCREENLAYOUT_COMPAT_NEEDED;
1041         final int layoutMask = Configuration.SCREENLAYOUT_LONG_MASK
1042                 | Configuration.SCREENLAYOUT_SIZE_MASK
1043                 | Configuration.SCREENLAYOUT_LAYOUTDIR_MASK
1044                 | Configuration.SCREENLAYOUT_COMPAT_NEEDED;
1045         Configuration c = new Configuration(mTask.getRequestedOverrideConfiguration());
1046         c.screenLayout = fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_LTR;
1047         mTask.onRequestedOverrideConfigurationChanged(c);
1048         prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_UNSPECIFIED);
1049 
1050         // The initial configuration should inherit from parent.
1051         assertEquals(fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_LTR,
1052                 mActivity.getConfiguration().screenLayout & layoutMask);
1053 
1054         mTask.getConfiguration().screenLayout = Configuration.SCREENLAYOUT_LAYOUTDIR_RTL
1055                 | Configuration.SCREENLAYOUT_LONG_YES | Configuration.SCREENLAYOUT_SIZE_LARGE;
1056         mActivity.onConfigurationChanged(mTask.getConfiguration());
1057 
1058         // The size and aspect ratio bits don't change, but the layout direction should be updated.
1059         assertEquals(fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_RTL,
1060                 mActivity.getConfiguration().screenLayout & layoutMask);
1061     }
1062 
1063     @Test
testResetNonVisibleActivity()1064     public void testResetNonVisibleActivity() {
1065         setUpDisplaySizeWithApp(1000, 2500);
1066         prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_UNSPECIFIED);
1067         final DisplayContent display = mTask.mDisplayContent;
1068         // Resize the display so the activity is in size compatibility mode.
1069         resizeDisplay(display, 900, 1800);
1070 
1071         mActivity.setState(STOPPED, "testSizeCompatMode");
1072         mActivity.setVisibleRequested(false);
1073         mActivity.visibleIgnoringKeyguard = false;
1074         mActivity.app.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
1075         mActivity.app.computeProcessActivityState();
1076 
1077         // Simulate the display changes orientation.
1078         final Configuration rotatedConfig = rotateDisplay(display, ROTATION_90);
1079         // Size compatibility mode is able to handle orientation change so the process shouldn't be
1080         // restarted and the override configuration won't be cleared.
1081         verify(mActivity, never()).restartProcessIfVisible();
1082         assertScaled();
1083         // Activity max bounds are sandboxed due to size compat mode, even if is not visible.
1084         assertActivityMaxBoundsSandboxed();
1085 
1086         // Change display density
1087         display.mBaseDisplayDensity = (int) (0.7f * display.mBaseDisplayDensity);
1088         display.computeScreenConfiguration(rotatedConfig);
1089         mAtm.mAmInternal = mock(ActivityManagerInternal.class);
1090         display.onRequestedOverrideConfigurationChanged(rotatedConfig);
1091 
1092         // The override configuration should be reset and the activity's process will be killed.
1093         assertFitted();
1094         verify(mActivity).restartProcessIfVisible();
1095         waitHandlerIdle(mAtm.mH);
1096         verify(mAtm.mAmInternal).killProcess(
1097                 eq(mActivity.app.mName), eq(mActivity.app.mUid), anyString());
1098     }
1099 
1100     /**
1101      * Ensures that {@link TaskOrganizerController} can receive callback about the activity in size
1102      * compatibility mode.
1103      */
1104     @Test
testHandleActivitySizeCompatModeChanged()1105     public void testHandleActivitySizeCompatModeChanged() {
1106         setUpDisplaySizeWithApp(1000, 2000);
1107         doReturn(true).when(mTask).isOrganized();
1108         mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged");
1109         prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
1110         assertFitted();
1111 
1112         // Resize the display so that the activity exercises size-compat mode.
1113         resizeDisplay(mTask.mDisplayContent, 1000, 2500);
1114 
1115         // Expect the exact token when the activity is in size compatibility mode.
1116         verify(mTask).onSizeCompatActivityChanged();
1117         ActivityManager.RunningTaskInfo taskInfo = mTask.getTaskInfo();
1118 
1119         assertTrue(taskInfo.topActivityInSizeCompat);
1120 
1121         // Make the activity resizable again by restarting it
1122         clearInvocations(mTask);
1123         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1124         mActivity.setVisibleRequested(true);
1125         mActivity.restartProcessIfVisible();
1126         // The full lifecycle isn't hooked up so manually set state to resumed
1127         mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged");
1128         mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(mActivity);
1129 
1130         // Expect null token when switching to non-size-compat mode activity.
1131         verify(mTask).onSizeCompatActivityChanged();
1132         taskInfo = mTask.getTaskInfo();
1133 
1134         assertFalse(taskInfo.topActivityInSizeCompat);
1135     }
1136 
1137     @Test
testHandleActivitySizeCompatModeChangedOnDifferentTask()1138     public void testHandleActivitySizeCompatModeChangedOnDifferentTask() {
1139         setUpDisplaySizeWithApp(1000, 2000);
1140         doReturn(true).when(mTask).isOrganized();
1141         mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged");
1142         prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
1143         assertFitted();
1144 
1145         // Resize the display so that the activity exercises size-compat mode.
1146         resizeDisplay(mTask.mDisplayContent, 1000, 2500);
1147 
1148         // Expect the exact token when the activity is in size compatibility mode.
1149         verify(mTask).onSizeCompatActivityChanged();
1150         ActivityManager.RunningTaskInfo taskInfo = mTask.getTaskInfo();
1151 
1152         assertTrue(taskInfo.topActivityInSizeCompat);
1153 
1154         // Create another Task to hold another size compat activity.
1155         clearInvocations(mTask);
1156         final Task secondTask = new TaskBuilder(mSupervisor).setDisplay(mTask.getDisplayContent())
1157                 .setCreateActivity(true).build();
1158         final ActivityRecord secondActivity = secondTask.getTopNonFinishingActivity();
1159         doReturn(true).when(secondTask).isOrganized();
1160         secondActivity.setState(RESUMED,
1161                 "testHandleActivitySizeCompatModeChanged");
1162         prepareUnresizable(secondActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
1163 
1164         // Resize the display so that the activity exercises size-compat mode.
1165         resizeDisplay(mTask.mDisplayContent, 1000, 3000);
1166 
1167         // Expect the exact token when the activity is in size compatibility mode.
1168         verify(secondTask).onSizeCompatActivityChanged();
1169         verify(mTask, never()).onSizeCompatActivityChanged();
1170         taskInfo = secondTask.getTaskInfo();
1171 
1172         assertTrue(taskInfo.topActivityInSizeCompat);
1173     }
1174 
1175     @Test
testShouldCreateCompatDisplayInsetsOnResizeableTask()1176     public void testShouldCreateCompatDisplayInsetsOnResizeableTask() {
1177         setUpDisplaySizeWithApp(1000, 2500);
1178 
1179         // Make the task root resizable.
1180         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1181 
1182         // Create an activity on the same task.
1183         final ActivityRecord activity = new ActivityBuilder(mAtm)
1184                 .setTask(mTask)
1185                 .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
1186                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
1187                 .build();
1188         assertTrue(activity.shouldCreateCompatDisplayInsets());
1189 
1190         // The non-resizable activity should not be size compat because it is on a resizable task
1191         // in multi-window mode.
1192         mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
1193         assertFalse(activity.shouldCreateCompatDisplayInsets());
1194         // Activity should not be sandboxed.
1195         assertMaxBoundsInheritDisplayAreaBounds();
1196 
1197         // The non-resizable activity should not be size compat because the display support
1198         // changing windowing mode from fullscreen to freeform.
1199         mTask.mDisplayContent.getDefaultTaskDisplayArea()
1200                 .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
1201         mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
1202         assertFalse(activity.shouldCreateCompatDisplayInsets());
1203         // Activity should not be sandboxed.
1204         assertMaxBoundsInheritDisplayAreaBounds();
1205     }
1206 
1207     @Test
testShouldCreateCompatDisplayInsetsWhenUnresizeableAndSupportsSizeChangesTrue()1208     public void testShouldCreateCompatDisplayInsetsWhenUnresizeableAndSupportsSizeChangesTrue() {
1209         setUpDisplaySizeWithApp(1000, 2500);
1210 
1211         // Make the task root resizable.
1212         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1213 
1214         // Create an activity on the same task.
1215         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */true,
1216                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
1217         assertFalse(activity.shouldCreateCompatDisplayInsets());
1218     }
1219 
1220     @Test
testShouldCreateCompatDisplayInsetsWhenUnresizeableAndSupportsSizeChangesFalse()1221     public void testShouldCreateCompatDisplayInsetsWhenUnresizeableAndSupportsSizeChangesFalse() {
1222         setUpDisplaySizeWithApp(1000, 2500);
1223 
1224         // Make the task root resizable.
1225         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1226 
1227         // Create an activity on the same task.
1228         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
1229                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
1230         assertTrue(activity.shouldCreateCompatDisplayInsets());
1231     }
1232 
1233     @Test
testShouldCreateCompatDisplayInsetsWhenResizeableAndSupportsSizeChangesFalse()1234     public void testShouldCreateCompatDisplayInsetsWhenResizeableAndSupportsSizeChangesFalse() {
1235         setUpDisplaySizeWithApp(1000, 2500);
1236 
1237         // Make the task root resizable.
1238         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1239 
1240         // Create an activity on the same task.
1241         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
1242                 RESIZE_MODE_RESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
1243         assertFalse(activity.shouldCreateCompatDisplayInsets());
1244     }
1245 
1246     @Test
1247     public void
testShouldCreateCompatDisplayInsetsWhenUnfixedOrientationSupportsSizeChangesFalse()1248             testShouldCreateCompatDisplayInsetsWhenUnfixedOrientationSupportsSizeChangesFalse() {
1249         setUpDisplaySizeWithApp(1000, 2500);
1250 
1251         // Make the task root resizable.
1252         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1253 
1254         // Create an activity on the same task.
1255         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
1256                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1257         assertFalse(activity.shouldCreateCompatDisplayInsets());
1258     }
1259 
1260     @Test
1261     @EnableCompatChanges({ActivityInfo.FORCE_RESIZE_APP})
testShouldCreateCompatDisplayInsetsWhenForceResizeAppOverrideSet()1262     public void testShouldCreateCompatDisplayInsetsWhenForceResizeAppOverrideSet() {
1263         setUpDisplaySizeWithApp(1000, 2500);
1264 
1265         // Make the task root resizable.
1266         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1267 
1268         // Create an activity on the same task.
1269         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
1270                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
1271         assertFalse(activity.shouldCreateCompatDisplayInsets());
1272     }
1273 
1274     @Test
1275     @EnableCompatChanges({ActivityInfo.FORCE_NON_RESIZE_APP})
testShouldCreateCompatDisplayInsetsWhenForceNonResizeOverrideSet()1276     public void testShouldCreateCompatDisplayInsetsWhenForceNonResizeOverrideSet() {
1277         setUpDisplaySizeWithApp(1000, 2500);
1278 
1279         // Make the task root resizable.
1280         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1281 
1282         // Create an activity on the same task.
1283         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */true,
1284                 RESIZE_MODE_RESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
1285         assertTrue(activity.shouldCreateCompatDisplayInsets());
1286     }
1287 
1288     @Test
1289     @EnableCompatChanges({ActivityInfo.FORCE_NON_RESIZE_APP})
testShouldCreateCompatDisplayInsetsWhenForceNonResizeSetAndUnfixedOrientation()1290     public void testShouldCreateCompatDisplayInsetsWhenForceNonResizeSetAndUnfixedOrientation() {
1291         setUpDisplaySizeWithApp(1000, 2500);
1292 
1293         // Make the task root resizable.
1294         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1295 
1296         // Create an activity on the same task.
1297         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */true,
1298                 RESIZE_MODE_RESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1299         assertTrue(activity.shouldCreateCompatDisplayInsets());
1300     }
1301 
1302     @Test
1303     @EnableCompatChanges({ActivityInfo.NEVER_SANDBOX_DISPLAY_APIS})
testNeverSandboxDisplayApis_configEnabled_sandboxingNotApplied()1304     public void testNeverSandboxDisplayApis_configEnabled_sandboxingNotApplied() {
1305         setUpDisplaySizeWithApp(1000, 1200);
1306 
1307         // Make the task root resizable.
1308         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1309 
1310         // Create an activity with a max aspect ratio on the same task.
1311         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
1312                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1313         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
1314         prepareUnresizable(activity, /* maxAspect=*/ 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
1315 
1316         // Activity max bounds should not be sandboxed, even though it is letterboxed.
1317         assertTrue(activity.isLetterboxedForFixedOrientationAndAspectRatio());
1318         assertThat(activity.getConfiguration().windowConfiguration.getMaxBounds())
1319                 .isEqualTo(activity.getDisplayArea().getBounds());
1320     }
1321 
1322     @Test
1323     @DisableCompatChanges({ActivityInfo.NEVER_SANDBOX_DISPLAY_APIS})
testNeverSandboxDisplayApis_configDisabled_sandboxingApplied()1324     public void testNeverSandboxDisplayApis_configDisabled_sandboxingApplied() {
1325         setUpDisplaySizeWithApp(1000, 1200);
1326 
1327         // Make the task root resizable.
1328         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1329 
1330         // Create an activity with a max aspect ratio on the same task.
1331         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
1332                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1333         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
1334         prepareUnresizable(activity, /* maxAspect=*/ 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
1335 
1336         // Activity max bounds should be sandboxed due to letterboxed and the config being disabled.
1337         assertActivityMaxBoundsSandboxed(activity);
1338     }
1339 
1340     @Test
testNeverConstrainDisplayApisDeviceConfig_allPackagesFlagTrue_sandboxNotApplied()1341     public void testNeverConstrainDisplayApisDeviceConfig_allPackagesFlagTrue_sandboxNotApplied() {
1342         setUpDisplaySizeWithApp(1000, 1200);
1343 
1344         setNeverConstrainDisplayApisAllPackagesFlag(true, false);
1345         // Setting 'never_constrain_display_apis' as well to make sure it is ignored.
1346         setNeverConstrainDisplayApisFlag("com.android.other::,com.android.other2::", false);
1347 
1348         // Make the task root resizable.
1349         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1350 
1351         // Create an activity with a max aspect ratio on the same task.
1352         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
1353                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1354         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
1355         prepareUnresizable(activity, /* maxAspect=*/ 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
1356 
1357         // Activity max bounds should not be sandboxed, even though it is letterboxed.
1358         assertTrue(activity.isLetterboxedForFixedOrientationAndAspectRatio());
1359         assertThat(activity.getConfiguration().windowConfiguration.getMaxBounds())
1360                 .isEqualTo(activity.getDisplayArea().getBounds());
1361     }
1362 
1363     @Test
testNeverConstrainDisplayApisDeviceConfig_packageInRange_sandboxingNotApplied()1364     public void testNeverConstrainDisplayApisDeviceConfig_packageInRange_sandboxingNotApplied() {
1365         setUpDisplaySizeWithApp(1000, 1200);
1366 
1367         setNeverConstrainDisplayApisFlag(
1368                 "com.android.frameworks.wmtests:20:,com.android.other::,"
1369                         + "com.android.frameworks.wmtests:0:10", false);
1370 
1371         // Make the task root resizable.
1372         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1373 
1374         // Create an activity with a max aspect ratio on the same task.
1375         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
1376                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1377         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
1378         prepareUnresizable(activity, /* maxAspect=*/ 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
1379 
1380         // Activity max bounds should not be sandboxed, even though it is letterboxed.
1381         assertTrue(activity.isLetterboxedForFixedOrientationAndAspectRatio());
1382         assertThat(activity.getConfiguration().windowConfiguration.getMaxBounds())
1383                 .isEqualTo(activity.getDisplayArea().getBounds());
1384     }
1385 
1386     @Test
testNeverConstrainDisplayApisDeviceConfig_packageOutsideRange_sandboxingApplied()1387     public void testNeverConstrainDisplayApisDeviceConfig_packageOutsideRange_sandboxingApplied() {
1388         setUpDisplaySizeWithApp(1000, 1200);
1389 
1390         setNeverConstrainDisplayApisFlag("com.android.other::,com.android.frameworks.wmtests:1:5",
1391                 false);
1392 
1393         // Make the task root resizable.
1394         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1395 
1396         // Create an activity with a max aspect ratio on the same task.
1397         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
1398                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1399         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
1400         prepareUnresizable(activity, /* maxAspect=*/ 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
1401 
1402         // Activity max bounds should be sandboxed due to letterboxed and the mismatch with flag.
1403         assertActivityMaxBoundsSandboxed(activity);
1404     }
1405 
1406     @Test
testNeverConstrainDisplayApisDeviceConfig_packageNotInFlag_sandboxingApplied()1407     public void testNeverConstrainDisplayApisDeviceConfig_packageNotInFlag_sandboxingApplied() {
1408         setUpDisplaySizeWithApp(1000, 1200);
1409 
1410         setNeverConstrainDisplayApisFlag("com.android.other::,com.android.other2::", false);
1411 
1412         // Make the task root resizable.
1413         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1414 
1415         // Create an activity with a max aspect ratio on the same task.
1416         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
1417                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1418         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
1419         prepareUnresizable(activity, /* maxAspect=*/ 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
1420 
1421         // Activity max bounds should be sandboxed due to letterboxed and the mismatch with flag.
1422         assertActivityMaxBoundsSandboxed(activity);
1423     }
1424 
1425     @Test
1426     @EnableCompatChanges({ActivityInfo.ALWAYS_SANDBOX_DISPLAY_APIS})
testAlwaysSandboxDisplayApis_configEnabled_sandboxingApplied_unresizable()1427     public void testAlwaysSandboxDisplayApis_configEnabled_sandboxingApplied_unresizable() {
1428         setUpDisplaySizeWithApp(1000, 1200);
1429 
1430         // Make the task root resizable.
1431         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1432 
1433         // Create an activity with a max aspect ratio on the same task.
1434         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
1435                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1436         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
1437         prepareUnresizable(activity, /* maxAspect=*/ 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
1438 
1439         // Activity max bounds should be sandboxed due to letterboxed and the config being enabled.
1440         assertActivityMaxBoundsSandboxed(activity);
1441     }
1442 
1443     @Test
1444     @DisableCompatChanges({ActivityInfo.ALWAYS_SANDBOX_DISPLAY_APIS})
testAlwaysSandboxDisplayApis_configDisabled_sandboxingApplied()1445     public void testAlwaysSandboxDisplayApis_configDisabled_sandboxingApplied() {
1446         setUpDisplaySizeWithApp(1000, 1200);
1447 
1448         // Make the task root resizable.
1449         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1450 
1451         // Create an activity with a max aspect ratio on the same task.
1452         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
1453                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1454         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
1455         prepareUnresizable(activity, /* maxAspect=*/ 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
1456 
1457         // Activity max bounds should be sandboxed due to letterbox and the config being disabled.
1458         assertActivityMaxBoundsSandboxed(activity);
1459     }
1460 
1461     @Test
1462     @EnableCompatChanges({ActivityInfo.ALWAYS_SANDBOX_DISPLAY_APIS})
testAlwaysSandboxDisplayApis_configEnabled_sandboxingApplied_resizableSplit()1463     public void testAlwaysSandboxDisplayApis_configEnabled_sandboxingApplied_resizableSplit() {
1464         setUpDisplaySizeWithApp(1000, 2800);
1465         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1466         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
1467                 RESIZE_MODE_RESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1468         final TestSplitOrganizer organizer =
1469                 new TestSplitOrganizer(mAtm, activity.getDisplayContent());
1470 
1471         // Activity max bounds should be sandboxed due the config being enabled.
1472         assertFalse(activity.inSizeCompatMode());
1473         assertActivityMaxBoundsSandboxed(activity);
1474 
1475         // Move activity to split screen which takes half of the screen.
1476         mTask.reparent(organizer.mPrimary, POSITION_TOP,
1477                 false /*moveParents*/, "test");
1478         organizer.mPrimary.setBounds(0, 0, 1000, 1400);
1479         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
1480         assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode());
1481 
1482         // Resizable activity is sandboxed due to config being enabled.
1483         assertActivityMaxBoundsSandboxed(activity);
1484     }
1485 
1486     @Test
testAlwaysConstrainDisplayApisDeviceConfig_packageInRange_sandboxingApplied()1487     public void testAlwaysConstrainDisplayApisDeviceConfig_packageInRange_sandboxingApplied() {
1488         setUpDisplaySizeWithApp(1000, 1200);
1489 
1490         setAlwaysConstrainDisplayApisFlag(
1491                 "com.android.frameworks.wmtests:20:,com.android.other::,"
1492                         + "com.android.frameworks.wmtests:0:10", false);
1493 
1494         // Make the task root resizable.
1495         mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
1496 
1497         // Create an activity with a max aspect ratio on the same task.
1498         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
1499                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1500         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
1501         prepareUnresizable(activity, /* maxAspect=*/ 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
1502 
1503         // Resizable activity is sandboxed due to match with flag.
1504         assertActivityMaxBoundsSandboxed(activity);
1505     }
1506 
1507     @Test
1508     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1509             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
testOverrideMinAspectRatioMedium()1510     public void testOverrideMinAspectRatioMedium() {
1511         setUpDisplaySizeWithApp(1000, 1200);
1512 
1513         // Create a size compat activity on the same task.
1514         final ActivityRecord activity = new ActivityBuilder(mAtm)
1515                 .setTask(mTask)
1516                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
1517                 .setComponent(ComponentName.createRelative(mContext,
1518                         SizeCompatTests.class.getName()))
1519                 .setUid(android.os.Process.myUid())
1520                 .build();
1521 
1522         // The per-package override forces the activity into a 3:2 aspect ratio
1523         assertEquals(1200, activity.getBounds().height());
1524         assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
1525                 activity.getBounds().width(), 0.5);
1526     }
1527 
1528     @Test
1529     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1530             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
testOverrideMinAspectRatioLowerThanManifest()1531     public void testOverrideMinAspectRatioLowerThanManifest() {
1532         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, 1800)
1533                 .setNotch(200).setSystemDecorations(true).build();
1534         mTask = new TaskBuilder(mSupervisor).setDisplay(display).build();
1535 
1536         // Create a size compat activity on the same task.
1537         final ActivityRecord activity = new ActivityBuilder(mAtm)
1538                 .setTask(mTask)
1539                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
1540                 .setComponent(ComponentName.createRelative(mContext,
1541                         SizeCompatTests.class.getName()))
1542                 .setMinAspectRatio(2f)
1543                 .setUid(android.os.Process.myUid())
1544                 .build();
1545 
1546         // The per-package override should have no effect, because the manifest aspect ratio is
1547         // larger (2:1)
1548         final Rect appBounds = activity.getWindowConfiguration().getAppBounds();
1549         assertEquals("App bounds must have min aspect ratio", 2f,
1550                 (float) appBounds.height() / appBounds.width(), 0.0001f /* delta */);
1551         assertEquals("Long side must fit task",
1552                 mTask.getWindowConfiguration().getAppBounds().height(), appBounds.height());
1553         assertEquals("Bounds can include insets", mTask.getBounds().height(),
1554                 activity.getBounds().height());
1555     }
1556 
1557     @Test
1558     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1559             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
testOverrideMinAspectRatioLargerThanManifest()1560     public void testOverrideMinAspectRatioLargerThanManifest() {
1561         setUpDisplaySizeWithApp(1400, 1600);
1562 
1563         // Create a size compat activity on the same task.
1564         final ActivityRecord activity = new ActivityBuilder(mAtm)
1565                 .setTask(mTask)
1566                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
1567                 .setComponent(ComponentName.createRelative(mContext,
1568                         SizeCompatTests.class.getName()))
1569                 .setMinAspectRatio(1.1f)
1570                 .setUid(android.os.Process.myUid())
1571                 .build();
1572 
1573         // The per-package override should have no effect, because the manifest aspect ratio is
1574         // larger (2:1)
1575         assertEquals(1600, activity.getBounds().height());
1576         assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
1577                 activity.getBounds().width(), 0.5);
1578     }
1579 
1580     @Test
1581     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1582             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
testOverrideMinAspectRatioLarge()1583     public void testOverrideMinAspectRatioLarge() {
1584         setUpDisplaySizeWithApp(1500, 1600);
1585 
1586         // Create a size compat activity on the same task.
1587         final ActivityRecord activity = new ActivityBuilder(mAtm)
1588                 .setTask(mTask)
1589                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
1590                 .setComponent(ComponentName.createRelative(mContext,
1591                         SizeCompatTests.class.getName()))
1592                 .setUid(android.os.Process.myUid())
1593                 .build();
1594 
1595         // The per-package override forces the activity into a 16:9 aspect ratio
1596         assertEquals(1600, activity.getBounds().height());
1597         assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
1598                 activity.getBounds().width(), 0.5);
1599     }
1600 
1601     @Test
1602     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1603             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM,
1604             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
testOverrideMinAspectRatio_Both()1605     public void testOverrideMinAspectRatio_Both() {
1606         // If multiple override aspect ratios are set, we should use the largest one
1607 
1608         setUpDisplaySizeWithApp(1400, 1600);
1609 
1610         // Create a size compat activity on the same task.
1611         final ActivityRecord activity = new ActivityBuilder(mAtm)
1612                 .setTask(mTask)
1613                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
1614                 .setComponent(ComponentName.createRelative(mContext,
1615                         SizeCompatTests.class.getName()))
1616                 .setUid(android.os.Process.myUid())
1617                 .build();
1618 
1619         // The per-package override forces the activity into a 16:9 aspect ratio
1620         assertEquals(1600, activity.getBounds().height());
1621         assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
1622                 activity.getBounds().width(), 0.5);
1623     }
1624 
1625     @Test
1626     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1627             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
1628             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
testOverrideMinAspectRatioScreenOrientationNotSetThenChangedToPortrait()1629     public void testOverrideMinAspectRatioScreenOrientationNotSetThenChangedToPortrait() {
1630         // In this test, the activity's orientation isn't fixed to portrait, therefore the override
1631         // isn't applied.
1632 
1633         setUpDisplaySizeWithApp(1000, 1200);
1634 
1635         // Create a size compat activity on the same task.
1636         final ActivityRecord activity = new ActivityBuilder(mAtm)
1637                 .setTask(mTask)
1638                 .setComponent(ComponentName.createRelative(mContext,
1639                         SizeCompatTests.class.getName()))
1640                 .setUid(android.os.Process.myUid())
1641                 .build();
1642 
1643         // The per-package override should have no effect
1644         assertEquals(1200, activity.getBounds().height());
1645         assertEquals(1000, activity.getBounds().width());
1646 
1647         // After changing the orientation to portrait the override should be applied.
1648         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
1649         activity.clearSizeCompatMode();
1650 
1651         // The per-package override forces the activity into a 3:2 aspect ratio
1652         assertEquals(1200, activity.getBounds().height());
1653         assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
1654                 activity.getBounds().width(), 0.5);
1655     }
1656 
1657     @Test
1658     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1659             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
1660             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
testOverrideMinAspectRatioScreenOrientationLandscapeThenChangedToPortrait()1661     public void testOverrideMinAspectRatioScreenOrientationLandscapeThenChangedToPortrait() {
1662         // In this test, the activity's orientation isn't fixed to portrait, therefore the override
1663         // isn't applied.
1664 
1665         setUpDisplaySizeWithApp(1000, 1200);
1666 
1667         // Create a size compat activity on the same task.
1668         final ActivityRecord activity = new ActivityBuilder(mAtm)
1669                 .setTask(mTask)
1670                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
1671                 .setComponent(ComponentName.createRelative(mContext,
1672                         SizeCompatTests.class.getName()))
1673                 .setUid(android.os.Process.myUid())
1674                 .build();
1675 
1676         // The per-package override should have no effect
1677         assertEquals(1200, activity.getBounds().height());
1678         assertEquals(1000, activity.getBounds().width());
1679 
1680         // After changing the orientation to portrait the override should be applied.
1681         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
1682         activity.clearSizeCompatMode();
1683 
1684         // The per-package override forces the activity into a 3:2 aspect ratio
1685         assertEquals(1200, activity.getBounds().height());
1686         assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
1687                 activity.getBounds().width(), 0.5);
1688     }
1689 
1690     @Test
1691     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1692             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
1693             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
testOverrideMinAspectRatioScreenOrientationPortraitThenChangedToUnspecified()1694     public void testOverrideMinAspectRatioScreenOrientationPortraitThenChangedToUnspecified() {
1695         setUpDisplaySizeWithApp(1000, 1200);
1696 
1697         // Create a size compat activity on the same task.
1698         final ActivityRecord activity = new ActivityBuilder(mAtm)
1699                 .setTask(mTask)
1700                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
1701                 .setComponent(ComponentName.createRelative(mContext,
1702                         SizeCompatTests.class.getName()))
1703                 .setUid(android.os.Process.myUid())
1704                 .build();
1705 
1706         // The per-package override forces the activity into a 3:2 aspect ratio
1707         assertEquals(1200, activity.getBounds().height());
1708         assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
1709                 activity.getBounds().width(), 0.5);
1710 
1711         // After changing the orientation to landscape the override shouldn't be applied.
1712         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1713         activity.clearSizeCompatMode();
1714 
1715         // The per-package override should have no effect
1716         assertEquals(1200, activity.getBounds().height());
1717         assertEquals(1000, activity.getBounds().width());
1718     }
1719 
1720     @Test
1721     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1722             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
1723     @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationNotSet()1724     public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationNotSet() {
1725         setUpDisplaySizeWithApp(1000, 1200);
1726 
1727         // Create a size compat activity on the same task.
1728         final ActivityRecord activity = new ActivityBuilder(mAtm)
1729                 .setTask(mTask)
1730                 .setComponent(ComponentName.createRelative(mContext,
1731                         SizeCompatTests.class.getName()))
1732                 .setUid(android.os.Process.myUid())
1733                 .build();
1734 
1735         // The per-package override forces the activity into a 3:2 aspect ratio
1736         assertEquals(1200, activity.getBounds().height());
1737         assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
1738                 activity.getBounds().width(), 0.5);
1739     }
1740 
1741     @Test
1742     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1743             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
1744     @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationLandscape()1745     public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationLandscape() {
1746         // In this test, the activity's orientation isn't fixed to portrait, therefore the override
1747         // isn't applied.
1748 
1749         setUpDisplaySizeWithApp(1000, 1200);
1750 
1751         // Create a size compat activity on the same task.
1752         final ActivityRecord activity = new ActivityBuilder(mAtm)
1753                 .setTask(mTask)
1754                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
1755                 .setComponent(ComponentName.createRelative(mContext,
1756                         SizeCompatTests.class.getName()))
1757                 .setUid(android.os.Process.myUid())
1758                 .build();
1759 
1760         // The per-package override forces the activity into a 3:2 aspect ratio
1761         assertEquals(1000 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
1762                 activity.getBounds().height(), 0.5);
1763         assertEquals(1000, activity.getBounds().width());
1764     }
1765 
1766     @Test
1767     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
testOverrideMinAspectRatioWithoutGlobalOverride()1768     public void testOverrideMinAspectRatioWithoutGlobalOverride() {
1769         // In this test, only OVERRIDE_MIN_ASPECT_RATIO_1_5 is set, which has no effect without
1770         // OVERRIDE_MIN_ASPECT_RATIO being also set.
1771 
1772         setUpDisplaySizeWithApp(1000, 1200);
1773 
1774         // Create a size compat activity on the same task.
1775         final ActivityRecord activity = new ActivityBuilder(mAtm)
1776                 .setTask(mTask)
1777                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
1778                 .setComponent(ComponentName.createRelative(mContext,
1779                         SizeCompatTests.class.getName()))
1780                 .setUid(android.os.Process.myUid())
1781                 .build();
1782 
1783         // The per-package override should have no effect
1784         assertEquals(1200, activity.getBounds().height());
1785         assertEquals(1000, activity.getBounds().width());
1786     }
1787 
1788     @Test
1789     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1790             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
testOverrideMinAspectRatioLargeForResizableAppInSplitScreen()1791     public void testOverrideMinAspectRatioLargeForResizableAppInSplitScreen() {
1792         setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800);
1793 
1794         // Create a size compat activity on the same task.
1795         final ActivityRecord activity = new ActivityBuilder(mAtm)
1796                 .setTask(mTask)
1797                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
1798                 .setComponent(ComponentName.createRelative(mContext,
1799                         SizeCompatTests.class.getName()))
1800                 .setUid(android.os.Process.myUid())
1801                 .build();
1802 
1803         final TestSplitOrganizer organizer =
1804                 new TestSplitOrganizer(mAtm, activity.getDisplayContent());
1805 
1806         // Move activity to split screen which takes half of the screen.
1807         mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
1808         organizer.mPrimary.setBounds(0, 0, 1000, 1400);
1809         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
1810         assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode());
1811 
1812         // The per-package override forces the activity into a 16:9 aspect ratio
1813         assertEquals(1400, activity.getBounds().height());
1814         assertEquals(1400 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
1815                 activity.getBounds().width(), 0.5);
1816     }
1817 
1818     @Test
testGetLetterboxInnerBounds_noScalingApplied()1819     public void testGetLetterboxInnerBounds_noScalingApplied() {
1820         // Set up a display in portrait and ignoring orientation request.
1821         final int dw = 1400;
1822         final int dh = 2800;
1823         setUpDisplaySizeWithApp(dw, dh);
1824         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
1825 
1826         // Rotate display to landscape.
1827         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
1828 
1829         // Portrait fixed app without max aspect.
1830         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_LANDSCAPE);
1831 
1832         // Need a window to call adjustBoundsForTaskbar with.
1833         addWindowToActivity(mActivity);
1834 
1835         // App should launch in fullscreen.
1836         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
1837         assertFalse(mActivity.inSizeCompatMode());
1838 
1839         // Activity inherits max bounds from TaskDisplayArea.
1840         assertMaxBoundsInheritDisplayAreaBounds();
1841 
1842         // Rotate display to portrait.
1843         rotateDisplay(mActivity.mDisplayContent, ROTATION_0);
1844 
1845         final Rect rotatedDisplayBounds = new Rect(mActivity.mDisplayContent.getBounds());
1846         final Rect rotatedActivityBounds = new Rect(mActivity.getBounds());
1847         assertTrue(rotatedDisplayBounds.width() < rotatedDisplayBounds.height());
1848 
1849         // App should be in size compat.
1850         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
1851         assertScaled();
1852         assertThat(mActivity.inSizeCompatMode()).isTrue();
1853         assertActivityMaxBoundsSandboxed();
1854 
1855 
1856 	final int scale = dh / dw;
1857 
1858         // App bounds should be dh / scale x dw / scale
1859         assertEquals(dw, rotatedDisplayBounds.width());
1860         assertEquals(dh, rotatedDisplayBounds.height());
1861 
1862         assertEquals(dh / scale, rotatedActivityBounds.width());
1863         assertEquals(dw / scale, rotatedActivityBounds.height());
1864 
1865         // Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}.
1866         mActivity.mRootWindowContainer.performSurfacePlacement();
1867 
1868         LetterboxDetails letterboxDetails = mActivity.mLetterboxUiController.getLetterboxDetails();
1869 
1870         assertEquals(dh / scale, letterboxDetails.getLetterboxInnerBounds().width());
1871         assertEquals(dw / scale, letterboxDetails.getLetterboxInnerBounds().height());
1872 
1873         assertEquals(dw, letterboxDetails.getLetterboxFullBounds().width());
1874         assertEquals(dh, letterboxDetails.getLetterboxFullBounds().height());
1875     }
1876 
1877     @Test
1878     public void testLaunchWithFixedRotationTransform() {
1879         final int dw = 1000;
1880         final int dh = 2500;
1881         final int notchHeight = 200;
1882         setUpApp(new TestDisplayContent.Builder(mAtm, dw, dh).setNotch(notchHeight).build());
1883         addStatusBar(mActivity.mDisplayContent);
1884 
1885         mActivity.setVisible(false);
1886         mActivity.mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
1887         mActivity.mDisplayContent.mOpeningApps.add(mActivity);
1888         final float maxAspect = 1.8f;
1889         prepareUnresizable(mActivity, maxAspect, SCREEN_ORIENTATION_LANDSCAPE);
1890 
1891         assertFitted();
1892         assertTrue(mActivity.isFixedRotationTransforming());
1893         // Display keeps in original orientation.
1894         assertEquals(Configuration.ORIENTATION_PORTRAIT,
1895                 mActivity.mDisplayContent.getConfiguration().orientation);
1896         // The width should be restricted by the max aspect ratio = 1000 * 1.8 = 1800.
1897         assertEquals((int) (dw * maxAspect), mActivity.getBounds().width());
1898         // The notch is at the left side of the landscape activity. The bounds should be horizontal
1899         // centered in the remaining area [200, 0 - 2500, 1000], so its left should be
1900         // 200 + (2300 - 1800) / 2 = 450. The bounds should be [450, 0 - 2250, 1000].
1901         assertEquals(notchHeight + (dh - notchHeight - mActivity.getBounds().width()) / 2,
1902                 mActivity.getBounds().left);
1903 
1904         // The letterbox needs a main window to layout.
1905         final WindowState w = addWindowToActivity(mActivity);
1906         // Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}.
1907         mActivity.mRootWindowContainer.performSurfacePlacement();
1908         // The letterbox insets should be [450, 0 - 250, 0].
1909         assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0),
1910                 mActivity.getLetterboxInsets());
1911 
1912         final DisplayPolicy displayPolicy = mActivity.mDisplayContent.getDisplayPolicy();
1913         // The activity doesn't fill the display, so the letterbox of the rotated activity is
1914         // overlapped with the rotated content frame of status bar. Hence the status bar shouldn't
1915         // be transparent.
1916         assertFalse(displayPolicy.isFullyTransparentAllowed(w, statusBars()));
1917 
1918         // Activity is sandboxed.
1919         assertActivityMaxBoundsSandboxed();
1920 
1921         // Make the activity fill the display.
1922         prepareUnresizable(mActivity, 10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
1923         w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
1924         // Refresh the letterbox.
1925         mActivity.mRootWindowContainer.performSurfacePlacement();
1926 
1927         // The letterbox should only cover the notch area, so status bar can be transparent.
1928         assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets());
1929         assertTrue(displayPolicy.isFullyTransparentAllowed(w, statusBars()));
1930         assertActivityMaxBoundsSandboxed();
1931 
1932         // The insets state for metrics should be rotated (landscape).
1933         final InsetsState insetsState = new InsetsState();
1934         mActivity.mDisplayContent.getInsetsPolicy().getInsetsForWindowMetrics(
1935                 mActivity, insetsState);
1936         assertEquals(dh, insetsState.getDisplayFrame().width());
1937     }
1938 
1939     @Test
1940     public void testDisplayIgnoreOrientationRequest_fixedOrientationAppLaunchedLetterbox() {
1941         // Set up a display in landscape and ignoring orientation request.
1942         setUpDisplaySizeWithApp(2800, 1400);
1943         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
1944 
1945         // Portrait fixed app without max aspect.
1946         prepareUnresizable(mActivity, /* maxAspect= */ 0, SCREEN_ORIENTATION_PORTRAIT);
1947 
1948         final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
1949         final Rect activityBounds = new Rect(mActivity.getBounds());
1950 
1951         // Display shouldn't be rotated.
1952         assertEquals(SCREEN_ORIENTATION_UNSPECIFIED,
1953                 mActivity.mDisplayContent.getLastOrientation());
1954         assertTrue(displayBounds.width() > displayBounds.height());
1955 
1956         // App should launch in fixed orientation letterbox.
1957         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
1958         assertFalse(mActivity.inSizeCompatMode());
1959         assertActivityMaxBoundsSandboxed();
1960 
1961         // Activity bounds should be 700x1400 with the ratio as the display.
1962         assertEquals(displayBounds.height(), activityBounds.height());
1963         assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
1964                 activityBounds.width());
1965     }
1966 
1967     @Test
testDisplayIgnoreOrientationRequest_fixedOrientationAppRespectMinAspectRatio()1968     public void testDisplayIgnoreOrientationRequest_fixedOrientationAppRespectMinAspectRatio() {
1969         // Set up a display in landscape and ignoring orientation request.
1970         setUpDisplaySizeWithApp(2800, 1400);
1971         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
1972 
1973         // Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed
1974         // orientation letterbox.
1975         mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
1976         mActivity.info.setMinAspectRatio(3);
1977         prepareUnresizable(mActivity, /* maxAspect= */ 0, SCREEN_ORIENTATION_PORTRAIT);
1978 
1979         final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
1980         final Rect activityBounds = new Rect(mActivity.getBounds());
1981 
1982         // Display shouldn't be rotated.
1983         assertEquals(SCREEN_ORIENTATION_UNSPECIFIED,
1984                 mActivity.mDisplayContent.getLastOrientation());
1985         assertTrue(displayBounds.width() > displayBounds.height());
1986 
1987         // App should launch in fixed orientation letterbox.
1988         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
1989         assertFalse(mActivity.inSizeCompatMode());
1990 
1991         // Activity bounds should respect minimum aspect ratio for activity.
1992         assertEquals(displayBounds.height(), activityBounds.height());
1993         assertEquals((int) Math.rint(displayBounds.height()
1994                         / mActivity.info.getManifestMinAspectRatio()),
1995                 activityBounds.width());
1996     }
1997 
1998     @Test
testDisplayIgnoreOrientationRequest_fixedOrientationAppRespectMaxAspectRatio()1999     public void testDisplayIgnoreOrientationRequest_fixedOrientationAppRespectMaxAspectRatio() {
2000         // Set up a display in landscape and ignoring orientation request.
2001         setUpDisplaySizeWithApp(2800, 1400);
2002         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2003 
2004         // Portrait fixed app with max aspect ratio lower that aspect ratio override for fixed
2005         // orientation letterbox.
2006         mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(3);
2007         prepareUnresizable(mActivity, /* maxAspect= */ 2, SCREEN_ORIENTATION_PORTRAIT);
2008 
2009         final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
2010         final Rect activityBounds = new Rect(mActivity.getBounds());
2011 
2012         // Display shouldn't be rotated.
2013         assertEquals(SCREEN_ORIENTATION_UNSPECIFIED,
2014                 mActivity.mDisplayContent.getLastOrientation());
2015         assertTrue(displayBounds.width() > displayBounds.height());
2016 
2017         // App should launch in fixed orientation letterbox.
2018         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2019         assertFalse(mActivity.inSizeCompatMode());
2020 
2021         // Activity bounds should respect maximum aspect ratio for activity.
2022         assertEquals(displayBounds.height(), activityBounds.height());
2023         assertEquals((int) Math.rint(displayBounds.height()
2024                         / mActivity.info.getMaxAspectRatio()),
2025                 activityBounds.width());
2026     }
2027 
2028     @Test
testDisplayIgnoreOrientationRequest_fixedOrientationAppWithAspectRatioOverride()2029     public void testDisplayIgnoreOrientationRequest_fixedOrientationAppWithAspectRatioOverride() {
2030         // Set up a display in landscape and ignoring orientation request.
2031         setUpDisplaySizeWithApp(2800, 1400);
2032         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2033 
2034         final float fixedOrientationLetterboxAspectRatio = 1.1f;
2035         mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(
2036                 fixedOrientationLetterboxAspectRatio);
2037         prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable= */ false);
2038 
2039         final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
2040         final Rect activityBounds = new Rect(mActivity.getBounds());
2041 
2042         // Display shouldn't be rotated.
2043         assertEquals(SCREEN_ORIENTATION_UNSPECIFIED,
2044                 mActivity.mDisplayContent.getLastOrientation());
2045         assertTrue(displayBounds.width() > displayBounds.height());
2046 
2047         // App should launch in fixed orientation letterbox.
2048         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2049         assertFalse(mActivity.inSizeCompatMode());
2050 
2051         // Activity bounds should respect aspect ratio override for fixed orientation letterbox.
2052         assertEquals(displayBounds.height(), activityBounds.height());
2053         assertEquals((int) Math.rint(displayBounds.height() / fixedOrientationLetterboxAspectRatio),
2054                 activityBounds.width());
2055     }
2056 
2057     @Test
testDefaultLetterboxAspectRatioForMultiWindowMode_fixedOrientationApp()2058     public void testDefaultLetterboxAspectRatioForMultiWindowMode_fixedOrientationApp() {
2059         // Set-up display in portrait.
2060         mAtm.mDevEnableNonResizableMultiWindow = true;
2061         final int screenWidth = 1100;
2062         final int screenHeight = 2100;
2063         setUpDisplaySizeWithApp(screenWidth, screenHeight);
2064 
2065         mActivity.mDisplayContent.getWindowConfiguration()
2066                 .setAppBounds(/* left */ 0, /* top */ 0, screenWidth, screenHeight);
2067 
2068         final TestSplitOrganizer organizer =
2069                 new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
2070         // Move activity to multi-window which takes half of the screen.
2071         mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
2072         organizer.mPrimary.setBounds(0, 0, screenWidth, getExpectedSplitSize(screenHeight));
2073         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
2074         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
2075 
2076         // Unresizable portrait-only activity.
2077         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
2078 
2079         // Activity should be letterboxed with an aspect ratio of 1.01.
2080         final Rect afterBounds = mActivity.getBounds();
2081         final float actualAspectRatio = 1f * afterBounds.height() / afterBounds.width();
2082         assertEquals(LetterboxConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW,
2083                 actualAspectRatio, 0.001f);
2084         assertTrue(mActivity.areBoundsLetterboxed());
2085     }
2086 
2087     @Test
2088     public void
testDefaultLetterboxAspectRatioForMultiWindowMode_fixedOrientationAppWithMinRatio()2089             testDefaultLetterboxAspectRatioForMultiWindowMode_fixedOrientationAppWithMinRatio() {
2090         // Set-up display in portrait.
2091         mAtm.mDevEnableNonResizableMultiWindow = true;
2092         final int screenWidth = 1100;
2093         final int screenHeight = 2100;
2094         setUpDisplaySizeWithApp(screenWidth, screenHeight);
2095 
2096         mActivity.mDisplayContent.getWindowConfiguration()
2097                 .setAppBounds(/* left */ 0, /* top */ 0, screenWidth, screenHeight);
2098 
2099         // Set min aspect ratio to value greater than the default letterbox aspect ratio for
2100         // multi-window mode.
2101         final float minAspectRatio = 1.2f;
2102         mActivity.info.setMinAspectRatio(minAspectRatio);
2103 
2104         final TestSplitOrganizer organizer =
2105                 new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
2106         // Move activity to multi-window which takes half of the screen.
2107         mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
2108         organizer.mPrimary.setBounds(0, 0, screenWidth, getExpectedSplitSize(screenHeight));
2109         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
2110         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
2111 
2112         // Unresizable portrait-only activity.
2113         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
2114 
2115         // Activity should be letterboxed with the min aspect ratio requested by the app NOT the
2116         // default letterbox aspect ratio for multi-window.
2117         final Rect afterBounds = mActivity.getBounds();
2118         final float actualAspectRatio = 1f * afterBounds.height() / afterBounds.width();
2119         assertEquals(minAspectRatio, actualAspectRatio, 0.001f);
2120         assertTrue(mActivity.areBoundsLetterboxed());
2121     }
2122 
2123     @Test
testDisplayIgnoreOrientationRequest_unresizableWithCorrespondingMinAspectRatio()2124     public void testDisplayIgnoreOrientationRequest_unresizableWithCorrespondingMinAspectRatio() {
2125         // Set up a display in landscape and ignoring orientation request.
2126         setUpDisplaySizeWithApp(2800, 1400);
2127         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2128 
2129         final float fixedOrientationLetterboxAspectRatio = 1.1f;
2130         mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(
2131                 fixedOrientationLetterboxAspectRatio);
2132         mActivity.mWmService.mLetterboxConfiguration.setDefaultMinAspectRatioForUnresizableApps(
2133                 1.5f);
2134         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
2135 
2136         final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
2137         final Rect activityBounds = new Rect(mActivity.getBounds());
2138 
2139         // Display shouldn't be rotated.
2140         assertEquals(SCREEN_ORIENTATION_UNSPECIFIED,
2141                 mActivity.mDisplayContent.getLastOrientation());
2142         assertTrue(displayBounds.width() > displayBounds.height());
2143 
2144         // App should launch in fixed orientation letterbox.
2145         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2146         assertFalse(mActivity.inSizeCompatMode());
2147 
2148         // Letterbox logic should use config_letterboxDefaultMinAspectRatioForUnresizableApps over
2149         // config_fixedOrientationLetterboxAspectRatio.
2150         assertEquals(displayBounds.height(), activityBounds.height());
2151         final float defaultAspectRatio = mActivity.mWmService.mLetterboxConfiguration
2152                 .getDefaultMinAspectRatioForUnresizableApps();
2153         assertEquals(displayBounds.height() / defaultAspectRatio, activityBounds.width(), 0.5);
2154     }
2155 
2156     @Test
testComputeConfigResourceOverrides_unresizableApp()2157     public void testComputeConfigResourceOverrides_unresizableApp() {
2158         // Set up a display in landscape and ignoring orientation request.
2159         setUpDisplaySizeWithApp(2800, 1400);
2160         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2161 
2162         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
2163 
2164         final Rect activityBounds = new Rect(mActivity.getBounds());
2165 
2166         int originalScreenWidthDp = mActivity.getConfiguration().screenWidthDp;
2167         int originalScreenHeighthDp = mActivity.getConfiguration().screenHeightDp;
2168 
2169         // App should launch in fixed orientation letterbox.
2170         // Activity bounds should be 700x1400 with the ratio as the display.
2171         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2172         assertFitted();
2173         assertEquals(originalScreenWidthDp, mActivity.getConfiguration().smallestScreenWidthDp);
2174         assertTrue(originalScreenWidthDp < originalScreenHeighthDp);
2175 
2176         // Rotate display to portrait.
2177         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
2178 
2179         // After we rotate, the activity should go in the size-compat mode and report the same
2180         // configuration values.
2181         assertScaled();
2182         assertEquals(originalScreenWidthDp, mActivity.getConfiguration().smallestScreenWidthDp);
2183         assertEquals(originalScreenWidthDp, mActivity.getConfiguration().screenWidthDp);
2184         assertEquals(originalScreenHeighthDp, mActivity.getConfiguration().screenHeightDp);
2185 
2186         // Restart activity
2187         mActivity.restartProcessIfVisible();
2188 
2189         // Now configuration should be updated
2190         assertFitted();
2191         assertNotEquals(originalScreenWidthDp, mActivity.getConfiguration().screenWidthDp);
2192         assertNotEquals(originalScreenHeighthDp, mActivity.getConfiguration().screenHeightDp);
2193         assertEquals(mActivity.getConfiguration().screenWidthDp,
2194                 mActivity.getConfiguration().smallestScreenWidthDp);
2195     }
2196 
2197     @Test
2198     public void testComputeConfigResourceOverrides_resizableFixedOrientationActivity() {
2199         // Set up a display in landscape and ignoring orientation request.
2200         setUpDisplaySizeWithApp(2800, 1400);
2201         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2202 
2203         // Portrait fixed app without max aspect.
2204         prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, false /* isUnresizable */);
2205 
2206         final Rect activityBounds = new Rect(mActivity.getBounds());
2207 
2208         int originalScreenWidthDp = mActivity.getConfiguration().screenWidthDp;
2209         int originalScreenHeighthDp = mActivity.getConfiguration().screenHeightDp;
2210 
2211         // App should launch in fixed orientation letterbox.
2212         // Activity bounds should be 700x1400 with the ratio as the display.
2213         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2214         assertFitted();
2215         assertEquals(originalScreenWidthDp, mActivity.getConfiguration().smallestScreenWidthDp);
2216         assertTrue(originalScreenWidthDp < originalScreenHeighthDp);
2217 
2218         // Rotate display to portrait.
2219         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
2220 
2221         // Now configuration should be updated
2222         assertFitted();
2223         assertNotEquals(originalScreenWidthDp, mActivity.getConfiguration().screenWidthDp);
2224         assertNotEquals(originalScreenHeighthDp, mActivity.getConfiguration().screenHeightDp);
2225         assertEquals(mActivity.getConfiguration().screenWidthDp,
2226                 mActivity.getConfiguration().smallestScreenWidthDp);
2227     }
2228 
2229     @Test
2230     public void testSplitAspectRatioForUnresizablePortraitApps() {
2231         // Set up a display in landscape and ignoring orientation request.
2232         int screenWidth = 1600;
2233         int screenHeight = 1400;
2234         setUpDisplaySizeWithApp(screenWidth, screenHeight);
2235         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2236         mActivity.mWmService.mLetterboxConfiguration
2237                         .setIsSplitScreenAspectRatioForUnresizableAppsEnabled(true);
2238 
2239         mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
2240 
2241         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
2242 
2243         final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
2244         final Rect activityBounds = new Rect(mActivity.getBounds());
2245 
2246         // App should launch in fixed orientation letterbox.
2247         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2248         // Checking that there is no size compat mode.
2249         assertFitted();
2250 
2251         assertEquals(displayBounds.height(), activityBounds.height());
2252         assertTrue(activityBounds.width() < displayBounds.width() / 2);
2253 
2254         final TestSplitOrganizer organizer =
2255                 new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
2256         // Move activity to split screen which takes half of the screen.
2257         mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
2258         organizer.mPrimary.setBounds(0, 0, getExpectedSplitSize(screenWidth), screenHeight);
2259         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
2260         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
2261         // Checking that there is no size compat mode.
2262         assertFitted();
2263     }
2264 
2265     @Test
2266     public void testUserOverrideSplitScreenAspectRatioForLandscapeDisplay() {
2267         final int displayWidth = 1600;
2268         final int displayHeight = 1400;
2269         setUpDisplaySizeWithApp(displayWidth, displayHeight);
2270 
2271         float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth);
2272 
2273         testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN);
2274     }
2275 
2276     @Test
2277     public void testUserOverrideSplitScreenAspectRatioForPortraitDisplay() {
2278         final int displayWidth = 1400;
2279         final int displayHeight = 1600;
2280         setUpDisplaySizeWithApp(displayWidth, displayHeight);
2281 
2282         float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight);
2283 
2284         testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN);
2285     }
2286 
2287     @Test
2288     public void testUserOverrideDisplaySizeAspectRatioForLandscapeDisplay() {
2289         final int displayWidth = 1600;
2290         final int displayHeight = 1400;
2291         setUpDisplaySizeWithApp(displayWidth, displayHeight);
2292 
2293         float expectedAspectRatio = 1f * displayWidth / displayHeight;
2294 
2295         testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE);
2296     }
2297 
2298     @Test
2299     public void testUserOverrideDisplaySizeAspectRatioForPortraitDisplay() {
2300         final int displayWidth = 1400;
2301         final int displayHeight = 1600;
2302         setUpDisplaySizeWithApp(displayWidth, displayHeight);
2303 
2304         float expectedAspectRatio = 1f * displayHeight / displayWidth;
2305 
2306         testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE);
2307     }
2308 
2309     @Test
2310     public void testUserOverride32AspectRatioForPortraitDisplay() {
2311         setUpDisplaySizeWithApp(/* dw */ 1400, /* dh */ 1600);
2312         testUserOverrideAspectRatio(3 / 2f, USER_MIN_ASPECT_RATIO_3_2);
2313     }
2314 
2315     @Test
2316     public void testUserOverride32AspectRatioForLandscapeDisplay() {
2317         setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400);
2318         testUserOverrideAspectRatio(3 / 2f, USER_MIN_ASPECT_RATIO_3_2);
2319     }
2320 
2321     @Test
2322     public void testUserOverride43AspectRatioForPortraitDisplay() {
2323         setUpDisplaySizeWithApp(/* dw */ 1400, /* dh */ 1600);
2324         testUserOverrideAspectRatio(4 / 3f, USER_MIN_ASPECT_RATIO_4_3);
2325     }
2326 
2327     @Test
2328     public void testUserOverride43AspectRatioForLandscapeDisplay() {
2329         setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400);
2330         testUserOverrideAspectRatio(4 / 3f, USER_MIN_ASPECT_RATIO_4_3);
2331     }
2332 
2333     @Test
2334     public void testUserOverride169AspectRatioForPortraitDisplay() {
2335         setUpDisplaySizeWithApp(/* dw */ 1800, /* dh */ 1500);
2336         testUserOverrideAspectRatio(16 / 9f, USER_MIN_ASPECT_RATIO_16_9);
2337     }
2338 
2339     @Test
2340     public void testUserOverride169AspectRatioForLandscapeDisplay() {
2341         setUpDisplaySizeWithApp(/* dw */ 1500, /* dh */ 1800);
2342         testUserOverrideAspectRatio(16 / 9f, USER_MIN_ASPECT_RATIO_16_9);
2343     }
2344 
2345     @Test
2346     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
2347             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
2348     public void testUserOverrideAspectRatioOverSystemOverride() {
2349         setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400);
2350 
2351         testUserOverrideAspectRatio(false,
2352                 SCREEN_ORIENTATION_PORTRAIT,
2353                 3 / 2f,
2354                 USER_MIN_ASPECT_RATIO_3_2,
2355                 true);
2356     }
2357 
2358     @Test
2359     public void testUserOverrideAspectRatioNotEnabled() {
2360         setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400);
2361 
2362         // App aspect ratio doesn't change
2363         testUserOverrideAspectRatio(false,
2364                 SCREEN_ORIENTATION_PORTRAIT,
2365                 1f * 1600 / 1400,
2366                 USER_MIN_ASPECT_RATIO_3_2,
2367                 false);
2368     }
2369 
2370     private void testUserOverrideAspectRatio(float expectedAspectRatio,
2371             @PackageManager.UserMinAspectRatio int aspectRatio) {
2372         testUserOverrideAspectRatio(true,
2373                 SCREEN_ORIENTATION_PORTRAIT,
2374                 expectedAspectRatio,
2375                 aspectRatio,
2376                 true);
2377 
2378         testUserOverrideAspectRatio(false,
2379                 SCREEN_ORIENTATION_PORTRAIT,
2380                 expectedAspectRatio,
2381                 aspectRatio,
2382                 true);
2383 
2384         testUserOverrideAspectRatio(true,
2385                 SCREEN_ORIENTATION_LANDSCAPE,
2386                 expectedAspectRatio,
2387                 aspectRatio,
2388                 true);
2389 
2390         testUserOverrideAspectRatio(false,
2391                 SCREEN_ORIENTATION_LANDSCAPE,
2392                 expectedAspectRatio,
2393                 aspectRatio,
2394                 true);
2395     }
2396 
2397     private void testUserOverrideAspectRatio(boolean isUnresizable, int screenOrientation,
2398             float expectedAspectRatio, @PackageManager.UserMinAspectRatio int aspectRatio,
2399             boolean enabled) {
2400         final ActivityRecord activity = new ActivityBuilder(mAtm)
2401                 .setTask(mTask)
2402                 .setComponent(ComponentName.createRelative(mContext,
2403                         SizeCompatTests.class.getName()))
2404                 .setUid(android.os.Process.myUid())
2405                 .build();
2406         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2407         spyOn(activity.mWmService.mLetterboxConfiguration);
2408         doReturn(enabled).when(activity.mWmService.mLetterboxConfiguration)
2409                 .isUserAppAspectRatioSettingsEnabled();
2410         // Set user aspect ratio override
2411         final IPackageManager pm = mAtm.getPackageManager();
2412         try {
2413             doReturn(aspectRatio).when(pm)
2414                     .getUserMinAspectRatio(activity.packageName, activity.mUserId);
2415         } catch (RemoteException ignored) {
2416         }
2417 
2418         prepareLimitedBounds(activity, screenOrientation, isUnresizable);
2419 
2420         final Rect afterBounds = activity.getBounds();
2421         final int width = afterBounds.width();
2422         final int height = afterBounds.height();
2423         final float afterAspectRatio =
2424                 (float) Math.max(width, height) / (float) Math.min(width, height);
2425 
2426         assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
2427     }
2428 
2429     @Test
2430     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
2431             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
2432     public void testOverrideSplitScreenAspectRatioForUnresizablePortraitApps() {
2433         final int displayWidth = 1400;
2434         final int displayHeight = 1600;
2435         setUpDisplaySizeWithApp(displayWidth, displayHeight);
2436         final ActivityRecord activity = new ActivityBuilder(mAtm)
2437                 .setTask(mTask)
2438                 .setComponent(ComponentName.createRelative(mContext,
2439                         SizeCompatTests.class.getName()))
2440                 .setMinAspectRatio(1.1f)
2441                 .setUid(android.os.Process.myUid())
2442                 .build();
2443         // Setup Letterbox Configuration
2444         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2445         activity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
2446         // Non-resizable portrait activity
2447         prepareUnresizable(activity, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
2448         float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight);
2449         final Rect afterBounds = activity.getBounds();
2450         final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width();
2451         assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
2452     }
2453 
2454     @Test
2455     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
2456             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
2457     public void testOverrideSplitScreenAspectRatioForUnresizablePortraitAppsFromLandscape() {
2458         final int displayWidth = 1600;
2459         final int displayHeight = 1400;
2460         setUpDisplaySizeWithApp(displayWidth, displayHeight);
2461         final ActivityRecord activity = new ActivityBuilder(mAtm)
2462                 .setTask(mTask)
2463                 .setComponent(ComponentName.createRelative(mContext,
2464                         SizeCompatTests.class.getName()))
2465                 .setMinAspectRatio(1.1f)
2466                 .setUid(android.os.Process.myUid())
2467                 .build();
2468         // Setup Letterbox Configuration
2469         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2470         activity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
2471         // Non-resizable portrait activity
2472         prepareUnresizable(activity, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
2473         float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth);
2474         final Rect afterBounds = activity.getBounds();
2475         final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width();
2476         assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
2477     }
2478 
2479     @Test
2480     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
2481             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
2482     @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
2483     public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeApps() {
2484         final int displayWidth = 1400;
2485         final int displayHeight = 1600;
2486         setUpDisplaySizeWithApp(displayWidth, displayHeight);
2487         final ActivityRecord activity = new ActivityBuilder(mAtm)
2488                 .setTask(mTask)
2489                 .setComponent(ComponentName.createRelative(mContext,
2490                         SizeCompatTests.class.getName()))
2491                 .setMinAspectRatio(1.1f)
2492                 .setUid(android.os.Process.myUid())
2493                 .build();
2494         // Setup Letterbox Configuration
2495         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2496         activity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
2497         // Non-resizable portrait activity
2498         prepareUnresizable(activity, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
2499         float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight);
2500         final Rect afterBounds = activity.getBounds();
2501         final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height();
2502         assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
2503     }
2504 
2505     @Test
2506     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
2507             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
2508     @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
2509     public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeAppsFromLandscape() {
2510         final int displayWidth = 1600;
2511         final int displayHeight = 1400;
2512         setUpDisplaySizeWithApp(displayWidth, displayHeight);
2513         final ActivityRecord activity = new ActivityBuilder(mAtm)
2514                 .setTask(mTask)
2515                 .setComponent(ComponentName.createRelative(mContext,
2516                         SizeCompatTests.class.getName()))
2517                 .setMinAspectRatio(1.1f)
2518                 .setUid(android.os.Process.myUid())
2519                 .build();
2520         // Setup Letterbox Configuration
2521         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2522         activity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
2523         // Non-resizable portrait activity
2524         prepareUnresizable(activity, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
2525         float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth);
2526         final Rect afterBounds = activity.getBounds();
2527         final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height();
2528         assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
2529     }
2530 
2531     @Test
2532     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
2533             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
2534     public void testOverrideSplitScreenAspectRatio_splitScreenActivityInPortrait_notLetterboxed() {
2535         mAtm.mDevEnableNonResizableMultiWindow = true;
2536         final int screenWidth = 1800;
2537         final int screenHeight = 1000;
2538         setUpDisplaySizeWithApp(screenWidth, screenHeight);
2539         final ActivityRecord activity = new ActivityBuilder(mAtm)
2540                 .setTask(mTask)
2541                 .setComponent(ComponentName.createRelative(mContext,
2542                         SizeCompatTests.class.getName()))
2543                 .setUid(android.os.Process.myUid())
2544                 .build();
2545 
2546         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2547         // Simulate real display with top insets.
2548         final int topInset = 30;
2549         activity.mDisplayContent.getWindowConfiguration()
2550                 .setAppBounds(0, topInset, screenWidth, screenHeight);
2551 
2552         final TestSplitOrganizer organizer =
2553                 new TestSplitOrganizer(mAtm, activity.getDisplayContent());
2554         // Move activity to split screen which takes half of the screen.
2555         mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
2556         organizer.mPrimary.setBounds(0, 0, getExpectedSplitSize(screenWidth), screenHeight);
2557         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
2558         assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode());
2559 
2560         // Unresizable portrait-only activity.
2561         prepareUnresizable(activity, 3f, SCREEN_ORIENTATION_PORTRAIT);
2562 
2563         // Activity should have the aspect ratio of a split screen activity and occupy exactly one
2564         // half of the screen, so there is no letterbox
2565         float expectedAspectRatio = 1f * screenHeight / getExpectedSplitSize(screenWidth);
2566         final Rect afterBounds = activity.getBounds();
2567         final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width();
2568         assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
2569         assertFalse(activity.areBoundsLetterboxed());
2570     }
2571 
2572     @Test
2573     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
2574             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
2575     public void testOverrideSplitScreenAspectRatio_splitScreenActivityInLandscape_notLetterboxed() {
2576         mAtm.mDevEnableNonResizableMultiWindow = true;
2577         final int screenWidth = 1000;
2578         final int screenHeight = 1800;
2579         setUpDisplaySizeWithApp(screenWidth, screenHeight);
2580         final ActivityRecord activity = new ActivityBuilder(mAtm)
2581                 .setTask(mTask)
2582                 .setComponent(ComponentName.createRelative(mContext,
2583                         SizeCompatTests.class.getName()))
2584                 .setUid(android.os.Process.myUid())
2585                 .build();
2586 
2587         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2588         // Simulate real display with top insets.
2589         final int leftInset = 30;
2590         activity.mDisplayContent.getWindowConfiguration()
2591                 .setAppBounds(leftInset, 0, screenWidth, screenHeight);
2592 
2593         final TestSplitOrganizer organizer =
2594                 new TestSplitOrganizer(mAtm, activity.getDisplayContent());
2595         // Move activity to split screen which takes half of the screen.
2596         mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
2597         organizer.mPrimary.setBounds(0, 0, screenWidth, getExpectedSplitSize(screenHeight));
2598         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
2599         assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode());
2600 
2601         // Unresizable landscape-only activity.
2602         prepareUnresizable(activity, 3f, SCREEN_ORIENTATION_LANDSCAPE);
2603 
2604         // Activity should have the aspect ratio of a split screen activity and occupy exactly one
2605         // half of the screen, so there is no letterbox
2606         float expectedAspectRatio = 1f * screenWidth / getExpectedSplitSize(screenHeight);
2607         final Rect afterBounds = activity.getBounds();
2608         final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height();
2609         assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
2610         assertFalse(activity.areBoundsLetterboxed());
2611     }
2612 
2613     @Test
2614     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
2615             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE,
2616             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN})
2617     public void testOverrideMinAspectRatioExcludePortraitFullscreen() {
2618         setUpDisplaySizeWithApp(2600, 1600);
2619         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2620         mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
2621 
2622         // Create a size compat activity on the same task.
2623         final ActivityRecord activity = new ActivityBuilder(mAtm)
2624                 .setTask(mTask)
2625                 .setComponent(ComponentName.createRelative(mContext,
2626                         SizeCompatTests.class.getName()))
2627                 .setUid(android.os.Process.myUid())
2628                 .build();
2629 
2630         // Non-resizable portrait activity
2631         prepareUnresizable(activity, 0f, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
2632 
2633         // At first, OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_FULLSCREEN does not apply, because the
2634         // display is in landscape
2635         assertEquals(1600, activity.getBounds().height());
2636         assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
2637                 activity.getBounds().width(), 0.5);
2638 
2639         rotateDisplay(activity.mDisplayContent, ROTATION_90);
2640         prepareUnresizable(activity, /* maxAspect */ 0, SCREEN_ORIENTATION_PORTRAIT);
2641 
2642         // Now the display is in portrait fullscreen, so the override is applied making the content
2643         // fullscreen
2644         assertEquals(activity.getBounds(), activity.mDisplayContent.getBounds());
2645     }
2646 
2647     @Test
2648     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
2649             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE,
2650             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN})
2651     public void testOverrideMinAspectRatioExcludePortraitFullscreenNotApplied() {
2652         // In this test, the activity is not in fullscreen, so the override is not applied
2653         setUpDisplaySizeWithApp(2600, 1600);
2654         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2655         mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
2656 
2657         // Create a size compat activity on the same task.
2658         final ActivityRecord activity = new ActivityBuilder(mAtm)
2659                 .setTask(mTask)
2660                 .setComponent(ComponentName.createRelative(mContext,
2661                         SizeCompatTests.class.getName()))
2662                 .setUid(android.os.Process.myUid())
2663                 .build();
2664 
2665         final TestSplitOrganizer organizer =
2666                 new TestSplitOrganizer(mAtm, activity.getDisplayContent());
2667 
2668         // Move first activity to split screen which takes half of the screen.
2669         organizer.mPrimary.setBounds(0, 0, 1300, 1600);
2670         organizer.putTaskToPrimary(mTask, true);
2671 
2672         // Non-resizable portrait activity
2673         prepareUnresizable(activity, /* maxAspect */ 0, SCREEN_ORIENTATION_PORTRAIT);
2674 
2675         // OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_FULLSCREEN does not apply here because the
2676         // display is not in fullscreen, so OVERRIDE_MIN_ASPECT_RATIO_LARGE applies instead
2677         assertEquals(1600, activity.getBounds().height());
2678         assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
2679                 activity.getBounds().width(), 0.5);
2680     }
2681 
2682     @Test
2683     @EnableCompatChanges({ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION})
2684     public void testOverrideRespectRequestedOrientationIsEnabled_orientationIsRespected() {
2685         // Set up a display in landscape
2686         setUpDisplaySizeWithApp(2800, 1400);
2687 
2688         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */ false,
2689                 RESIZE_MODE_UNRESIZEABLE, SCREEN_ORIENTATION_PORTRAIT);
2690         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2691 
2692         // Display should be rotated.
2693         assertEquals(SCREEN_ORIENTATION_PORTRAIT, activity.mDisplayContent.getOrientation());
2694 
2695         // No size compat mode
2696         assertFalse(activity.inSizeCompatMode());
2697     }
2698 
2699     @Test
2700     @EnableCompatChanges({ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION})
2701     public void testOverrideRespectRequestedOrientationIsEnabled_multiWindow_orientationIgnored() {
2702         // Set up a display in landscape
2703         setUpDisplaySizeWithApp(2800, 1400);
2704 
2705         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */ false,
2706                 RESIZE_MODE_UNRESIZEABLE, SCREEN_ORIENTATION_PORTRAIT);
2707         TaskFragment taskFragment = activity.getTaskFragment();
2708         spyOn(taskFragment);
2709         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2710         doReturn(WINDOWING_MODE_MULTI_WINDOW).when(taskFragment).getWindowingMode();
2711 
2712         // Display should not be rotated.
2713         assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, activity.mDisplayContent.getOrientation());
2714 
2715         // No size compat mode
2716         assertFalse(activity.inSizeCompatMode());
2717     }
2718 
2719     @Test
2720     public void testSplitAspectRatioForUnresizableLandscapeApps() {
2721         // Set up a display in portrait and ignoring orientation request.
2722         int screenWidth = 1400;
2723         int screenHeight = 1600;
2724         setUpDisplaySizeWithApp(screenWidth, screenHeight);
2725         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2726         mActivity.mWmService.mLetterboxConfiguration
2727                         .setIsSplitScreenAspectRatioForUnresizableAppsEnabled(true);
2728 
2729         mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
2730 
2731         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
2732 
2733         final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
2734         final Rect activityBounds = new Rect(mActivity.getBounds());
2735 
2736         // App should launch in fixed orientation letterbox.
2737         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2738         // Checking that there is no size compat mode.
2739         assertFitted();
2740 
2741         assertEquals(displayBounds.width(), activityBounds.width());
2742         assertTrue(activityBounds.height() < displayBounds.height() / 2);
2743 
2744         final TestSplitOrganizer organizer =
2745                 new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
2746         // Move activity to split screen which takes half of the screen.
2747         mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
2748         organizer.mPrimary.setBounds(0, 0, screenWidth, getExpectedSplitSize(screenHeight));
2749         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
2750         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
2751         // Checking that there is no size compat mode.
2752         assertFitted();
2753     }
2754 
2755     @Test
2756     public void testDisplayAspectRatioForResizablePortraitApps() {
2757         // Set up a display in portrait and ignoring orientation request.
2758         int displayWidth = 1400;
2759         int displayHeight = 1600;
2760         setUpDisplaySizeWithApp(displayWidth, displayHeight);
2761         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2762         mWm.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(2f);
2763 
2764         // Enable display aspect ratio to take precedence before
2765         // fixedOrientationLetterboxAspectRatio
2766         mWm.mLetterboxConfiguration
2767                 .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
2768 
2769         // Set up resizable app in portrait
2770         prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, false /* isUnresizable */);
2771 
2772         final TestSplitOrganizer organizer =
2773                 new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
2774         // Move activity to split screen which takes half of the screen.
2775         mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
2776         organizer.mPrimary.setBounds(0, 0, displayWidth, getExpectedSplitSize(displayHeight));
2777         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
2778         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
2779 
2780         // App should launch in fixed orientation letterbox.
2781         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2782         // Checking that there is no size compat mode.
2783         assertFitted();
2784         // Check that the display aspect ratio is used by the app.
2785         final float targetMinAspectRatio = 1f * displayHeight / displayWidth;
2786         final float delta = 0.01f;
2787         assertEquals(targetMinAspectRatio, ActivityRecord
2788                 .computeAspectRatio(mActivity.getBounds()), delta);
2789     }
2790 
2791     @Test
2792     public void testDisplayAspectRatioForResizableLandscapeApps() {
2793         // Set up a display in landscape and ignoring orientation request.
2794         int displayWidth = 1600;
2795         int displayHeight = 1400;
2796         setUpDisplaySizeWithApp(displayWidth, displayHeight);
2797         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2798         mWm.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(2f);
2799 
2800         // Enable display aspect ratio to take precedence before
2801         // fixedOrientationLetterboxAspectRatio
2802         mWm.mLetterboxConfiguration
2803                 .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
2804 
2805         // Set up resizable app in landscape
2806         prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_LANDSCAPE, false /* isUnresizable */);
2807 
2808         final TestSplitOrganizer organizer =
2809                 new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
2810         // Move activity to split screen which takes half of the screen.
2811         mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
2812         organizer.mPrimary.setBounds(0, 0, getExpectedSplitSize(displayWidth), displayHeight);
2813         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
2814         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
2815 
2816         // App should launch in fixed orientation letterbox.
2817         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2818         // Checking that there is no size compat mode.
2819         assertFitted();
2820         // Check that the display aspect ratio is used by the app.
2821         final float targetMinAspectRatio = 1f * displayWidth / displayHeight;
2822         final float delta = 0.01f;
2823         assertEquals(targetMinAspectRatio, ActivityRecord
2824                 .computeAspectRatio(mActivity.getBounds()), delta);
2825     }
2826 
2827     @Test
2828     public void testDisplayAspectRatioForUnresizableLandscapeApps() {
2829         // Set up a display in portrait and ignoring orientation request.
2830         int displayWidth = 1400;
2831         int displayHeight = 1600;
2832         setUpDisplaySizeWithApp(displayWidth, displayHeight);
2833         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2834 
2835         mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
2836         // Enable display aspect ratio to take precedence before
2837         // fixedOrientationLetterboxAspectRatio
2838         mWm.mLetterboxConfiguration
2839                 .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
2840 
2841         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
2842 
2843         // App should launch in fixed orientation letterbox.
2844         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2845         // Checking that there is no size compat mode.
2846         assertFitted();
2847         // Check that the display aspect ratio is used by the app.
2848         final float targetMinAspectRatio = 1f * displayHeight / displayWidth;
2849         final float delta = 0.01f;
2850         assertEquals(targetMinAspectRatio, ActivityRecord
2851                 .computeAspectRatio(mActivity.getBounds()), delta);
2852     }
2853 
2854     @Test
2855     public void testDisplayAspectRatioForUnresizablePortraitApps() {
2856         // Set up a display in landscape and ignoring orientation request.
2857         int displayWidth = 1600;
2858         int displayHeight = 1400;
2859         setUpDisplaySizeWithApp(displayWidth, displayHeight);
2860         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2861 
2862         mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
2863         // Enable display aspect ratio to take precedence before
2864         // fixedOrientationLetterboxAspectRatio
2865         mWm.mLetterboxConfiguration
2866                 .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
2867 
2868         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
2869 
2870         // App should launch in fixed orientation letterbox.
2871         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2872         // Checking that there is no size compat mode.
2873         assertFitted();
2874         // Check that the display aspect ratio is used by the app.
2875         final float targetMinAspectRatio = 1f * displayWidth / displayHeight;
2876         final float delta = 0.01f;
2877         assertEquals(targetMinAspectRatio, ActivityRecord
2878                 .computeAspectRatio(mActivity.getBounds()), delta);
2879     }
2880 
2881     @Test
2882     public void
2883             testDisplayIgnoreOrientationRequest_orientationLetterboxBecameSizeCompatAfterRotate() {
2884         // Set up a display in landscape and ignoring orientation request.
2885         setUpDisplaySizeWithApp(2800, 1400);
2886         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2887 
2888         // Portrait fixed app without max aspect.
2889         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
2890 
2891         final Rect activityBounds = new Rect(mActivity.getBounds());
2892 
2893         // Rotate display to portrait.
2894         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
2895 
2896         final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
2897         final Rect newActivityBounds = new Rect(mActivity.getBounds());
2898         assertTrue(displayBounds.width() < displayBounds.height());
2899 
2900         // App should be in size compat.
2901         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2902         assertScaled();
2903         assertEquals(activityBounds.width(), newActivityBounds.width());
2904         assertEquals(activityBounds.height(), newActivityBounds.height());
2905         assertActivityMaxBoundsSandboxed();
2906     }
2907 
2908     @Test
2909     public void testDisplayIgnoreOrientationRequest_sizeCompatAfterRotate() {
2910         // Set up a display in portrait and ignoring orientation request.
2911         setUpDisplaySizeWithApp(1400, 2800);
2912         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2913 
2914         // Portrait fixed app without max aspect.
2915         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
2916 
2917         // App should launch in fullscreen.
2918         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2919         assertFalse(mActivity.inSizeCompatMode());
2920         // Activity inherits max bounds from TaskDisplayArea.
2921         assertMaxBoundsInheritDisplayAreaBounds();
2922 
2923         // Rotate display to landscape.
2924         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
2925 
2926         final Rect rotatedDisplayBounds = new Rect(mActivity.mDisplayContent.getBounds());
2927         final Rect rotatedActivityBounds = new Rect(mActivity.getBounds());
2928         assertTrue(rotatedDisplayBounds.width() > rotatedDisplayBounds.height());
2929 
2930         // App should be in size compat.
2931         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2932         assertScaled();
2933         assertThat(mActivity.inSizeCompatMode()).isTrue();
2934         assertActivityMaxBoundsSandboxed();
2935 
2936         // App bounds should be 700x1400 with the ratio as the display.
2937         assertEquals(rotatedDisplayBounds.height(), rotatedActivityBounds.height());
2938         assertEquals(rotatedDisplayBounds.height() * rotatedDisplayBounds.height()
2939                         / rotatedDisplayBounds.width(), rotatedActivityBounds.width());
2940     }
2941 
2942     @Test
2943     public void testDisplayIgnoreOrientationRequest_newLaunchedOrientationAppInLetterbox() {
2944         // Set up a display in landscape and ignoring orientation request.
2945         setUpDisplaySizeWithApp(2800, 1400);
2946         final DisplayContent display = mActivity.mDisplayContent;
2947         display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2948 
2949         // Portrait fixed app without max aspect.
2950         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
2951 
2952         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2953         assertFalse(mActivity.inSizeCompatMode());
2954 
2955         // Launch another portrait fixed app.
2956         spyOn(mTask);
2957         setBooted(display.mWmService.mAtmService);
2958         final ActivityRecord newActivity = new ActivityBuilder(display.mWmService.mAtmService)
2959                 .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
2960                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
2961                 .setTask(mTask)
2962                 .build();
2963 
2964         // Update with new activity requested orientation and recompute bounds with no previous
2965         // size compat cache.
2966         verify(mTask).onDescendantOrientationChanged(same(newActivity));
2967 
2968         final Rect displayBounds = new Rect(display.getBounds());
2969         final Rect taskBounds = new Rect(mTask.getBounds());
2970         final Rect newActivityBounds = new Rect(newActivity.getBounds());
2971 
2972         // Task and display bounds should be equal while activity should be letterboxed and
2973         // has 700x1400 bounds with the ratio as the display.
2974         assertTrue(newActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2975         assertFalse(newActivity.inSizeCompatMode());
2976         // Activity max bounds are sandboxed due to size compat mode.
2977         assertThat(newActivity.getConfiguration().windowConfiguration.getMaxBounds())
2978                 .isEqualTo(newActivity.getWindowConfiguration().getBounds());
2979         assertEquals(taskBounds, displayBounds);
2980         assertEquals(displayBounds.height(), newActivityBounds.height());
2981         assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
2982                 newActivityBounds.width());
2983     }
2984 
2985     @Test
2986     public void testDisplayIgnoreOrientationRequest_orientationChangedToUnspecified() {
2987         // Set up a display in landscape and ignoring orientation request.
2988         setUpDisplaySizeWithApp(2800, 1400);
2989         final DisplayContent display = mActivity.mDisplayContent;
2990         display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
2991 
2992         // Portrait fixed app without max aspect.
2993         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
2994 
2995         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
2996         assertFalse(mActivity.inSizeCompatMode());
2997 
2998         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
2999         // Activity is not in size compat mode because the orientation change request came from the
3000         // app itself
3001         assertFalse(mActivity.inSizeCompatMode());
3002         assertEquals(mActivity.getResolvedOverrideConfiguration().orientation,
3003                 Configuration.ORIENTATION_UNDEFINED);
3004     }
3005 
3006     @Test
3007     public void testDisplayIgnoreOrientationRequest_newLaunchedMaxAspectApp() {
3008         // Set up a display in landscape and ignoring orientation request.
3009         setUpDisplaySizeWithApp(2800, 1400);
3010         final DisplayContent display = mActivity.mDisplayContent;
3011         display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3012 
3013         // Portrait fixed app without max aspect.
3014         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
3015 
3016         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
3017         assertFalse(mActivity.inSizeCompatMode());
3018 
3019         // Launch another portrait fixed app with max aspect ratio as 1.3.
3020         spyOn(mTask);
3021         setBooted(display.mWmService.mAtmService);
3022         final ActivityRecord newActivity = new ActivityBuilder(display.mWmService.mAtmService)
3023                 .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
3024                 .setMaxAspectRatio(1.3f)
3025                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
3026                 .setTask(mTask)
3027                 .build();
3028 
3029         // Update with new activity requested orientation and recompute bounds with no previous
3030         // size compat cache.
3031         verify(mTask).onDescendantOrientationChanged(same(newActivity));
3032 
3033         final Rect displayBounds = new Rect(display.getBounds());
3034         final Rect taskBounds = new Rect(mTask.getBounds());
3035         final Rect newActivityBounds = new Rect(newActivity.getBounds());
3036 
3037         // Task bounds should fill parent bounds.
3038         assertEquals(displayBounds, taskBounds);
3039 
3040         // Prior and new activity max bounds are sandboxed due to letterbox.
3041         assertThat(newActivity.getConfiguration().windowConfiguration.getMaxBounds())
3042                 .isEqualTo(newActivityBounds);
3043         assertActivityMaxBoundsSandboxed();
3044 
3045         // Activity bounds should be (1400 / 1.3 = 1076)x1400 with the app requested ratio.
3046         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
3047         assertFalse(newActivity.inSizeCompatMode());
3048         assertEquals(displayBounds.height(), newActivityBounds.height());
3049         assertEquals((long) Math.rint(newActivityBounds.height()
3050                         / newActivity.info.getMaxAspectRatio()),
3051                 newActivityBounds.width());
3052     }
3053 
3054     @Test
3055     public void testDisplayIgnoreOrientationRequest_pausedAppNotLostSizeCompat() {
3056         // Set up a display in landscape and ignoring orientation request.
3057         setUpDisplaySizeWithApp(2800, 1400);
3058         final DisplayContent display = mActivity.mDisplayContent;
3059         display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3060 
3061         // Portrait fixed app.
3062         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
3063         clearInvocations(mActivity);
3064 
3065         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
3066         assertFalse(mActivity.inSizeCompatMode());
3067 
3068         // Rotate display to portrait.
3069         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
3070 
3071         // App should be in size compat.
3072         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
3073         assertScaled();
3074         assertThat(mActivity.inSizeCompatMode()).isTrue();
3075         // Activity max bounds are sandboxed due to size compat mode.
3076         assertActivityMaxBoundsSandboxed();
3077 
3078         final Rect activityBounds = new Rect(mActivity.getBounds());
3079         mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
3080 
3081         // App still in size compat, and the bounds don't change.
3082         verify(mActivity, never()).clearSizeCompatMode();
3083         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
3084         assertScaled();
3085         assertEquals(activityBounds, mActivity.getBounds());
3086         // Activity max bounds are sandboxed due to size compat.
3087         assertActivityMaxBoundsSandboxed();
3088     }
3089 
3090     @Test
3091     public void testDisplayIgnoreOrientationRequest_rotated180_notInSizeCompat() {
3092         // Set up a display in landscape and ignoring orientation request.
3093         setUpDisplaySizeWithApp(2800, 1400);
3094         final DisplayContent display = mActivity.mDisplayContent;
3095         display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3096 
3097         // Portrait fixed app.
3098         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
3099 
3100         // In fixed orientation letterbox
3101         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
3102         assertFalse(mActivity.inSizeCompatMode());
3103         assertActivityMaxBoundsSandboxed();
3104 
3105         // Rotate display to portrait.
3106         rotateDisplay(display, ROTATION_90);
3107 
3108         // App should be in size compat.
3109         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
3110         assertScaled();
3111         assertActivityMaxBoundsSandboxed();
3112 
3113         // Rotate display to landscape.
3114         rotateDisplay(display, ROTATION_180);
3115 
3116         // In activity letterbox
3117         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
3118         assertFalse(mActivity.inSizeCompatMode());
3119         assertActivityMaxBoundsSandboxed();
3120     }
3121 
3122     @Test
3123     public void testDisplayIgnoreOrientationRequestWithInsets_rotated180_notInSizeCompat() {
3124         // Set up a display in portrait with display cutout and ignoring orientation request.
3125         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, 2800)
3126                 .setNotch(75)
3127                 .build();
3128         setUpApp(display);
3129         display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3130 
3131         // Landscape fixed app.
3132         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_LANDSCAPE);
3133 
3134         // In fixed orientation letterbox
3135         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
3136         assertFalse(mActivity.inSizeCompatMode());
3137         assertActivityMaxBoundsSandboxed();
3138 
3139         // Rotate display to landscape.
3140         rotateDisplay(display, ROTATION_90);
3141 
3142         // App should be in size compat.
3143         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
3144         assertScaled();
3145         assertActivityMaxBoundsSandboxed();
3146 
3147         // Rotate display to portrait.
3148         rotateDisplay(display, ROTATION_180);
3149 
3150         // In fixed orientation letterbox
3151         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
3152         assertFalse(mActivity.inSizeCompatMode());
3153         assertActivityMaxBoundsSandboxed();
3154     }
3155 
3156     @Test
3157     public void testDisplayIgnoreOrientationRequest_disabledViaDeviceConfig_orientationRespected() {
3158         // Set up a display in landscape
3159         setUpDisplaySizeWithApp(2800, 1400);
3160 
3161         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */ false,
3162                 RESIZE_MODE_UNRESIZEABLE, SCREEN_ORIENTATION_PORTRAIT);
3163         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3164 
3165         spyOn(activity.mWmService.mLetterboxConfiguration);
3166         doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
3167                 .isIgnoreOrientationRequestAllowed();
3168 
3169         // Display should not be rotated.
3170         assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, activity.mDisplayContent.getOrientation());
3171 
3172         doReturn(false).when(activity.mWmService.mLetterboxConfiguration)
3173                 .isIgnoreOrientationRequestAllowed();
3174 
3175         // Display should be rotated.
3176         assertEquals(SCREEN_ORIENTATION_PORTRAIT, activity.mDisplayContent.getOrientation());
3177     }
3178 
3179     @Test
3180     public void testSandboxDisplayApis_unresizableAppNotSandboxed() {
3181         // Set up a display in landscape with an unresizable app.
3182         setUpDisplaySizeWithApp(2500, 1000);
3183         mActivity.mDisplayContent.setSandboxDisplayApis(false /* sandboxDisplayApis */);
3184         prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
3185         assertFitted();
3186 
3187         // Activity max bounds not be sandboxed since sandboxing is disabled.
3188         assertMaxBoundsInheritDisplayAreaBounds();
3189     }
3190 
3191     @Test
3192     public void testSandboxDisplayApis_unresizableAppSandboxed() {
3193         // Set up a display in landscape with an unresizable app.
3194         setUpDisplaySizeWithApp(2500, 1000);
3195         mActivity.mDisplayContent.setSandboxDisplayApis(true /* sandboxDisplayApis */);
3196         prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
3197         assertFitted();
3198 
3199         // Activity max bounds should be sandboxed since sandboxing is enabled.
3200         assertActivityMaxBoundsSandboxed();
3201     }
3202 
3203     @Test
3204     public void testResizableApp_notSandboxed() {
3205         // Set up a display in landscape with a fully resizable app.
3206         setUpDisplaySizeWithApp(2500, 1000);
3207         prepareLimitedBounds(mActivity, /* maxAspect= */ -1,
3208                 SCREEN_ORIENTATION_UNSPECIFIED, /* isUnresizable= */ false);
3209         assertFitted();
3210 
3211         // Activity max bounds not be sandboxed since app is fully resizable.
3212         assertMaxBoundsInheritDisplayAreaBounds();
3213     }
3214 
3215     @Test
3216     public void testResizableMaxAspectApp_notSandboxed() {
3217         // Set up a display in landscape with a fully resizable app.
3218         setUpDisplaySizeWithApp(2500, 1000);
3219         prepareLimitedBounds(mActivity, /* maxAspect= */ 1,
3220                 SCREEN_ORIENTATION_UNSPECIFIED, /* isUnresizable= */ false);
3221         assertFitted();
3222 
3223         // Activity max bounds not be sandboxed since app is fully resizable.
3224         assertMaxBoundsInheritDisplayAreaBounds();
3225     }
3226 
3227     @Test
3228     public void testResizableOrientationRequestApp_notSandboxed() {
3229         // Set up a display in landscape with a fully resizable app.
3230         setUpDisplaySizeWithApp(2500, 1000);
3231         prepareLimitedBounds(mActivity, /* maxAspect= */ -1,
3232                 SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable= */ false);
3233         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3234         assertFitted();
3235 
3236         // Activity max bounds not be sandboxed since app is fully resizable.
3237         assertMaxBoundsInheritDisplayAreaBounds();
3238     }
3239 
3240     @Test
3241     public void testResizableMaxAspectOrientationRequestApp_notSandboxed() {
3242         // Set up a display in landscape with a fully resizable app.
3243         setUpDisplaySizeWithApp(2500, 1000);
3244         prepareLimitedBounds(mActivity, /* maxAspect= */ 1,
3245                 SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable= */ false);
3246         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3247         assertFitted();
3248 
3249         // Activity max bounds not be sandboxed since app is fully resizable.
3250         assertMaxBoundsInheritDisplayAreaBounds();
3251     }
3252 
3253     @Test
3254     public void testUnresizableApp_isSandboxed() {
3255         // Set up a display in landscape with a fully resizable app.
3256         setUpDisplaySizeWithApp(2500, 1000);
3257         prepareLimitedBounds(mActivity, /* maxAspect= */ -1,
3258                 SCREEN_ORIENTATION_UNSPECIFIED, /* isUnresizable= */ true);
3259         assertFitted();
3260 
3261         // Activity max bounds are sandboxed since app may enter size compat mode.
3262         assertActivityMaxBoundsSandboxed();
3263         assertFalse(mActivity.inSizeCompatMode());
3264     }
3265 
3266     @Test
3267     public void testUnresizableMaxAspectApp_isSandboxed() {
3268         // Set up a display in landscape with a fully resizable app.
3269         setUpDisplaySizeWithApp(2500, 1000);
3270         prepareLimitedBounds(mActivity, /* maxAspect= */ 1,
3271                 SCREEN_ORIENTATION_UNSPECIFIED, /* isUnresizable= */ true);
3272         assertFitted();
3273 
3274         // Activity max bounds are sandboxed since app may enter size compat mode.
3275         assertActivityMaxBoundsSandboxed();
3276         assertFalse(mActivity.inSizeCompatMode());
3277         assertTrue(mActivity.shouldCreateCompatDisplayInsets());
3278 
3279         // Resize display to half the width.
3280         resizeDisplay(mActivity.getDisplayContent(), 500, 1000);
3281 
3282         // Activity now in size compat mode.
3283         assertActivityMaxBoundsSandboxed();
3284         assertTrue(mActivity.inSizeCompatMode());
3285     }
3286 
3287     @Test
3288     public void testTaskDisplayAreaNotFillDisplay() {
3289         setUpDisplaySizeWithApp(1400, 2800);
3290         final DisplayContent display = mActivity.mDisplayContent;
3291         final TaskDisplayArea taskDisplayArea = mActivity.getDisplayArea();
3292         taskDisplayArea.setBounds(0, 0, 1000, 2400);
3293 
3294         // Portrait fixed app.
3295         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_LANDSCAPE);
3296 
3297         final Rect displayBounds = new Rect(display.getBounds());
3298         assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
3299         assertEquals(2800, displayBounds.width());
3300         assertEquals(1400, displayBounds.height());
3301         Rect displayAreaBounds = new Rect(0, 0, 2400, 1000);
3302         taskDisplayArea.setBounds(displayAreaBounds);
3303 
3304         final Rect activityBounds = new Rect(mActivity.getBounds());
3305         assertFalse(mActivity.inSizeCompatMode());
3306         assertEquals(2400, activityBounds.width());
3307         assertEquals(1000, activityBounds.height());
3308         // Task and activity maximum bounds inherit from TaskDisplayArea bounds.
3309         assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
3310                 .isEqualTo(displayAreaBounds);
3311         assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
3312                 .isEqualTo(displayAreaBounds);
3313     }
3314 
3315     @Test
3316     public void testSupportsNonResizableInSplitScreen_letterboxForDifferentOrientation() {
3317         // Support non resizable in multi window
3318         mAtm.mDevEnableNonResizableMultiWindow = true;
3319         setUpDisplaySizeWithApp(1000, 2800);
3320         final TestSplitOrganizer organizer =
3321                 new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
3322 
3323         // Non-resizable landscape activity
3324         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
3325         final Rect originalBounds = new Rect(mActivity.getBounds());
3326 
3327         // Move activity to split screen which takes half of the screen.
3328         mTask.reparent(organizer.mPrimary, POSITION_TOP,
3329                 false /*moveParents*/, "test");
3330         organizer.mPrimary.setBounds(0, 0, 1000, 1400);
3331         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
3332         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
3333 
3334         // Non-resizable activity in size compat mode
3335         assertScaled();
3336         final Rect newBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
3337         assertEquals(originalBounds.width(), newBounds.width());
3338         assertEquals(originalBounds.height(), newBounds.height());
3339         assertActivityMaxBoundsSandboxed();
3340 
3341         recomputeNaturalConfigurationOfUnresizableActivity();
3342 
3343         // Split screen is also in portrait [1000,1400], so activity should be in fixed orientation
3344         // letterbox.
3345         assertEquals(ORIENTATION_PORTRAIT, mTask.getConfiguration().orientation);
3346         assertEquals(ORIENTATION_LANDSCAPE, mActivity.getConfiguration().orientation);
3347         assertFitted();
3348         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
3349         assertActivityMaxBoundsSandboxed();
3350 
3351         // Letterbox should fill the gap between the split screen and the letterboxed activity.
3352         assertLetterboxSurfacesDrawnBetweenActivityAndParentBounds(organizer.mPrimary.getBounds());
3353     }
3354 
3355     @Test
3356     public void testResizableFixedOrientationAppInSplitScreen_letterboxForDifferentOrientation() {
3357         setUpDisplaySizeWithApp(1000, 2800);
3358         final TestSplitOrganizer organizer =
3359                 new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
3360 
3361         // Resizable landscape-only activity.
3362         prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_LANDSCAPE, /* isUnresizable= */ false);
3363 
3364         final Rect originalBounds = new Rect(mActivity.getBounds());
3365 
3366         // Move activity to split screen which takes half of the screen.
3367         mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
3368         organizer.mPrimary.setBounds(0, 0, 1000, 1400);
3369         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
3370         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
3371 
3372         // Resizable activity is not in size compat mode but in the letterbox for fixed orientation.
3373         assertFitted();
3374         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
3375     }
3376 
3377     @Test
3378     public void testSupportsNonResizableInSplitScreen_aspectRatioLetterboxInSameOrientation() {
3379         // Support non resizable in multi window
3380         mAtm.mDevEnableNonResizableMultiWindow = true;
3381         setUpDisplaySizeWithApp(1000, 2800);
3382         final TestSplitOrganizer organizer =
3383                 new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
3384 
3385         // Non-resizable portrait activity
3386         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
3387         final Rect originalBounds = new Rect(mActivity.getBounds());
3388 
3389         // Move activity to split screen which takes half of the screen.
3390         mTask.reparent(organizer.mPrimary, POSITION_TOP,
3391                 false /*moveParents*/, "test");
3392         organizer.mPrimary.setBounds(0, 0, 1000, 1400);
3393         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
3394         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
3395 
3396         // Non-resizable activity in size compat mode
3397         assertScaled();
3398         final Rect newBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
3399         assertEquals(originalBounds.width(), newBounds.width());
3400         assertEquals(originalBounds.height(), newBounds.height());
3401         assertActivityMaxBoundsSandboxed();
3402 
3403         recomputeNaturalConfigurationOfUnresizableActivity();
3404 
3405         // Split screen is also in portrait [1000,1400], which meets the activity request. It should
3406         // sandbox to the activity bounds for non-resizable.
3407         assertEquals(ORIENTATION_PORTRAIT, mTask.getConfiguration().orientation);
3408         assertEquals(ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
3409         assertFitted();
3410         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
3411         assertActivityMaxBoundsSandboxed();
3412 
3413         // Activity bounds fill split screen.
3414         final Rect primarySplitBounds = new Rect(organizer.mPrimary.getBounds());
3415         final Rect letterboxedBounds = new Rect(mActivity.getBounds());
3416         assertEquals(primarySplitBounds, letterboxedBounds);
3417     }
3418 
3419     @Test
3420     public void testSupportsNonResizableInSplitScreen_letterboxForAspectRatioRestriction() {
3421         // Support non resizable in multi window
3422         mAtm.mDevEnableNonResizableMultiWindow = true;
3423         setUpDisplaySizeWithApp(/* dw */ 1000, /* dh */ 2800);
3424         final TestSplitOrganizer organizer =
3425                 new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
3426 
3427         prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
3428 
3429         // Bounds are letterboxed to respect the provided max aspect ratio.
3430         assertEquals(mActivity.getBounds(), new Rect(0, 0, 1000, 1100));
3431 
3432         // Move activity to split screen which has landscape size.
3433         mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents */ false, "test");
3434         organizer.mPrimary.setBounds(0, 0, 1000, 800);
3435 
3436         // Non-resizable activity should be in size compat mode.
3437         assertScaled();
3438         assertEquals(mActivity.getBounds(), new Rect(60, 0, 940, 800));
3439 
3440         recomputeNaturalConfigurationOfUnresizableActivity();
3441 
3442         // Activity should still be letterboxed but not in the size compat mode.
3443         assertFitted();
3444         assertEquals(mActivity.getBounds(), new Rect(60, 0, 940, 800));
3445 
3446         // Letterbox should fill the gap between the split screen and the letterboxed activity.
3447         assertLetterboxSurfacesDrawnBetweenActivityAndParentBounds(organizer.mPrimary.getBounds());
3448     }
3449 
3450     @Test
3451     public void testIsHorizontalReachabilityEnabled_splitScreen_false() {
3452         mAtm.mDevEnableNonResizableMultiWindow = true;
3453         setUpDisplaySizeWithApp(2800, 1000);
3454         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3455         mWm.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(true);
3456         final TestSplitOrganizer organizer =
3457                 new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
3458 
3459         // Unresizable portrait-only activity.
3460         prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_PORTRAIT);
3461 
3462         // Move activity to split screen which takes half of the screen.
3463         mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
3464         organizer.mPrimary.setBounds(0, 0, 1400, 1000);
3465         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
3466         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
3467 
3468         // Horizontal reachability is disabled because the app is in split screen.
3469         assertFalse(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
3470     }
3471 
3472     @Test
3473     public void testIsVerticalReachabilityEnabled_splitScreen_false() {
3474         mAtm.mDevEnableNonResizableMultiWindow = true;
3475         setUpDisplaySizeWithApp(1000, 2800);
3476         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3477         mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
3478         final TestSplitOrganizer organizer =
3479                 new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
3480 
3481         // Unresizable landscape-only activity.
3482         prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_LANDSCAPE);
3483 
3484         // Move activity to split screen which takes half of the screen.
3485         mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
3486         organizer.mPrimary.setBounds(0, 0, 1000, 1400);
3487         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
3488         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
3489 
3490         // Vertical reachability is disabled because the app is in split screen.
3491         assertFalse(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
3492     }
3493 
3494     @Test
3495     public void testIsVerticalReachabilityEnabled_doesNotMatchParentWidth_false() {
3496         setUpDisplaySizeWithApp(1000, 2800);
3497         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3498         mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
3499 
3500         // Unresizable landscape-only activity.
3501         prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_LANDSCAPE);
3502 
3503         // Rotate to put activity in size compat mode.
3504         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
3505 
3506         // Activity now in size compat mode.
3507         assertTrue(mActivity.inSizeCompatMode());
3508 
3509         // Vertical reachability is disabled because the app does not match parent width
3510         assertNotEquals(mActivity.getScreenResolvedBounds().width(),
3511                 mActivity.mDisplayContent.getBounds().width());
3512         assertFalse(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
3513     }
3514 
3515     @Test
3516     public void testIsVerticalReachabilityEnabled_emptyBounds_true() {
3517         setUpDisplaySizeWithApp(/* dw */ 1000, /* dh */ 2800);
3518         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3519         mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
3520 
3521         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
3522 
3523         // Set up activity with empty bounds to mock loading of app
3524         mActivity.getWindowConfiguration().setBounds(null);
3525         assertEquals(new Rect(0, 0, 0, 0), mActivity.getBounds());
3526 
3527         // Vertical reachability is still enabled as resolved bounds is not empty
3528         assertTrue(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
3529     }
3530 
3531     @Test
3532     public void testIsHorizontalReachabilityEnabled_emptyBounds_true() {
3533         setUpDisplaySizeWithApp(/* dw */ 2800, /* dh */ 1000);
3534         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3535         mWm.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(true);
3536 
3537         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
3538 
3539         // Set up activity with empty bounds to mock loading of app
3540         mActivity.getWindowConfiguration().setBounds(null);
3541         assertEquals(new Rect(0, 0, 0, 0), mActivity.getBounds());
3542 
3543         // Horizontal reachability is still enabled as resolved bounds is not empty
3544         assertTrue(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
3545     }
3546 
3547     @Test
3548     public void testIsHorizontalReachabilityEnabled_doesNotMatchParentHeight_false() {
3549         setUpDisplaySizeWithApp(2800, 1000);
3550         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3551         mWm.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(true);
3552 
3553         // Unresizable portrait-only activity.
3554         prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_PORTRAIT);
3555 
3556         // Rotate to put activity in size compat mode.
3557         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
3558 
3559         // Activity now in size compat mode.
3560         assertTrue(mActivity.inSizeCompatMode());
3561 
3562         // Horizontal reachability is disabled because the app does not match parent height
3563         assertNotEquals(mActivity.getScreenResolvedBounds().height(),
3564                 mActivity.mDisplayContent.getBounds().height());
3565         assertFalse(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
3566     }
3567 
3568     @Test
3569     public void testIsHorizontalReachabilityEnabled_inSizeCompatMode_matchesParentHeight_true() {
3570         setUpDisplaySizeWithApp(1800, 2200);
3571         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3572         mWm.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(true);
3573 
3574         // Unresizable portrait-only activity.
3575         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
3576 
3577         // Rotate to put activity in size compat mode.
3578         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
3579 
3580         // Activity now in size compat mode.
3581         assertTrue(mActivity.inSizeCompatMode());
3582 
3583         // Horizontal reachability is enabled because the app matches parent height
3584         assertEquals(mActivity.getScreenResolvedBounds().height(),
3585                 mActivity.mDisplayContent.getBounds().height());
3586         assertTrue(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
3587     }
3588 
3589     @Test
3590     public void testIsVerticalReachabilityEnabled_inSizeCompatMode_matchesParentWidth_true() {
3591         setUpDisplaySizeWithApp(2200, 1800);
3592         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3593         mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
3594 
3595         // Unresizable landscape-only activity.
3596         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
3597 
3598         // Rotate to put activity in size compat mode.
3599         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
3600 
3601         // Activity now in size compat mode.
3602         assertTrue(mActivity.inSizeCompatMode());
3603 
3604         // Vertical reachability is enabled because the app matches parent width
3605         assertEquals(mActivity.getScreenResolvedBounds().width(),
3606                 mActivity.mDisplayContent.getBounds().width());
3607         assertTrue(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
3608     }
3609 
3610     @Test
3611     public void testAppRequestsOrientationChange_notInSizeCompat() {
3612         setUpDisplaySizeWithApp(2200, 1800);
3613         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3614 
3615         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
3616 
3617         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
3618 
3619         // Activity is not in size compat mode because the orientation change request came from the
3620         // app itself
3621         assertFalse(mActivity.inSizeCompatMode());
3622 
3623         rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
3624         // Activity should go into size compat mode now because the orientation change came from the
3625         // system (device rotation)
3626         assertTrue(mActivity.inSizeCompatMode());
3627     }
3628 
3629     @Test
3630     public void testLetterboxDetailsForStatusBar_noLetterbox() {
3631         setUpDisplaySizeWithApp(2800, 1000);
3632         addStatusBar(mActivity.mDisplayContent);
3633         addWindowToActivity(mActivity); // Add a window to the activity so that we can get an
3634         // appearance inside letterboxDetails
3635 
3636         DisplayPolicy displayPolicy = mActivity.getDisplayContent().getDisplayPolicy();
3637         StatusBarManagerInternal statusBar = displayPolicy.getStatusBarManagerInternal();
3638         // We should get a null LetterboxDetails object as there is no letterboxed activity, so
3639         // nothing will get passed to SysUI
3640         verify(statusBar, never()).onSystemBarAttributesChanged(anyInt(), anyInt(),
3641                 any(), anyBoolean(), anyInt(), anyInt(), isNull(), isNull());
3642 
3643     }
3644 
3645     @Test
3646     public void testLetterboxDetailsForStatusBar_letterboxedForMaxAspectRatio() {
3647         setUpDisplaySizeWithApp(2800, 1000);
3648         addStatusBar(mActivity.mDisplayContent);
3649         addWindowToActivity(mActivity); // Add a window to the activity so that we can get an
3650         // appearance inside letterboxDetails
3651         // Prepare unresizable activity with max aspect ratio
3652         prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
3653         // Refresh the letterbox
3654         mActivity.mRootWindowContainer.performSurfacePlacement();
3655 
3656         Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
3657         assertEquals(mBounds, new Rect(850, 0, 1950, 1000));
3658 
3659         DisplayPolicy displayPolicy = mActivity.getDisplayContent().getDisplayPolicy();
3660         LetterboxDetails[] expectedLetterboxDetails = {new LetterboxDetails(
3661                 mBounds,
3662                 mActivity.getDisplayContent().getBounds(),
3663                 mActivity.findMainWindow().mAttrs.insetsFlags.appearance
3664         )};
3665 
3666         // Check that letterboxDetails actually gets passed to SysUI
3667         StatusBarManagerInternal statusBar = displayPolicy.getStatusBarManagerInternal();
3668         verify(statusBar).onSystemBarAttributesChanged(anyInt(), anyInt(),
3669                 any(), anyBoolean(), anyInt(), anyInt(), isNull(), eq(expectedLetterboxDetails));
3670     }
3671 
3672     @Test
3673     public void testLetterboxDetailsForStatusBar_letterboxNotOverlappingStatusBar() {
3674         // Align to center so that we don't overlap with the status bar
3675         mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
3676         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2800)
3677                 .setNotch(100)
3678                 .build();
3679         setUpApp(display);
3680         TestWindowState statusBar = addStatusBar(mActivity.mDisplayContent);
3681         spyOn(statusBar);
3682         doReturn(new Rect(0, 0, statusBar.mRequestedWidth, statusBar.mRequestedHeight))
3683                 .when(statusBar).getFrame();
3684         addWindowToActivity(mActivity); // Add a window to the activity so that we can get an
3685         // appearance inside letterboxDetails
3686         // Prepare unresizable activity with max aspect ratio
3687         prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
3688         // Refresh the letterbox
3689         mActivity.mRootWindowContainer.performSurfacePlacement();
3690 
3691         Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
3692         assertEquals(mBounds, new Rect(0, 900, 1000, 2000));
3693 
3694         DisplayPolicy displayPolicy = mActivity.getDisplayContent().getDisplayPolicy();
3695         LetterboxDetails[] expectedLetterboxDetails = {new LetterboxDetails(
3696                 mBounds,
3697                 mActivity.getDisplayContent().getBounds(),
3698                 mActivity.findMainWindow().mAttrs.insetsFlags.appearance
3699         )};
3700 
3701         // Check that letterboxDetails actually gets passed to SysUI
3702         StatusBarManagerInternal statusBarManager = displayPolicy.getStatusBarManagerInternal();
3703         verify(statusBarManager).onSystemBarAttributesChanged(anyInt(), anyInt(),
3704                 any(), anyBoolean(), anyInt(), anyInt(), isNull(), eq(expectedLetterboxDetails));
3705     }
3706 
3707     @Test
3708     public void testLetterboxDetailsForTaskBar_letterboxNotOverlappingTaskBar() {
3709         mAtm.mDevEnableNonResizableMultiWindow = true;
3710         final int screenHeight = 2200;
3711         final int screenWidth = 1400;
3712         final int taskbarHeight = 200;
3713         setUpDisplaySizeWithApp(screenWidth, screenHeight);
3714 
3715         final TestSplitOrganizer organizer =
3716                 new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
3717 
3718         // Move first activity to split screen which takes half of the screen.
3719         organizer.mPrimary.setBounds(0, screenHeight / 2, screenWidth, screenHeight);
3720         organizer.putTaskToPrimary(mTask, true);
3721 
3722         final InsetsSource navSource = new InsetsSource(
3723                 InsetsSource.createId(null, 0, navigationBars()), navigationBars());
3724         navSource.setFlags(FLAG_INSETS_ROUNDED_CORNER, FLAG_INSETS_ROUNDED_CORNER);
3725         navSource.setFrame(new Rect(0, screenHeight - taskbarHeight, screenWidth, screenHeight));
3726 
3727         mActivity.mWmService.mLetterboxConfiguration.setLetterboxActivityCornersRadius(15);
3728 
3729         final WindowState w1 = addWindowToActivity(mActivity);
3730         w1.mAboveInsetsState.addSource(navSource);
3731 
3732         // Prepare unresizable activity with max aspect ratio
3733         prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
3734 
3735         // Refresh the letterboxes
3736         mActivity.mRootWindowContainer.performSurfacePlacement();
3737 
3738         final ArgumentCaptor<Rect> cropCapturer = ArgumentCaptor.forClass(Rect.class);
3739         verify(mTransaction, times(2)).setCrop(
3740                 eq(w1.getSurfaceControl()),
3741                 cropCapturer.capture()
3742         );
3743         final List<Rect> capturedCrops = cropCapturer.getAllValues();
3744 
3745         final int expectedHeight = screenHeight / 2 - taskbarHeight;
3746         assertEquals(2, capturedCrops.size());
3747         assertEquals(expectedHeight, capturedCrops.get(0).bottom);
3748         assertEquals(expectedHeight, capturedCrops.get(1).bottom);
3749     }
3750 
3751     @Test
3752     public void testLetterboxAlignedToBottom_NotOverlappingNavbar() {
3753         assertLandscapeActivityAlignedToBottomWithNavbar(false /* immersive */);
3754     }
3755 
3756     @Test
3757     public void testImmersiveLetterboxAlignedToBottom_OverlappingNavbar() {
3758         assertLandscapeActivityAlignedToBottomWithNavbar(true /* immersive */);
3759     }
3760 
3761     private void assertLandscapeActivityAlignedToBottomWithNavbar(boolean immersive) {
3762         final int screenHeight = 2800;
3763         final int screenWidth = 1400;
3764         final int taskbarHeight = 200;
3765         setUpDisplaySizeWithApp(screenWidth, screenHeight);
3766 
3767         mActivity.mDisplayContent.setIgnoreOrientationRequest(true);
3768         mActivity.mWmService.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(1.0f);
3769 
3770         final InsetsSource navSource = new InsetsSource(
3771                 InsetsSource.createId(null, 0, navigationBars()), navigationBars());
3772         navSource.setFlags(FLAG_INSETS_ROUNDED_CORNER, FLAG_INSETS_ROUNDED_CORNER);
3773         // Immersive activity has transient navbar
3774         navSource.setVisible(!immersive);
3775         navSource.setFrame(new Rect(0, screenHeight - taskbarHeight, screenWidth, screenHeight));
3776         mActivity.mWmService.mLetterboxConfiguration.setLetterboxActivityCornersRadius(15);
3777 
3778         final WindowState w1 = addWindowToActivity(mActivity);
3779         w1.mAboveInsetsState.addSource(navSource);
3780 
3781         // Prepare unresizable landscape activity
3782         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
3783         final DisplayPolicy displayPolicy = mActivity.mDisplayContent.getDisplayPolicy();
3784         doReturn(immersive).when(displayPolicy).isImmersiveMode();
3785 
3786         mActivity.mRootWindowContainer.performSurfacePlacement();
3787 
3788         LetterboxDetails letterboxDetails = mActivity.mLetterboxUiController.getLetterboxDetails();
3789 
3790         // Letterboxed activity at bottom
3791         assertEquals(new Rect(0, 2100, 1400, 2800), mActivity.getBounds());
3792         final int expectedHeight = immersive ? screenHeight : screenHeight - taskbarHeight;
3793         assertEquals(expectedHeight, letterboxDetails.getLetterboxInnerBounds().bottom);
3794     }
3795 
3796     @Test
3797     public void testSplitScreenLetterboxDetailsForStatusBar_twoLetterboxedApps() {
3798         mAtm.mDevEnableNonResizableMultiWindow = true;
3799         setUpDisplaySizeWithApp(2800, 1000);
3800         addStatusBar(mActivity.mDisplayContent);
3801         // Create another task for the second activity
3802         final Task newTask = new TaskBuilder(mSupervisor).setDisplay(mActivity.getDisplayContent())
3803                 .setCreateActivity(true).build();
3804         ActivityRecord newActivity = newTask.getTopNonFinishingActivity();
3805 
3806         final TestSplitOrganizer organizer =
3807                 new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
3808 
3809         // Move first activity to split screen which takes half of the screen.
3810         organizer.mPrimary.setBounds(0, 0, 1400, 1000);
3811         organizer.putTaskToPrimary(mTask, true);
3812         // Move second activity to split screen which takes half of the screen.
3813         organizer.mSecondary.setBounds(1400, 0, 2800, 1000);
3814         organizer.putTaskToSecondary(newTask, true);
3815 
3816         addWindowToActivity(mActivity); // Add a window to the activity so that we can get an
3817         // appearance inside letterboxDetails
3818         // Prepare unresizable activity with max aspect ratio
3819         prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
3820         addWindowToActivity(newActivity);
3821         prepareUnresizable(newActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
3822 
3823         // Refresh the letterboxes
3824         newActivity.mRootWindowContainer.performSurfacePlacement();
3825 
3826         Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
3827         assertEquals(mBounds, new Rect(150, 0, 1250, 1000));
3828         final Rect newBounds = new Rect(newActivity.getWindowConfiguration().getBounds());
3829         assertEquals(newBounds, new Rect(1550, 0, 2650, 1000));
3830 
3831         DisplayPolicy displayPolicy = mActivity.getDisplayContent().getDisplayPolicy();
3832         LetterboxDetails[] expectedLetterboxDetails = { new LetterboxDetails(
3833                 mBounds,
3834                 organizer.mPrimary.getBounds(),
3835                 mActivity.findMainWindow().mAttrs.insetsFlags.appearance
3836         ), new LetterboxDetails(
3837                 newBounds,
3838                 organizer.mSecondary.getBounds(),
3839                 newActivity.findMainWindow().mAttrs.insetsFlags.appearance
3840         )};
3841 
3842         // Check that letterboxDetails actually gets passed to SysUI
3843         StatusBarManagerInternal statusBar = displayPolicy.getStatusBarManagerInternal();
3844         verify(statusBar).onSystemBarAttributesChanged(anyInt(), anyInt(),
3845                 any(), anyBoolean(), anyInt(), anyInt(), isNull(), eq(expectedLetterboxDetails));
3846     }
3847 
3848     private void recomputeNaturalConfigurationOfUnresizableActivity() {
3849         // Recompute the natural configuration of the non-resizable activity and the split screen.
3850         mActivity.clearSizeCompatMode();
3851 
3852         // Draw letterbox.
3853         mActivity.setVisible(false);
3854         mActivity.mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
3855         mActivity.mDisplayContent.mOpeningApps.add(mActivity);
3856         addWindowToActivity(mActivity);
3857         mActivity.mRootWindowContainer.performSurfacePlacement();
3858     }
3859 
3860     private void assertLetterboxSurfacesDrawnBetweenActivityAndParentBounds(Rect parentBounds) {
3861         // Letterbox should fill the gap between the parent bounds and the letterboxed activity.
3862         final Rect letterboxedBounds = new Rect(mActivity.getBounds());
3863         assertTrue(parentBounds.contains(letterboxedBounds));
3864         assertEquals(new Rect(letterboxedBounds.left - parentBounds.left,
3865                 letterboxedBounds.top - parentBounds.top,
3866                 parentBounds.right - letterboxedBounds.right,
3867                 parentBounds.bottom - letterboxedBounds.bottom),
3868                 mActivity.getLetterboxInsets());
3869     }
3870 
3871     @Test
3872     public void testUpdateResolvedBoundsHorizontalPosition_leftInsets_appCentered() {
3873         // Set up folded display
3874         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1100, 2100)
3875                 .setCanRotate(true)
3876                 .build();
3877         display.setIgnoreOrientationRequest(true);
3878         final DisplayPolicy policy = display.getDisplayPolicy();
3879         DisplayPolicy.DecorInsets.Info decorInfo = policy.getDecorInsetsInfo(ROTATION_90,
3880                 display.mBaseDisplayHeight, display.mBaseDisplayWidth);
3881         decorInfo.mNonDecorInsets.set(130, 0,  60, 0);
3882         spyOn(policy);
3883         doReturn(decorInfo).when(policy).getDecorInsetsInfo(ROTATION_90,
3884                 display.mBaseDisplayHeight, display.mBaseDisplayWidth);
3885         mWm.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
3886 
3887         setUpApp(display);
3888         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
3889 
3890         // Resize the display to simulate unfolding in portrait
3891         resizeDisplay(mTask.mDisplayContent, 2200, 1800);
3892         assertTrue(mActivity.inSizeCompatMode());
3893 
3894         // Simulate real display not taking non-decor insets into consideration
3895         display.getWindowConfiguration().setAppBounds(0, 0, 2200, 1800);
3896 
3897         // Rotate display to landscape
3898         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
3899 
3900         // App is centered
3901         assertEquals(mActivity.getBounds(), new Rect(350, 50, 1450, 2150));
3902     }
3903 
3904     @Test
3905     public void testUpdateResolvedBoundsHorizontalPosition_left() {
3906         // Display configured as (2800, 1400).
3907         assertHorizontalPositionForDifferentDisplayConfigsForPortraitActivity(
3908                 /* letterboxHorizontalPositionMultiplier */ 0.0f,
3909                 // At launch.
3910                 /* fixedOrientationLetterbox */ new Rect(0, 0, 700, 1400),
3911                 // After 90 degree rotation.
3912                 /* sizeCompatUnscaled */ new Rect(0, 0, 700, 1400),
3913                 // After the display is resized to (700, 1400).
3914                 /* sizeCompatScaled */ new Rect(0, 0, 350, 700));
3915     }
3916 
3917     @Test
3918     public void testUpdateResolvedBoundsHorizontalPosition_center() {
3919         // Display configured as (2800, 1400).
3920         assertHorizontalPositionForDifferentDisplayConfigsForPortraitActivity(
3921                 /* letterboxHorizontalPositionMultiplier */ 0.5f,
3922                 // At launch.
3923                 /* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
3924                 // After 90 degree rotation.
3925                 /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
3926                 // After the display is resized to (700, 1400).
3927                 /* sizeCompatScaled */ new Rect(525, 0, 875, 700));
3928     }
3929 
3930     @Test
3931     public void testUpdateResolvedBoundsHorizontalPosition_invalidMultiplier_defaultToCenter() {
3932         // Display configured as (2800, 1400).
3933 
3934         // Below 0.0.
3935         assertHorizontalPositionForDifferentDisplayConfigsForPortraitActivity(
3936                 /* letterboxHorizontalPositionMultiplier */ -1.0f,
3937                 // At launch.
3938                 /* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
3939                 // After 90 degree rotation.
3940                 /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
3941                 // After the display is resized to (700, 1400).
3942                 /* sizeCompatScaled */ new Rect(525, 0, 875, 700));
3943 
3944         // Above 1.0
3945         assertHorizontalPositionForDifferentDisplayConfigsForPortraitActivity(
3946                 /* letterboxHorizontalPositionMultiplier */ 2.0f,
3947                 // At launch.
3948                 /* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
3949                 // After 90 degree rotation.
3950                 /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
3951                 // After the display is resized to (700, 1400).
3952                 /* sizeCompatScaled */ new Rect(525, 0, 875, 700));
3953     }
3954 
3955     @Test
3956     public void testUpdateResolvedBoundsHorizontalPosition_right() {
3957         // Display configured as (2800, 1400).
3958         assertHorizontalPositionForDifferentDisplayConfigsForPortraitActivity(
3959                 /* letterboxHorizontalPositionMultiplier */ 1.0f,
3960                 // At launch.
3961                 /* fixedOrientationLetterbox */ new Rect(2100, 0, 2800, 1400),
3962                 // After 90 degree rotation.
3963                 /* sizeCompatUnscaled */ new Rect(700, 0, 1400, 1400),
3964                 // After the display is resized to (700, 1400).
3965                 /* sizeCompatScaled */ new Rect(1050, 0, 1400, 700));
3966     }
3967 
3968     private void assertHorizontalPositionForDifferentDisplayConfigsForPortraitActivity(
3969             float letterboxHorizontalPositionMultiplier, Rect fixedOrientationLetterbox,
3970             Rect sizeCompatUnscaled, Rect sizeCompatScaled) {
3971         // Set up a display in landscape and ignoring orientation request.
3972         setUpDisplaySizeWithApp(2800, 1400);
3973         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
3974 
3975         mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
3976                 letterboxHorizontalPositionMultiplier);
3977         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
3978         assertEquals(fixedOrientationLetterbox, mActivity.getBounds());
3979         // Rotate to put activity in size compat mode.
3980         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
3981         assertTrue(mActivity.inSizeCompatMode());
3982         // Activity is in size compat mode but not scaled.
3983         assertEquals(sizeCompatUnscaled, mActivity.getBounds());
3984         // Force activity to scaled down for size compat mode.
3985         resizeDisplay(mTask.mDisplayContent, 700, 1400);
3986         assertTrue(mActivity.inSizeCompatMode());
3987         assertScaled();
3988         assertEquals(sizeCompatScaled, mActivity.getBounds());
3989     }
3990 
3991     @Test
3992     public void testApplyAspectRatio_activityAlignWithParentAppVertical() {
3993         // The display's app bounds will be (0, 100, 1000, 2350)
3994         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500)
3995                 .setCanRotate(false)
3996                 .setCutout(0, 100, 0, 150)
3997                 .build();
3998 
3999         setUpApp(display);
4000         prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
4001         // The activity height is 2100 and the display's app bounds height is 2250, so the activity
4002         // can be aligned inside parentAppBounds
4003         assertEquals(mActivity.getBounds(), new Rect(0, 0, 1000, 2200));
4004     }
4005     @Test
4006     public void testApplyAspectRatio_activityCannotAlignWithParentAppVertical() {
4007         // The display's app bounds will be (0, 100, 1000, 2150)
4008         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2300)
4009                 .setCanRotate(false)
4010                 .setCutout(0, 100, 0, 150)
4011                 .build();
4012 
4013         setUpApp(display);
4014         prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
4015         // The activity height is 2100 and the display's app bounds height is 2050, so the activity
4016         // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display
4017         assertEquals(mActivity.getBounds(), display.getBounds());
4018     }
4019 
4020     @Test
4021     public void testApplyAspectRatio_activityAlignWithParentAppHorizontal() {
4022         // The display's app bounds will be (100, 0, 2350, 1000)
4023         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2500, 1000)
4024                 .setCanRotate(false)
4025                 .setCutout(100, 0, 150, 0)
4026                 .build();
4027 
4028         setUpApp(display);
4029         prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
4030         // The activity width is 2100 and the display's app bounds width is 2250, so the activity
4031         // can be aligned inside parentAppBounds
4032         assertEquals(mActivity.getBounds(), new Rect(175, 0, 2275, 1000));
4033     }
4034     @Test
4035     public void testApplyAspectRatio_activityCannotAlignWithParentAppHorizontal() {
4036         // The display's app bounds will be (100, 0, 2150, 1000)
4037         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2300, 1000)
4038                 .setCanRotate(false)
4039                 .setCutout(100, 0, 150, 0)
4040                 .build();
4041 
4042         setUpApp(display);
4043         prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
4044         // The activity width is 2100 and the display's app bounds width is 2050, so the activity
4045         // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display
4046         assertEquals(mActivity.getBounds(), display.getBounds());
4047     }
4048 
4049     @Test
4050     public void testApplyAspectRatio_containingRatioAlmostEqualToMaxRatio_boundsUnchanged() {
4051         setUpDisplaySizeWithApp(1981, 2576);
4052         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4053         mWm.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
4054 
4055         final Rect originalBounds = new Rect(mActivity.getBounds());
4056         prepareUnresizable(mActivity, 1.3f, SCREEN_ORIENTATION_UNSPECIFIED);
4057 
4058         // The containing aspect ratio is now 1.3003534, while the desired aspect ratio is 1.3. The
4059         // bounds of the activity should not be changed as the difference is too small
4060         assertEquals(mActivity.getBounds(), originalBounds);
4061     }
4062 
4063     @Test
4064     public void testUpdateResolvedBoundsHorizontalPosition_activityFillParentWidth() {
4065         // When activity width equals parent width, multiplier shouldn't have any effect.
4066         assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
4067                 /* letterboxHorizontalPositionMultiplier */ 0.0f);
4068         assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
4069                 /* letterboxHorizontalPositionMultiplier */ 0.5f);
4070         assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
4071                 /* letterboxHorizontalPositionMultiplier */ 1.0f);
4072     }
4073 
4074     @Test
4075     public void testUpdateResolvedBoundsVerticalPosition_topInsets_appCentered() {
4076         // Set up folded display
4077         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2100, 1100)
4078                 .setCanRotate(true)
4079                 .build();
4080         display.setIgnoreOrientationRequest(true);
4081         final DisplayPolicy policy = display.getDisplayPolicy();
4082         DisplayPolicy.DecorInsets.Info decorInfo = policy.getDecorInsetsInfo(ROTATION_90,
4083                 display.mBaseDisplayHeight, display.mBaseDisplayWidth);
4084         decorInfo.mNonDecorInsets.set(0, 130,  0, 60);
4085         spyOn(policy);
4086         doReturn(decorInfo).when(policy).getDecorInsetsInfo(ROTATION_90,
4087                 display.mBaseDisplayHeight, display.mBaseDisplayWidth);
4088         mWm.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
4089 
4090         setUpApp(display);
4091         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
4092 
4093         // Resize the display to simulate unfolding in portrait
4094         resizeDisplay(mTask.mDisplayContent, 1800, 2200);
4095         assertTrue(mActivity.inSizeCompatMode());
4096 
4097         // Simulate real display not taking non-decor insets into consideration
4098         display.getWindowConfiguration().setAppBounds(0, 0, 1800, 2200);
4099 
4100         // Rotate display to landscape
4101         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
4102 
4103         // App is centered
4104         assertEquals(mActivity.getBounds(), new Rect(50, 350, 2150, 1450));
4105     }
4106 
4107     @Test
4108     public void testUpdateResolvedBoundsVerticalPosition_top() {
4109         // Display configured as (1400, 2800).
4110         assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
4111                 /* letterboxVerticalPositionMultiplier */ 0.0f,
4112                 // At launch.
4113                 /* fixedOrientationLetterbox */ new Rect(0, 0, 1400, 700),
4114                 // After 90 degree rotation.
4115                 /* sizeCompatUnscaled */ new Rect(700, 0, 2100, 700),
4116                 // After the display is resized to (1400, 700).
4117                 /* sizeCompatScaled */ new Rect(0, 0, 700, 350));
4118     }
4119 
4120     @Test
4121     public void testUpdateResolvedBoundsVerticalPosition_center() {
4122         // Display configured as (1400, 2800).
4123         assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
4124                 /* letterboxVerticalPositionMultiplier */ 0.5f,
4125                 // At launch.
4126                 /* fixedOrientationLetterbox */ new Rect(0, 1050, 1400, 1750),
4127                 // After 90 degree rotation.
4128                 /* sizeCompatUnscaled */ new Rect(700, 350, 2100, 1050),
4129                 // After the display is resized to (1400, 700).
4130                 /* sizeCompatScaled */ new Rect(0, 525, 700, 875));
4131     }
4132 
4133     @Test
4134     public void testUpdateResolvedBoundsVerticalPosition_invalidMultiplier_defaultToCenter() {
4135         // Display configured as (1400, 2800).
4136 
4137         // Below 0.0.
4138         assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
4139                 /* letterboxVerticalPositionMultiplier */ -1.0f,
4140                 // At launch.
4141                 /* fixedOrientationLetterbox */ new Rect(0, 1050, 1400, 1750),
4142                 // After 90 degree rotation.
4143                 /* sizeCompatUnscaled */ new Rect(700, 350, 2100, 1050),
4144                 // After the display is resized to (1400, 700).
4145                 /* sizeCompatScaled */ new Rect(0, 525, 700, 875));
4146 
4147         // Above 1.0
4148         assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
4149                 /* letterboxVerticalPositionMultiplier */ 2.0f,
4150                 // At launch.
4151                 /* fixedOrientationLetterbox */ new Rect(0, 1050, 1400, 1750),
4152                 // After 90 degree rotation.
4153                 /* sizeCompatUnscaled */ new Rect(700, 350, 2100, 1050),
4154                 // After the display is resized to (1400, 700).
4155                 /* sizeCompatScaled */ new Rect(0, 525, 700, 875));
4156     }
4157 
4158     @Test
4159     public void testUpdateResolvedBoundsVerticalPosition_bottom() {
4160         // Display configured as (1400, 2800).
4161         assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
4162                 /* letterboxVerticalPositionMultiplier */ 1.0f,
4163                 // At launch.
4164                 /* fixedOrientationLetterbox */ new Rect(0, 2100, 1400, 2800),
4165                 // After 90 degree rotation.
4166                 /* sizeCompatUnscaled */ new Rect(700, 700, 2100, 1400),
4167                 // After the display is resized to (1400, 700).
4168                 /* sizeCompatScaled */ new Rect(0, 1050, 700, 1400));
4169     }
4170 
4171     @Test
4172     public void testUpdateResolvedBoundsVerticalPosition_tabletop() {
4173         // Set up a display in portrait with a fixed-orientation LANDSCAPE app
4174         setUpDisplaySizeWithApp(1400, 2800);
4175         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4176         mActivity.mWmService.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(
4177                 1.0f /*letterboxVerticalPositionMultiplier*/);
4178         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
4179 
4180         Rect letterboxNoFold = new Rect(0, 2100, 1400, 2800);
4181         assertEquals(letterboxNoFold, mActivity.getBounds());
4182 
4183         // Make the activity full-screen
4184         mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
4185 
4186         setFoldablePosture(true /* isHalfFolded */, true /* isTabletop */);
4187 
4188         Rect letterboxHalfFold = new Rect(0, 0, 1400, 700);
4189         assertEquals(letterboxHalfFold, mActivity.getBounds());
4190 
4191         setFoldablePosture(false /* isHalfFolded */, false /* isTabletop */);
4192 
4193         assertEquals(letterboxNoFold, mActivity.getBounds());
4194     }
4195 
4196     @Test
4197     public void testGetFixedOrientationLetterboxAspectRatio_tabletop_centered() {
4198         // Set up a display in portrait with a fixed-orientation LANDSCAPE app
4199         setUpDisplaySizeWithApp(1400, 2800);
4200         mWm.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
4201                 LETTERBOX_POSITION_MULTIPLIER_CENTER);
4202         mActivity.mWmService.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(
4203                 1.0f /*letterboxVerticalPositionMultiplier*/);
4204         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
4205 
4206         setFoldablePosture(true /* isHalfFolded */, true /* isTabletop */);
4207 
4208         Configuration parentConfig = mActivity.getParent().getConfiguration();
4209 
4210         float actual = mActivity.mLetterboxUiController
4211                 .getFixedOrientationLetterboxAspectRatio(parentConfig);
4212         float expected = mActivity.mLetterboxUiController.getSplitScreenAspectRatio();
4213 
4214         assertEquals(expected, actual, 0.01);
4215     }
4216 
4217     @Test
4218     public void testUpdateResolvedBoundsHorizontalPosition_bookModeEnabled() {
4219         // Set up a display in landscape with a fixed-orientation PORTRAIT app
4220         setUpDisplaySizeWithApp(2800, 1400);
4221         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4222         mWm.mLetterboxConfiguration.setIsAutomaticReachabilityInBookModeEnabled(true);
4223         mWm.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
4224                 1.0f /*letterboxHorizontalPositionMultiplier*/);
4225         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
4226 
4227         Rect letterboxNoFold = new Rect(2100, 0, 2800, 1400);
4228         assertEquals(letterboxNoFold, mActivity.getBounds());
4229 
4230         // Make the activity full-screen
4231         mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
4232 
4233         setFoldablePosture(true /* isHalfFolded */, false /* isTabletop */);
4234 
4235         Rect letterboxHalfFold = new Rect(0, 0, 700, 1400);
4236         assertEquals(letterboxHalfFold, mActivity.getBounds());
4237 
4238         setFoldablePosture(false /* isHalfFolded */, false /* isTabletop */);
4239 
4240         assertEquals(letterboxNoFold, mActivity.getBounds());
4241     }
4242 
4243     @Test
4244     public void testUpdateResolvedBoundsHorizontalPosition_bookModeDisabled_centered() {
4245         // Set up a display in landscape with a fixed-orientation PORTRAIT app
4246         setUpDisplaySizeWithApp(2800, 1400);
4247         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4248         mWm.mLetterboxConfiguration.setIsAutomaticReachabilityInBookModeEnabled(false);
4249         mWm.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(0.5f);
4250         prepareUnresizable(mActivity, 1.75f, SCREEN_ORIENTATION_PORTRAIT);
4251 
4252         Rect letterboxNoFold = new Rect(1000, 0, 1800, 1400);
4253         assertEquals(letterboxNoFold, mActivity.getBounds());
4254 
4255         // Make the activity full-screen
4256         mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
4257 
4258         // Stay centered and bounds don't change
4259         setFoldablePosture(true /* isHalfFolded */, false /* isTabletop */);
4260         assertEquals(letterboxNoFold, mActivity.getBounds());
4261 
4262         setFoldablePosture(false /* isHalfFolded */, false /* isTabletop */);
4263         assertEquals(letterboxNoFold, mActivity.getBounds());
4264     }
4265 
4266     private void setFoldablePosture(ActivityRecord activity, boolean isHalfFolded,
4267             boolean isTabletop) {
4268         final DisplayRotation r = activity.mDisplayContent.getDisplayRotation();
4269         doReturn(isHalfFolded).when(r).isDisplaySeparatingHinge();
4270         doReturn(false).when(r).isDeviceInPosture(any(DeviceState.class), anyBoolean());
4271         if (isHalfFolded) {
4272             doReturn(true).when(r)
4273                     .isDeviceInPosture(DeviceState.HALF_FOLDED, isTabletop);
4274         }
4275         activity.recomputeConfiguration();
4276     }
4277 
4278     private void setFoldablePosture(boolean isHalfFolded, boolean isTabletop) {
4279         setFoldablePosture(mActivity, isHalfFolded, isTabletop);
4280     }
4281 
4282     @Test
4283     public void testUpdateResolvedBoundsPosition_alignToTop() {
4284         final int notchHeight = 100;
4285         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2800)
4286                 .setNotch(notchHeight)
4287                 .build();
4288         setUpApp(display);
4289 
4290         // Prepare unresizable activity with max aspect ratio
4291         prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
4292 
4293         Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
4294         Rect appBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
4295         // The insets should be cut for aspect ratio and then added back because the appBounds
4296         // are aligned to the top of the parentAppBounds
4297         assertEquals(mBounds, new Rect(0, 0, 1000, 1200));
4298         assertEquals(appBounds, new Rect(0, notchHeight, 1000, 1200));
4299     }
4300 
4301     private void assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
4302             float letterboxVerticalPositionMultiplier, Rect fixedOrientationLetterbox,
4303             Rect sizeCompatUnscaled, Rect sizeCompatScaled) {
4304         // Set up a display in portrait and ignoring orientation request.
4305         setUpDisplaySizeWithApp(1400, 2800);
4306         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4307 
4308         mActivity.mWmService.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(
4309                 letterboxVerticalPositionMultiplier);
4310         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
4311 
4312         assertEquals(fixedOrientationLetterbox, mActivity.getBounds());
4313 
4314         // Rotate to put activity in size compat mode.
4315         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
4316 
4317         assertTrue(mActivity.inSizeCompatMode());
4318         // Activity is in size compat mode but not scaled.
4319         assertEquals(sizeCompatUnscaled, mActivity.getBounds());
4320 
4321         // Force activity to scaled down for size compat mode.
4322         resizeDisplay(mTask.mDisplayContent, 1400, 700);
4323 
4324         assertTrue(mActivity.inSizeCompatMode());
4325         assertScaled();
4326         assertEquals(sizeCompatScaled, mActivity.getBounds());
4327     }
4328 
4329     @Test
4330     public void testUpdateResolvedBoundsVerticalPosition_activityFillParentHeight() {
4331         // When activity height equals parent height, multiplier shouldn't have any effect.
4332         assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity(
4333                 /* letterboxVerticalPositionMultiplier */ 0.0f);
4334         assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity(
4335                 /* letterboxVerticalPositionMultiplier */ 0.5f);
4336         assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity(
4337                 /* letterboxVerticalPositionMultiplier */ 1.0f);
4338     }
4339 
4340     @Test
4341     public void testAreBoundsLetterboxed_letterboxedForAspectRatio_returnsTrue() {
4342         setUpDisplaySizeWithApp(1000, 2500);
4343 
4344         assertFalse(mActivity.areBoundsLetterboxed());
4345         verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
4346 
4347         prepareUnresizable(mActivity, /* maxAspect= */ 2, SCREEN_ORIENTATION_PORTRAIT);
4348         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
4349         assertFalse(mActivity.inSizeCompatMode());
4350         assertTrue(mActivity.areBoundsLetterboxed());
4351 
4352         verifyLogAppCompatState(mActivity,
4353                 APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO);
4354 
4355         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
4356         verifyLogAppCompatState(mActivity,
4357                 APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE);
4358         rotateDisplay(mActivity.mDisplayContent, ROTATION_0);
4359 
4360         // After returning to the original rotation, bounds are computed in
4361         // ActivityRecord#resolveSizeCompatModeConfiguration because mCompatDisplayInsets aren't
4362         // null but activity doesn't enter size compat mode. Checking that areBoundsLetterboxed()
4363         // still returns true because of the aspect ratio restrictions.
4364         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
4365         assertFalse(mActivity.inSizeCompatMode());
4366         assertTrue(mActivity.areBoundsLetterboxed());
4367         verifyLogAppCompatState(mActivity,
4368                 APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO);
4369 
4370         // After setting the visibility of the activity to false, areBoundsLetterboxed() still
4371         // returns true but the NOT_VISIBLE App Compat state is logged.
4372         mActivity.setVisibility(false);
4373         assertTrue(mActivity.areBoundsLetterboxed());
4374         verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE);
4375         mActivity.setVisibility(true);
4376         assertTrue(mActivity.areBoundsLetterboxed());
4377         verifyLogAppCompatState(mActivity,
4378                 APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO);
4379     }
4380 
4381     @Test
4382     public void testAreBoundsLetterboxed_letterboxedForFixedOrientation_returnsTrue() {
4383         setUpDisplaySizeWithApp(2500, 1000);
4384         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4385 
4386         assertFalse(mActivity.areBoundsLetterboxed());
4387         verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
4388 
4389         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
4390 
4391         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
4392         assertFalse(mActivity.inSizeCompatMode());
4393         assertTrue(mActivity.areBoundsLetterboxed());
4394         verifyLogAppCompatState(mActivity,
4395                 APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION);
4396     }
4397 
4398     @Test
4399     public void testAreBoundsLetterboxed_letterboxedForSizeCompat_returnsTrue() {
4400         setUpDisplaySizeWithApp(1000, 2500);
4401         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4402         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
4403 
4404         assertFalse(mActivity.areBoundsLetterboxed());
4405         verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
4406 
4407         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
4408 
4409         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
4410         assertTrue(mActivity.inSizeCompatMode());
4411         assertTrue(mActivity.areBoundsLetterboxed());
4412         verifyLogAppCompatState(mActivity,
4413                 APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE);
4414     }
4415 
4416     @Test
4417     public void testIsEligibleForLetterboxEducation_educationNotEnabled_returnsFalse() {
4418         setUpDisplaySizeWithApp(2500, 1000);
4419         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4420         mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(false);
4421 
4422         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
4423 
4424         assertFalse(mActivity.isEligibleForLetterboxEducation());
4425     }
4426 
4427     @Test
4428     public void testIsEligibleForLetterboxEducation_notEligibleForFixedOrientation_returnsFalse() {
4429         setUpDisplaySizeWithApp(1000, 2500);
4430         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4431         mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
4432 
4433         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
4434 
4435         assertFalse(mActivity.isEligibleForLetterboxEducation());
4436     }
4437 
4438     @Test
4439     public void testIsEligibleForLetterboxEducation_windowingModeMultiWindow_returnsFalse() {
4440         // Support non resizable in multi window
4441         mAtm.mDevEnableNonResizableMultiWindow = true;
4442         setUpDisplaySizeWithApp(1000, 1200);
4443         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4444         mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
4445         final TestSplitOrganizer organizer =
4446                 new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
4447 
4448         // Non-resizable landscape activity
4449         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
4450         final Rect originalBounds = new Rect(mActivity.getBounds());
4451 
4452         // Move activity to split screen which takes half of the screen.
4453         mTask.reparent(organizer.mPrimary, POSITION_TOP,
4454                 false /*moveParents*/, "test");
4455         organizer.mPrimary.setBounds(0, 0, 1000, 600);
4456 
4457         assertFalse(mActivity.isEligibleForLetterboxEducation());
4458         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
4459     }
4460 
4461     @Test
4462     public void testIsEligibleForLetterboxEducation_fixedOrientationLandscape_returnsFalse() {
4463         setUpDisplaySizeWithApp(1000, 2500);
4464         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4465         mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
4466 
4467         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
4468 
4469         assertFalse(mActivity.isEligibleForLetterboxEducation());
4470         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
4471     }
4472 
4473     @Test
4474     public void testIsEligibleForLetterboxEducation_hasStartingWindow_returnsFalseUntilRemoved() {
4475         setUpDisplaySizeWithApp(2500, 1000);
4476         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4477         mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
4478 
4479         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
4480         mActivity.mStartingData = mock(StartingData.class);
4481         mActivity.attachStartingWindow(
4482                 createWindowState(new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING),
4483                         mActivity));
4484 
4485         assertFalse(mActivity.isEligibleForLetterboxEducation());
4486 
4487         // Verify that after removing the starting window isEligibleForLetterboxEducation returns
4488         // true and mTask.dispatchTaskInfoChangedIfNeeded is called.
4489         spyOn(mTask);
4490         mActivity.removeStartingWindow();
4491 
4492         assertTrue(mActivity.isEligibleForLetterboxEducation());
4493         verify(mTask).dispatchTaskInfoChangedIfNeeded(true);
4494     }
4495 
4496     @Test
4497     public void testIsEligibleForLetterboxEducation_hasStartingWindowAndEducationNotEnabled() {
4498         setUpDisplaySizeWithApp(2500, 1000);
4499         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4500         mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(false);
4501 
4502         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
4503         mActivity.mStartingData = mock(StartingData.class);
4504         mActivity.attachStartingWindow(
4505                 createWindowState(new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING),
4506                         mActivity));
4507 
4508         assertFalse(mActivity.isEligibleForLetterboxEducation());
4509 
4510         // Verify that after removing the starting window isEligibleForLetterboxEducation still
4511         // returns false and mTask.dispatchTaskInfoChangedIfNeeded isn't called.
4512         spyOn(mTask);
4513         mActivity.removeStartingWindow();
4514 
4515         assertFalse(mActivity.isEligibleForLetterboxEducation());
4516         verify(mTask, never()).dispatchTaskInfoChangedIfNeeded(true);
4517     }
4518 
4519     @Test
4520     public void testIsEligibleForLetterboxEducation_letterboxedForFixedOrientation_returnsTrue() {
4521         setUpDisplaySizeWithApp(2500, 1000);
4522         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4523         mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
4524 
4525         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
4526 
4527         assertTrue(mActivity.isEligibleForLetterboxEducation());
4528         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
4529     }
4530 
4531     @Test
4532     public void testIsEligibleForLetterboxEducation_sizeCompatAndEligibleForFixedOrientation() {
4533         setUpDisplaySizeWithApp(1000, 2500);
4534         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4535         mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
4536 
4537         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
4538 
4539         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
4540 
4541         assertTrue(mActivity.isEligibleForLetterboxEducation());
4542         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
4543         assertTrue(mActivity.inSizeCompatMode());
4544     }
4545 
4546     @Test
4547     public void testTopActivityInSizeCompatMode_pausedAndInSizeCompatMode_returnsTrue() {
4548         setUpDisplaySizeWithApp(1000, 2500);
4549         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4550 
4551         spyOn(mActivity);
4552         doReturn(mTask).when(mActivity).getOrganizedTask();
4553         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
4554 
4555         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
4556         mActivity.setState(PAUSED, "test");
4557 
4558         assertTrue(mActivity.inSizeCompatMode());
4559         assertEquals(mActivity.getState(), PAUSED);
4560         assertTrue(mActivity.isVisible());
4561         assertTrue(mTask.getTaskInfo().topActivityInSizeCompat);
4562     }
4563 
4564     /**
4565      * Tests that all three paths in which aspect ratio logic can be applied yield the same
4566      * result, which is that aspect ratio is respected on app bounds. The three paths are
4567      * fixed orientation, no fixed orientation but fixed aspect ratio, and size compat mode.
4568      */
4569     @Test
4570     public void testAllAspectRatioLogicConsistent() {
4571         // Create display that has all stable insets and does not rotate. Make sure that status bar
4572         // height is greater than notch height so that stable bounds do not equal app bounds.
4573         final int notchHeight = 75;
4574         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 600)
4575                 .setSystemDecorations(true).setNotch(notchHeight)
4576                 .setStatusBarHeight(notchHeight + 20).setCanRotate(false).build();
4577 
4578         // Create task on test display.
4579         final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
4580 
4581         // Target min aspect ratio must be larger than parent aspect ratio to be applied.
4582         final float targetMinAspectRatio = 3.0f;
4583 
4584         // Create fixed portait activity with min aspect ratio greater than parent aspect ratio.
4585         final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm)
4586                 .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
4587                 .setMinAspectRatio(targetMinAspectRatio).build();
4588         final Rect fixedOrientationAppBounds = new Rect(fixedOrientationActivity.getConfiguration()
4589                 .windowConfiguration.getAppBounds());
4590 
4591         // Create activity with no fixed orientation and min aspect ratio greater than parent aspect
4592         // ratio.
4593         final ActivityRecord minAspectRatioActivity = new ActivityBuilder(mAtm).setTask(task)
4594                 .setMinAspectRatio(targetMinAspectRatio).build();
4595         final Rect minAspectRatioAppBounds = new Rect(minAspectRatioActivity.getConfiguration()
4596                 .windowConfiguration.getAppBounds());
4597 
4598         // Create unresizeable fixed portait activity with min aspect ratio greater than parent
4599         // aspect ratio.
4600         final ActivityRecord sizeCompatActivity = new ActivityBuilder(mAtm)
4601                 .setTask(task).setResizeMode(RESIZE_MODE_UNRESIZEABLE)
4602                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
4603                 .setMinAspectRatio(targetMinAspectRatio).build();
4604         // Resize display running unresizeable activity to make it enter size compat mode.
4605         resizeDisplay(display, 1800, 1000);
4606         final Rect sizeCompatAppBounds = new Rect(sizeCompatActivity.getConfiguration()
4607                 .windowConfiguration.getAppBounds());
4608 
4609         // Check that aspect ratio of app bounds is equal to the min aspect ratio.
4610         final float delta = 0.01f;
4611         assertEquals(targetMinAspectRatio, ActivityRecord
4612                 .computeAspectRatio(fixedOrientationAppBounds), delta);
4613         assertEquals(targetMinAspectRatio, ActivityRecord
4614                 .computeAspectRatio(minAspectRatioAppBounds), delta);
4615         assertEquals(targetMinAspectRatio, ActivityRecord
4616                 .computeAspectRatio(sizeCompatAppBounds), delta);
4617     }
4618 
4619     @Test
4620     public void testClearSizeCompat_resetOverrideConfig() {
4621         final int origDensity = 480;
4622         final int newDensity = 520;
4623         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 600, 800)
4624                 .setDensityDpi(origDensity)
4625                 .build();
4626         setUpApp(display);
4627         prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
4628 
4629         // Activity should enter size compat with old density after display density change.
4630         display.setForcedDensity(newDensity, UserHandle.USER_CURRENT);
4631 
4632         assertScaled();
4633         assertEquals(origDensity, mActivity.getConfiguration().densityDpi);
4634 
4635         // Activity should exit size compat with new density.
4636         mActivity.clearSizeCompatMode();
4637 
4638         assertFitted();
4639         assertEquals(newDensity, mActivity.getConfiguration().densityDpi);
4640     }
4641 
4642     @Test
4643     public void testShouldSendFakeFocus_compatFakeFocusEnabled() {
4644         final ActivityRecord activity = new ActivityBuilder(mAtm)
4645                 .setCreateTask(true)
4646                 .setOnTop(true)
4647                 // Set the component to be that of the test class in order to enable compat changes
4648                 .setComponent(ComponentName.createRelative(mContext,
4649                         com.android.server.wm.SizeCompatTests.class.getName()))
4650                 .build();
4651         final Task task = activity.getTask();
4652         spyOn(activity.mLetterboxUiController);
4653         doReturn(true).when(activity.mLetterboxUiController).shouldSendFakeFocus();
4654 
4655         task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
4656         assertTrue(activity.shouldSendCompatFakeFocus());
4657 
4658         task.setWindowingMode(WINDOWING_MODE_PINNED);
4659         assertFalse(activity.shouldSendCompatFakeFocus());
4660 
4661         task.setWindowingMode(WINDOWING_MODE_FREEFORM);
4662         assertFalse(activity.shouldSendCompatFakeFocus());
4663     }
4664 
4665     @Test
4666     public void testShouldSendFakeFocus_compatFakeFocusDisabled() {
4667         final ActivityRecord activity = new ActivityBuilder(mAtm)
4668                 .setCreateTask(true)
4669                 .setOnTop(true)
4670                 // Set the component to be that of the test class in order to enable compat changes
4671                 .setComponent(ComponentName.createRelative(mContext,
4672                         com.android.server.wm.SizeCompatTests.class.getName()))
4673                 .build();
4674         final Task task = activity.getTask();
4675         spyOn(activity.mLetterboxUiController);
4676         doReturn(false).when(activity.mLetterboxUiController).shouldSendFakeFocus();
4677 
4678         task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
4679         assertFalse(activity.shouldSendCompatFakeFocus());
4680 
4681         task.setWindowingMode(WINDOWING_MODE_PINNED);
4682         assertFalse(activity.shouldSendCompatFakeFocus());
4683 
4684         task.setWindowingMode(WINDOWING_MODE_FREEFORM);
4685         assertFalse(activity.shouldSendCompatFakeFocus());
4686     }
4687 
4688     private int getExpectedSplitSize(int dimensionToSplit) {
4689         int dividerWindowWidth =
4690                 mActivity.mWmService.mContext.getResources().getDimensionPixelSize(
4691                         com.android.internal.R.dimen.docked_stack_divider_thickness);
4692         int dividerInsets =
4693                 mActivity.mWmService.mContext.getResources().getDimensionPixelSize(
4694                         com.android.internal.R.dimen.docked_stack_divider_insets);
4695         return (dimensionToSplit - (dividerWindowWidth - dividerInsets * 2)) / 2;
4696     }
4697 
4698     private void assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
4699             float letterboxHorizontalPositionMultiplier) {
4700         // Set up a display in landscape and ignoring orientation request.
4701         setUpDisplaySizeWithApp(2800, 1400);
4702         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4703 
4704         mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
4705                 letterboxHorizontalPositionMultiplier);
4706         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
4707         assertFitted();
4708         // Rotate to put activity in size compat mode.
4709         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
4710         assertTrue(mActivity.inSizeCompatMode());
4711         // Activity is in size compat mode but not scaled.
4712         assertEquals(new Rect(0, 0, 1400, 700), mActivity.getBounds());
4713     }
4714 
4715     private void assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity(
4716             float letterboxVerticalPositionMultiplier) {
4717         // Set up a display in portrait and ignoring orientation request.
4718         setUpDisplaySizeWithApp(1400, 2800);
4719         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
4720 
4721         mActivity.mWmService.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(
4722                 letterboxVerticalPositionMultiplier);
4723         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
4724 
4725         assertFitted();
4726 
4727         // Rotate to put activity in size compat mode.
4728         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
4729 
4730         assertTrue(mActivity.inSizeCompatMode());
4731         // Activity is in size compat mode but not scaled.
4732         assertEquals(new Rect(1050, 0, 1750, 1400), mActivity.getBounds());
4733     }
4734 
4735     private static WindowState addWindowToActivity(ActivityRecord activity) {
4736         final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
4737         params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
4738         params.setFitInsetsSides(0);
4739         params.setFitInsetsTypes(0);
4740         final TestWindowState w = new TestWindowState(
4741                 activity.mWmService, mock(Session.class), new TestIWindow(), params, activity);
4742         makeWindowVisible(w);
4743         w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
4744         activity.addWindow(w);
4745         return w;
4746     }
4747 
4748     private static TestWindowState addStatusBar(DisplayContent displayContent) {
4749         final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
4750         doReturn(true).when(displayPolicy).hasStatusBar();
4751         displayPolicy.onConfigurationChanged();
4752 
4753         final TestWindowToken token = createTestWindowToken(
4754                 TYPE_STATUS_BAR, displayContent);
4755         final WindowManager.LayoutParams attrs =
4756                 new WindowManager.LayoutParams(TYPE_STATUS_BAR);
4757         final Binder owner = new Binder();
4758         attrs.gravity = android.view.Gravity.TOP;
4759         attrs.layoutInDisplayCutoutMode =
4760                 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
4761         attrs.setFitInsetsTypes(0 /* types */);
4762         attrs.providedInsets = new InsetsFrameProvider[] {
4763                 new InsetsFrameProvider(owner, 0, WindowInsets.Type.statusBars()),
4764                 new InsetsFrameProvider(owner, 0, WindowInsets.Type.tappableElement()),
4765                 new InsetsFrameProvider(owner, 0, WindowInsets.Type.mandatorySystemGestures())
4766         };
4767         final TestWindowState statusBar = new TestWindowState(
4768                 displayContent.mWmService, mock(Session.class), new TestIWindow(), attrs, token);
4769         token.addWindow(statusBar);
4770         statusBar.setRequestedSize(displayContent.mBaseDisplayWidth,
4771                 SystemBarUtils.getStatusBarHeight(displayContent.getDisplayUiContext()));
4772 
4773         displayPolicy.addWindowLw(statusBar, attrs);
4774         displayPolicy.layoutWindowLw(statusBar, null, displayContent.mDisplayFrames);
4775         return statusBar;
4776     }
4777 
4778     /**
4779      * Returns an ActivityRecord instance with the specified attributes on the same task. By
4780      * constructing the ActivityRecord, forces {@link ActivityInfo} to be loaded with the compat
4781      * config settings.
4782      */
4783     private ActivityRecord buildActivityRecord(boolean supportsSizeChanges, int resizeMode,
4784             @ScreenOrientation int screenOrientation) {
4785         return new ActivityBuilder(mAtm)
4786                 .setTask(mTask)
4787                 .setResizeMode(resizeMode)
4788                 .setSupportsSizeChanges(supportsSizeChanges)
4789                 .setScreenOrientation(screenOrientation)
4790                 .setComponent(ComponentName.createRelative(mContext,
4791                         SizeCompatTests.class.getName()))
4792                 .setUid(android.os.Process.myUid())
4793                 .build();
4794     }
4795 
4796     static void prepareUnresizable(ActivityRecord activity, int screenOrientation) {
4797         prepareUnresizable(activity, -1 /* maxAspect */, screenOrientation);
4798     }
4799 
4800     static void prepareUnresizable(ActivityRecord activity, float maxAspect,
4801             int screenOrientation) {
4802         prepareLimitedBounds(activity, maxAspect, screenOrientation, true /* isUnresizable */);
4803     }
4804 
4805     static void prepareLimitedBounds(ActivityRecord activity, int screenOrientation,
4806             boolean isUnresizable) {
4807         prepareLimitedBounds(activity, -1 /* maxAspect */, screenOrientation, isUnresizable);
4808     }
4809 
4810     /**
4811      * Setups {@link #mActivity} with restriction on its bounds, such as maxAspect, fixed
4812      * orientation, and/or whether it is resizable.
4813      */
4814     static void prepareLimitedBounds(ActivityRecord activity, float maxAspect,
4815             int screenOrientation, boolean isUnresizable) {
4816         activity.info.resizeMode = isUnresizable
4817                 ? RESIZE_MODE_UNRESIZEABLE
4818                 : RESIZE_MODE_RESIZEABLE;
4819         final Task task = activity.getTask();
4820         if (task != null) {
4821             // Update the Task resize value as activity will follow the task.
4822             task.mResizeMode = activity.info.resizeMode;
4823             task.getRootActivity().info.resizeMode = activity.info.resizeMode;
4824         }
4825         activity.setVisibleRequested(true);
4826         if (maxAspect >= 0) {
4827             activity.info.setMaxAspectRatio(maxAspect);
4828         }
4829         if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
4830             activity.info.screenOrientation = screenOrientation;
4831             activity.setRequestedOrientation(screenOrientation);
4832         }
4833         // Make sure to use the provided configuration to construct the size compat fields.
4834         activity.clearSizeCompatMode();
4835         activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
4836         // Make sure the display configuration reflects the change of activity.
4837         if (activity.mDisplayContent.updateOrientation()) {
4838             activity.mDisplayContent.sendNewConfiguration();
4839         }
4840     }
4841 
4842     /** Asserts that the size of activity is larger than its parent so it is scaling. */
4843     private void assertScaled() {
4844         assertTrue(mActivity.inSizeCompatMode());
4845         assertNotEquals(1f, mActivity.getCompatScale(), 0.0001f /* delta */);
4846     }
4847 
4848     /** Asserts that the activity is best fitted in the parent. */
4849     private void assertFitted() {
4850         final boolean inSizeCompatMode = mActivity.inSizeCompatMode();
4851         final String failedConfigInfo = inSizeCompatMode
4852                 ? ("ParentConfig=" + mActivity.getParent().getConfiguration()
4853                         + " ActivityConfig=" + mActivity.getConfiguration())
4854                 : "";
4855         assertFalse(failedConfigInfo, inSizeCompatMode);
4856         assertFalse(mActivity.hasSizeCompatBounds());
4857     }
4858 
4859     /** Asserts the activity max bounds inherit from the TaskDisplayArea. */
4860     private void assertMaxBoundsInheritDisplayAreaBounds() {
4861         assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
4862                 .isEqualTo(mTask.getDisplayArea().getBounds());
4863     }
4864 
4865     /**
4866      * Asserts activity-level letterbox or size compat mode size compat mode, so activity max
4867      * bounds are sandboxed.
4868      */
4869     private void assertActivityMaxBoundsSandboxed() {
4870         assertActivityMaxBoundsSandboxed(mActivity);
4871     }
4872 
4873     /**
4874      * Asserts activity-level letterbox or size compat mode size compat mode on the specified
4875      * activity, so activity max bounds are sandboxed.
4876      */
4877     private void assertActivityMaxBoundsSandboxed(ActivityRecord activity) {
4878         // Activity max bounds are sandboxed due to size compat mode.
4879         assertThat(activity.getConfiguration().windowConfiguration.getMaxBounds())
4880                 .isEqualTo(activity.getWindowConfiguration().getBounds());
4881     }
4882 
4883     private void verifyLogAppCompatState(ActivityRecord activity, int state) {
4884         verify(mActivityMetricsLogger, atLeastOnce()).logAppCompatState(
4885                 argThat(r -> activity == r && r.getAppCompatState() == state));
4886     }
4887 
4888     static Configuration rotateDisplay(DisplayContent display, int rotation) {
4889         final Configuration c = new Configuration();
4890         display.getDisplayRotation().setRotation(rotation);
4891         display.computeScreenConfiguration(c);
4892         display.onRequestedOverrideConfigurationChanged(c);
4893         return c;
4894     }
4895 
4896     private static void setNeverConstrainDisplayApisFlag(@Nullable String value,
4897             boolean makeDefault) {
4898         DeviceConfig.setProperty(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
4899                 CONFIG_NEVER_CONSTRAIN_DISPLAY_APIS, value, makeDefault);
4900     }
4901 
4902     private static void setNeverConstrainDisplayApisAllPackagesFlag(boolean value,
4903             boolean makeDefault) {
4904         DeviceConfig.setProperty(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
4905                 CONFIG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES, String.valueOf(value),
4906                 makeDefault);
4907     }
4908 
4909     private static void setAlwaysConstrainDisplayApisFlag(@Nullable String value,
4910             boolean makeDefault) {
4911         DeviceConfig.setProperty(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
4912                 CONFIG_ALWAYS_CONSTRAIN_DISPLAY_APIS, value, makeDefault);
4913     }
4914 }
4915