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