• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.window;
18 
19 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
20 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
22 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
23 import static android.server.wm.WindowMetricsTestHelper.assertMetricsMatchDisplay;
24 import static android.server.wm.WindowMetricsTestHelper.maxWindowBoundsSandboxed;
25 
26 import static org.junit.Assert.assertEquals;
27 import static org.junit.Assert.assertTrue;
28 import static org.junit.Assert.fail;
29 import static org.junit.Assume.assumeTrue;
30 
31 import android.app.Activity;
32 import android.app.PictureInPictureParams;
33 import android.content.ComponentName;
34 import android.graphics.Point;
35 import android.graphics.Rect;
36 import android.platform.test.annotations.Presubmit;
37 import android.server.wm.Condition;
38 import android.server.wm.MetricsActivity;
39 import android.server.wm.WindowManagerState;
40 import android.server.wm.WindowManagerTestBase;
41 import android.server.wm.WindowMetricsTestHelper;
42 import android.server.wm.WindowMetricsTestHelper.OnLayoutChangeListener;
43 import android.view.Display;
44 import android.view.WindowManager;
45 import android.view.WindowMetrics;
46 
47 import com.android.compatibility.common.util.ApiTest;
48 
49 import org.junit.Test;
50 
51 import java.util.function.Supplier;
52 
53 /**
54  * Tests that verify the behavior of {@link WindowMetrics} APIs on {@link Activity activities}.
55  *
56  * Build/Install/Run:
57  *     atest CtsWindowManagerDeviceWindow:WindowMetricsActivityTests
58  */
59 @Presubmit
60 @ApiTest(apis = {"android.view.WindowManager#getCurrentWindowMetrics",
61         "android.view.WindowManager#getMaximumWindowMetrics",
62         "android.app.Activity#getWindowManager"})
63 public class WindowMetricsActivityTests extends WindowManagerTestBase {
64     private static final Rect WINDOW_BOUNDS = new Rect(100, 100, 900, 900);
65     private static final int MOVE_OFFSET = 100;
66 
67     @Test
testMetricsMatchesLayoutOnActivityOnCreate()68     public void testMetricsMatchesLayoutOnActivityOnCreate() {
69         final MetricsActivity activity = startActivityInWindowingModeFullScreen(
70                 MetricsActivity.class);
71         final OnLayoutChangeListener listener = activity.getListener();
72 
73         listener.waitForLayout();
74 
75         WindowMetricsTestHelper.assertMetricsMatchesLayout(
76                 activity.getOnCreateCurrentMetrics(),
77                 activity.getOnCreateMaximumMetrics(),
78                 listener.getLayoutBounds(),
79                 listener.getLayoutInsets(),
80                 activity.isInMultiWindowMode()
81         );
82     }
83 
84     @Test
testMetricsMatchesDisplayAreaOnActivity()85     public void testMetricsMatchesDisplayAreaOnActivity() {
86         final MetricsActivity activity = startActivityInWindowingModeFullScreen(
87                 MetricsActivity.class);
88 
89         assertMetricsValidity(activity);
90     }
91 
92     @Test
testMetricsMatchesActivityBoundsOnNonresizableActivity()93     public void testMetricsMatchesActivityBoundsOnNonresizableActivity() {
94         assumeTrue("Skipping test: no rotation support", supportsRotation());
95 
96         final MinAspectRatioActivity activity = startActivityInWindowingModeFullScreen(
97                 MinAspectRatioActivity.class);
98         mWmState.computeState(activity.getComponentName());
99 
100         assertMetricsValidityForNonresizableActivity(activity);
101     }
102 
103     @Test
testMetricsMatchesLayoutOnPipActivity()104     public void testMetricsMatchesLayoutOnPipActivity() {
105         assumeTrue(supportsPip());
106 
107         final MetricsActivity activity = startActivityInWindowingModeFullScreen(
108                 MetricsActivity.class);
109 
110         assertMetricsMatchesLayout(activity);
111 
112         activity.enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
113         waitForEnterPipAnimationComplete(activity.getComponentName());
114 
115         assertMetricsMatchesLayout(activity);
116     }
117 
118     @Test
testMetricsMatchesDisplayAreaOnPipActivity()119     public void testMetricsMatchesDisplayAreaOnPipActivity() {
120         assumeTrue(supportsPip());
121 
122         final MetricsActivity activity = startActivityInWindowingModeFullScreen(
123                 MetricsActivity.class);
124 
125         assertMetricsValidity(activity);
126 
127         activity.enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
128         waitForEnterPipAnimationComplete(activity.getComponentName());
129 
130         assertMetricsValidity(activity);
131     }
132 
133     @Test
testMetricsMatchesLayoutOnSplitActivity()134     public void testMetricsMatchesLayoutOnSplitActivity() {
135         assumeTrue(supportsSplitScreenMultiWindow());
136 
137         final MetricsActivity activity = startActivityInWindowingModeFullScreen(
138                 MetricsActivity.class);
139 
140         assertMetricsMatchesLayout(activity);
141 
142         mWmState.computeState(activity.getComponentName());
143         putActivityInPrimarySplit(activity.getComponentName());
144 
145         mWmState.computeState(activity.getComponentName());
146         assertEquals(WINDOWING_MODE_MULTI_WINDOW,
147                 mWmState.getActivity(activity.getComponentName()).getWindowingMode());
148 
149         assertMetricsMatchesLayout(activity);
150     }
151 
152     @Test
testMetricsMatchesDisplayAreaOnSplitActivity()153     public void testMetricsMatchesDisplayAreaOnSplitActivity() {
154         assumeTrue(supportsSplitScreenMultiWindow());
155 
156         final MetricsActivity activity = startActivityInWindowingModeFullScreen(
157                 MetricsActivity.class);
158 
159         assertMetricsValidity(activity);
160 
161         mWmState.computeState(activity.getComponentName());
162         putActivityInPrimarySplit(activity.getComponentName());
163 
164         mWmState.computeState(activity.getComponentName());
165         assertTrue(mWmState.getActivity(activity.getComponentName()).getWindowingMode()
166                 == WINDOWING_MODE_MULTI_WINDOW);
167 
168         assertMetricsValidity(activity);
169     }
170 
171     @Test
testMetricsMatchesLayoutOnFreeformActivity()172     public void testMetricsMatchesLayoutOnFreeformActivity() {
173         assumeTrue(supportsFreeform());
174 
175         final MetricsActivity activity = startActivityInWindowingModeFullScreen(
176                 MetricsActivity.class);
177 
178         assertMetricsMatchesLayout(activity);
179 
180         launchActivity(new ComponentName(mTargetContext, MetricsActivity.class),
181                 WINDOWING_MODE_FREEFORM);
182 
183         Condition.waitFor(new Condition<>("Activity becomes freeform mode",
184                 activity::isInMultiWindowMode)
185                 .setRetryIntervalMs(500).setRetryLimit(10)
186                 .setOnFailure(unused -> fail("Activity must be in multi-window mode.")));
187         assertMetricsMatchesLayout(activity);
188 
189         final Rect boundsBeforeResize = activity.getListener().getLayoutBounds();
190         // Resize the task.
191         resizeActivityTask(activity.getComponentName(), WINDOW_BOUNDS.left, WINDOW_BOUNDS.top,
192                 WINDOW_BOUNDS.right, WINDOW_BOUNDS.bottom);
193 
194         Condition.waitFor(new Condition<>("layout bounds change",
195                 () -> !boundsBeforeResize.equals(activity.getListener().getLayoutBounds()))
196                 .setRetryIntervalMs(500).setRetryLimit(10)
197                 .setOnFailure(unused -> fail("Layout bounds must be changed due to task resize.")));
198         assertMetricsMatchesLayout(activity);
199 
200         final Rect boundsBeforeMove = activity.getListener().getLayoutBounds();
201         // Move the task.
202         resizeActivityTask(activity.getComponentName(), MOVE_OFFSET + WINDOW_BOUNDS.left,
203                 MOVE_OFFSET + WINDOW_BOUNDS.top, MOVE_OFFSET + WINDOW_BOUNDS.right,
204                 MOVE_OFFSET + WINDOW_BOUNDS.bottom);
205 
206         Condition.waitFor(new Condition<>("layout bounds change",
207                 () -> !boundsBeforeMove.equals(activity.getListener().getLayoutBounds()))
208                 .setRetryIntervalMs(500).setRetryLimit(10)
209                 .setOnFailure(unused -> fail("Layout bounds must be changed due to task move.")));
210         assertMetricsMatchesLayout(activity);
211     }
212 
213     @Test
testMetricsMatchesDisplayAreaOnFreeformActivity()214     public void testMetricsMatchesDisplayAreaOnFreeformActivity() {
215         assumeTrue(supportsFreeform());
216 
217         final MetricsActivity activity = startActivityInWindowingModeFullScreen(
218                 MetricsActivity.class);
219 
220         assertMetricsValidity(activity);
221 
222         launchActivity(new ComponentName(mTargetContext, MetricsActivity.class),
223                 WINDOWING_MODE_FREEFORM);
224 
225         // Resize the task.
226         resizeActivityTask(activity.getComponentName(), WINDOW_BOUNDS.left, WINDOW_BOUNDS.top,
227                 WINDOW_BOUNDS.right, WINDOW_BOUNDS.bottom);
228 
229         assertMetricsValidity(activity);
230 
231         // Move the task.
232         resizeActivityTask(activity.getComponentName(), MOVE_OFFSET + WINDOW_BOUNDS.left,
233                 MOVE_OFFSET + WINDOW_BOUNDS.top, MOVE_OFFSET + WINDOW_BOUNDS.right,
234                 MOVE_OFFSET + WINDOW_BOUNDS.bottom);
235 
236         assertMetricsValidity(activity);
237     }
238 
assertMetricsMatchesLayout(MetricsActivity activity)239     private static void assertMetricsMatchesLayout(MetricsActivity activity) {
240         final OnLayoutChangeListener listener = activity.getListener();
241         listener.waitForLayout();
242 
243         final Supplier<WindowMetrics> currentMetrics =
244                 () -> activity.getWindowManager().getCurrentWindowMetrics();
245         final Supplier<WindowMetrics> maxMetrics =
246                 () -> activity.getWindowManager().getMaximumWindowMetrics();
247 
248         Condition.waitFor(new Condition<>("WindowMetrics matches layout metrics",
249                 () -> currentMetrics.get().getBounds().equals(listener.getLayoutBounds()))
250                 .setRetryIntervalMs(500).setRetryLimit(10)
251                 .setOnFailure(unused -> fail("WindowMetrics must match layout metrics. Layout"
252                         + "bounds is " + listener.getLayoutBounds() + ", while current window"
253                         + "metrics is " + currentMetrics.get().getBounds())));
254 
255         final int windowingMode = activity.getResources().getConfiguration().windowConfiguration
256                 .getWindowingMode();
257         WindowMetricsTestHelper.assertMetricsMatchesLayout(currentMetrics.get(), maxMetrics.get(),
258                 listener.getLayoutBounds(), listener.getLayoutInsets(),
259                 inMultiWindowMode(windowingMode));
260     }
261 
262     // Copied from WindowConfiguration#inMultiWindowMode(int windowingMode)
263     // TODO(b/250741386): make it a @TestApi in U
inMultiWindowMode(int windowingMode)264     private static boolean inMultiWindowMode(int windowingMode) {
265         return windowingMode != WINDOWING_MODE_FULLSCREEN
266                 && windowingMode != WINDOWING_MODE_UNDEFINED;
267     }
268 
269     /**
270      * Verifies two scenarios for an {@link Activity}. If the activity is freeform, then the bounds
271      * should not include insets for navigation bar and cutout area.
272      * <ul>
273      *     <li>{@link WindowManager#getCurrentWindowMetrics()} matches
274      *     {@link Display#getSize(Point)}</li>
275      *     <li>{@link WindowManager#getMaximumWindowMetrics()} and {@link Display#getSize(Point)}
276      *     either matches DisplayArea bounds which the {@link Activity} is attached to, or matches
277      *     {@link WindowManager#getCurrentWindowMetrics()} if sandboxing is applied.</li>
278      * </ul>
279      */
assertMetricsValidity(Activity activity)280     private void assertMetricsValidity(Activity activity) {
281         mWmState.computeState(activity.getComponentName());
282         WindowMetricsTestHelper.assertMetricsValidity(activity,
283                 getTaskDisplayAreaBounds(activity.getComponentName()));
284     }
285 
286     /**
287      * Verifies two scenarios for a non-resizable {@link Activity}. Similar to
288      * {@link #assertMetricsValidity(Activity)}, verifies the values of window metrics against
289      * Display size. {@link WindowManager#getMaximumWindowMetrics()} must match activity bounds
290      * if the activity is sandboxed.
291      *
292      * App bounds calculation of a nonresizable activity depends on the orientation of the display
293      * and the app, and the display and activity bounds. Directly compare maximum WindowMetrics
294      * against the value used for sandboxing, since other ways of accessing activity bounds may
295      * have different insets applied.
296      *
297      * @param activity the activity under test
298      */
assertMetricsValidityForNonresizableActivity(Activity activity)299     private void assertMetricsValidityForNonresizableActivity(Activity activity) {
300         ComponentName activityName = activity.getComponentName();
301         mWmState.computeState(activityName);
302         WindowManagerState.Activity activityContainer = mWmState.getActivity(activityName);
303         final boolean shouldBoundsIncludeInsets =
304                 activity.getResources().getConfiguration().windowConfiguration
305                         .getWindowingMode() == WINDOWING_MODE_FREEFORM
306                         || activityContainer.inSizeCompatMode();
307         final WindowManager windowManager = activity.getWindowManager();
308         final WindowMetrics currentMetrics = windowManager.getCurrentWindowMetrics();
309         final WindowMetrics maxMetrics = windowManager.getMaximumWindowMetrics();
310 
311         final Rect maxBounds = windowManager.getMaximumWindowMetrics().getBounds();
312         final Display display = activity.getDisplay();
313 
314         assertMetricsMatchDisplay(maxMetrics, currentMetrics, display, shouldBoundsIncludeInsets);
315 
316         // Max window bounds should match either DisplayArea bounds, or activity bounds if it is
317         // sandboxed.
318         final Rect displayAreaBounds = getTaskDisplayAreaBounds(activityName);
319         final Rect activityBounds =
320                 activityContainer.getFullConfiguration().windowConfiguration.getBounds();
321         if (maxWindowBoundsSandboxed(displayAreaBounds, maxBounds)) {
322             // Max window bounds are sandboxed, so max window bounds should match activity bounds.
323             assertEquals("Maximum window metrics of a non-resizable activity matches "
324                     + "activity bounds, when sandboxed", activityBounds, maxBounds);
325         } else {
326             // Max window bounds are not sandboxed, so max window bounds should match display area
327             // bounds.
328             assertEquals("Display area bounds must match max window size", displayAreaBounds,
329                     maxBounds);
330         }
331     }
332 
getTaskDisplayAreaBounds(ComponentName activityName)333     private Rect getTaskDisplayAreaBounds(ComponentName activityName) {
334         WindowManagerState.DisplayArea tda = mWmState.getTaskDisplayArea(activityName);
335         return tda.getFullConfiguration().windowConfiguration.getBounds();
336     }
337 
338     public static class MinAspectRatioActivity extends MetricsActivity {
339     }
340 }
341