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