1/* 2 * Copyright (C) 2016 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 17package android.server.cts; 18 19import com.android.tradefed.device.ITestDevice; 20 21import junit.framework.Assert; 22 23import android.server.cts.ActivityManagerState.ActivityStack; 24import android.server.cts.ActivityManagerState.ActivityTask; 25import android.server.cts.WindowManagerState.WindowStack; 26import android.server.cts.WindowManagerState.WindowTask; 27 28import java.awt.Rectangle; 29import java.util.ArrayList; 30import java.util.List; 31import java.util.Objects; 32 33import static android.server.cts.ActivityManagerTestBase.FREEFORM_WORKSPACE_STACK_ID; 34import static android.server.cts.ActivityManagerTestBase.PINNED_STACK_ID; 35import static android.server.cts.StateLogger.log; 36 37/** Combined state of the activity manager and window manager. */ 38class ActivityAndWindowManagersState extends Assert { 39 40 // Clone of android DisplayMetrics.DENSITY_DEFAULT (DENSITY_MEDIUM) 41 // (Needed in host-side tests to convert dp to px.) 42 private static final int DISPLAY_DENSITY_DEFAULT = 160; 43 44 // Default minimal size of resizable task, used if none is set explicitly. 45 // Must be kept in sync with 'default_minimal_size_resizable_task' dimen from frameworks/base. 46 private static final int DEFAULT_RESIZABLE_TASK_SIZE_DP = 220; 47 48 private ActivityManagerState mAmState = new ActivityManagerState(); 49 private WindowManagerState mWmState = new WindowManagerState(); 50 51 private final List<WindowManagerState.WindowState> mTempWindowList = new ArrayList<>(); 52 53 /** 54 * Compute AM and WM state of device, check sanity and bounds. 55 * WM state will include only visible windows, stack and task bounds will be compared. 56 * 57 * @param device test device. 58 * @param waitForActivitiesVisible array of activity names to wait for. 59 */ 60 void computeState(ITestDevice device, String[] waitForActivitiesVisible) throws Exception { 61 computeState(device, waitForActivitiesVisible, true); 62 } 63 64 /** 65 * Compute AM and WM state of device, check sanity and bounds. 66 * WM state will include only visible windows. 67 * 68 * @param device test device. 69 * @param waitForActivitiesVisible array of activity names to wait for. 70 * @param compareTaskAndStackBounds pass 'true' if stack and task bounds should be compared, 71 * 'false' otherwise. 72 */ 73 void computeState(ITestDevice device, String[] waitForActivitiesVisible, 74 boolean compareTaskAndStackBounds) throws Exception { 75 computeState(device, true, waitForActivitiesVisible, compareTaskAndStackBounds); 76 } 77 78 /** 79 * Compute AM and WM state of device, check sanity and bounds. 80 * Stack and task bounds will be compared. 81 * 82 * @param device test device. 83 * @param visibleOnly pass 'true' to include only visible windows in WM state. 84 * @param waitForActivitiesVisible array of activity names to wait for. 85 */ 86 void computeState(ITestDevice device, boolean visibleOnly, String[] waitForActivitiesVisible) 87 throws Exception { 88 computeState(device, visibleOnly, waitForActivitiesVisible, true); 89 } 90 91 /** 92 * Compute AM and WM state of device, check sanity and bounds. 93 * 94 * @param device test device. 95 * @param visibleOnly pass 'true' if WM state should include only visible windows. 96 * @param waitForActivitiesVisible array of activity names to wait for. 97 * @param compareTaskAndStackBounds pass 'true' if stack and task bounds should be compared, 98 * 'false' otherwise. 99 */ 100 void computeState(ITestDevice device, boolean visibleOnly, String[] waitForActivitiesVisible, 101 boolean compareTaskAndStackBounds) throws Exception { 102 waitForValidState(device, visibleOnly, waitForActivitiesVisible, null, 103 compareTaskAndStackBounds); 104 105 assertSanity(); 106 assertValidBounds(compareTaskAndStackBounds); 107 } 108 109 /** 110 * Wait for consistent state in AM and WM. 111 * 112 * @param device test device. 113 * @param visibleOnly pass 'true' if WM state should include only visible windows. 114 * @param waitForActivitiesVisible array of activity names to wait for. 115 * @param stackIds ids of stack where provided activities should be found. 116 * Pass null to skip this check. 117 */ 118 void waitForValidState(ITestDevice device, boolean visibleOnly, 119 String[] waitForActivitiesVisible, int[] stackIds, 120 boolean compareTaskAndStackBounds) throws Exception { 121 int retriesLeft = 5; 122 do { 123 // TODO: Get state of AM and WM at the same time to avoid mismatches caused by 124 // requesting dump in some intermediate state. 125 mAmState.computeState(device); 126 mWmState.computeState(device, visibleOnly); 127 if (shouldWaitForValidStacks(compareTaskAndStackBounds) 128 || shouldWaitForActivities(waitForActivitiesVisible, stackIds)) { 129 log("***Waiting for valid stacks and activities states..."); 130 try { 131 Thread.sleep(1000); 132 } catch (InterruptedException e) { 133 log(e.toString()); 134 // Well I guess we are not waiting... 135 } 136 } else { 137 break; 138 } 139 } while (retriesLeft-- > 0); 140 } 141 142 void waitForHomeActivityVisible(ITestDevice device) throws Exception { 143 int retriesLeft = 5; 144 do { 145 mAmState.computeState(device); 146 if (!mAmState.isHomeActivityVisible()) { 147 log("***Waiting for home activity to be visible..."); 148 try { 149 Thread.sleep(1000); 150 } catch (InterruptedException e) { 151 log(e.toString()); 152 // Well I guess we are not waiting... 153 } 154 } else { 155 break; 156 } 157 } while (retriesLeft-- > 0); 158 } 159 160 private boolean shouldWaitForValidStacks(boolean compareTaskAndStackBounds) { 161 if (!taskListsInAmAndWmAreEqual()) { 162 // We want to wait for equal task lists in AM and WM in case we caught them in the 163 // middle of some state change operations. 164 log("***taskListsInAmAndWmAreEqual=false"); 165 return true; 166 } 167 if (!stackBoundsInAMAndWMAreEqual()) { 168 // We want to wait a little for the stacks in AM and WM to have equal bounds as there 169 // might be a transition animation ongoing when we got the states from WM AM separately. 170 log("***stackBoundsInAMAndWMAreEqual=false"); 171 return true; 172 } 173 try { 174 // Temporary fix to avoid catching intermediate state with different task bounds in AM 175 // and WM. 176 assertValidBounds(compareTaskAndStackBounds); 177 } catch (AssertionError e) { 178 log("***taskBoundsInAMAndWMAreEqual=false : " + e.getMessage()); 179 return true; 180 } 181 return false; 182 } 183 184 private boolean shouldWaitForActivities(String[] waitForActivitiesVisible, int[] stackIds) { 185 if (waitForActivitiesVisible == null || waitForActivitiesVisible.length == 0) { 186 return false; 187 } 188 // If the caller is interested in us waiting for some particular activity windows to be 189 // visible before compute the state. Check for the visibility of those activity windows 190 // and for placing them in correct stacks (if requested). 191 boolean allActivityWindowsVisible = true; 192 boolean tasksInCorrectStacks = true; 193 List<WindowManagerState.WindowState> matchingWindowStates = new ArrayList<>(); 194 for (int i = 0; i < waitForActivitiesVisible.length; i++) { 195 // Check if window is visible - it should be represented as one of the window states. 196 final String windowName = 197 ActivityManagerTestBase.getWindowName(waitForActivitiesVisible[i]); 198 mWmState.getMatchingWindowState(windowName, matchingWindowStates); 199 boolean activityWindowVisible = !matchingWindowStates.isEmpty(); 200 if (!activityWindowVisible) { 201 log("Activity window not visible: " + waitForActivitiesVisible[i]); 202 allActivityWindowsVisible = false; 203 } else if (stackIds != null) { 204 // Check if window is already in stack requested by test. 205 boolean windowInCorrectStack = false; 206 for (WindowManagerState.WindowState ws : matchingWindowStates) { 207 if (ws.getStackId() == stackIds[i]) { 208 windowInCorrectStack = true; 209 break; 210 } 211 } 212 if (!windowInCorrectStack) { 213 log("Window in incorrect stack: " + waitForActivitiesVisible[i]); 214 tasksInCorrectStacks = false; 215 } 216 } 217 } 218 return !allActivityWindowsVisible || !tasksInCorrectStacks; 219 } 220 221 ActivityManagerState getAmState() { 222 return mAmState; 223 } 224 225 WindowManagerState getWmState() { 226 return mWmState; 227 } 228 229 void assertSanity() throws Exception { 230 assertTrue("Must have stacks", mAmState.getStackCount() > 0); 231 assertEquals("There should be one and only one resumed activity in the system.", 232 1, mAmState.getResumedActivitiesCount()); 233 assertNotNull("Must have focus activity.", mAmState.getFocusedActivity()); 234 235 for (ActivityStack aStack : mAmState.getStacks()) { 236 final int stackId = aStack.mStackId; 237 for (ActivityTask aTask : aStack.getTasks()) { 238 assertEquals("Stack can only contain its own tasks", stackId, aTask.mStackId); 239 } 240 } 241 242 assertNotNull("Must have front window.", mWmState.getFrontWindow()); 243 assertNotNull("Must have focused window.", mWmState.getFocusedWindow()); 244 assertNotNull("Must have app.", mWmState.getFocusedApp()); 245 } 246 247 void assertContainsStack(String msg, int stackId) throws Exception { 248 assertTrue(msg, mAmState.containsStack(stackId)); 249 assertTrue(msg, mWmState.containsStack(stackId)); 250 } 251 252 void assertDoesNotContainStack(String msg, int stackId) throws Exception { 253 assertFalse(msg, mAmState.containsStack(stackId)); 254 assertFalse(msg, mWmState.containsStack(stackId)); 255 } 256 257 void assertFrontStack(String msg, int stackId) throws Exception { 258 assertEquals(msg, stackId, mAmState.getFrontStackId()); 259 assertEquals(msg, stackId, mWmState.getFrontStackId()); 260 } 261 262 void assertFocusedStack(String msg, int stackId) throws Exception { 263 assertEquals(msg, stackId, mAmState.getFocusedStackId()); 264 } 265 266 void assertNotFocusedStack(String msg, int stackId) throws Exception { 267 if (stackId == mAmState.getFocusedStackId()) { 268 failNotEquals(msg, stackId, mAmState.getFocusedStackId()); 269 } 270 } 271 272 void assertFocusedActivity(String msg, String activityName) throws Exception { 273 final String componentName = ActivityManagerTestBase.getActivityComponentName(activityName); 274 assertEquals(msg, componentName, mAmState.getFocusedActivity()); 275 assertEquals(msg, componentName, mWmState.getFocusedApp()); 276 } 277 278 void assertNotFocusedActivity(String msg, String activityName) throws Exception { 279 final String componentName = ActivityManagerTestBase.getActivityComponentName(activityName); 280 if (mAmState.getFocusedActivity().equals(componentName)) { 281 failNotEquals(msg, mAmState.getFocusedActivity(), componentName); 282 } 283 if (mWmState.getFocusedApp().equals(componentName)) { 284 failNotEquals(msg, mWmState.getFocusedApp(), componentName); 285 } 286 } 287 288 void assertResumedActivity(String msg, String activityName) throws Exception { 289 final String componentName = ActivityManagerTestBase.getActivityComponentName(activityName); 290 assertEquals(msg, componentName, mAmState.getResumedActivity()); 291 } 292 293 void assertNotResumedActivity(String msg, String activityName) throws Exception { 294 final String componentName = ActivityManagerTestBase.getActivityComponentName(activityName); 295 if (mAmState.getResumedActivity().equals(componentName)) { 296 failNotEquals(msg, mAmState.getResumedActivity(), componentName); 297 } 298 } 299 300 void assertFocusedWindow(String msg, String windowName) { 301 assertEquals(msg, windowName, mWmState.getFocusedWindow()); 302 } 303 304 void assertNotFocusedWindow(String msg, String windowName) { 305 if (mWmState.getFocusedWindow().equals(windowName)) { 306 failNotEquals(msg, mWmState.getFocusedWindow(), windowName); 307 } 308 } 309 310 void assertFrontWindow(String msg, String windowName) { 311 assertEquals(msg, windowName, mWmState.getFrontWindow()); 312 } 313 314 void assertVisibility(String activityName, boolean visible) { 315 final String activityComponentName = 316 ActivityManagerTestBase.getActivityComponentName(activityName); 317 final String windowName = 318 ActivityManagerTestBase.getWindowName(activityName); 319 320 final boolean activityVisible = mAmState.isActivityVisible(activityComponentName); 321 final boolean windowVisible = mWmState.isWindowVisible(windowName); 322 323 if (visible) { 324 assertTrue("Activity=" + activityComponentName + " must be visible.", activityVisible); 325 assertTrue("Window=" + windowName + " must be visible.", windowVisible); 326 } else { 327 assertFalse("Activity=" + activityComponentName + " must NOT be visible.", 328 activityVisible); 329 assertFalse("Window=" + windowName + " must NOT be visible.", windowVisible); 330 } 331 } 332 333 void assertHomeActivityVisible(boolean visible) { 334 final boolean activityVisible = mAmState.isHomeActivityVisible(); 335 336 if (visible) { 337 assertTrue("Home activity must be visible.", activityVisible); 338 } else { 339 assertFalse("Home activity must NOT be visible.", activityVisible); 340 } 341 } 342 343 boolean taskListsInAmAndWmAreEqual() { 344 for (ActivityStack aStack : mAmState.getStacks()) { 345 final int stackId = aStack.mStackId; 346 final WindowStack wStack = mWmState.getStack(stackId); 347 if (wStack == null) { 348 log("Waiting for stack setup in WM, stackId=" + stackId); 349 return false; 350 } 351 352 for (ActivityTask aTask : aStack.getTasks()) { 353 if (wStack.getTask(aTask.mTaskId) == null) { 354 log("Task is in AM but not in WM, waiting for it to settle, taskId=" 355 + aTask.mTaskId); 356 return false; 357 } 358 } 359 360 for (WindowTask wTask : wStack.mTasks) { 361 if (aStack.getTask(wTask.mTaskId) == null) { 362 log("Task is in WM but not in AM, waiting for it to settle, taskId=" 363 + wTask.mTaskId); 364 return false; 365 } 366 } 367 } 368 return true; 369 } 370 371 boolean stackBoundsInAMAndWMAreEqual() { 372 for (ActivityStack aStack : mAmState.getStacks()) { 373 final int stackId = aStack.mStackId; 374 final WindowStack wStack = mWmState.getStack(stackId); 375 if (aStack.isFullscreen() != wStack.isFullscreen()) { 376 log("Waiting for correct fullscreen state, stackId=" + stackId); 377 return false; 378 } 379 380 final Rectangle aStackBounds = aStack.getBounds(); 381 final Rectangle wStackBounds = wStack.getBounds(); 382 383 if (aStack.isFullscreen()) { 384 if (aStackBounds != null) { 385 log("Waiting for correct stack state in AM, stackId=" + stackId); 386 return false; 387 } 388 } else if (!Objects.equals(aStackBounds, wStackBounds)) { 389 // If stack is not fullscreen - comparing bounds. Not doing it always because 390 // for fullscreen stack bounds in WM can be either null or equal to display size. 391 log("Waiting for stack bound equality in AM and WM, stackId=" + stackId); 392 return false; 393 } 394 } 395 396 return true; 397 } 398 399 /** Check task bounds when docked to top/left. */ 400 void assertDockedTaskBounds(int taskSize, String activityName) { 401 // Task size can be affected by default minimal size. 402 int defaultMinimalTaskSize = defaultMinimalTaskSize( 403 mAmState.getStackById(ActivityManagerTestBase.DOCKED_STACK_ID).mDisplayId); 404 int targetSize = Math.max(taskSize, defaultMinimalTaskSize); 405 406 assertEquals(new Rectangle(0, 0, targetSize, targetSize), 407 mAmState.getTaskByActivityName(activityName).getBounds()); 408 } 409 410 void assertValidBounds(boolean compareTaskAndStackBounds) { 411 for (ActivityStack aStack : mAmState.getStacks()) { 412 final int stackId = aStack.mStackId; 413 final WindowStack wStack = mWmState.getStack(stackId); 414 assertNotNull("stackId=" + stackId + " in AM but not in WM?", wStack); 415 416 assertEquals("Stack fullscreen state in AM and WM must be equal stackId=" + stackId, 417 aStack.isFullscreen(), wStack.isFullscreen()); 418 419 final Rectangle aStackBounds = aStack.getBounds(); 420 final Rectangle wStackBounds = wStack.getBounds(); 421 422 if (aStack.isFullscreen()) { 423 assertNull("Stack bounds in AM must be null stackId=" + stackId, aStackBounds); 424 } else { 425 assertEquals("Stack bounds in AM and WM must be equal stackId=" + stackId, 426 aStackBounds, wStackBounds); 427 } 428 429 for (ActivityTask aTask : aStack.getTasks()) { 430 final int taskId = aTask.mTaskId; 431 final WindowTask wTask = wStack.getTask(taskId); 432 assertNotNull( 433 "taskId=" + taskId + " in AM but not in WM? stackId=" + stackId, wTask); 434 435 final boolean aTaskIsFullscreen = aTask.isFullscreen(); 436 final boolean wTaskIsFullscreen = wTask.isFullscreen(); 437 assertEquals("Task fullscreen state in AM and WM must be equal taskId=" + taskId 438 + ", stackId=" + stackId, aTaskIsFullscreen, wTaskIsFullscreen); 439 440 final Rectangle aTaskBounds = aTask.getBounds(); 441 final Rectangle wTaskBounds = wTask.getBounds(); 442 443 if (aTaskIsFullscreen) { 444 assertNull("Task bounds in AM must be null for fullscreen taskId=" + taskId, 445 aTaskBounds); 446 } else { 447 assertEquals("Task bounds in AM and WM must be equal taskId=" + taskId 448 + ", stackId=" + stackId, aTaskBounds, wTaskBounds); 449 450 if (compareTaskAndStackBounds && stackId != FREEFORM_WORKSPACE_STACK_ID) { 451 int aTaskMinWidth = aTask.getMinWidth(); 452 int aTaskMinHeight = aTask.getMinHeight(); 453 454 if (aTaskMinWidth == -1 || aTaskMinHeight == -1) { 455 // Minimal dimension(s) not set for task - it should be using defaults. 456 int defaultMinimalSize = defaultMinimalTaskSize(aStack.mDisplayId); 457 458 if (aTaskMinWidth == -1) { 459 aTaskMinWidth = defaultMinimalSize; 460 } 461 if (aTaskMinHeight == -1) { 462 aTaskMinHeight = defaultMinimalSize; 463 } 464 } 465 466 if (aStackBounds.getWidth() >= aTaskMinWidth 467 && aStackBounds.getHeight() >= aTaskMinHeight 468 || stackId == PINNED_STACK_ID) { 469 // Bounds are not smaller then minimal possible, so stack and task 470 // bounds must be equal. 471 assertEquals("Task bounds must be equal to stack bounds taskId=" 472 + taskId + ", stackId=" + stackId, aStackBounds, wTaskBounds); 473 } else { 474 // Minimal dimensions affect task size, so bounds of task and stack must 475 // be different - will compare dimensions instead. 476 int targetWidth = (int) Math.max(aTaskMinWidth, 477 aStackBounds.getWidth()); 478 assertEquals("Task width must be set according to minimal width" 479 + " taskId=" + taskId + ", stackId=" + stackId, 480 targetWidth, (int) wTaskBounds.getWidth()); 481 int targetHeight = (int) Math.max(aTaskMinHeight, 482 aStackBounds.getHeight()); 483 assertEquals("Task height must be set according to minimal height" 484 + " taskId=" + taskId + ", stackId=" + stackId, 485 targetHeight, (int) wTaskBounds.getHeight()); 486 } 487 } 488 } 489 } 490 } 491 } 492 493 static int dpToPx(float dp, int densityDpi){ 494 return (int) (dp * densityDpi / DISPLAY_DENSITY_DEFAULT + 0.5f); 495 } 496 497 int defaultMinimalTaskSize(int displayId) { 498 return dpToPx(DEFAULT_RESIZABLE_TASK_SIZE_DP, mWmState.getDisplay(displayId).getDpi()); 499 } 500} 501