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;
18 
19 import static android.server.wm.app.Components.UI_SCALING_TEST_ACTIVITY;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNotNull;
24 import static org.junit.Assert.assertTrue;
25 
26 import android.content.ComponentName;
27 
28 import org.junit.After;
29 import org.junit.Before;
30 import org.junit.Test;
31 import org.junit.runner.RunWith;
32 import org.junit.runners.Parameterized;
33 
34 import java.util.Arrays;
35 
36 /**
37  * The test is focused on compatibility scaling, and tests the feature form two sides.
38  * 1. It checks that the applications "sees" the metrics in PXs, but the DP metrics remain the same.
39  * 2. It checks the WindowManagerServer state, and makes sure that the scaling is correctly
40  * reflected in the WindowState.
41  *
42  * This is achieved by launching a {@link android.server.wm.app.UiScalingTestActivity} and having it
43  * reporting the metrics it receives.
44  * The Activity also draws 3 UI elements: a text, a red square with a 100dp side and a blue square
45  * with a 100px side.
46  * The text and the red square should have the same when rendered on the screen (by HWC) both when
47  * the compat downscaling is enabled and disabled.
48  * TODO(b/180098454): Add tests to make sure that the UI elements, which have their sizes declared
49  * in DPs (the text and the red square) have the same sizes on the screen (after composition).
50  *
51  * <p>Build/Install/Run:
52  *     atest CtsWindowManagerDeviceTestCases:CompatScaleTests
53  */
54 @RunWith(Parameterized.class)
55 public class CompatScaleTests extends ActivityManagerTestBase {
56 
57     @Parameterized.Parameters(name = "{0}")
data()58     public static Iterable<Object[]> data() {
59         return Arrays.asList(new Object[][] {
60                 { "DOWNSCALE_30", 0.3f },
61                 { "DOWNSCALE_35", 0.35f },
62                 { "DOWNSCALE_40", 0.4f },
63                 { "DOWNSCALE_45", 0.45f },
64                 { "DOWNSCALE_50", 0.5f },
65                 { "DOWNSCALE_55", 0.55f },
66                 { "DOWNSCALE_60", 0.6f },
67                 { "DOWNSCALE_65", 0.65f },
68                 { "DOWNSCALE_70", 0.7f },
69                 { "DOWNSCALE_75", 0.75f },
70                 { "DOWNSCALE_80", 0.8f },
71                 { "DOWNSCALE_85", 0.85f },
72                 { "DOWNSCALE_90", 0.9f },
73         });
74     }
75 
76     private static final ComponentName ACTIVITY_UNDER_TEST = UI_SCALING_TEST_ACTIVITY;
77     private static final String PACKAGE_UNDER_TEST = ACTIVITY_UNDER_TEST.getPackageName();
78     private static final float EPSILON_GLOBAL_SCALE = 0.01f;
79 
80     private final String mCompatChangeName;
81     private final float mCompatScale;
82     private final float mInvCompatScale;
83     private CommandSession.SizeInfo mAppSizesNormal;
84     private CommandSession.SizeInfo mAppSizesDownscaled;
85     private WindowManagerState.WindowState mWindowStateNormal;
86     private WindowManagerState.WindowState mWindowStateDownscaled;
87 
CompatScaleTests(String compatChangeName, float compatScale)88     public CompatScaleTests(String compatChangeName, float compatScale) {
89         mCompatChangeName = compatChangeName;
90         mCompatScale = compatScale;
91         mInvCompatScale = 1 / mCompatScale;
92     }
93 
94     // TODO(b/180343437): replace @Before with @BeforeParam
95     @Before
launchInNormalAndDownscaleMode_collectSizesAndWindowState()96     public void launchInNormalAndDownscaleMode_collectSizesAndWindowState() {
97         // Launch activity with downscaling *disabled* and get the sizes it reports and its Window
98         // state.
99         launchActivity();
100         mAppSizesNormal = getActivityReportedSizes();
101         mWindowStateNormal = getPackageWindowState();
102 
103         // Now launch the same activity with downscaling *enabled* and get the sizes it reports and
104         // its Window state.
105         enableDownscaling(mCompatChangeName);
106         launchActivity();
107         mAppSizesDownscaled = getActivityReportedSizes();
108         mWindowStateDownscaled = getPackageWindowState();
109     }
110 
111     /**
112      * Tests that the Density DPI that the application receives from the
113      * {@link android.content.res.Configuration} is correctly scaled in the downscaled mode.
114      * @see android.content.res.Configuration#densityDpi
115      */
116     @Test
test_config_densityDpi_scalesCorrectly_inCompatDownscalingMode()117     public void test_config_densityDpi_scalesCorrectly_inCompatDownscalingMode() {
118         assertScaled("Density DPI should scale by " + mCompatScale,
119                 mAppSizesNormal.densityDpi, mCompatScale, mAppSizesDownscaled.densityDpi);
120     }
121 
122     /**
123      * Tests that the screen sizes in DPs that the application receives from the
124      * {@link android.content.res.Configuration} are NOT scaled in the downscaled mode.
125      * @see android.content.res.Configuration#screenWidthDp
126      * @see android.content.res.Configuration#screenHeightDp
127      * @see android.content.res.Configuration#smallestScreenWidthDp
128      */
129     @Test
test_config_screenSize_inDPs_doesNotChange_inCompatDownscalingMode()130     public void test_config_screenSize_inDPs_doesNotChange_inCompatDownscalingMode() {
131         assertEquals("Width shouldn't change",
132                 mAppSizesNormal.widthDp, mAppSizesDownscaled.widthDp);
133         assertEquals("Height shouldn't change",
134                 mAppSizesNormal.heightDp, mAppSizesDownscaled.heightDp);
135         assertEquals("Smallest Width shouldn't change",
136                 mAppSizesNormal.smallestWidthDp, mAppSizesDownscaled.smallestWidthDp);
137     }
138 
139     /**
140      * Tests that the Window sizes in PXs that the application receives from the
141      * {@link android.content.res.Configuration} are scaled correctly in the downscaled mode.
142      * @see android.content.res.Configuration#windowConfiguration
143      * @see android.app.WindowConfiguration#getBounds()
144      * @see android.app.WindowConfiguration#getAppBounds()
145      */
146     @Test
test_config_windowSizes_inPXs_scaleCorrectly_inCompatDownscalingMode()147     public void test_config_windowSizes_inPXs_scaleCorrectly_inCompatDownscalingMode() {
148         assertScaled("Width should scale by " + mCompatScale,
149                 mAppSizesNormal.windowWidth, mCompatScale, mAppSizesDownscaled.windowWidth);
150         assertScaled("Height should scale by " + mCompatScale,
151                 mAppSizesNormal.windowHeight, mCompatScale, mAppSizesDownscaled.windowHeight);
152         assertScaled("App width should scale by " + mCompatScale,
153                 mAppSizesNormal.windowAppWidth, mCompatScale, mAppSizesDownscaled.windowAppWidth);
154         assertScaled("App height should scale by " + mCompatScale,
155                 mAppSizesNormal.windowAppHeight, mCompatScale, mAppSizesDownscaled.windowAppHeight);
156     }
157 
158     /**
159      * Tests that the {@link android.util.DisplayMetrics} in PXs that the application can obtain via
160      * {@link android.content.res.Resources#getDisplayMetrics()} are scaled correctly in the
161      * downscaled mode.
162      * @see android.util.DisplayMetrics#widthPixels
163      * @see android.util.DisplayMetrics#heightPixels
164      */
165     @Test
test_displayMetrics_inPXs_scaleCorrectly_inCompatDownscalingMode()166     public void test_displayMetrics_inPXs_scaleCorrectly_inCompatDownscalingMode() {
167         assertScaled("Width should scale by " + mCompatScale,
168                 mAppSizesNormal.metricsWidth, mCompatScale, mAppSizesDownscaled.metricsWidth);
169         assertScaled("Height should scale by " + mCompatScale,
170                 mAppSizesNormal.metricsHeight, mCompatScale, mAppSizesDownscaled.metricsHeight);
171     }
172 
173     /**
174      * Tests that the dimensions of a {@link android.view.Display} in PXs that the application can
175      * obtain via {@link android.view.View#getDisplay()} are scaled correctly in the downscaled
176      * mode.
177      * @see android.view.Display#getSize(android.graphics.Point)
178      */
179     @Test
test_displaySize_inPXs_scaleCorrectly_inCompatDownscalingMode()180     public void test_displaySize_inPXs_scaleCorrectly_inCompatDownscalingMode() {
181         assertScaled("Width should scale by " + mCompatScale,
182                 mAppSizesNormal.displayWidth, mCompatScale, mAppSizesDownscaled.displayWidth);
183         assertScaled("Height should scale by " + mCompatScale,
184                 mAppSizesNormal.displayHeight, mCompatScale, mAppSizesDownscaled.displayHeight);
185     }
186 
187     /**
188      * Test that compatibility downscaling is reflected correctly on the WM side.
189      * @see android.server.wm.WindowManagerState.WindowState
190      */
191     @Test
test_windowState_inCompatDownscalingMode()192     public void test_windowState_inCompatDownscalingMode() {
193         // Check the "normal" window's state for disabled compat mode and appropriate global scale.
194         assertFalse("The Window should not be in the size compat mode",
195                 mWindowStateNormal.hasCompatScale());
196         assertEquals("The window should not be scaled",
197                 1f, mWindowStateNormal.getGlobalScale(), EPSILON_GLOBAL_SCALE);
198 
199         // Check the "downscaled" window's state for enabled compat mode and appropriate global
200         // scale.
201         assertTrue("The Window should be in the size compat mode",
202                 mWindowStateDownscaled.hasCompatScale());
203         assertEquals("The window should have global scale of " + mInvCompatScale,
204                 mInvCompatScale, mWindowStateDownscaled.getGlobalScale(), EPSILON_GLOBAL_SCALE);
205 
206         // Make sure the frame sizes changed correctly.
207         assertEquals("Window frame on should not change",
208                 mWindowStateNormal.getFrame(), mWindowStateDownscaled.getFrame());
209         assertScaled("Requested width should scale by " + mCompatScale,
210                 mWindowStateNormal.getRequestedWidth(), mCompatScale,
211                 mWindowStateDownscaled.getRequestedWidth());
212         assertScaled("Requested height should scale by " + mCompatScale,
213                 mWindowStateNormal.getRequestedHeight(), mCompatScale,
214                 mWindowStateDownscaled.getRequestedHeight());
215     }
216 
217     @After
tearDown()218     public void tearDown() {
219         disableDownscaling(mCompatChangeName);
220     }
221 
launchActivity()222     private void launchActivity() {
223         launchActivityInNewTask(ACTIVITY_UNDER_TEST);
224         mWmState.computeState(new WaitForValidActivityState(ACTIVITY_UNDER_TEST));
225     }
226 
getActivityReportedSizes()227     private CommandSession.SizeInfo getActivityReportedSizes() {
228         final CommandSession.SizeInfo details =
229                 getLastReportedSizesForActivity(ACTIVITY_UNDER_TEST);
230         assertNotNull(details);
231         return details;
232     }
233 
getPackageWindowState()234     private WindowManagerState.WindowState getPackageWindowState() {
235         return getPackageWindowState(PACKAGE_UNDER_TEST);
236     }
237 
enableDownscaling(String compatChangeName)238     private static void enableDownscaling(String compatChangeName) {
239         executeShellCommand("am compat enable " + compatChangeName + " " + PACKAGE_UNDER_TEST);
240         executeShellCommand("am compat enable DOWNSCALED " + PACKAGE_UNDER_TEST);
241     }
242 
disableDownscaling(String compatChangeName)243     private static void disableDownscaling(String compatChangeName) {
244         executeShellCommand("am compat disable DOWNSCALED " + PACKAGE_UNDER_TEST);
245         executeShellCommand("am compat disable " + compatChangeName + " " + PACKAGE_UNDER_TEST);
246     }
247 
assertScaled(String message, int baseValue, float expectedScale, int actualValue)248     private static void assertScaled(String message, int baseValue, float expectedScale,
249             int actualValue) {
250         // In order to account for possible rounding errors, let's calculate the actual scale and
251         // compare it's against the expected scale (allowing a small delta).
252         final float actualScale = ((float) actualValue) / baseValue;
253         assertEquals(message, expectedScale, actualScale, EPSILON_GLOBAL_SCALE);
254     }
255 }
256