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 mWmState.waitForActivityRemoved(ACTIVITY_UNDER_TEST); 107 launchActivity(); 108 mAppSizesDownscaled = getActivityReportedSizes(); 109 mWindowStateDownscaled = getPackageWindowState(); 110 } 111 112 /** 113 * Tests that the Density DPI that the application receives from the 114 * {@link android.content.res.Configuration} is correctly scaled in the downscaled mode. 115 * @see android.content.res.Configuration#densityDpi 116 */ 117 @Test test_config_densityDpi_scalesCorrectly_inCompatDownscalingMode()118 public void test_config_densityDpi_scalesCorrectly_inCompatDownscalingMode() { 119 assertScaled("Density DPI should scale by " + mCompatScale, 120 mAppSizesNormal.densityDpi, mCompatScale, mAppSizesDownscaled.densityDpi); 121 } 122 123 /** 124 * Tests that the screen sizes in DPs that the application receives from the 125 * {@link android.content.res.Configuration} are NOT scaled in the downscaled mode. 126 * @see android.content.res.Configuration#screenWidthDp 127 * @see android.content.res.Configuration#screenHeightDp 128 * @see android.content.res.Configuration#smallestScreenWidthDp 129 */ 130 @Test test_config_screenSize_inDPs_doesNotChange_inCompatDownscalingMode()131 public void test_config_screenSize_inDPs_doesNotChange_inCompatDownscalingMode() { 132 assertEquals("Width shouldn't change", 133 mAppSizesNormal.widthDp, mAppSizesDownscaled.widthDp); 134 assertEquals("Height shouldn't change", 135 mAppSizesNormal.heightDp, mAppSizesDownscaled.heightDp); 136 assertEquals("Smallest Width shouldn't change", 137 mAppSizesNormal.smallestWidthDp, mAppSizesDownscaled.smallestWidthDp); 138 } 139 140 /** 141 * Tests that the Window sizes in PXs that the application receives from the 142 * {@link android.content.res.Configuration} are scaled correctly in the downscaled mode. 143 * @see android.content.res.Configuration#windowConfiguration 144 * @see android.app.WindowConfiguration#getBounds() 145 * @see android.app.WindowConfiguration#getAppBounds() 146 */ 147 @Test test_config_windowSizes_inPXs_scaleCorrectly_inCompatDownscalingMode()148 public void test_config_windowSizes_inPXs_scaleCorrectly_inCompatDownscalingMode() { 149 assertScaled("Width should scale by " + mCompatScale, 150 mAppSizesNormal.windowWidth, mCompatScale, mAppSizesDownscaled.windowWidth); 151 assertScaled("Height should scale by " + mCompatScale, 152 mAppSizesNormal.windowHeight, mCompatScale, mAppSizesDownscaled.windowHeight); 153 assertScaled("App width should scale by " + mCompatScale, 154 mAppSizesNormal.windowAppWidth, mCompatScale, mAppSizesDownscaled.windowAppWidth); 155 assertScaled("App height should scale by " + mCompatScale, 156 mAppSizesNormal.windowAppHeight, mCompatScale, mAppSizesDownscaled.windowAppHeight); 157 } 158 159 /** 160 * Tests that the {@link android.util.DisplayMetrics} in PXs that the application can obtain via 161 * {@link android.content.res.Resources#getDisplayMetrics()} are scaled correctly in the 162 * downscaled mode. 163 * @see android.util.DisplayMetrics#widthPixels 164 * @see android.util.DisplayMetrics#heightPixels 165 */ 166 @Test test_displayMetrics_inPXs_scaleCorrectly_inCompatDownscalingMode()167 public void test_displayMetrics_inPXs_scaleCorrectly_inCompatDownscalingMode() { 168 assertScaled("Width should scale by " + mCompatScale, 169 mAppSizesNormal.metricsWidth, mCompatScale, mAppSizesDownscaled.metricsWidth); 170 assertScaled("Height should scale by " + mCompatScale, 171 mAppSizesNormal.metricsHeight, mCompatScale, mAppSizesDownscaled.metricsHeight); 172 } 173 174 /** 175 * Tests that the dimensions of a {@link android.view.Display} in PXs that the application can 176 * obtain via {@link android.view.View#getDisplay()} are scaled correctly in the downscaled 177 * mode. 178 * @see android.view.Display#getSize(android.graphics.Point) 179 */ 180 @Test test_displaySize_inPXs_scaleCorrectly_inCompatDownscalingMode()181 public void test_displaySize_inPXs_scaleCorrectly_inCompatDownscalingMode() { 182 assertScaled("Width should scale by " + mCompatScale, 183 mAppSizesNormal.displayWidth, mCompatScale, mAppSizesDownscaled.displayWidth); 184 assertScaled("Height should scale by " + mCompatScale, 185 mAppSizesNormal.displayHeight, mCompatScale, mAppSizesDownscaled.displayHeight); 186 } 187 188 /** 189 * Test that compatibility downscaling is reflected correctly on the WM side. 190 * @see android.server.wm.WindowManagerState.WindowState 191 */ 192 @Test test_windowState_inCompatDownscalingMode()193 public void test_windowState_inCompatDownscalingMode() { 194 // Check the "normal" window's state for disabled compat mode and appropriate global scale. 195 assertFalse("The Window should not be in the size compat mode", 196 mWindowStateNormal.hasCompatScale()); 197 assertEquals("The window should not be scaled", 198 1f, mWindowStateNormal.getGlobalScale(), EPSILON_GLOBAL_SCALE); 199 200 // Check the "downscaled" window's state for enabled compat mode and appropriate global 201 // scale. 202 assertTrue("The Window should be in the size compat mode", 203 mWindowStateDownscaled.hasCompatScale()); 204 assertEquals("The window should have global scale of " + mInvCompatScale, 205 mInvCompatScale, mWindowStateDownscaled.getGlobalScale(), EPSILON_GLOBAL_SCALE); 206 207 // Make sure the frame sizes changed correctly. 208 assertEquals("Window frame on should not change", 209 mWindowStateNormal.getFrame(), mWindowStateDownscaled.getFrame()); 210 assertScaled("Requested width should scale by " + mCompatScale, 211 mWindowStateNormal.getRequestedWidth(), mCompatScale, 212 mWindowStateDownscaled.getRequestedWidth()); 213 assertScaled("Requested height should scale by " + mCompatScale, 214 mWindowStateNormal.getRequestedHeight(), mCompatScale, 215 mWindowStateDownscaled.getRequestedHeight()); 216 } 217 218 @After tearDown()219 public void tearDown() { 220 disableDownscaling(mCompatChangeName); 221 } 222 launchActivity()223 private void launchActivity() { 224 launchActivityInNewTask(ACTIVITY_UNDER_TEST); 225 mWmState.computeState(new WaitForValidActivityState(ACTIVITY_UNDER_TEST)); 226 } 227 getActivityReportedSizes()228 private CommandSession.SizeInfo getActivityReportedSizes() { 229 final CommandSession.SizeInfo details = 230 getLastReportedSizesForActivity(ACTIVITY_UNDER_TEST); 231 assertNotNull(details); 232 return details; 233 } 234 getPackageWindowState()235 private WindowManagerState.WindowState getPackageWindowState() { 236 return getPackageWindowState(PACKAGE_UNDER_TEST); 237 } 238 enableDownscaling(String compatChangeName)239 private static void enableDownscaling(String compatChangeName) { 240 executeShellCommand("am compat enable " + compatChangeName + " " + PACKAGE_UNDER_TEST); 241 executeShellCommand("am compat enable DOWNSCALED " + PACKAGE_UNDER_TEST); 242 } 243 disableDownscaling(String compatChangeName)244 private static void disableDownscaling(String compatChangeName) { 245 executeShellCommand("am compat disable DOWNSCALED " + PACKAGE_UNDER_TEST); 246 executeShellCommand("am compat disable " + compatChangeName + " " + PACKAGE_UNDER_TEST); 247 } 248 assertScaled(String message, int baseValue, float expectedScale, int actualValue)249 private static void assertScaled(String message, int baseValue, float expectedScale, 250 int actualValue) { 251 // In order to account for possible rounding errors, let's calculate the actual scale and 252 // compare it's against the expected scale (allowing a small delta). 253 final float actualScale = ((float) actualValue) / baseValue; 254 assertEquals(message, expectedScale, actualScale, EPSILON_GLOBAL_SCALE); 255 } 256 } 257