• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import static android.server.wm.app.Components.UiScalingTestActivity.COMMAND_ADD_SUBVIEW;
21 import static android.server.wm.app.Components.UiScalingTestActivity.COMMAND_CLEAR_DEFAULT_VIEW;
22 import static android.server.wm.app.Components.UiScalingTestActivity.COMMAND_GET_RESOURCES_CONFIG;
23 import static android.server.wm.app.Components.UiScalingTestActivity.COMMAND_GET_SUBVIEW_SIZE;
24 import static android.server.wm.app.Components.UiScalingTestActivity.COMMAND_UPDATE_RESOURCES_CONFIG;
25 import static android.server.wm.app.Components.UiScalingTestActivity.KEY_COMMAND_SUCCESS;
26 import static android.server.wm.app.Components.UiScalingTestActivity.KEY_RESOURCES_CONFIG;
27 import static android.server.wm.app.Components.UiScalingTestActivity.KEY_SUBVIEW_ID;
28 import static android.server.wm.app.Components.UiScalingTestActivity.KEY_TEXT_SIZE;
29 import static android.server.wm.app.Components.UiScalingTestActivity.KEY_VIEW_SIZE;
30 import static android.server.wm.app.Components.UiScalingTestActivity.SUBVIEW_ID1;
31 import static android.server.wm.app.Components.UiScalingTestActivity.SUBVIEW_ID2;
32 
33 import static org.hamcrest.Matchers.closeTo;
34 import static org.hamcrest.Matchers.equalTo;
35 import static org.hamcrest.Matchers.is;
36 import static org.hamcrest.Matchers.not;
37 import static org.hamcrest.Matchers.notNullValue;
38 
39 import android.content.ComponentName;
40 import android.content.res.Configuration;
41 import android.graphics.Rect;
42 import android.os.Bundle;
43 import android.os.LocaleList;
44 
45 import org.junit.Rule;
46 import org.junit.Test;
47 import org.junit.rules.ErrorCollector;
48 import org.junit.runner.RunWith;
49 import org.junit.runners.Parameterized;
50 
51 import java.util.Arrays;
52 
53 /**
54  * The test is focused on compatibility scaling, and tests the feature form two sides.
55  * 1. It checks that the applications "sees" the metrics in PXs, but the DP metrics remain the same.
56  * 2. It checks the WindowManagerServer state, and makes sure that the scaling is correctly
57  * reflected in the WindowState.
58  *
59  * This is achieved by launching a {@link android.server.wm.app.UiScalingTestActivity} and having it
60  * reporting the metrics it receives.
61  * The Activity also draws 3 UI elements: a text, a red square with a 100dp side and a blue square
62  * with a 100px side.
63  * The text and the red square should have the same when rendered on the screen (by HWC) both when
64  * the compat downscaling is enabled and disabled.
65  * TODO(b/180098454): Add tests to make sure that the UI elements, which have their sizes declared
66  * in DPs (the text and the red square) have the same sizes on the screen (after composition).
67  *
68  * <p>Build/Install/Run:
69  *     atest CtsWindowManagerDeviceTestCases:CompatScaleTests
70  */
71 @RunWith(Parameterized.class)
72 public class CompatScaleTests extends ActivityManagerTestBase {
73     /**
74      * If application size is 1280, then Upscaling by 0.3 will make the surface 1280/0.3 = 4267.
75      * Some devices do not support this high resolution, so limiting Upscaling test case for
76      * scaling >= 0.5.
77      */
78     public static float MAX_UPSCALING_TESTED = 0.5f;
79 
80     @Parameterized.Parameters(name = "{0}")
data()81     public static Iterable<Object[]> data() {
82         return Arrays.asList(new Object[][] {
83                 { "DOWNSCALE_30", 0.3f },
84                 { "DOWNSCALE_35", 0.35f },
85                 { "DOWNSCALE_40", 0.4f },
86                 { "DOWNSCALE_45", 0.45f },
87                 { "DOWNSCALE_50", 0.5f },
88                 { "DOWNSCALE_55", 0.55f },
89                 { "DOWNSCALE_60", 0.6f },
90                 { "DOWNSCALE_65", 0.65f },
91                 { "DOWNSCALE_70", 0.7f },
92                 { "DOWNSCALE_75", 0.75f },
93                 { "DOWNSCALE_80", 0.8f },
94                 { "DOWNSCALE_85", 0.85f },
95                 { "DOWNSCALE_90", 0.9f },
96         });
97     }
98 
99     @Rule
100     public ErrorCollector collector = new ErrorCollector();
101 
102     private static final ComponentName ACTIVITY_UNDER_TEST = UI_SCALING_TEST_ACTIVITY;
103     private static final String PACKAGE_UNDER_TEST = ACTIVITY_UNDER_TEST.getPackageName();
104     private static final float EPSILON_GLOBAL_SCALE = 0.01f;
105     private final String mCompatChangeName;
106     private final float mCompatScale;
107     private final float mInvCompatScale;
108     private CommandSession.SizeInfo mAppSizesNormal;
109     private CommandSession.SizeInfo mAppSizesDownscaled;
110     private CommandSession.SizeInfo mAppSizesUpscaled;
111     private WindowManagerState.WindowState mWindowStateNormal;
112     private WindowManagerState.WindowState mWindowStateDownscaled;
113     private WindowManagerState.WindowState mWindowStateUpscaled;
114 
CompatScaleTests(String compatChangeName, float compatScale)115     public CompatScaleTests(String compatChangeName, float compatScale) {
116         mCompatChangeName = compatChangeName;
117         mCompatScale = compatScale;
118         mInvCompatScale = 1 / mCompatScale;
119     }
120 
121     @Test
testUpdateResourcesConfiguration()122     public void testUpdateResourcesConfiguration() {
123         // Launch activity with down/up scaling *disabled*
124         try (var session = new BaseActivitySessionCloseable(ACTIVITY_UNDER_TEST)) {
125             runTestUpdateResourcesConfiguration(session.getActivitySession());
126         }
127 
128         try (var scale = new CompatChangeCloseable(mCompatChangeName, PACKAGE_UNDER_TEST)) {
129             // Now launch the same activity with downscaling *enabled*
130             try (var down = new CompatChangeCloseable("DOWNSCALED", PACKAGE_UNDER_TEST);
131                  var session = new BaseActivitySessionCloseable(ACTIVITY_UNDER_TEST)) {
132                 runTestUpdateResourcesConfiguration(session.getActivitySession());
133             }
134 
135             if (mCompatScale >= MAX_UPSCALING_TESTED) {
136                 // Now launch the same activity with upscaling *enabled*
137                 try (var up = new CompatChangeCloseable("DOWNSCALED_INVERSE", PACKAGE_UNDER_TEST);
138                      var session = new BaseActivitySessionCloseable(ACTIVITY_UNDER_TEST)) {
139                     runTestUpdateResourcesConfiguration(session.getActivitySession());
140                 }
141             }
142         }
143     }
144 
runTestUpdateResourcesConfiguration(CommandSession.ActivitySession activity)145     private void runTestUpdateResourcesConfiguration(CommandSession.ActivitySession activity) {
146         activity.sendCommandAndWaitReply(COMMAND_CLEAR_DEFAULT_VIEW);
147         addSubview(activity, SUBVIEW_ID1);
148         Bundle subviewSize1 = getSubViewSize(activity, SUBVIEW_ID1);
149         collector.checkThat(subviewSize1.getParcelable(KEY_TEXT_SIZE, Rect.class),
150                 not(equalTo(new Rect())));
151         collector.checkThat(subviewSize1.getParcelable(KEY_VIEW_SIZE, Rect.class),
152                 not(equalTo(new Rect())));
153         collector.checkThat(subviewSize1.getBoolean(KEY_COMMAND_SUCCESS), is(true));
154         Configuration config = activity.sendCommandAndWaitReply(
155                 COMMAND_GET_RESOURCES_CONFIG).getParcelable(KEY_RESOURCES_CONFIG,
156                 Configuration.class);
157         config.setLocales(LocaleList.forLanguageTags("en-US,en-XC"));
158         Bundle data = new Bundle();
159         data.putParcelable(KEY_RESOURCES_CONFIG, config);
160         collector.checkThat("Failed to update resources configuration",
161                 activity.sendCommandAndWaitReply(COMMAND_UPDATE_RESOURCES_CONFIG,
162                         data).getBoolean(
163                         KEY_COMMAND_SUCCESS),
164                 is(true));
165 
166         addSubview(activity, SUBVIEW_ID2);
167         Bundle subviewSize2 = getSubViewSize(activity, SUBVIEW_ID2);
168         collector.checkThat(subviewSize2.getBoolean(KEY_COMMAND_SUCCESS), is(true));
169         collector.checkThat(subviewSize1.getParcelable(KEY_TEXT_SIZE, Rect.class),
170                 equalTo(subviewSize2.getParcelable(KEY_TEXT_SIZE, Rect.class)));
171         collector.checkThat(subviewSize1.getParcelable(KEY_VIEW_SIZE, Rect.class),
172                 equalTo(subviewSize2.getParcelable(KEY_VIEW_SIZE, Rect.class)));
173     }
174 
175     /**
176      * Tests that the parameters that the application receives from the
177      * {@link android.content.res.Configuration} are correctly scaled.
178      */
179     @Test
test_scalesCorrectly()180     public void test_scalesCorrectly() {
181         // Launch activity with down/up scaling *disabled* and get the sizes it reports and its
182         // Window state.
183         try (var session = new BaseActivitySessionCloseable(ACTIVITY_UNDER_TEST)) {
184             mAppSizesNormal = getActivityReportedSizes();
185             mWindowStateNormal = getPackageWindowState();
186         }
187 
188         try (var scale = new CompatChangeCloseable(mCompatChangeName, PACKAGE_UNDER_TEST)) {
189             // Now launch the same activity with downscaling *enabled* and get the sizes it reports
190             // and its Window state.
191             try (var down = new CompatChangeCloseable("DOWNSCALED", PACKAGE_UNDER_TEST);
192                  var session = new BaseActivitySessionCloseable(ACTIVITY_UNDER_TEST)) {
193                 mAppSizesDownscaled = getActivityReportedSizes();
194                 mWindowStateDownscaled = getPackageWindowState();
195             }
196             test_scalesCorrectly_inCompatDownscalingMode();
197             test_windowState_inCompatDownscalingMode();
198 
199             if (mCompatScale >= MAX_UPSCALING_TESTED) {
200                 // Now launch the same activity with upscaling *enabled* and get the sizes it
201                 // reports and its Window state.
202                 try (var up = new CompatChangeCloseable("DOWNSCALED_INVERSE", PACKAGE_UNDER_TEST);
203                      var session = new BaseActivitySessionCloseable(ACTIVITY_UNDER_TEST)) {
204                     mAppSizesUpscaled = getActivityReportedSizes();
205                     mWindowStateUpscaled = getPackageWindowState();
206                 }
207                 test_scalesCorrectly_inCompatUpscalingMode();
208                 test_windowState_inCompatUpscalingMode();
209             }
210         }
211 
212     }
213 
test_scalesCorrectly_inCompatDownscalingMode()214     private void test_scalesCorrectly_inCompatDownscalingMode() {
215         checkScaled("Density DPI should scale by " + mCompatScale,
216                 mAppSizesNormal.densityDpi, mCompatScale, mAppSizesDownscaled.densityDpi);
217         collector.checkThat("Width shouldn't change",
218                 mAppSizesNormal.widthDp, equalTo(mAppSizesDownscaled.widthDp));
219         collector.checkThat("Height shouldn't change",
220                 mAppSizesNormal.heightDp, equalTo(mAppSizesDownscaled.heightDp));
221         collector.checkThat("Smallest Width shouldn't change",
222                 mAppSizesNormal.smallestWidthDp, equalTo(mAppSizesDownscaled.smallestWidthDp));
223         checkScaled("Width should scale by " + mCompatScale,
224                 mAppSizesNormal.windowWidth, mCompatScale, mAppSizesDownscaled.windowWidth);
225         checkScaled("Height should scale by " + mCompatScale,
226                 mAppSizesNormal.windowHeight, mCompatScale, mAppSizesDownscaled.windowHeight);
227         checkScaled("App width should scale by " + mCompatScale,
228                 mAppSizesNormal.windowAppWidth, mCompatScale, mAppSizesDownscaled.windowAppWidth);
229         checkScaled("App height should scale by " + mCompatScale,
230                 mAppSizesNormal.windowAppHeight, mCompatScale, mAppSizesDownscaled.windowAppHeight);
231         checkScaled("Width should scale by " + mCompatScale,
232                 mAppSizesNormal.metricsWidth, mCompatScale, mAppSizesDownscaled.metricsWidth);
233         checkScaled("Height should scale by " + mCompatScale,
234                 mAppSizesNormal.metricsHeight, mCompatScale, mAppSizesDownscaled.metricsHeight);
235         checkScaled("Width should scale by " + mCompatScale,
236                 mAppSizesNormal.displayWidth, mCompatScale, mAppSizesDownscaled.displayWidth);
237         checkScaled("Height should scale by " + mCompatScale,
238                 mAppSizesNormal.displayHeight, mCompatScale, mAppSizesDownscaled.displayHeight);
239     }
240 
test_scalesCorrectly_inCompatUpscalingMode()241     private void test_scalesCorrectly_inCompatUpscalingMode() {
242         checkScaled("Density DPI should scale by " + mInvCompatScale,
243                 mAppSizesNormal.densityDpi, mInvCompatScale, mAppSizesUpscaled.densityDpi);
244         collector.checkThat("Width shouldn't change",
245                 mAppSizesNormal.widthDp, equalTo(mAppSizesUpscaled.widthDp));
246         collector.checkThat("Height shouldn't change",
247                 mAppSizesNormal.heightDp, equalTo(mAppSizesUpscaled.heightDp));
248         collector.checkThat("Smallest Width shouldn't change",
249                 mAppSizesNormal.smallestWidthDp, equalTo(mAppSizesUpscaled.smallestWidthDp));
250         checkScaled("Width should scale by " + mInvCompatScale,
251                 mAppSizesNormal.windowWidth, mInvCompatScale, mAppSizesUpscaled.windowWidth);
252         checkScaled("Height should scale by " + mInvCompatScale,
253                 mAppSizesNormal.windowHeight, mInvCompatScale, mAppSizesUpscaled.windowHeight);
254         checkScaled("App width should scale by " + mInvCompatScale,
255                 mAppSizesNormal.windowAppWidth, mInvCompatScale, mAppSizesUpscaled.windowAppWidth);
256         checkScaled("App height should scale by " + mInvCompatScale,
257                 mAppSizesNormal.windowAppHeight, mInvCompatScale,
258                 mAppSizesUpscaled.windowAppHeight);
259         checkScaled("Width should scale by " + mInvCompatScale,
260                 mAppSizesNormal.metricsWidth, mInvCompatScale, mAppSizesUpscaled.metricsWidth);
261         checkScaled("Height should scale by " + mInvCompatScale,
262                 mAppSizesNormal.metricsHeight, mInvCompatScale, mAppSizesUpscaled.metricsHeight);
263         checkScaled("Width should scale by " + mInvCompatScale,
264                 mAppSizesNormal.displayWidth, mInvCompatScale, mAppSizesUpscaled.displayWidth);
265         checkScaled("Height should scale by " + mInvCompatScale,
266                 mAppSizesNormal.displayHeight, mInvCompatScale, mAppSizesUpscaled.displayHeight);
267     }
268 
test_windowState_inCompatDownscalingMode()269     private void test_windowState_inCompatDownscalingMode() {
270         // Check the "normal" window's state for disabled compat mode and appropriate global scale.
271         collector.checkThat("The Window should not be in the size compat mode",
272                 mWindowStateNormal.hasCompatScale(), is(false));
273         collector.checkThat("The window should not be scaled",
274                 1d, closeTo(mWindowStateNormal.getGlobalScale(), EPSILON_GLOBAL_SCALE));
275 
276         // Check the "downscaled" window's state for enabled compat mode and appropriate global
277         // scale.
278         collector.checkThat("The Window should be in the size compat mode",
279                 mWindowStateDownscaled.hasCompatScale(), is(true));
280         collector.checkThat("The window should have global scale of " + mInvCompatScale,
281                 (double) mInvCompatScale,
282                 closeTo(mWindowStateDownscaled.getGlobalScale(), EPSILON_GLOBAL_SCALE));
283 
284         // Make sure the frame sizes changed correctly.
285         collector.checkThat("Window frame on should not change",
286                 mWindowStateNormal.getFrame(), equalTo(mWindowStateDownscaled.getFrame()));
287         checkScaled("Requested width should scale by " + mCompatScale,
288                 mWindowStateNormal.getRequestedWidth(), mCompatScale,
289                 mWindowStateDownscaled.getRequestedWidth());
290         checkScaled("Requested height " + mWindowStateNormal.getRequestedHeight()
291                         + " should scale by " + mCompatScale,
292                 mWindowStateNormal.getRequestedHeight(), mCompatScale,
293                 mWindowStateDownscaled.getRequestedHeight());
294     }
295 
test_windowState_inCompatUpscalingMode()296     private void test_windowState_inCompatUpscalingMode() {
297         // Check the "normal" window's state for disabled compat mode and appropriate global scale.
298         collector.checkThat("The Window should not be in the size compat mode",
299                 mWindowStateNormal.hasCompatScale(), is(false));
300         collector.checkThat("The window should not be scaled",
301                 1d, closeTo(mWindowStateNormal.getGlobalScale(), EPSILON_GLOBAL_SCALE));
302 
303         // Check the "upscaled" window's state for enabled compat mode and appropriate global
304         // scale.
305         collector.checkThat("The Window should be in the size compat mode",
306                 mWindowStateUpscaled.hasCompatScale(), is(true));
307         collector.checkThat("The window should have global scale of " + mCompatScale,
308                 (double) mCompatScale,
309                 closeTo(mWindowStateUpscaled.getGlobalScale(), EPSILON_GLOBAL_SCALE));
310 
311         // Make sure the frame sizes changed correctly.
312         collector.checkThat("Window frame on should not change",
313                 mWindowStateNormal.getFrame(), equalTo(mWindowStateUpscaled.getFrame()));
314         checkScaled("Requested width should scale by " + mInvCompatScale,
315                 mWindowStateNormal.getRequestedWidth(), mInvCompatScale,
316                 mWindowStateUpscaled.getRequestedWidth());
317         checkScaled("Requested height should scale by " + mInvCompatScale,
318                 mWindowStateNormal.getRequestedHeight(), mInvCompatScale,
319                 mWindowStateUpscaled.getRequestedHeight());
320     }
321 
getActivityReportedSizes()322     private CommandSession.SizeInfo getActivityReportedSizes() {
323         final CommandSession.SizeInfo details =
324                 getLastReportedSizesForActivity(ACTIVITY_UNDER_TEST);
325         collector.checkThat(details, notNullValue());
326         return details;
327     }
328 
getPackageWindowState()329     private WindowManagerState.WindowState getPackageWindowState() {
330         return getPackageWindowState(PACKAGE_UNDER_TEST);
331     }
332 
addSubview(CommandSession.ActivitySession activity, String subviewId)333     private void addSubview(CommandSession.ActivitySession activity, String subviewId) {
334         Bundle data = new Bundle();
335         data.putString(KEY_SUBVIEW_ID, subviewId);
336         Bundle res = activity.sendCommandAndWaitReply(COMMAND_ADD_SUBVIEW, data);
337         collector.checkThat("Failed to add subview " + subviewId,
338                 res.getBoolean(KEY_COMMAND_SUCCESS), is(true));
339     }
340 
getSubViewSize(CommandSession.ActivitySession activity, String subviewId)341     private Bundle getSubViewSize(CommandSession.ActivitySession activity, String subviewId) {
342         Bundle data = new Bundle();
343         data.putString(KEY_SUBVIEW_ID, subviewId);
344         return activity.sendCommandAndWaitReply(COMMAND_GET_SUBVIEW_SIZE, data);
345     }
346 
checkScaled(String message, int baseValue, double expectedScale, int actualValue)347     private void checkScaled(String message, int baseValue, double expectedScale,
348             int actualValue) {
349         // In order to account for possible rounding errors, let's calculate the actual scale and
350         // compare it's against the expected scale (allowing a small delta).
351         final double actualScale = ((double) actualValue) / baseValue;
352         collector.checkThat(message, actualScale, closeTo(expectedScale, EPSILON_GLOBAL_SCALE));
353     }
354 }
355