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