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