1 /*
2  * Copyright (C) 2021 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 android.server.wm.display;
18 
19 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
20 import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
21 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
22 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
23 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
24 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE;
25 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE;
26 import static android.content.pm.ActivityInfo.OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS;
27 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
28 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
29 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
30 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
31 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
32 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
33 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
34 import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
35 import static android.server.wm.allowdisplayorientationoverride.Components.ALLOW_DISPLAY_ORIENTATION_OVERRIDE_ACTIVITY;
36 import static android.server.wm.allowignoringorientationrequestwhenloopdetectedoptin.Components.ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED_OPT_IN_ACTIVITY;
37 import static android.server.wm.allowignoringorientationrequestwhenloopdetectedoptout.Components.ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED_OPT_OUT_ACTIVITY;
38 import static android.server.wm.allowignoringorientationrequestwhenloopdetectedunset.Components.ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED_UNSET_ACTIVITY;
39 import static android.server.wm.allowminaspectratiooverrideoptin.Components.ALLOW_MIN_ASPECT_RATIO_OVERRIDE_OPT_IN_ACTIVITY;
40 import static android.server.wm.allowminaspectratiooverrideoptout.Components.ALLOW_MIN_ASPECT_RATIO_OVERRIDE_OPT_OUT_ACTIVITY;
41 import static android.server.wm.alloworientationoverride.Components.ALLOW_ORIENTATION_OVERRIDE_LANDSCAPE_ACTIVITY;
42 import static android.server.wm.alloworientationoverride.Components.ALLOW_ORIENTATION_OVERRIDE_RESPONSIVE_ACTIVITY;
43 import static android.server.wm.allowresizeableactivityoverridesoptin.Components.ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES_OPT_IN_ACTIVITY;
44 import static android.server.wm.allowresizeableactivityoverridesoptout.Components.ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES_OPT_OUT_ACTIVITY;
45 import static android.server.wm.allowsandboxingviewboundsapis.Components.ACTION_TEST_VIEW_SANDBOX_ALLOWED_PASSED;
46 import static android.server.wm.allowsandboxingviewboundsapis.Components.ACTION_TEST_VIEW_SANDBOX_NOT_ALLOWED_PASSED;
47 import static android.server.wm.allowsandboxingviewboundsapis.Components.TEST_VIEW_SANDBOX_ALLOWED_ACTIVITY;
48 import static android.server.wm.allowsandboxingviewboundsapis.Components.TEST_VIEW_SANDBOX_ALLOWED_TIMEOUT_MS;
49 import static android.server.wm.allowuseraspectratiofullscreenoverrideoptin.Components.ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE_OPT_IN_ACTIVITY;
50 import static android.server.wm.allowuseraspectratiofullscreenoverrideoptout.Components.ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE_OPT_OUT_ACTIVITY;
51 import static android.server.wm.allowuseraspectratiooverrideoptin.Components.ALLOW_USER_ASPECT_RATIO_OVERRIDE_OPT_IN_ACTIVITY;
52 import static android.server.wm.allowuseraspectratiooverrideoptout.Components.ALLOW_USER_ASPECT_RATIO_OVERRIDE_OPT_OUT_ACTIVITY;
53 import static android.server.wm.enablefakefocusoptin.Components.ENABLE_FAKE_FOCUS_OPT_IN_LEFT_ACTIVITY;
54 import static android.server.wm.enablefakefocusoptin.Components.ENABLE_FAKE_FOCUS_OPT_IN_RIGHT_ACTIVITY;
55 import static android.server.wm.enablefakefocusoptout.Components.ENABLE_FAKE_FOCUS_OPT_OUT_LEFT_ACTIVITY;
56 import static android.server.wm.enablefakefocusoptout.Components.ENABLE_FAKE_FOCUS_OPT_OUT_RIGHT_ACTIVITY;
57 import static android.server.wm.ignorerequestedorientationoverrideoptin.Components.OPT_IN_CHANGE_ORIENTATION_WHILE_RELAUNCHING_ACTIVITY;
58 import static android.server.wm.ignorerequestedorientationoverrideoptout.Components.OPT_OUT_CHANGE_ORIENTATION_WHILE_RELAUNCHING_ACTIVITY;
59 import static android.server.wm.optoutsandboxingviewboundsapis.Components.ACTION_TEST_VIEW_SANDBOX_OPT_OUT_PASSED;
60 import static android.server.wm.optoutsandboxingviewboundsapis.Components.TEST_VIEW_SANDBOX_OPT_OUT_ACTIVITY;
61 import static android.server.wm.optoutsandboxingviewboundsapis.Components.TEST_VIEW_SANDBOX_OPT_OUT_TIMEOUT_MS;
62 import static android.server.wm.propertycameracompatallowforcerotation.Components.CAMERA_COMPAT_ALLOW_FORCE_ROTATION_ACTIVITY;
63 import static android.server.wm.propertycameracompatallowrefresh.Components.CAMERA_COMPAT_ALLOW_REFRESH_ACTIVITY;
64 import static android.server.wm.propertycameracompatenablerefreshviapauseoptin.Components.CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE_OPT_IN_ACTIVITY;
65 import static android.server.wm.propertycameracompatenablerefreshviapauseoptout.Components.CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE_OPT_OUT_ACTIVITY;
66 import static android.view.Surface.ROTATION_0;
67 import static android.view.Surface.ROTATION_90;
68 
69 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
70 
71 import static com.google.common.truth.Truth.assertThat;
72 
73 import static org.junit.Assert.assertEquals;
74 import static org.junit.Assert.assertFalse;
75 import static org.junit.Assert.assertNotNull;
76 import static org.junit.Assert.assertTrue;
77 import static org.junit.Assume.assumeFalse;
78 import static org.junit.Assume.assumeTrue;
79 
80 import android.app.Activity;
81 import android.app.WindowConfiguration;
82 import android.compat.testing.PlatformCompatChangeRule;
83 import android.content.BroadcastReceiver;
84 import android.content.ComponentName;
85 import android.content.Context;
86 import android.content.Intent;
87 import android.content.IntentFilter;
88 import android.content.pm.ActivityInfo;
89 import android.content.pm.PackageManager;
90 import android.content.res.Resources;
91 import android.graphics.Rect;
92 import android.os.Bundle;
93 import android.os.ConditionVariable;
94 import android.platform.test.annotations.Presubmit;
95 import android.platform.test.annotations.RequiresFlagsEnabled;
96 import android.platform.test.flag.junit.CheckFlagsRule;
97 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
98 import android.provider.DeviceConfig;
99 import android.server.wm.HelperActivities;
100 import android.server.wm.MultiDisplayTestBase;
101 import android.server.wm.WindowManagerState;
102 import android.server.wm.app.AbstractLifecycleLogActivity;
103 import android.util.Size;
104 
105 import androidx.annotation.NonNull;
106 import androidx.annotation.Nullable;
107 
108 import com.android.compatibility.common.util.ApiTest;
109 import com.android.window.flags.Flags;
110 
111 import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
112 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
113 
114 import org.junit.Before;
115 import org.junit.Ignore;
116 import org.junit.Rule;
117 import org.junit.Test;
118 import org.junit.rules.TestRule;
119 
120 import java.util.Collections;
121 import java.util.HashMap;
122 import java.util.Map;
123 
124 /**
125  * The test is focused on compatibility changes that have an effect on WM logic, and tests that
126  * enabling these changes has the correct effect.
127  *
128  * This is achieved by launching a custom activity with certain properties (e.g., a resizeable
129  * portrait activity) that behaves in a certain way (e.g., enter size compat mode after resizing the
130  * display) and enabling a compatibility change (e.g., {@link ActivityInfo#FORCE_RESIZE_APP}) that
131  * changes that behavior (e.g., not enter size compat mode).
132  *
133  * The behavior without enabling a compatibility change is also tested as a baseline.
134  *
135  * <p>Build/Install/Run:
136  * atest CtsWindowManagerDeviceDisplay:CompatChangeTests
137  */
138 @Presubmit
139 public final class CompatChangeTests extends MultiDisplayTestBase {
140     private static final ComponentName RESIZEABLE_PORTRAIT_ACTIVITY =
141             component(HelperActivities.ResizeablePortraitActivity.class);
142     private static final ComponentName NON_RESIZEABLE_PORTRAIT_ACTIVITY =
143             component(HelperActivities.NonResizeablePortraitActivity.class);
144     private static final ComponentName NON_RESIZEABLE_LANDSCAPE_ACTIVITY =
145             component(HelperActivities.NonResizeableLandscapeActivity.class);
146     private static final ComponentName NON_RESIZEABLE_NON_FIXED_ORIENTATION_ACTIVITY =
147             component(HelperActivities.NonResizeableNonFixedOrientationActivity.class);
148     private static final ComponentName NON_RESIZEABLE_ASPECT_RATIO_ACTIVITY =
149             component(HelperActivities.NonResizeableAspectRatioActivity.class);
150     private static final ComponentName NON_RESIZEABLE_LARGE_ASPECT_RATIO_ACTIVITY =
151             component(HelperActivities.NonResizeableLargeAspectRatioActivity.class);
152     private static final ComponentName SUPPORTS_SIZE_CHANGES_PORTRAIT_ACTIVITY =
153             component(HelperActivities.SupportsSizeChangesPortraitActivity.class);
154     private static final ComponentName RESIZEABLE_LEFT_ACTIVITY =
155             component(HelperActivities.ResizeableLeftActivity.class);
156     private static final ComponentName RESIZEABLE_RIGHT_ACTIVITY =
157             component(HelperActivities.ResizeableRightActivity.class);
158     private static final ComponentName RESPONSIVE_ACTIVITY =
159             component(HelperActivities.ResponsiveActivity.class);
160     private static final ComponentName NO_PROPERTY_CHANGE_ORIENTATION_WHILE_RELAUNCHING_ACTIVITY =
161             component(HelperActivities.NoPropertyChangeOrientationWhileRelaunchingActivity.class);
162 
163     // Fixed orientation min aspect ratio
164     private static final float FIXED_ORIENTATION_MIN_ASPECT_RATIO = 1.03f;
165     // The min aspect ratio of NON_RESIZEABLE_ASPECT_RATIO_ACTIVITY (as defined in the manifest).
166     private static final float ACTIVITY_MIN_ASPECT_RATIO = 1.6f;
167     // The min aspect ratio of NON_RESIZEABLE_LARGE_ASPECT_RATIO_ACTIVITY (as defined in the
168     // manifest). This needs to be higher than the aspect ratio of any device, which according to
169     // CDD is at most 21:9.
170     private static final float ACTIVITY_LARGE_MIN_ASPECT_RATIO = 4f;
171 
172     private static final float FLOAT_EQUALITY_DELTA = 0.01f;
173 
174     @Rule
175     public TestRule compatChangeRule = new PlatformCompatChangeRule();
176     @Rule
177     public final CheckFlagsRule checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
178 
179     @Before
180     @Override
setUp()181     public void setUp() throws Exception {
182         super.setUp();
183         enableAndAssumeGestureNavigationMode();
184         createManagedLetterboxAspectRatioSession(FIXED_ORIENTATION_MIN_ASPECT_RATIO);
185         mObjectTracker.manage(setAlwaysConstrainDisplayApisFlag(null));
186         mObjectTracker.manage(setNeverConstrainDisplayApisAllPackagesFlag(null));
187         mObjectTracker.manage(setNeverConstrainDisplayApisFlag(null));
188     }
189 
190     @Test
testOverrideUndefinedOrientationToPortrait_propertyIsFalse_overrideNotApplied()191     public void testOverrideUndefinedOrientationToPortrait_propertyIsFalse_overrideNotApplied() {
192         try (var compatChange = new CompatChangeCloseable(
193                 ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT,
194                 ALLOW_ORIENTATION_OVERRIDE_RESPONSIVE_ACTIVITY.getPackageName());
195              var session = new ActivitySessionCloseable(
196                      ALLOW_ORIENTATION_OVERRIDE_RESPONSIVE_ACTIVITY)) {
197             waitAssertEquals("expected unspecified orientation",
198                     SCREEN_ORIENTATION_UNSPECIFIED,
199                     () -> session.getActivityState().getOverrideOrientation());
200         }
201     }
202 
203     @Test
204     @EnableCompatChanges({ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT})
testOverrideUndefinedOrientationToPortrait()205     public void testOverrideUndefinedOrientationToPortrait() {
206         try (var session = new ActivitySessionCloseable(RESPONSIVE_ACTIVITY)) {
207             waitAssertEquals("expected portrait orientation", SCREEN_ORIENTATION_PORTRAIT,
208                     () -> session.getActivityState().getOverrideOrientation());
209         }
210     }
211 
212     @Test
testOverrideUndefinedOrientationToNoSensor_propertyIsFalse_overrideNotApplied()213     public void testOverrideUndefinedOrientationToNoSensor_propertyIsFalse_overrideNotApplied() {
214         try (var compatChange = new CompatChangeCloseable(
215                 ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR,
216                 ALLOW_ORIENTATION_OVERRIDE_RESPONSIVE_ACTIVITY.getPackageName());
217             var session = new ActivitySessionCloseable(
218                     ALLOW_ORIENTATION_OVERRIDE_RESPONSIVE_ACTIVITY)) {
219             waitAssertEquals("expected unspecified orientation",
220                     SCREEN_ORIENTATION_UNSPECIFIED,
221                     () -> session.getActivityState().getOverrideOrientation());
222         }
223     }
224 
225     @Test
226     @EnableCompatChanges({ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR})
testOverrideUndefinedOrientationToNosensor()227     public void testOverrideUndefinedOrientationToNosensor() {
228         try (var session = new ActivitySessionCloseable(RESPONSIVE_ACTIVITY)) {
229             waitAssertEquals("expected no-sensor orientation",
230                     SCREEN_ORIENTATION_NOSENSOR,
231                     () -> session.getActivityState().getOverrideOrientation());
232         }
233     }
234 
235     @Test
236     public void
testOverrideLandscapeOrientationToReverseLandscape_propertyIsFalse_overrideNotApply()237             testOverrideLandscapeOrientationToReverseLandscape_propertyIsFalse_overrideNotApply() {
238         try (var compatChange = new CompatChangeCloseable(
239                 ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE,
240                 ALLOW_ORIENTATION_OVERRIDE_LANDSCAPE_ACTIVITY.getPackageName());
241              var session = new ActivitySessionCloseable(
242                      ALLOW_ORIENTATION_OVERRIDE_LANDSCAPE_ACTIVITY)) {
243             waitAssertEquals("expected landscape orientation", SCREEN_ORIENTATION_LANDSCAPE,
244                     () -> session.getActivityState().getOverrideOrientation());
245         }
246     }
247 
248     @Test
249     @EnableCompatChanges({ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE})
testOverrideLandscapeOrientationToReverseLandscape()250     public void testOverrideLandscapeOrientationToReverseLandscape() {
251         try (var session = new ActivitySessionCloseable(NON_RESIZEABLE_LANDSCAPE_ACTIVITY)) {
252             waitAssertEquals("expected reverse landscape orientation",
253                     SCREEN_ORIENTATION_REVERSE_LANDSCAPE,
254                     () -> session.getActivityState().getOverrideOrientation());
255         }
256     }
257 
258     @Test
259     @EnableCompatChanges({ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
testOverrideUseDisplayLandscapeNaturalOrientation()260     public void testOverrideUseDisplayLandscapeNaturalOrientation() {
261         try (var displayMetricsSession = new DisplayMetricsWaitCloseable()) {
262             // Run this test only when natural orientation is landscape
263             Size displaySize = displayMetricsSession.getInitialDisplayMetrics().getSize();
264             assumeTrue(displaySize.getHeight() < displaySize.getWidth());
265         }
266 
267         try (var portrait = new DeviceOrientationCloseable(ORIENTATION_PORTRAIT);
268              var session = new ActivitySessionCloseable(RESPONSIVE_ACTIVITY)) {
269             // Verifying that orientation is overridden
270             waitAssertEquals("expected rotation 0",
271                     ROTATION_0, () -> mWmState.getRotation());
272         }
273     }
274 
275     @Test
276     public void
testOverrideUseDisplayLandscapeNaturalOrientation_propertyIsFalse_overrideNotApplied()277             testOverrideUseDisplayLandscapeNaturalOrientation_propertyIsFalse_overrideNotApplied() {
278         try (var displayMetricsSession = new DisplayMetricsWaitCloseable()) {
279             // Run this test only when natural orientation is landscape
280             Size displaySize = displayMetricsSession.getInitialDisplayMetrics().getSize();
281             assumeTrue(displaySize.getHeight() < displaySize.getWidth());
282         }
283 
284         final int originalRotation = mWmState.getRotation();
285 
286         try (var portrait = new DeviceOrientationCloseable(ORIENTATION_PORTRAIT);
287              var compatChange = new CompatChangeCloseable(
288                 ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION,
289                 ALLOW_DISPLAY_ORIENTATION_OVERRIDE_ACTIVITY.getPackageName());
290              var session = new ActivitySessionCloseable(
291                     ALLOW_DISPLAY_ORIENTATION_OVERRIDE_ACTIVITY)) {
292 
293             // Verifying that orientation not overridden
294             if (portrait.isRotationApplied()) {
295                 // If the screen was rotated away from natural orientation (to portrait)
296                 waitAssertEquals("expected rotation 90",
297                         ROTATION_90, () -> mWmState.getRotation());
298             } else {
299                 // If the screen was already in portrait (rotated away from natural orientation)
300                 waitAssertEquals("expected originalRotation=" + originalRotation,
301                         originalRotation, () -> mWmState.getRotation());
302             }
303         }
304     }
305 
306     @Test
testEnableFakeFocus_propertyIsFalse_overrideNotApplied()307     public void testEnableFakeFocus_propertyIsFalse_overrideNotApplied() {
308         assumeTrue("Skipping test: no split multi-window support",
309                 supportsSplitScreenMultiWindow());
310         assumeTrue("Skipping test: config_isCompatFakeFocusEnabled not enabled",
311                 getFakeFocusEnabledConfig());
312 
313         try (var compatChange = new CompatChangeCloseable(
314                 OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS,
315                 ENABLE_FAKE_FOCUS_OPT_OUT_LEFT_ACTIVITY.getPackageName());
316              var splitScreen = new SplitScreenActivitiesCloseable(
317                      ENABLE_FAKE_FOCUS_OPT_OUT_LEFT_ACTIVITY,
318                      ENABLE_FAKE_FOCUS_OPT_OUT_RIGHT_ACTIVITY)) {
319             waitAssertEquals("expected should not send compat fake focus",
320                     /* expected */ false,
321                     () -> splitScreen.getPrimaryActivity()
322                             .getActivityState().getShouldSendCompatFakeFocus());
323         }
324     }
325 
326     @Test
testEnableFakeFocus_propertyIsTrue_returnsTrue()327     public void testEnableFakeFocus_propertyIsTrue_returnsTrue() {
328         assumeTrue("Skipping test: no split multi-window support",
329                 supportsSplitScreenMultiWindow());
330         assumeTrue("Skipping test: config_isCompatFakeFocusEnabled not enabled",
331                 getFakeFocusEnabledConfig());
332 
333         try (var splitScreen = new SplitScreenActivitiesCloseable(
334                      ENABLE_FAKE_FOCUS_OPT_IN_LEFT_ACTIVITY,
335                      ENABLE_FAKE_FOCUS_OPT_IN_RIGHT_ACTIVITY)) {
336             waitAssertEquals("expected should send compat fake focus", /* expected */ true,
337                     () -> splitScreen.getPrimaryActivity()
338                             .getActivityState().getShouldSendCompatFakeFocus());
339         }
340     }
341 
342     @Test
343     @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
testEnableFakeFocus_overrideApplied_returnsTrue()344     public void testEnableFakeFocus_overrideApplied_returnsTrue() {
345         assumeTrue("Skipping test: no split multi-window support",
346                 supportsSplitScreenMultiWindow());
347         assumeTrue("Skipping test: config_isCompatFakeFocusEnabled not enabled",
348                 getFakeFocusEnabledConfig());
349 
350         try (var splitScreen = new SplitScreenActivitiesCloseable(
351                      RESIZEABLE_LEFT_ACTIVITY,
352                      RESIZEABLE_RIGHT_ACTIVITY)) {
353             waitAssertEquals("expected should send compat fake focus", /* expected */ true,
354                     () -> splitScreen.getPrimaryActivity()
355                             .getActivityState().getShouldSendCompatFakeFocus());
356         }
357     }
358 
getFakeFocusEnabledConfig()359     boolean getFakeFocusEnabledConfig() {
360         return mContext.getResources().getBoolean(
361                 Resources.getSystem().getIdentifier(
362                         "config_isCompatFakeFocusEnabled",
363                         "bool", "android"));
364     }
365 
366     @Test
367     @Ignore("b/295873734 flaky")
testOverrideIgnoreRequestedOrientation_propertyIsFalse_overrideNotApplied()368     public void testOverrideIgnoreRequestedOrientation_propertyIsFalse_overrideNotApplied() {
369         assumeTrue("Skipping test: "
370                     + "config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled not enabled",
371                 isPolicyForIgnoringRequestedOrientationEnabled());
372 
373         try (var compatChange = new CompatChangeCloseable(
374                     ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION,
375                     OPT_OUT_CHANGE_ORIENTATION_WHILE_RELAUNCHING_ACTIVITY.getPackageName());
376              var session = new ActivitySessionCloseable(
377                      OPT_OUT_CHANGE_ORIENTATION_WHILE_RELAUNCHING_ACTIVITY)) {
378             waitAssertEquals("expected landscape orientation",
379                     SCREEN_ORIENTATION_LANDSCAPE,
380                     () -> session.getActivityState().getOverrideOrientation());
381         }
382     }
383 
384     @Test
testOverrideIgnoreRequestedOrientation_isDisabled_propertyIsTrue_overrideApplied()385     public void testOverrideIgnoreRequestedOrientation_isDisabled_propertyIsTrue_overrideApplied() {
386         assumeTrue("Skipping test: "
387                     + "config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled not enabled",
388                 isPolicyForIgnoringRequestedOrientationEnabled());
389 
390         try (var session = new ActivitySessionCloseable(
391                      OPT_IN_CHANGE_ORIENTATION_WHILE_RELAUNCHING_ACTIVITY)) {
392             waitAssertEquals("expected portrait orientation",
393                     SCREEN_ORIENTATION_PORTRAIT,
394                     () -> session.getActivityState().getOverrideOrientation());
395         }
396     }
397 
398     @Test
399     @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
testOverrideIgnoreRequestedOrientation()400     public void testOverrideIgnoreRequestedOrientation() {
401         assumeTrue("Skipping test: "
402                     + "config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled not enabled",
403                 isPolicyForIgnoringRequestedOrientationEnabled());
404 
405         try (var session = new ActivitySessionCloseable(
406                      NO_PROPERTY_CHANGE_ORIENTATION_WHILE_RELAUNCHING_ACTIVITY)) {
407             waitAssertEquals("expected portrait orientation",
408                     SCREEN_ORIENTATION_PORTRAIT,
409                     () -> session.getActivityState().getOverrideOrientation());
410         }
411     }
412 
413     /**
414      * Test that if an orientation loop is detected, the orientation request is ignored when
415      * {@link ActivityInfo#OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED} is
416      * enabled and {@link
417      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}
418      * is set to true.
419      */
420     @Test
421     @ApiTest(apis = {"android.content.pm.ActivityInfo#OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED",
422             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED"})
423     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testOverrideIgnoreOrientationRequestWhenLoopDetected_propertyTrue_overrideEnabled_overrideApplied()424     public void testOverrideIgnoreOrientationRequestWhenLoopDetected_propertyTrue_overrideEnabled_overrideApplied() {
425         assumeTrue("Skipping test: "
426                         + "config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled disabled",
427                 isPolicyForIgnoringRequestedOrientationEnabled());
428 
429         try (var compatChange = new CompatChangeCloseable(
430                 OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED,
431                 ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED_OPT_IN_ACTIVITY
432                         .getPackageName());
433                 var session = new ActivitySessionCloseable(
434                         ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED_OPT_IN_ACTIVITY)) {
435             // Activity with property set to true and override enabled. Orientation request should
436             // be ignored if a loop is detected.
437             assertTrue(session.getActivityState().getShouldIgnoreOrientationRequestLoop());
438         }
439     }
440 
441     /**
442      * Test that if an orientation loop is detected, the orientation request is respected when
443      * {@link ActivityInfo#OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED} is
444      * enabled but {@link
445      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}
446      * is set to false.
447      */
448     @Test
449     @ApiTest(apis = {"android.content.pm.ActivityInfo#OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED",
450             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED"})
451     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testOverrideIgnoreOrientationRequestWhenLoopDetected_propertyFalse_overrideEnabled_overrideNotApplied()452     public void testOverrideIgnoreOrientationRequestWhenLoopDetected_propertyFalse_overrideEnabled_overrideNotApplied() {
453         assumeTrue("Skipping test: "
454                         + "config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled disabled",
455                 isPolicyForIgnoringRequestedOrientationEnabled());
456 
457         try (var compatChange = new CompatChangeCloseable(
458                 OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED,
459                 ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED_OPT_OUT_ACTIVITY
460                         .getPackageName());
461                 var session = new ActivitySessionCloseable(
462                         ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED_OPT_OUT_ACTIVITY)) {
463             // Activity with property set to false and override enabled. Orientation request should
464             // be respected even if a loop is detected.
465             assertFalse(session.getActivityState().getShouldIgnoreOrientationRequestLoop());
466 
467 
468         }
469     }
470 
471     /**
472      * Test that if an orientation loop is detected, the orientation request is respected when
473      * {@link ActivityInfo#OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED} is
474      * disabled but {@link
475      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}
476      * is set to true.
477      */
478     @Test
479     @ApiTest(apis = {"android.content.pm.ActivityInfo#OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED",
480             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED"})
481     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testOverrideIgnoreOrientationRequestWhenLoopDetected_propertyTrue_overrideDisabled_overrideNotApplied()482     public void testOverrideIgnoreOrientationRequestWhenLoopDetected_propertyTrue_overrideDisabled_overrideNotApplied() {
483         assumeTrue("Skipping test: "
484                         + "config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled disabled",
485                 isPolicyForIgnoringRequestedOrientationEnabled());
486 
487         try (var session = new ActivitySessionCloseable(
488                      ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED_OPT_IN_ACTIVITY)) {
489             // Activity with property set to true but with override disabled. Orientation request
490             // should be respected even if a loop is detected.
491             assertFalse(session.getActivityState().getShouldIgnoreOrientationRequestLoop());
492         }
493     }
494 
495     /**
496      * Test that if an orientation loop is detected, the orientation request is respected when
497      * {@link ActivityInfo#OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED} is
498      * disabled and {@link
499      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}
500      * is set to false.
501      */
502     @Test
503     @ApiTest(apis = {"android.content.pm.ActivityInfo#OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED",
504             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED"})
505     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testOverrideIgnoreOrientationRequestWhenLoopDetected_propertyFalse_overrideDisabled_overrideNotApplied()506     public void testOverrideIgnoreOrientationRequestWhenLoopDetected_propertyFalse_overrideDisabled_overrideNotApplied() {
507         assumeTrue("Skipping test: "
508                         + "config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled disabled",
509                 isPolicyForIgnoringRequestedOrientationEnabled());
510 
511         try (var session = new ActivitySessionCloseable(
512                      ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED_OPT_OUT_ACTIVITY)) {
513             // Activity with property set to false and with override disabled. Orientation request
514             // should be respected even if a loop is detected.
515             assertFalse(session.getActivityState().getShouldIgnoreOrientationRequestLoop());
516         }
517     }
518 
519     /**
520      * Test that if an orientation loop is detected, the orientation request is ignored when
521      * {@link ActivityInfo#OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED} is
522      * enabled and {@link
523      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}
524      * is unset.
525      */
526     @Test
527     @ApiTest(apis = {"android.content.pm.ActivityInfo#OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED",
528             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED"})
529     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testOverrideIgnoreOrientationRequestWhenLoopDetected_propertyNotSet_overrideEnabled_overrideApplied()530     public void testOverrideIgnoreOrientationRequestWhenLoopDetected_propertyNotSet_overrideEnabled_overrideApplied() {
531         assumeTrue("Skipping test: "
532                         + "config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled disabled",
533                 isPolicyForIgnoringRequestedOrientationEnabled());
534 
535         try (var compatChange = new CompatChangeCloseable(
536                 OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED,
537                 ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED_UNSET_ACTIVITY
538                         .getPackageName());
539                 var session = new ActivitySessionCloseable(
540                         ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED_UNSET_ACTIVITY)) {
541             // Activity without property set and override enabled. Property will default to true.
542             // Orientation request should be ignored if a loop is detected.
543             assertTrue(session.getActivityState().getShouldIgnoreOrientationRequestLoop());
544         }
545     }
546 
547     /**
548      * Test that if an orientation loop is detected, the orientation request is respected when
549      * {@link ActivityInfo#OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED} is
550      * disabled and {@link
551      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}
552      * is unset.
553      */
554     @Test
555     @ApiTest(apis = {"android.content.pm.ActivityInfo#OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED",
556             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED"})
557     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testOverrideIgnoreOrientationRequestWhenLoopDetected_propertyNotSet_overrideDisabled_overrideNotApplied()558     public void testOverrideIgnoreOrientationRequestWhenLoopDetected_propertyNotSet_overrideDisabled_overrideNotApplied() {
559         assumeTrue("Skipping test: "
560                         + "config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled disabled",
561                 isPolicyForIgnoringRequestedOrientationEnabled());
562 
563         try (var session = new ActivitySessionCloseable(
564                 ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED_UNSET_ACTIVITY)) {
565             // Activity without property set and with override disabled. Orientation request should
566             // be respected even if a loop is detected.
567             assertFalse(session.getActivityState().getShouldIgnoreOrientationRequestLoop());
568         }
569     }
570 
571 
572     @Test
testOptOutPropertyCameraCompatForceRotation_rotationDisabled()573     public void testOptOutPropertyCameraCompatForceRotation_rotationDisabled() {
574         assumeTrue("Skipping test: config_isWindowManagerCameraCompatTreatmentEnabled not enabled",
575                 isCameraCompatForceRotationTreatmentConfigEnabled());
576 
577         try (var session = new ActivitySessionCloseable(RESIZEABLE_PORTRAIT_ACTIVITY)) {
578             // Activity without property or override is eligible for force rotation.
579             waitAssertEquals("expected to force rotate for camera compat",
580                     /* expected */ true,
581                     () -> session.getActivityState().getShouldForceRotateForCameraCompat());
582         }
583 
584         try (var session = new ActivitySessionCloseable(
585                      CAMERA_COMPAT_ALLOW_FORCE_ROTATION_ACTIVITY)) {
586             waitAssertEquals("expected to not force rotate for camera compat",
587                     /* expected */ false,
588                     () -> session.getActivityState().getShouldForceRotateForCameraCompat());
589         }
590     }
591 
592     @Test
593     @EnableCompatChanges({ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION})
testOverrideForCameraCompatForceRotation_rotationDisabled()594     public void testOverrideForCameraCompatForceRotation_rotationDisabled() {
595         assumeTrue("Skipping test: config_isWindowManagerCameraCompatTreatmentEnabled not enabled",
596                 isCameraCompatForceRotationTreatmentConfigEnabled());
597 
598         try (var session = new ActivitySessionCloseable(RESIZEABLE_PORTRAIT_ACTIVITY)) {
599             waitAssertEquals("expected to not force rotate for camera compat",
600                     /* expected */ false,
601                     () -> session.getActivityState().getShouldForceRotateForCameraCompat());
602         }
603     }
604 
605     @Test
testOptOutPropertyCameraCompatRefresh()606     public void testOptOutPropertyCameraCompatRefresh() {
607         assumeTrue("Skipping test: config_isWindowManagerCameraCompatTreatmentEnabled not enabled",
608                 isCameraCompatForceRotationTreatmentConfigEnabled());
609 
610         try (var session = new ActivitySessionCloseable(RESIZEABLE_PORTRAIT_ACTIVITY)) {
611             // Activity without property or override is eligible for refresh.
612             waitAssertEquals("expected to refresh activity for camera compat",
613                     /* expected */ true,
614                     () -> session.getActivityState().getShouldRefreshActivityForCameraCompat());
615         }
616 
617         try (var session = new ActivitySessionCloseable(CAMERA_COMPAT_ALLOW_REFRESH_ACTIVITY)) {
618             waitAssertEquals("expected to not refresh activity for camera compat",
619                     /* expected */ false,
620                     () -> session.getActivityState().getShouldRefreshActivityForCameraCompat());
621         }
622     }
623 
624     @Test
625     @EnableCompatChanges({ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH})
testOverrideForCameraCompatRefresh()626     public void testOverrideForCameraCompatRefresh() {
627         assumeTrue("Skipping test: config_isWindowManagerCameraCompatTreatmentEnabled not enabled",
628                 isCameraCompatForceRotationTreatmentConfigEnabled());
629 
630         try (var session = new ActivitySessionCloseable(RESIZEABLE_PORTRAIT_ACTIVITY)) {
631             waitAssertEquals("expected to not refresh activity for camera compat",
632                     /* expected */ false,
633                     () -> session.getActivityState().getShouldRefreshActivityForCameraCompat());
634         }
635     }
636 
637     @Test
testOptInPropertyCameraCompatRefreshViaPause()638     public void testOptInPropertyCameraCompatRefreshViaPause() {
639         assumeTrue("Skipping test: config_isWindowManagerCameraCompatTreatmentEnabled not enabled",
640                 isCameraCompatForceRotationTreatmentConfigEnabled());
641 
642         try (var session = new ActivitySessionCloseable(RESIZEABLE_PORTRAIT_ACTIVITY)) {
643             // Activity without property or override doesn't refresh via
644             // "resumed -> paused -> resumed".
645             waitAssertEquals("expected to not refresh activity via pause for camera compat",
646                     /* expected */ false,
647                     () -> session.getActivityState()
648                             .getShouldRefreshActivityViaPauseForCameraCompat());
649         }
650 
651         try (var session = new ActivitySessionCloseable(
652                 CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE_OPT_IN_ACTIVITY)) {
653             waitAssertEquals("expected to Refresh activity via pause for camera compat",
654                     /* expected */ true,
655                     () -> session.getActivityState()
656                             .getShouldRefreshActivityViaPauseForCameraCompat());
657         }
658     }
659 
660     @Test
661     @EnableCompatChanges({ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE})
testOverrideForCameraCompatRefreshViaPause()662     public void testOverrideForCameraCompatRefreshViaPause() {
663         assumeTrue("Skipping test: config_isWindowManagerCameraCompatTreatmentEnabled not enabled",
664                 isCameraCompatForceRotationTreatmentConfigEnabled());
665 
666         try (var session = new ActivitySessionCloseable(RESIZEABLE_PORTRAIT_ACTIVITY)) {
667             waitAssertEquals("expected to Refresh activity via pause for camera compat",
668                     /* expected */ true,
669                     () -> session.getActivityState()
670                             .getShouldRefreshActivityViaPauseForCameraCompat());
671         }
672     }
673 
674     @Test
testOptOutPropertyCameraCompatRefreshViaPause()675     public void testOptOutPropertyCameraCompatRefreshViaPause() {
676         assumeTrue("Skipping test: config_isWindowManagerCameraCompatTreatmentEnabled not enabled",
677                 isCameraCompatForceRotationTreatmentConfigEnabled());
678 
679         try (var compatChange = new CompatChangeCloseable(
680                 ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE,
681                 CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE_OPT_OUT_ACTIVITY.getPackageName());
682              var session = new ActivitySessionCloseable(
683                      CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE_OPT_OUT_ACTIVITY)) {
684             waitAssertEquals("expected to not refresh activity via pause for camera compat",
685                     /* expected */ false,
686                     () -> session.getActivityState()
687                             .getShouldRefreshActivityViaPauseForCameraCompat());
688         }
689     }
690 
691     /**
692      * Test that an activity is forced to be resizeable when {@link
693      * ActivityInfo#FORCE_RESIZE_APP} is enabled and {@link
694      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES} is set to
695      * true.
696      */
697     @Test
698     @ApiTest(apis = {"android.content.pm.ActivityInfo#FORCE_RESIZE_APP",
699             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES"})
700     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testOverrideForceResizeApp_propertyTrue_overrideEnabled_overrideApplied()701     public void testOverrideForceResizeApp_propertyTrue_overrideEnabled_overrideApplied() {
702         try (var compatChange = new CompatChangeCloseable(
703                 FORCE_RESIZE_APP,
704                 ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES_OPT_IN_ACTIVITY.getPackageName());
705                 var session = new ActivitySessionCloseable(
706                         ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES_OPT_IN_ACTIVITY)) {
707             // Activity with property set to true and override enabled. Activity should be forced to
708             // be resizeable.
709             assertTrue(session.getActivityState().getShouldOverrideForceResizeApp());
710         }
711     }
712 
713     /**
714      * Test that an activity is not forced to be resizeable when {@link
715      * ActivityInfo#FORCE_RESIZE_APP} is enabled but {@link
716      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES} is set to
717      * false.
718      */
719     @Test
720     @ApiTest(apis = {"android.content.pm.ActivityInfo#FORCE_RESIZE_APP",
721             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES"})
722     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testOverrideForceResizeApp_propertyFalse_overrideEnabled_overrideNotApplied()723     public void testOverrideForceResizeApp_propertyFalse_overrideEnabled_overrideNotApplied() {
724         try (var compatChange = new CompatChangeCloseable(
725                 FORCE_RESIZE_APP,
726                 ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES_OPT_OUT_ACTIVITY.getPackageName());
727                 var session = new ActivitySessionCloseable(
728                         ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES_OPT_OUT_ACTIVITY)) {
729             // Activity with property set to false and override enabled. Activity should not be
730             // forced to be resizeable.
731             assertFalse(session.getActivityState().getShouldOverrideForceResizeApp());
732         }
733     }
734 
735     /**
736      * Test that an activity is not forced to be resizeable when {@link
737      * ActivityInfo#FORCE_RESIZE_APP} is disabled but {@link
738      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES} is set to
739      * true.
740      */
741     @Test
742     @ApiTest(apis = {"android.content.pm.ActivityInfo#FORCE_RESIZE_APP",
743             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES"})
744     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testOverrideForceResizeApp_propertyTrue_overrideDisabled_overrideNotApplied()745     public void testOverrideForceResizeApp_propertyTrue_overrideDisabled_overrideNotApplied() {
746         try (var session = new ActivitySessionCloseable(
747                      ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES_OPT_IN_ACTIVITY)) {
748             // Activity with property set to true and override disabled. Activity should not be
749             // forced to be resizeable.
750             assertFalse(session.getActivityState().getShouldOverrideForceResizeApp());
751         }
752     }
753 
754     /**
755      * Test that an activity is not forced to be resizeable when {@link
756      * ActivityInfo#FORCE_RESIZE_APP} is disabled and {@link
757      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES} is set to
758      * false.
759      */
760     @Test
761     @ApiTest(apis = {"android.content.pm.ActivityInfo#FORCE_RESIZE_APP",
762             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES"})
763     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testOverrideForceResizeApp_propertyFalse_overrideDisabled_overrideApplied()764     public void testOverrideForceResizeApp_propertyFalse_overrideDisabled_overrideApplied() {
765         try (var session = new ActivitySessionCloseable(
766                      ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES_OPT_OUT_ACTIVITY)) {
767             // Activity with property set to false and override disabled. Activity should not be
768             // forced to be resizeable.
769             assertFalse(session.getActivityState().getShouldOverrideForceResizeApp());
770         }
771     }
772 
773     /**
774      * Test that an activity is forced to be resizeable when {@link
775      * ActivityInfo#FORCE_RESIZE_APP} is enabled and {@link
776      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES} is unset.
777      */
778     @Test
779     @ApiTest(apis = {"android.content.pm.ActivityInfo#FORCE_RESIZE_APP",
780             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES"})
781     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
782     @EnableCompatChanges({ActivityInfo.FORCE_RESIZE_APP})
testOverrideForceResizeApp_propertyUnset_overrideEnabled_overrideApplied()783     public void testOverrideForceResizeApp_propertyUnset_overrideEnabled_overrideApplied() {
784         try (var session = new ActivitySessionCloseable(
785                      NON_RESIZEABLE_NON_FIXED_ORIENTATION_ACTIVITY)) {
786             // Activity with property unset and override enabled. Property will default to true.
787             // Activity should be forced to be resizeable.
788             assertTrue(session.getActivityState().getShouldOverrideForceResizeApp());
789         }
790     }
791 
792     /**
793      * Test that an activity is not forced to be resizeable when {@link
794      * ActivityInfo#FORCE_RESIZE_APP} is disabled and {@link
795      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES} is unset.
796      */
797     @Test
798     @ApiTest(apis = {"android.content.pm.ActivityInfo#FORCE_RESIZE_APP",
799             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES"})
800     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
801     @DisableCompatChanges({ActivityInfo.FORCE_RESIZE_APP})
testOverrideForceResizeApp_propertyUnset_overrideDisabled_overrideNotApplied()802     public void testOverrideForceResizeApp_propertyUnset_overrideDisabled_overrideNotApplied() {
803         try (var session = new ActivitySessionCloseable(
804                 NON_RESIZEABLE_NON_FIXED_ORIENTATION_ACTIVITY)) {
805             // Activity with property unset and override disabled. Activity should not be forced to
806             // be resizeable.
807             assertFalse(session.getActivityState().getShouldOverrideForceResizeApp());
808         }
809     }
810 
811     /**
812      * Test that a non-resizeable portrait activity enters size compat mode after resizing the
813      * display.
814      */
815     @Test
testSizeCompatForNonResizeableActivity()816     public void testSizeCompatForNonResizeableActivity() {
817         runSizeCompatTest(NON_RESIZEABLE_PORTRAIT_ACTIVITY, /* inSizeCompatModeAfterResize */ true);
818     }
819 
820     /**
821      * Test that a non-resizeable portrait activity doesn't enter size compat mode after resizing
822      * the display, when the {@link ActivityInfo#FORCE_RESIZE_APP} compat change is enabled.
823      */
824     @Test
825     @EnableCompatChanges({ActivityInfo.FORCE_RESIZE_APP})
testSizeCompatForNonResizeableActivityForceResizeEnabled()826     public void testSizeCompatForNonResizeableActivityForceResizeEnabled() {
827         runSizeCompatTest(NON_RESIZEABLE_PORTRAIT_ACTIVITY, /*inSizeCompatModeAfterResize */ false);
828     }
829 
830     /**
831      * Test that a resizeable portrait activity doesn't enter size compat mode after resizing
832      * the display.
833      */
834     @Test
testSizeCompatForResizeableActivity()835     public void testSizeCompatForResizeableActivity() {
836         runSizeCompatTest(RESIZEABLE_PORTRAIT_ACTIVITY, /* inSizeCompatModeAfterResize */ false);
837     }
838 
839     /**
840      * Test that a non-resizeable portrait activity that supports size changes doesn't enter size
841      * compat mode after resizing the display.
842      */
843     @Test
testSizeCompatForSupportsSizeChangesActivity()844     public void testSizeCompatForSupportsSizeChangesActivity() {
845         runSizeCompatTest(
846                 SUPPORTS_SIZE_CHANGES_PORTRAIT_ACTIVITY, /* inSizeCompatModeAfterResize */ false);
847     }
848 
849     /**
850      * Test that a resizeable portrait activity enters size compat mode after resizing
851      * the display, when the {@link ActivityInfo#FORCE_NON_RESIZE_APP} compat change is enabled.
852      */
853     @Test
854     @EnableCompatChanges({ActivityInfo.FORCE_NON_RESIZE_APP})
testSizeCompatForResizeableActivityForceNonResizeEnabled()855     public void testSizeCompatForResizeableActivityForceNonResizeEnabled() {
856         runSizeCompatTest(RESIZEABLE_PORTRAIT_ACTIVITY, /* inSizeCompatModeAfterResize */ true);
857     }
858 
859     /**
860      * Test that a non-resizeable portrait activity that supports size changes enters size compat
861      * mode after resizing the display, when the {@link ActivityInfo#FORCE_NON_RESIZE_APP} compat
862      * change is enabled.
863      */
864     @Test
865     @EnableCompatChanges({ActivityInfo.FORCE_NON_RESIZE_APP})
testSizeCompatForSupportsSizeChangesActivityForceNonResizeEnabled()866     public void testSizeCompatForSupportsSizeChangesActivityForceNonResizeEnabled() {
867         runSizeCompatTest(
868                 SUPPORTS_SIZE_CHANGES_PORTRAIT_ACTIVITY, /* inSizeCompatModeAfterResize */ true);
869     }
870 
871     /**
872      * Test that a min aspect ratio activity eligible for size compat mode results in sandboxed
873      * Display APIs.
874      */
875     @Test
testSandboxForNonResizableAspectRatioActivity()876     public void testSandboxForNonResizableAspectRatioActivity() {
877         runSizeCompatModeSandboxTest(NON_RESIZEABLE_LARGE_ASPECT_RATIO_ACTIVITY,
878                 /* isSandboxed */ true, /* inSizeCompatModeAfterResize */ true);
879     }
880 
881      // =================
882      // NEVER_SANDBOX test cases
883      // =================
884      // Validates that an activity forced into size compat mode never has sandboxing applied to the
885      // max bounds. It is expected that an activity in size compat mode normally always has
886      // sandboxing applied.
887 
888     /**
889      * Test that a min aspect ratio activity eligible for size compat mode does not have the Display
890      * APIs sandboxed when the {@link ActivityInfo#NEVER_SANDBOX_DISPLAY_APIS} compat change is
891      * enabled.
892      */
893     @Test
894     @EnableCompatChanges({ActivityInfo.NEVER_SANDBOX_DISPLAY_APIS})
testSandboxForNonResizableAspectRatioActivityNeverSandboxDisplayApisEnabled()895     public void testSandboxForNonResizableAspectRatioActivityNeverSandboxDisplayApisEnabled() {
896         runSizeCompatModeSandboxTest(NON_RESIZEABLE_LARGE_ASPECT_RATIO_ACTIVITY,
897                 /* isSandboxed */ false, /* inSizeCompatModeAfterResize */ true);
898     }
899 
900     /**
901      * Test that a min aspect ratio activity eligible for size compat mode does not have the
902      * Display APIs sandboxed when the 'never_constrain_display_apis_all_packages' Device Config
903      * flag is true.
904      */
905     @Test
testSandboxForNonResizableActivityNeverSandboxDeviceConfigAllPackagesFlagTrue()906     public void testSandboxForNonResizableActivityNeverSandboxDeviceConfigAllPackagesFlagTrue() {
907         try (var neverForAll = setNeverConstrainDisplayApisAllPackagesFlag("true");
908                 var neverAnApp = setNeverConstrainDisplayApisFlag("com.android.other::")) {
909             runSizeCompatModeSandboxTest(NON_RESIZEABLE_LARGE_ASPECT_RATIO_ACTIVITY,
910                     /* isSandboxed */ false, /* inSizeCompatModeAfterResize */ true);
911         }
912     }
913 
914     /**
915      * Test that a min aspect ratio activity eligible for size compat mode does not have the Display
916      * APIs sandboxed when the 'never_constrain_display_apis' Device Config flag contains the test
917      * package with an open ended range.
918      */
919     @Test
testSandboxForNonResizableActivityPackageUnboundedInNeverSandboxDeviceConfigFlag()920     public void testSandboxForNonResizableActivityPackageUnboundedInNeverSandboxDeviceConfigFlag() {
921         ComponentName activity = NON_RESIZEABLE_LARGE_ASPECT_RATIO_ACTIVITY;
922         try (var neverForApp = setNeverConstrainDisplayApisFlag(
923                 "com.android.other::," + activity.getPackageName() + "::")) {
924             runSizeCompatModeSandboxTest(activity, /* isSandboxed */ false,
925                     /* inSizeCompatModeAfterResize */ true);
926         }
927     }
928 
929     /**
930      * Test that a min aspect ratio activity eligible for size compat mode does not have the Display
931      * APIs sandboxed when the 'never_constrain_display_apis' Device Config flag contains the test
932      * package with a version range that matches the installed version of the package.
933      */
934     @Test
testSandboxForNonResizableActivityPackageWithinRangeInNeverSandboxDeviceConfig()935     public void testSandboxForNonResizableActivityPackageWithinRangeInNeverSandboxDeviceConfig() {
936         ComponentName activity = NON_RESIZEABLE_LARGE_ASPECT_RATIO_ACTIVITY;
937         long version = getPackageVersion(activity);
938         try (var neverForApp = setNeverConstrainDisplayApisFlag(
939                 "com.android.other::," + activity.getPackageName() + ":" + String.valueOf(
940                         version - 1) + ":" + String.valueOf(version + 1))) {
941             runSizeCompatModeSandboxTest(activity, /* isSandboxed */ false,
942                     /* inSizeCompatModeAfterResize */ true);
943         }
944     }
945 
946     /**
947      * Test that a min aspect ratio activity eligible for size compat mode does have the Display
948      * APIs sandboxed when the 'never_constrain_display_apis' Device Config flag contains the test
949      * package with a version range that doesn't match the installed version of the package.
950      */
951     @Test
testSandboxForNonResizableActivityPackageOutsideRangeInNeverSandboxDeviceConfig()952     public void testSandboxForNonResizableActivityPackageOutsideRangeInNeverSandboxDeviceConfig() {
953         ComponentName activity = NON_RESIZEABLE_LARGE_ASPECT_RATIO_ACTIVITY;
954         long version = getPackageVersion(activity);
955         try (var neverForApp = setNeverConstrainDisplayApisFlag(
956                 "com.android.other::," + activity.getPackageName() + ":" + String.valueOf(
957                         version + 1) + ":")) {
958             runSizeCompatModeSandboxTest(activity, /* isSandboxed */ true,
959                     /* inSizeCompatModeAfterResize */ true);
960         }
961     }
962 
963     /**
964      * Test that a min aspect ratio activity eligible for size compat mode does have the Display
965      * APIs sandboxed when the 'never_constrain_display_apis' Device Config flag doesn't contain the
966      * test package.
967      */
968     @Test
testSandboxForNonResizableActivityPackageNotInNeverSandboxDeviceConfigFlag()969     public void testSandboxForNonResizableActivityPackageNotInNeverSandboxDeviceConfigFlag() {
970         try (var neverForApp = setNeverConstrainDisplayApisFlag(
971                 "com.android.other::,com.android.other2::")) {
972             runSizeCompatModeSandboxTest(NON_RESIZEABLE_LARGE_ASPECT_RATIO_ACTIVITY,
973                     /* isSandboxed */ true, /* inSizeCompatModeAfterResize */ true);
974         }
975     }
976 
977     /**
978      * Test that a min aspect ratio activity eligible for size compat mode does have the Display
979      * APIs sandboxed when the 'never_constrain_display_apis' Device Config flag is empty.
980      */
981     @Test
testSandboxForNonResizableActivityNeverSandboxDeviceConfigFlagEmpty()982     public void testSandboxForNonResizableActivityNeverSandboxDeviceConfigFlagEmpty() {
983         try (var empty = setNeverConstrainDisplayApisFlag("")) {
984             runSizeCompatModeSandboxTest(NON_RESIZEABLE_LARGE_ASPECT_RATIO_ACTIVITY,
985                     /* isSandboxed */ true, /* inSizeCompatModeAfterResize */ true);
986         }
987     }
988 
989     /**
990      * Test that a min aspect ratio activity eligible for size compat mode does have the Display
991      * APIs sandboxed when the 'never_constrain_display_apis' Device Config flag contains an invalid
992      * entry for the test package.
993      */
994     @Test
testSandboxForNonResizableActivityInvalidEntryInNeverSandboxDeviceConfigFlag()995     public void testSandboxForNonResizableActivityInvalidEntryInNeverSandboxDeviceConfigFlag() {
996         ComponentName activity = NON_RESIZEABLE_LARGE_ASPECT_RATIO_ACTIVITY;
997         try (var neverForApp = setNeverConstrainDisplayApisFlag(
998                 "com.android.other::," + activity.getPackageName() + ":::")) {
999             runSizeCompatModeSandboxTest(activity, /* isSandboxed */ true,
1000                     /* inSizeCompatModeAfterResize */ true);
1001         }
1002     }
1003 
1004     /** =================
1005      * SANDBOX_VIEW_BOUNDS_APIS test cases
1006      * @see #testSandbox_viewApiForLetterboxedActivity
1007      * @see #testNoSandbox_viewApiForLetterboxedActivity
1008      * @see #testNoSandbox_viewApiForLetterboxedActivityOptOut
1009      * =================
1010      * Validates that an activity in letterbox mode has sandboxing applied to the
1011      * view bounds when OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS is set.
1012      * Without this flag or with
1013      * {@link android.view.WindowManager#PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS}
1014      * value=false in AndroidManifest.xml
1015      * {@link android.view.View#getLocationOnScreen},
1016      * {@link android.view.View#getWindowDisplayFrame}
1017      * {@link android.view.View#getBoundsOnScreen}
1018      * and {@link android.view.View#getWindowVisibleDisplayFrame}
1019      * return location or display frame offset by the window location on the screen:
1020      * {@link WindowConfiguration#getBounds}
1021      */
1022     @Test
testSandbox_viewApiForLetterboxedActivity()1023     public void testSandbox_viewApiForLetterboxedActivity() {
1024         // Enable OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS changeId for the test application
1025         try (var aspectRatio = new DisplayAspectRatioCloseable(ORIENTATION_LANDSCAPE, 2.0f);
1026              var compatChange = new CompatChangeCloseable(
1027                 OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS,
1028                 TEST_VIEW_SANDBOX_ALLOWED_ACTIVITY.getPackageName());
1029              var receiver = new BroadcastReceiverCloseable(mContext,
1030                      ACTION_TEST_VIEW_SANDBOX_ALLOWED_PASSED)) {
1031 
1032             try (var session = new ActivitySessionCloseable(TEST_VIEW_SANDBOX_ALLOWED_ACTIVITY)) {
1033                 // Wait for the broadcast action
1034                 boolean testPassed = receiver
1035                         .getBroadcastReceivedVariable(ACTION_TEST_VIEW_SANDBOX_ALLOWED_PASSED)
1036                         .block(TEST_VIEW_SANDBOX_ALLOWED_TIMEOUT_MS);
1037 
1038                 assertThat(testPassed).isTrue();
1039             }
1040         }
1041     }
1042 
1043     @Test
testNoSandbox_viewApiForLetterboxedActivity()1044     public void testNoSandbox_viewApiForLetterboxedActivity() {
1045         // Enable OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS changeId for the test application
1046         try (var aspectRatio = new DisplayAspectRatioCloseable(ORIENTATION_LANDSCAPE, 2.0f);
1047              var receiver = new BroadcastReceiverCloseable(mContext,
1048                      ACTION_TEST_VIEW_SANDBOX_NOT_ALLOWED_PASSED)) {
1049 
1050             try (var session = new ActivitySessionCloseable(TEST_VIEW_SANDBOX_ALLOWED_ACTIVITY)) {
1051                 // Wait for the broadcast action
1052                 boolean testPassed = receiver
1053                         .getBroadcastReceivedVariable(ACTION_TEST_VIEW_SANDBOX_NOT_ALLOWED_PASSED)
1054                         .block(TEST_VIEW_SANDBOX_ALLOWED_TIMEOUT_MS);
1055 
1056                 assertThat(testPassed).isTrue();
1057             }
1058         }
1059     }
1060 
1061     @Test
testNoSandbox_viewApiForLetterboxedActivityOptOut()1062     public void testNoSandbox_viewApiForLetterboxedActivityOptOut() {
1063         // Enable OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS changeId for the test application
1064         try (var aspectRatio = new DisplayAspectRatioCloseable(ORIENTATION_LANDSCAPE, 2.0f);
1065              var compatChange = new CompatChangeCloseable(
1066                 OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS,
1067                 TEST_VIEW_SANDBOX_OPT_OUT_ACTIVITY.getPackageName());
1068              var receiver = new BroadcastReceiverCloseable(mContext,
1069                      ACTION_TEST_VIEW_SANDBOX_OPT_OUT_PASSED)) {
1070 
1071             try (var session = new ActivitySessionCloseable(TEST_VIEW_SANDBOX_OPT_OUT_ACTIVITY)) {
1072                 // Wait for the broadcast action
1073                 boolean testPassed = receiver
1074                         .getBroadcastReceivedVariable(ACTION_TEST_VIEW_SANDBOX_OPT_OUT_PASSED)
1075                         .block(TEST_VIEW_SANDBOX_OPT_OUT_TIMEOUT_MS);
1076 
1077                 assertThat(testPassed).isTrue();
1078             }
1079         }
1080     }
1081 
1082     // =================
1083     // ALWAYS_SANDBOX test cases
1084     // =================
1085     // Validates that an activity simply in letterbox mode has sandboxing applied to the max
1086     // bounds when ALWAYS_SANDBOX is set. Without the flag, we would not expect a letterbox activity
1087     // to be sandboxed, unless it is also eligible for size compat mode.
1088 
1089     /**
1090      * Test that a portrait activity not eligible for size compat mode does have the
1091      * Display APIs sandboxed when the {@link ActivityInfo#ALWAYS_SANDBOX_DISPLAY_APIS} compat
1092      * change is enabled.
1093      */
1094     @Test
1095     @EnableCompatChanges({ActivityInfo.ALWAYS_SANDBOX_DISPLAY_APIS})
testSandboxForResizableActivityAlwaysSandboxDisplayApisEnabled()1096     public void testSandboxForResizableActivityAlwaysSandboxDisplayApisEnabled() {
1097         runLetterboxSandboxTest(RESIZEABLE_PORTRAIT_ACTIVITY, /* isSandboxed */ true);
1098     }
1099 
1100     /**
1101      * Test that a portrait activity not eligible for size compat mode does not have the
1102      * Display APIs sandboxed when the 'always_constrain_display_apis' Device Config flag is empty.
1103      */
1104     @Test
testSandboxResizableActivityAlwaysSandboxDeviceConfigFlagEmpty()1105     public void testSandboxResizableActivityAlwaysSandboxDeviceConfigFlagEmpty() {
1106         runLetterboxSandboxTest(RESIZEABLE_PORTRAIT_ACTIVITY, /* isSandboxed */ false);
1107     }
1108 
1109     /**
1110      * Test that a portrait activity not eligible for size compat mode does have the Display
1111      * APIs sandboxed when the 'always_constrain_display_apis' Device Config flag contains the test
1112      * package.
1113      */
1114     @Test
testSandboxResizableActivityPackageInAlwaysSandboxDeviceConfigFlag()1115     public void testSandboxResizableActivityPackageInAlwaysSandboxDeviceConfigFlag() {
1116         ComponentName activity = RESIZEABLE_PORTRAIT_ACTIVITY;
1117         try (var alwaysForApp = setAlwaysConstrainDisplayApisFlag(
1118                 "com.android.other::," + activity.getPackageName() + "::")) {
1119             runLetterboxSandboxTest(activity, /* isSandboxed */ true);
1120         }
1121     }
1122 
1123     /**
1124      * Test that only applying {@link ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO} has no effect on its
1125      * own. The aspect ratio of the activity should be the same as that of the task, which should be
1126      * in line with that of the display.
1127      */
1128     @Test
1129     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
testOverrideMinAspectRatioMissingSpecificOverride()1130     public void testOverrideMinAspectRatioMissingSpecificOverride() {
1131         runMinAspectRatioTest(NON_RESIZEABLE_PORTRAIT_ACTIVITY, /* expected */ 0);
1132     }
1133 
1134     /**
1135      * Test that only applying {@link ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO_LARGE} has no effect on
1136      * its own without the presence of {@link ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO}.
1137      */
1138     @Test
1139     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
testOverrideMinAspectRatioMissingGeneralOverride()1140     public void testOverrideMinAspectRatioMissingGeneralOverride() {
1141         runMinAspectRatioTest(NON_RESIZEABLE_PORTRAIT_ACTIVITY, /* expected */ 0);
1142     }
1143 
1144     /**
1145      * Test that applying {@link ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO_LARGE} has no effect on
1146      * activities whose orientation is fixed to landscape.
1147      */
1148     @Test
1149     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1150             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
testOverrideMinAspectRatioForLandscapeActivity()1151     public void testOverrideMinAspectRatioForLandscapeActivity() {
1152         runMinAspectRatioTest(NON_RESIZEABLE_LANDSCAPE_ACTIVITY, /* expected */ 0);
1153     }
1154 
1155     /**
1156      * Test that applying {@link ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO_LARGE} has no effect on
1157      * activities whose orientation isn't fixed.
1158      */
1159     @Test
1160     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1161             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
1162     @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
testOverrideMinAspectRatioForNonFixedOrientationActivityPortraitOnlyDisabled()1163     public void testOverrideMinAspectRatioForNonFixedOrientationActivityPortraitOnlyDisabled() {
1164         runMinAspectRatioTest(NON_RESIZEABLE_NON_FIXED_ORIENTATION_ACTIVITY, /* expected */
1165                 OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE);
1166     }
1167 
1168     /**
1169      * Test that applying {@link ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO_LARGE} has no effect on
1170      * activities whose orientation is fixed to landscape.
1171      */
1172     @Test
1173     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1174             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
1175     @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
testOverrideMinAspectRatioForLandscapeActivityPortraitOnlyDisabled()1176     public void testOverrideMinAspectRatioForLandscapeActivityPortraitOnlyDisabled() {
1177         runMinAspectRatioTest(NON_RESIZEABLE_LANDSCAPE_ACTIVITY, /* expected */
1178                 OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE);
1179     }
1180 
1181     /**
1182      * Test that applying {@link ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO_LARGE} has no effect on
1183      * activities whose orientation isn't fixed.
1184      */
1185     @Test
1186     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1187             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
testOverrideMinAspectRatioForNonFixedOrientationActivity()1188     public void testOverrideMinAspectRatioForNonFixedOrientationActivity() {
1189         runMinAspectRatioTest(NON_RESIZEABLE_NON_FIXED_ORIENTATION_ACTIVITY, /* expected */ 0);
1190     }
1191 
1192     /**
1193      * Test that applying {@link ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO_LARGE} sets the min aspect
1194      * ratio to {@link ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE}.
1195      */
1196     @Test
1197     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1198             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
testOverrideMinAspectRatioLargeAspectRatio()1199     public void testOverrideMinAspectRatioLargeAspectRatio() {
1200         runMinAspectRatioTest(NON_RESIZEABLE_PORTRAIT_ACTIVITY,
1201                 /* expected */ OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE);
1202     }
1203 
1204     /**
1205      * Test that applying {@link ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO_MEDIUM} sets the min aspect
1206      * ratio to {@link ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE}.
1207      */
1208     @Test
1209     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1210             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
testOverrideMinAspectRatioMediumAspectRatio()1211     public void testOverrideMinAspectRatioMediumAspectRatio() {
1212         runMinAspectRatioTest(NON_RESIZEABLE_PORTRAIT_ACTIVITY,
1213                 /* expected */ OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE);
1214     }
1215 
1216     /**
1217      * Test that applying multiple min aspect ratio overrides result in the largest one taking
1218      * effect.
1219      */
1220     @Test
1221     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1222             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE,
1223             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
testOverrideMinAspectRatioBothAspectRatios()1224     public void testOverrideMinAspectRatioBothAspectRatios() {
1225         runMinAspectRatioTest(NON_RESIZEABLE_PORTRAIT_ACTIVITY,
1226                 /* expected */ OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE);
1227     }
1228 
1229     /**
1230      * Test that the min aspect ratio of the activity as defined in the manifest is ignored if
1231      * there is an override for a larger min aspect ratio present (16:9 > 1.6).
1232      */
1233     @Test
1234     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1235             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
testOverrideMinAspectRatioActivityMinAspectRatioSmallerThanOverride()1236     public void testOverrideMinAspectRatioActivityMinAspectRatioSmallerThanOverride() {
1237         runMinAspectRatioTest(NON_RESIZEABLE_ASPECT_RATIO_ACTIVITY,
1238                 /* expected */ OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE);
1239     }
1240 
1241     /**
1242      * Test that the min aspect ratio of the activity as defined in the manifest is upheld if
1243      * there is an override for a smaller min aspect ratio present (3:2 < 1.6).
1244      */
1245     @Test
1246     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
1247             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
testOverrideMinAspectRatioActivityMinAspectRatioLargerThanOverride()1248     public void testOverrideMinAspectRatioActivityMinAspectRatioLargerThanOverride() {
1249         runMinAspectRatioTest(NON_RESIZEABLE_ASPECT_RATIO_ACTIVITY,
1250                 /* expected */ ACTIVITY_MIN_ASPECT_RATIO);
1251     }
1252 
1253     /**
1254      * Test that an activities min aspect ratio is overridden when {@link
1255      * ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO} is enabled and {@link
1256      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE} is true.
1257      */
1258     @Test
1259     @ApiTest(apis = {"android.content.pm.ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO",
1260             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE"})
1261     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testOverrideMinAspectRatio_propertyTrue_overrideEnabled_overrideApplied()1262     public void testOverrideMinAspectRatio_propertyTrue_overrideEnabled_overrideApplied() {
1263         try (var compatChange = new CompatChangeCloseable(OVERRIDE_MIN_ASPECT_RATIO,
1264                 ALLOW_MIN_ASPECT_RATIO_OVERRIDE_OPT_IN_ACTIVITY.getPackageName());
1265                 var session = new ActivitySessionCloseable(
1266                         ALLOW_MIN_ASPECT_RATIO_OVERRIDE_OPT_IN_ACTIVITY)) {
1267             assertTrue(session.getActivityState().getShouldOverrideMinAspectRatio());
1268         }
1269     }
1270 
1271     /**
1272      * Test that an activities min aspect ratio is not overridden when {@link
1273      * ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO} is enabled but {@link
1274      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE} is false.
1275      */
1276     @Test
1277     @ApiTest(apis = {"android.content.pm.ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO",
1278             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE"})
1279     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testOverrideMinAspectRatio_propertyFalse_overrideEnabled_overrideNotApplied()1280     public void testOverrideMinAspectRatio_propertyFalse_overrideEnabled_overrideNotApplied() {
1281         try (var compatChange = new CompatChangeCloseable(OVERRIDE_MIN_ASPECT_RATIO,
1282                 ALLOW_MIN_ASPECT_RATIO_OVERRIDE_OPT_IN_ACTIVITY.getPackageName());
1283                 var session = new ActivitySessionCloseable(
1284                         ALLOW_MIN_ASPECT_RATIO_OVERRIDE_OPT_OUT_ACTIVITY)) {
1285             assertFalse(session.getActivityState().getShouldOverrideMinAspectRatio());
1286         }
1287     }
1288 
1289     /**
1290      * Test that an activities min aspect ratio is overridden when {@link
1291      * ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO} is enabled and {@link
1292      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE} is unset.
1293      */
1294     @Test
1295     @ApiTest(apis = {"android.content.pm.ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO",
1296             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE"})
1297     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
1298     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
testOverrideMinAspectRatio_propertyNotSet_overrideEnabled_overrideApplied()1299     public void testOverrideMinAspectRatio_propertyNotSet_overrideEnabled_overrideApplied() {
1300         try (var session = new ActivitySessionCloseable(
1301                 NON_RESIZEABLE_NON_FIXED_ORIENTATION_ACTIVITY)) {
1302             assertTrue(session.getActivityState().getShouldOverrideMinAspectRatio());
1303         }
1304     }
1305 
1306     /**
1307      * Test that an activities min aspect ratio is not overridden when {@link
1308      * ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO} is disabled and {@link
1309      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE} is unset.
1310      */
1311     @Test
1312     @ApiTest(apis = {"android.content.pm.ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO",
1313             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE"})
1314     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
1315     @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
testOverrideMinAspectRatio_propertyNotSet_overrideDisabled_overrideNotApplied()1316     public void testOverrideMinAspectRatio_propertyNotSet_overrideDisabled_overrideNotApplied() {
1317         try (var session = new ActivitySessionCloseable(
1318                 NON_RESIZEABLE_NON_FIXED_ORIENTATION_ACTIVITY)) {
1319             assertFalse(session.getActivityState().getShouldOverrideMinAspectRatio());
1320         }
1321     }
1322 
1323     /**
1324      * Test that an activities min aspect ratio is not overridden when {@link
1325      * ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO} is disabled but {@link
1326      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE} is true.
1327      */
1328     @Test
1329     @ApiTest(apis = {"android.content.pm.ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO",
1330             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE"})
1331     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testOverrideMinAspectRatio_propertyTrue_overrideUnset_overrideNotApplied()1332     public void testOverrideMinAspectRatio_propertyTrue_overrideUnset_overrideNotApplied() {
1333         try (var session = new ActivitySessionCloseable(
1334                 ALLOW_MIN_ASPECT_RATIO_OVERRIDE_OPT_IN_ACTIVITY)) {
1335             assertFalse(session.getActivityState().getShouldOverrideMinAspectRatio());
1336         }
1337     }
1338 
1339     /**
1340      * Test that an activities min aspect ratio is not overridden when {@link
1341      * ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO} is disabled and {@link
1342      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE} is false.
1343      */
1344     @Test
1345     @ApiTest(apis = {"android.content.pm.ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO",
1346             "android.view.WindowManager#PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE"})
1347     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testOverrideMinAspectRatio_propertyFalse_overrideUnset_overrideNotApplied()1348     public void testOverrideMinAspectRatio_propertyFalse_overrideUnset_overrideNotApplied() {
1349         try (var session = new ActivitySessionCloseable(
1350                 ALLOW_MIN_ASPECT_RATIO_OVERRIDE_OPT_OUT_ACTIVITY)) {
1351             assertFalse(session.getActivityState().getShouldOverrideMinAspectRatio());
1352         }
1353     }
1354 
1355     /**
1356      * Test that the user aspect ratio settings are enabled when {@link
1357      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE} is set to true
1358      * and {@link com.android.internal.R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled}
1359      * is true.
1360      */
1361     @Test
1362     @ApiTest(apis = {"android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE"})
1363     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testAllowUserAspectRatioSettings_propertyTrue_configEnabled_overrideAllowed()1364     public void testAllowUserAspectRatioSettings_propertyTrue_configEnabled_overrideAllowed() {
1365         assumeTrue("Skipping test: config_appCompatUserAppAspectRatioSettingsIsEnabled not enabled",
1366                 isUserAppAspectRatioSettingsIsEnabled());
1367 
1368         try (var session = new ActivitySessionCloseable(
1369                 ALLOW_USER_ASPECT_RATIO_OVERRIDE_OPT_IN_ACTIVITY)) {
1370             assertTrue(session.getActivityState().getShouldEnableUserAspectRatioSettings());
1371         }
1372     }
1373 
1374     /**
1375      * Test that the user aspect ratio settings are disabled when {@link
1376      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE} is set to true
1377      * but {@link com.android.internal.R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled}
1378      * is false.
1379      */
1380     @Test
1381     @ApiTest(apis = {"android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE"})
1382     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testAllowUserAspectRatioSettings_propertyTrue_configDisabled_overrideNotAllowed()1383     public void testAllowUserAspectRatioSettings_propertyTrue_configDisabled_overrideNotAllowed() {
1384         assumeFalse("Skipping test: config_appCompatUserAppAspectRatioSettingsIsEnabled is enabled",
1385                 isUserAppAspectRatioSettingsIsEnabled());
1386 
1387         try (var session = new ActivitySessionCloseable(
1388                 ALLOW_USER_ASPECT_RATIO_OVERRIDE_OPT_IN_ACTIVITY)) {
1389             assertFalse(session.getActivityState().getShouldEnableUserAspectRatioSettings());
1390         }
1391     }
1392 
1393     /**
1394      * Test that the user aspect ratio settings are disabled when {@link
1395      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE} is set to false
1396      * but {@link com.android.internal.R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled}
1397      * is true.
1398      */
1399     @Test
1400     @ApiTest(apis = {"android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE"})
1401     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testAllowUserAspectRatioSettings_propertyFalse_configEnabled_overrideNotAllowed()1402     public void testAllowUserAspectRatioSettings_propertyFalse_configEnabled_overrideNotAllowed() {
1403         assumeTrue("Skipping test: config_appCompatUserAppAspectRatioSettingsIsEnabled not enabled",
1404                 isUserAppAspectRatioSettingsIsEnabled());
1405 
1406         try (var session = new ActivitySessionCloseable(
1407                 ALLOW_USER_ASPECT_RATIO_OVERRIDE_OPT_OUT_ACTIVITY)) {
1408             assertFalse(session.getActivityState().getShouldEnableUserAspectRatioSettings());
1409         }
1410     }
1411 
1412     /**
1413      * Test that the user aspect ratio settings are disabled when {@link
1414      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE} is set to false
1415      * and {@link com.android.internal.R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled}
1416      * is false.
1417      */
1418     @Test
1419     @ApiTest(apis = {"android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE"})
1420     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testAllowUserAspectRatioSettings_propertyFalse_configDisabled_overrideNotAllowed()1421     public void testAllowUserAspectRatioSettings_propertyFalse_configDisabled_overrideNotAllowed() {
1422         assumeFalse("Skipping test: config_appCompatUserAppAspectRatioSettingsIsEnabled is enabled",
1423                 isUserAppAspectRatioSettingsIsEnabled());
1424 
1425         try (var session = new ActivitySessionCloseable(
1426                 ALLOW_USER_ASPECT_RATIO_OVERRIDE_OPT_OUT_ACTIVITY)) {
1427             assertFalse(session.getActivityState().getShouldEnableUserAspectRatioSettings());
1428         }
1429     }
1430 
1431     /**
1432      * Test that the user aspect ratio settings are enabled when {@link
1433      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE} is unset and
1434      * {@link com.android.internal.R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled}
1435      * is true.
1436      */
1437     @Test
1438     @ApiTest(apis = {"android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE"})
1439     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testAllowUserAspectRatioSettings_propertyUnset_configEnabled_overrideAllowed()1440     public void testAllowUserAspectRatioSettings_propertyUnset_configEnabled_overrideAllowed() {
1441         assumeTrue("Skipping test: config_appCompatUserAppAspectRatioSettingsIsEnabled not enabled",
1442                 isUserAppAspectRatioSettingsIsEnabled());
1443 
1444         try (var session = new ActivitySessionCloseable(
1445                 NON_RESIZEABLE_NON_FIXED_ORIENTATION_ACTIVITY)) {
1446             assertTrue(session.getActivityState().getShouldEnableUserAspectRatioSettings());
1447         }
1448     }
1449 
1450     /**
1451      * Test that the user aspect ratio settings are disabled when {@link
1452      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE} is unset but
1453      * {@link com.android.internal.R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled}
1454      * is false.
1455      */
1456     @Test
1457     @ApiTest(apis = {"android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE"})
1458     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testAllowUserAspectRatioSettings_propertyUnset_configDisabled_overrideNotAllowed()1459     public void testAllowUserAspectRatioSettings_propertyUnset_configDisabled_overrideNotAllowed() {
1460         assumeFalse("Skipping test: config_appCompatUserAppAspectRatioSettingsIsEnabled is enabled",
1461                 isUserAppAspectRatioSettingsIsEnabled());
1462 
1463         try (var session = new ActivitySessionCloseable(
1464                 NON_RESIZEABLE_NON_FIXED_ORIENTATION_ACTIVITY)) {
1465             assertFalse(session.getActivityState().getShouldEnableUserAspectRatioSettings());
1466         }
1467     }
1468 
1469     /**
1470      * Test that the user aspect ratio fullscreen override is applicable through the user aspect
1471      * ratio settings when {@link
1472      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE} is
1473      * set to true and {@link
1474      * com.android.internal.R.bool.config_appCompatUserAppAspectRatioFullscreenIsEnabled} is true.
1475      */
1476     @Test
1477     @ApiTest(apis = {"android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE"})
1478     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testAllowUserAspectRatioFullscreen_propertyTrue_configEnabled_overrideAllowed()1479     public void testAllowUserAspectRatioFullscreen_propertyTrue_configEnabled_overrideAllowed() {
1480         assumeTrue("Skipping test: config_appCompatUserAppAspectRatioSettingsIsEnabled not enabled",
1481                 isUserAppAspectRatioSettingsIsEnabled());
1482         assumeTrue(
1483                 "Skipping test: config_appCompatUserAppAspectRatioFullscreenIsEnabled not enabled",
1484                 isUserAppAspectRatioFullscreenIsEnabled());
1485 
1486         try (var session = new ActivitySessionCloseable(
1487                 ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE_OPT_IN_ACTIVITY)) {
1488             assertTrue(session.getActivityState().getIsUserFullscreenOverrideEnabled());
1489         }
1490     }
1491 
1492     /**
1493      * Test that the user aspect ratio fullscreen override is not applicable through the user aspect
1494      * ratio settings when {@link
1495      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE} is
1496      * set to true but {@link
1497      * com.android.internal.R.bool.config_appCompatUserAppAspectRatioFullscreenIsEnabled} is false.
1498      */
1499     @Test
1500     @ApiTest(apis = {"android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE"})
1501     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
1502     public void
testAllowUserAspectRatioFullscreen_propertyTrue_configDisabled_overrideNotAllowed()1503             testAllowUserAspectRatioFullscreen_propertyTrue_configDisabled_overrideNotAllowed() {
1504         assumeTrue("Skipping test: config_appCompatUserAppAspectRatioSettingsIsEnabled not enabled",
1505                 isUserAppAspectRatioSettingsIsEnabled());
1506         assumeFalse(
1507                 "Skipping test: config_appCompatUserAppAspectRatioFullscreenIsEnabled is enabled",
1508                 isUserAppAspectRatioFullscreenIsEnabled());
1509 
1510         try (var session = new ActivitySessionCloseable(
1511                 ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE_OPT_IN_ACTIVITY)) {
1512             assertFalse(session.getActivityState().getIsUserFullscreenOverrideEnabled());
1513         }
1514     }
1515 
1516     /**
1517      * Test that the user aspect ratio fullscreen override is not applicable through the user aspect
1518      * ratio settings when {@link
1519      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE} is
1520      * set to false but {@link
1521      * com.android.internal.R.bool.config_appCompatUserAppAspectRatioFullscreenIsEnabled} is true.
1522      */
1523     @Test
1524     @ApiTest(apis = {"android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE"})
1525     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
1526     public void
testAllowUserAspectRatioFullscreen_propertyFalse_configEnabled_overrideNotAllowed()1527             testAllowUserAspectRatioFullscreen_propertyFalse_configEnabled_overrideNotAllowed() {
1528         assumeTrue("Skipping test: config_appCompatUserAppAspectRatioSettingsIsEnabled not enabled",
1529                 isUserAppAspectRatioSettingsIsEnabled());
1530         assumeTrue(
1531                 "Skipping test: config_appCompatUserAppAspectRatioFullscreenIsEnabled not enabled",
1532                 isUserAppAspectRatioFullscreenIsEnabled());
1533 
1534         try (var session = new ActivitySessionCloseable(
1535                 ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE_OPT_OUT_ACTIVITY)) {
1536             assertFalse(session.getActivityState().getIsUserFullscreenOverrideEnabled());
1537         }
1538     }
1539 
1540     /**
1541      * Test that the user aspect ratio fullscreen override is not applicable through the user aspect
1542      * ratio settings when {@link
1543      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE} is
1544      * set to false and {@link
1545      * com.android.internal.R.bool.config_appCompatUserAppAspectRatioFullscreenIsEnabled} is false.
1546      */
1547     @Test
1548     @ApiTest(apis = {"android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE"})
1549     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
1550     public void
testAllowUserAspectRatioFullscreen_propertyFalse_configDisabled_overrideNotAllowed()1551             testAllowUserAspectRatioFullscreen_propertyFalse_configDisabled_overrideNotAllowed() {
1552         assumeTrue("Skipping test: config_appCompatUserAppAspectRatioSettingsIsEnabled not enabled",
1553                 isUserAppAspectRatioSettingsIsEnabled());
1554         assumeFalse(
1555                 "Skipping test: config_appCompatUserAppAspectRatioFullscreenIsEnabled is enabled",
1556                 isUserAppAspectRatioFullscreenIsEnabled());
1557 
1558         try (var session = new ActivitySessionCloseable(
1559                 ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE_OPT_OUT_ACTIVITY)) {
1560             assertFalse(session.getActivityState().getIsUserFullscreenOverrideEnabled());
1561         }
1562     }
1563 
1564     /**
1565      * Test that the user aspect ratio fullscreen override is applicable through the user aspect
1566      * ratio settings when {@link
1567      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE} is
1568      * unset but {@link
1569      * com.android.internal.R.bool.config_appCompatUserAppAspectRatioFullscreenIsEnabled} is true.
1570      */
1571     @Test
1572     @ApiTest(apis = {"android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE"})
1573     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
testAllowUserAspectRatioFullscreen_propertyUnset_configEnabled_overrideAllowed()1574     public void testAllowUserAspectRatioFullscreen_propertyUnset_configEnabled_overrideAllowed() {
1575         assumeTrue("Skipping test: config_appCompatUserAppAspectRatioSettingsIsEnabled not enabled",
1576                 isUserAppAspectRatioSettingsIsEnabled());
1577         assumeTrue(
1578                 "Skipping test: config_appCompatUserAppAspectRatioFullscreenIsEnabled not enabled",
1579                 isUserAppAspectRatioFullscreenIsEnabled());
1580 
1581         try (var session = new ActivitySessionCloseable(
1582                 NON_RESIZEABLE_NON_FIXED_ORIENTATION_ACTIVITY)) {
1583             assertTrue(session.getActivityState().getIsUserFullscreenOverrideEnabled());
1584         }
1585     }
1586 
1587     /**
1588      * Test that the user aspect ratio fullscreen override is not applicable through the user aspect
1589      * ratio settings when {@link
1590      * android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE} is
1591      * set to unset but {@link
1592      * com.android.internal.R.bool.config_appCompatUserAppAspectRatioFullscreenIsEnabled} is false.
1593      */
1594     @Test
1595     @ApiTest(apis = {"android.view.WindowManager#PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE"})
1596     @RequiresFlagsEnabled(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
1597     public void
testAllowUserAspectRatioFullscreen_propertyUnset_configDisabled_overrideNotAllowed()1598             testAllowUserAspectRatioFullscreen_propertyUnset_configDisabled_overrideNotAllowed() {
1599         assumeTrue("Skipping test: config_appCompatUserAppAspectRatioSettingsIsEnabled not enabled",
1600                 isUserAppAspectRatioSettingsIsEnabled());
1601         assumeFalse(
1602                 "Skipping test: config_appCompatUserAppAspectRatioFullscreenIsEnabled is enabled",
1603                 isUserAppAspectRatioFullscreenIsEnabled());
1604 
1605         try (var session = new ActivitySessionCloseable(
1606                 NON_RESIZEABLE_NON_FIXED_ORIENTATION_ACTIVITY)) {
1607             assertFalse(session.getActivityState().getIsUserFullscreenOverrideEnabled());
1608         }
1609     }
1610 
isUserAppAspectRatioFullscreenIsEnabled()1611     private boolean isUserAppAspectRatioFullscreenIsEnabled() {
1612         return getBooleanConfig("config_appCompatUserAppAspectRatioFullscreenIsEnabled");
1613     }
1614 
isUserAppAspectRatioSettingsIsEnabled()1615     private boolean isUserAppAspectRatioSettingsIsEnabled() {
1616         return getBooleanConfig("config_appCompatUserAppAspectRatioSettingsIsEnabled");
1617     }
1618 
isCameraCompatForceRotationTreatmentConfigEnabled()1619     private boolean isCameraCompatForceRotationTreatmentConfigEnabled() {
1620         return getBooleanConfig("config_isWindowManagerCameraCompatTreatmentEnabled");
1621     }
1622 
isPolicyForIgnoringRequestedOrientationEnabled()1623     private boolean isPolicyForIgnoringRequestedOrientationEnabled() {
1624         return getBooleanConfig("config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled");
1625     }
1626 
getBooleanConfig(String configName)1627     private boolean getBooleanConfig(String configName) {
1628         return mContext.getResources().getBoolean(
1629                 Resources.getSystem().getIdentifier(configName, "bool", "android"));
1630     }
1631 
1632     /**
1633      * Launches the provided activity into size compat mode twice. The first time, the display
1634      * is resized to be half the size. The second time, the display is resized to be twice the
1635      * original size.
1636      *
1637      * @param activity                    the activity under test.
1638      * @param inSizeCompatModeAfterResize if the activity should be in size compat mode after
1639      *                                    resizing the display
1640      */
runSizeCompatTest(ComponentName activity, boolean inSizeCompatModeAfterResize)1641     private void runSizeCompatTest(ComponentName activity, boolean inSizeCompatModeAfterResize) {
1642         try (var session = new ActivitySessionCloseable(activity)) {
1643             runSizeCompatTestForActivity(activity, /* resizeRatio */ 0.5,
1644                     inSizeCompatModeAfterResize);
1645         }
1646 
1647         try (var session = new ActivitySessionCloseable(activity)) {
1648             runSizeCompatTestForActivity(activity, /* resizeRatio */ 2,
1649                     inSizeCompatModeAfterResize);
1650         }
1651     }
1652 
1653     /**
1654      * Launches the provided activity on the default display, initially not in size compat mode.
1655      * After resizing the display, verifies if activity is in size compat mode or not
1656      *
1657      * @param activity                    the activity under test
1658      * @param resizeRatio                 the ratio to resize the display
1659      * @param inSizeCompatModeAfterResize if the activity should be in size compat mode after
1660      *                                    resizing the display
1661      */
runSizeCompatTestForActivity(ComponentName activity, final double resizeRatio, final boolean inSizeCompatModeAfterResize)1662     private void runSizeCompatTestForActivity(ComponentName activity, final double resizeRatio,
1663             final boolean inSizeCompatModeAfterResize) {
1664 
1665         waitAndAssertSizeCompatMode(activity, /* expectedInSizeCompatMode */ false);
1666 
1667         try (var resized = new DisplaySizeScaleCloseable(resizeRatio, activity)) {
1668             assertThat(resized.getInitialDisplayAspectRatio())
1669                     .isLessThan(ACTIVITY_LARGE_MIN_ASPECT_RATIO);
1670             waitAndAssertSizeCompatMode(activity, inSizeCompatModeAfterResize);
1671         }
1672     }
1673 
waitAndAssertSizeCompatMode(final ComponentName activity, final boolean expectedInSizeCompatMode)1674     private void waitAndAssertSizeCompatMode(final ComponentName activity,
1675             final boolean expectedInSizeCompatMode) {
1676         waitAssertEquals("The window must be inSizeCompatMode==" + expectedInSizeCompatMode,
1677                 expectedInSizeCompatMode,
1678                 () -> getActivityWaitState(activity).inSizeCompatMode());
1679     }
1680 
1681     /**
1682      * Similar to {@link #runSizeCompatTest(ComponentName, boolean)}, but the activity is
1683      * expected to be in size compat mode after resizing the display.
1684      *
1685      * @param activity                    the activity under test
1686      * @param isSandboxed                 when {@code true},
1687      * {@link android.app.WindowConfiguration#getMaxBounds()}
1688      *                                    are sandboxed to the activity bounds. Otherwise, they
1689      *                                    inherit the
1690      *                                    DisplayArea bounds
1691      * @param inSizeCompatModeAfterResize if the activity should be in size compat mode after
1692      *                                    resizing the display
1693      */
runSizeCompatModeSandboxTest(ComponentName activity, boolean isSandboxed, boolean inSizeCompatModeAfterResize)1694     private void runSizeCompatModeSandboxTest(ComponentName activity, boolean isSandboxed,
1695             boolean inSizeCompatModeAfterResize) {
1696         try (var session = new ActivitySessionCloseable(activity, WINDOWING_MODE_FULLSCREEN)) {
1697             runSizeCompatTestForActivity(activity, /* resizeRatio */ 0.5,
1698                     inSizeCompatModeAfterResize);
1699             assertSandboxedByProvidesMaxBounds(activity, isSandboxed);
1700             assertSandboxedByBounds(activity, isSandboxed);
1701         }
1702 
1703         try (var session = new ActivitySessionCloseable(activity, WINDOWING_MODE_FULLSCREEN)) {
1704             runSizeCompatTestForActivity(activity, /* resizeRatio */ 2,
1705                     inSizeCompatModeAfterResize);
1706             assertSandboxedByProvidesMaxBounds(activity, isSandboxed);
1707             assertSandboxedByBounds(activity, isSandboxed);
1708         }
1709     }
1710 
1711     /**
1712      * Similar to {@link #runSizeCompatModeSandboxTest(ComponentName, boolean, boolean)}, but the
1713      * activity is put into letterbox mode after resizing the display.
1714      *
1715      * @param activityName the activity under test
1716      * @param isSandboxed  when {@code true}, {@link android.app.WindowConfiguration#getMaxBounds()}
1717      *                     are sandboxed to the activity bounds. Otherwise, they inherit the
1718      *                     DisplayArea bounds
1719      */
runLetterboxSandboxTest(ComponentName activityName, boolean isSandboxed)1720     private void runLetterboxSandboxTest(ComponentName activityName, boolean isSandboxed) {
1721         try (var aspectRatio = new DisplayAspectRatioCloseable(ORIENTATION_LANDSCAPE, 2.0f)) {
1722             assertThat(aspectRatio.getInitialDisplayAspectRatio())
1723                     .isLessThan(ACTIVITY_LARGE_MIN_ASPECT_RATIO);
1724 
1725             try (var session = new ActivitySessionCloseable(activityName)) {
1726                 assertSandboxedByProvidesMaxBounds(activityName, isSandboxed);
1727             }
1728         }
1729     }
1730 
assertSandboxedByBounds(ComponentName activityName, boolean isSandboxed)1731     private void assertSandboxedByBounds(ComponentName activityName, boolean isSandboxed) {
1732         final WindowManagerState.Activity activity = getActivityWaitState(activityName);
1733         assertNotNull(activity);
1734         final Rect activityBounds = activity.getBounds();
1735         final Rect maxBounds = activity.getMaxBounds();
1736         WindowManagerState.DisplayArea tda = mWmState.getTaskDisplayArea(activityName);
1737         assertNotNull(tda);
1738         if (isSandboxed) {
1739             assertEquals(
1740                     "The window has max bounds sandboxed to the window bounds",
1741                     activityBounds, maxBounds);
1742         } else {
1743             assertEquals(
1744                     "The window is not sandboxed, with max bounds reflecting the DisplayArea",
1745                     tda.getBounds(), maxBounds);
1746         }
1747     }
1748 
assertSandboxedByProvidesMaxBounds(ComponentName activityName, boolean isSandboxed)1749     private void assertSandboxedByProvidesMaxBounds(ComponentName activityName,
1750             boolean isSandboxed) {
1751         final WindowManagerState.Activity activity = getActivityWaitState(activityName);
1752         assertNotNull(activity);
1753         if (isSandboxed) {
1754             assertTrue(
1755                     "The window should have max bounds sandboxed to the window bounds",
1756                     activity.providesMaxBounds());
1757         } else {
1758             assertFalse(
1759                     "The window should not be sandboxed; max bounds should reflect the DisplayArea",
1760                     activity.providesMaxBounds());
1761         }
1762     }
1763 
setNeverConstrainDisplayApisFlag(@ullable String value)1764     private CloseableDeviceConfig setNeverConstrainDisplayApisFlag(@Nullable String value) {
1765         return setConstrainDisplayApisFlag("never_constrain_display_apis", value);
1766     }
1767 
setNeverConstrainDisplayApisAllPackagesFlag( @ullable String value)1768     private CloseableDeviceConfig setNeverConstrainDisplayApisAllPackagesFlag(
1769             @Nullable String value) {
1770         return setConstrainDisplayApisFlag("never_constrain_display_apis_all_packages", value);
1771     }
1772 
setAlwaysConstrainDisplayApisFlag( @ullable String value)1773     private CloseableDeviceConfig setAlwaysConstrainDisplayApisFlag(
1774             @Nullable String value) {
1775         return setConstrainDisplayApisFlag("always_constrain_display_apis", value);
1776     }
1777 
setConstrainDisplayApisFlag(@onNull String flagName, @Nullable String value)1778     private CloseableDeviceConfig setConstrainDisplayApisFlag(@NonNull String flagName,
1779             @Nullable String value) {
1780         return new CloseableDeviceConfig(NAMESPACE_CONSTRAIN_DISPLAY_APIS, flagName, value);
1781     }
1782 
1783     /**
1784      * Launches the provided activity and verifies that its min aspect ratio is equal to {@code
1785      * expected}.
1786      *
1787      * @param activity the activity under test.
1788      * @param expected the expected min aspect ratio in both portrait and landscape displays.
1789      */
runMinAspectRatioTest(ComponentName activity, float expected)1790     private void runMinAspectRatioTest(ComponentName activity, float expected) {
1791         try (var session = new ActivitySessionCloseable(activity)) {
1792             assertNotNull(session.getActivityState());
1793             assertEquals(expected,
1794                     session.getActivityState().getMinAspectRatio(),
1795                     FLOAT_EQUALITY_DELTA);
1796         }
1797     }
1798 
getPackageVersion(ComponentName activity)1799     private long getPackageVersion(ComponentName activity) {
1800         try {
1801             return mContext.getPackageManager().getPackageInfo(activity.getPackageName(),
1802                     /* flags */ 0).getLongVersionCode();
1803         } catch (PackageManager.NameNotFoundException e) {
1804             throw new RuntimeException(e);
1805         }
1806     }
1807 
component(Class<? extends Activity> activity)1808     private static ComponentName component(Class<? extends Activity> activity) {
1809         return new ComponentName(getInstrumentation().getContext(), activity);
1810     }
1811 
1812     public static class ResizeablePortraitActivity extends AbstractLifecycleLogActivity {
1813     }
1814 
1815     public static class ResponsiveActivity extends AbstractLifecycleLogActivity {
1816     }
1817 
1818     public static class NonResizeablePortraitActivity extends AbstractLifecycleLogActivity {
1819     }
1820 
1821     public static class NonResizeableLandscapeActivity extends AbstractLifecycleLogActivity {
1822     }
1823 
1824     public static class NonResizeableNonFixedOrientationActivity extends
1825             AbstractLifecycleLogActivity {
1826     }
1827 
1828     public static class NonResizeableAspectRatioActivity extends AbstractLifecycleLogActivity {
1829     }
1830 
1831     public static class NonResizeableLargeAspectRatioActivity extends AbstractLifecycleLogActivity {
1832     }
1833 
1834     public static class SupportsSizeChangesPortraitActivity extends AbstractLifecycleLogActivity {
1835     }
1836 
1837     public static class ResizeableLeftActivity extends AbstractLifecycleLogActivity {
1838     }
1839 
1840     public static class ResizeableRightActivity extends AbstractLifecycleLogActivity {
1841     }
1842 
1843     public static class NoPropertyChangeOrientationWhileRelaunchingActivity extends
1844             AbstractLifecycleLogActivity {
1845 
1846         private static boolean sHasChangeOrientationInOnResume;
1847 
1848         @Override
onCreate(Bundle instance)1849         protected void onCreate(Bundle instance) {
1850             super.onCreate(instance);
1851             // When OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION is enabled this request
1852             // should be ignored if sHasChangeOrientationInOnResume is true.
1853             setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
1854         }
1855 
1856         @Override
onResume()1857         protected void onResume() {
1858             super.onResume();
1859             if (!sHasChangeOrientationInOnResume) {
1860                 setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
1861                 sHasChangeOrientationInOnResume = true;
1862             }
1863         }
1864     }
1865 
1866     /**
1867      * Registers broadcast receiver which receives result actions from Activities under test.
1868      */
1869     private static class BroadcastReceiverCloseable implements AutoCloseable {
1870         private final Context mContext;
1871         private final Map<String, ConditionVariable> mBroadcastsReceived;
1872         private final BroadcastReceiver mAppCommunicator = new BroadcastReceiver() {
1873             @Override
1874             public void onReceive(Context context, Intent intent) {
1875                 getBroadcastReceivedVariable(intent.getAction()).open();
1876             }
1877         };
1878 
BroadcastReceiverCloseable(final Context context, final String action)1879         BroadcastReceiverCloseable(final Context context, final String action) {
1880             this.mContext = context;
1881             // Keep the received broadcast items in the map.
1882             mBroadcastsReceived = Collections.synchronizedMap(new HashMap<>());
1883             // Register for broadcast actions.
1884             IntentFilter filter = new IntentFilter();
1885             filter.addAction(action);
1886             mContext.registerReceiver(mAppCommunicator, filter, Context.RECEIVER_EXPORTED);
1887         }
1888 
getBroadcastReceivedVariable(String action)1889         ConditionVariable getBroadcastReceivedVariable(String action) {
1890             return mBroadcastsReceived.computeIfAbsent(action, key -> new ConditionVariable());
1891         }
1892 
1893         @Override
close()1894         public void close() {
1895             mContext.unregisterReceiver(mAppCommunicator);
1896         }
1897     }
1898 
1899     /**
1900      * Resets device config to the original value after the try-with-resources block finishes
1901      * try (var dc = new CloseableDeviceConfig()) {...}
1902      */
1903     private class CloseableDeviceConfig implements AutoCloseable {
1904         private String mOriginalValue;
1905         private final String mNamespace;
1906         private final String mFlagName;
1907 
CloseableDeviceConfig(@onNull String namespace, @NonNull String flagName, @Nullable String value)1908         CloseableDeviceConfig(@NonNull String namespace, @NonNull String flagName,
1909                 @Nullable String value) {
1910             mNamespace = namespace;
1911             mFlagName = flagName;
1912             runWithShellPermission(() -> {
1913                 mOriginalValue = DeviceConfig.getProperty(namespace, flagName);
1914                 DeviceConfig.setProperty(namespace, flagName, value, /* makeDefault */
1915                         false);
1916             });
1917         }
1918 
1919         @Override
close()1920         public void close() {
1921             runWithShellPermission(() -> DeviceConfig.setProperty(mNamespace, mFlagName,
1922                     mOriginalValue, /* makeDefault */ false));
1923         }
1924     }
1925 }
1926