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 17 package android.server.wm; 18 19 import static android.app.ActivityTaskManager.INVALID_STACK_ID; 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 23 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; 24 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 25 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; 26 import static android.server.wm.ComponentNameUtils.getActivityName; 27 import static android.server.wm.ComponentNameUtils.getWindowName; 28 import static android.server.wm.StateLogger.log; 29 import static android.server.wm.StateLogger.logAlways; 30 import static android.server.wm.StateLogger.logE; 31 import static android.util.DisplayMetrics.DENSITY_DEFAULT; 32 import static android.view.Display.DEFAULT_DISPLAY; 33 34 import static org.hamcrest.Matchers.greaterThan; 35 import static org.hamcrest.Matchers.greaterThanOrEqualTo; 36 import static org.hamcrest.Matchers.lessThan; 37 import static org.junit.Assert.assertEquals; 38 import static org.junit.Assert.assertFalse; 39 import static org.junit.Assert.assertNotEquals; 40 import static org.junit.Assert.assertNotNull; 41 import static org.junit.Assert.assertNull; 42 import static org.junit.Assert.assertThat; 43 import static org.junit.Assert.assertTrue; 44 import static org.junit.Assert.fail; 45 import static org.junit.Assume.assumeTrue; 46 47 import android.content.ComponentName; 48 import android.graphics.Rect; 49 import android.os.SystemClock; 50 import android.server.wm.ActivityManagerState.ActivityStack; 51 import android.server.wm.ActivityManagerState.ActivityTask; 52 import android.server.wm.WindowManagerState.Display; 53 import android.server.wm.WindowManagerState.WindowStack; 54 import android.server.wm.WindowManagerState.WindowState; 55 import android.server.wm.WindowManagerState.WindowTask; 56 import android.util.SparseArray; 57 58 import java.util.Arrays; 59 import java.util.List; 60 import java.util.Objects; 61 import java.util.function.BiPredicate; 62 import java.util.function.BooleanSupplier; 63 import java.util.function.Predicate; 64 import java.util.function.Supplier; 65 import java.util.stream.Collectors; 66 67 /** 68 * Combined state of the activity manager and window manager. 69 */ 70 public class ActivityAndWindowManagersState { 71 72 // Default minimal size of resizable task, used if none is set explicitly. 73 // Must be kept in sync with 'default_minimal_size_resizable_task' dimen from frameworks/base. 74 private static final int DEFAULT_RESIZABLE_TASK_SIZE_DP = 220; 75 76 // Default minimal size of a resizable PiP task, used if none is set explicitly. 77 // Must be kept in sync with 'default_minimal_size_pip_resizable_task' dimen from 78 // frameworks/base. 79 private static final int DEFAULT_PIP_RESIZABLE_TASK_SIZE_DP = 108; 80 81 private final ActivityManagerState mAmState = new ActivityManagerState(); 82 private final WindowManagerState mWmState = new WindowManagerState(); 83 84 /** 85 * Compute AM and WM state of device, check sanity and bounds. 86 * WM state will include only visible windows, stack and task bounds will be compared. 87 * 88 * @param componentNames array of activity names to wait for. 89 */ computeState(ComponentName... componentNames)90 public void computeState(ComponentName... componentNames) { 91 waitForValidState(true /* compareTaskAndStackBounds */, 92 Arrays.stream(componentNames) 93 .map(WaitForValidActivityState::new) 94 .toArray(WaitForValidActivityState[]::new)); 95 } 96 97 /** 98 * Compute AM and WM state of device, check sanity and bounds. 99 * WM state will include only visible windows, stack and task bounds will be compared. 100 * 101 * @param waitForActivitiesVisible array of activity names to wait for. 102 */ computeState(WaitForValidActivityState... waitForActivitiesVisible)103 public void computeState(WaitForValidActivityState... waitForActivitiesVisible) { 104 waitForValidState(true /* compareTaskAndStackBounds */, waitForActivitiesVisible); 105 } 106 107 /** 108 * Compute AM and WM state of device, check sanity and bounds. 109 * 110 * @param compareTaskAndStackBounds pass 'true' if stack and task bounds should be compared, 111 * 'false' otherwise. 112 * @param waitForActivitiesVisible array of activity states to wait for. 113 */ computeState(boolean compareTaskAndStackBounds, WaitForValidActivityState... waitForActivitiesVisible)114 void computeState(boolean compareTaskAndStackBounds, 115 WaitForValidActivityState... waitForActivitiesVisible) { 116 waitForValidState(compareTaskAndStackBounds, waitForActivitiesVisible); 117 } 118 119 /** 120 * Wait for the activities to appear and for valid state in AM and WM. 121 * 122 * @param activityNames name list of activities to wait for. 123 */ waitForValidState(ComponentName... activityNames)124 public void waitForValidState(ComponentName... activityNames) { 125 waitForValidState(false /* compareTaskAndStackBounds */, 126 Arrays.stream(activityNames) 127 .map(WaitForValidActivityState::new) 128 .toArray(WaitForValidActivityState[]::new)); 129 130 } 131 132 /** Wait for the activity to appear and for valid state in AM and WM. */ waitForValidState(WaitForValidActivityState... waitForActivityVisible)133 void waitForValidState(WaitForValidActivityState... waitForActivityVisible) { 134 waitForValidState(false /* compareTaskAndStackBounds */, waitForActivityVisible); 135 } 136 137 /** 138 * Wait for the activities to appear in proper stacks and for valid state in AM and WM. 139 * 140 * @param compareTaskAndStackBounds flag indicating if we should compare task and stack bounds 141 * for equality. 142 * @param waitForActivitiesVisible array of activity states to wait for. 143 */ waitForValidState(boolean compareTaskAndStackBounds, WaitForValidActivityState... waitForActivitiesVisible)144 private void waitForValidState(boolean compareTaskAndStackBounds, 145 WaitForValidActivityState... waitForActivitiesVisible) { 146 for (int retry = 1; retry <= 5; retry++) { 147 // TODO: Get state of AM and WM at the same time to avoid mismatches caused by 148 // requesting dump in some intermediate state. 149 mAmState.computeState(); 150 mWmState.computeState(); 151 if (shouldWaitForSanityCheck(compareTaskAndStackBounds) 152 || shouldWaitForValidStacks(compareTaskAndStackBounds) 153 || shouldWaitForActivities(waitForActivitiesVisible) 154 || shouldWaitForWindows()) { 155 logAlways("***Waiting for valid stacks and activities states... retry=" + retry); 156 SystemClock.sleep(1000); 157 } else { 158 return; 159 } 160 } 161 logE("***Waiting for states failed: " + Arrays.toString(waitForActivitiesVisible)); 162 } 163 164 /** 165 * Ensures all exiting windows have been removed. 166 */ waitForAllExitingWindows()167 void waitForAllExitingWindows() { 168 List<WindowState> exitingWindows = null; 169 for (int retry = 1; retry <= 5; retry++) { 170 mWmState.computeState(); 171 exitingWindows = mWmState.getExitingWindows(); 172 if (exitingWindows.isEmpty()) { 173 return; 174 } 175 logAlways("***Waiting for all exiting windows have been removed... retry=" + retry); 176 SystemClock.sleep(1000); 177 } 178 fail("All exiting windows have been removed, actual=" + exitingWindows.stream() 179 .map(WindowState::getName) 180 .collect(Collectors.joining(","))); 181 } 182 waitForAllStoppedActivities()183 void waitForAllStoppedActivities() { 184 for (int retry = 1; retry <= 5; retry++) { 185 mAmState.computeState(); 186 if (!mAmState.containsStartedActivities()) { 187 return; 188 } 189 logAlways("***Waiting for all started activities have been removed... retry=" + retry); 190 SystemClock.sleep(1500); 191 } 192 fail("All started activities have been removed"); 193 } 194 195 /** 196 * Compute AM and WM state of device, wait for the activity records to be added, and 197 * wait for debugger window to show up. 198 * 199 * This should only be used when starting with -D (debugger) option, where we pop up the 200 * waiting-for-debugger window, but real activity window won't show up since we're waiting 201 * for debugger. 202 */ waitForDebuggerWindowVisible(ComponentName activityName)203 void waitForDebuggerWindowVisible(ComponentName activityName) { 204 for (int retry = 1; retry <= 5; retry++) { 205 mAmState.computeState(); 206 mWmState.computeState(); 207 if (shouldWaitForDebuggerWindow(activityName) 208 || shouldWaitForActivityRecords(activityName)) { 209 logAlways("***Waiting for debugger window... retry=" + retry); 210 SystemClock.sleep(1000); 211 } else { 212 return; 213 } 214 } 215 logE("***Waiting for debugger window failed"); 216 } 217 waitForValidProduct(Supplier<T> supplier, String productName, Predicate<T> tester)218 <T> T waitForValidProduct(Supplier<T> supplier, String productName, Predicate<T> tester) { 219 T product = null; 220 for (int retry = 1; retry <= 5; retry++) { 221 product = supplier.get(); 222 if (product != null) { 223 if (tester.test(product)) { 224 break; 225 } 226 } 227 logAlways("***Waiting for valid " + productName + "... retry=" + retry); 228 SystemClock.sleep(1000); 229 } 230 return product; 231 } 232 waitForHomeActivityVisible()233 void waitForHomeActivityVisible() { 234 ComponentName homeActivity = mAmState.getHomeActivityName(); 235 // Sometimes this function is called before we know what Home Activity is 236 if (homeActivity == null) { 237 logAlways("Computing state to determine Home Activity"); 238 computeState(true); 239 homeActivity = mAmState.getHomeActivityName(); 240 } 241 assertNotNull("homeActivity should not be null", homeActivity); 242 waitForValidState(homeActivity); 243 } 244 waitForRecentsActivityVisible()245 void waitForRecentsActivityVisible() { 246 if (mAmState.isHomeRecentsComponent()) { 247 waitForHomeActivityVisible(); 248 } else { 249 waitForWithAmState(ActivityManagerState::isRecentsActivityVisible, 250 "***Waiting for recents activity to be visible..."); 251 } 252 } 253 waitForKeyguardShowingAndNotOccluded()254 void waitForKeyguardShowingAndNotOccluded() { 255 waitForWithAmState(state -> state.getKeyguardControllerState().keyguardShowing 256 && !state.getKeyguardControllerState().isKeyguardOccluded(DEFAULT_DISPLAY), 257 "***Waiting for Keyguard showing..."); 258 } 259 waitForKeyguardShowingAndOccluded()260 void waitForKeyguardShowingAndOccluded() { 261 waitForWithAmState(state -> state.getKeyguardControllerState().keyguardShowing 262 && state.getKeyguardControllerState().isKeyguardOccluded(DEFAULT_DISPLAY), 263 "***Waiting for Keyguard showing and occluded..."); 264 } 265 waitForAodShowing()266 void waitForAodShowing() { 267 waitForWithAmState(state -> state.getKeyguardControllerState().aodShowing, 268 "***Waiting for AOD showing..."); 269 270 } 271 waitForKeyguardGone()272 void waitForKeyguardGone() { 273 waitForWithAmState(state -> !state.getKeyguardControllerState().keyguardShowing, 274 "***Waiting for Keyguard gone..."); 275 } 276 277 /** Wait for specific rotation for the default display. Values are Surface#Rotation */ waitForRotation(int rotation)278 void waitForRotation(int rotation) { 279 waitForWithWmState(state -> state.getRotation() == rotation, 280 "***Waiting for Rotation: " + rotation); 281 } 282 283 /** 284 * Wait for specific orientation for the default display. 285 * Values are ActivityInfo.ScreenOrientation 286 */ waitForLastOrientation(int orientation)287 void waitForLastOrientation(int orientation) { 288 waitForWithWmState(state -> state.getLastOrientation() == orientation, 289 "***Waiting for LastOrientation: " + orientation); 290 } 291 292 /** 293 * Wait for orientation for the Activity 294 */ waitForActivityOrientation(ComponentName activityName, int orientation)295 void waitForActivityOrientation(ComponentName activityName, int orientation) { 296 waitForWithAmState(amState -> { 297 final ActivityTask task = amState.getTaskByActivity(activityName); 298 if (task == null) { 299 return false; 300 } 301 return task.mFullConfiguration.orientation == orientation; 302 }, "***Waiting for Activity orientation: " + orientation); 303 } 304 waitForDisplayUnfrozen()305 void waitForDisplayUnfrozen() { 306 waitForWithWmState(state -> !state.isDisplayFrozen(), 307 "***Waiting for Display unfrozen"); 308 } 309 waitForActivityState(ComponentName activityName, String activityState)310 public void waitForActivityState(ComponentName activityName, String activityState) { 311 waitForWithAmState(state -> state.hasActivityState(activityName, activityState), 312 "***Waiting for Activity State: " + activityState); 313 } 314 waitForActivityRemoved(ComponentName activityName)315 public void waitForActivityRemoved(ComponentName activityName) { 316 waitForWithAmState((state) -> !state.containsActivity(activityName), 317 "Waiting for activity to be removed"); 318 waitForWithWmState((state) -> !state.containsWindow(getWindowName(activityName)), 319 "Waiting for activity window to be gone"); 320 } 321 322 @Deprecated waitForFocusedStack(int stackId)323 void waitForFocusedStack(int stackId) { 324 waitForWithAmState(state -> state.getFocusedStackId() == stackId, 325 "***Waiting for focused stack..."); 326 } 327 waitForFocusedStack(int windowingMode, int activityType)328 void waitForFocusedStack(int windowingMode, int activityType) { 329 waitForWithAmState(state -> 330 (activityType == ACTIVITY_TYPE_UNDEFINED 331 || state.getFocusedStackActivityType() == activityType) 332 && (windowingMode == WINDOWING_MODE_UNDEFINED 333 || state.getFocusedStackWindowingMode() == windowingMode), 334 "***Waiting for focused stack..."); 335 } 336 waitForPendingActivityContain(ComponentName activity)337 void waitForPendingActivityContain(ComponentName activity) { 338 waitForWithAmState(state -> state.pendingActivityContain(activity), 339 "***Waiting for activity in pending list..."); 340 } 341 waitForAppTransitionIdleOnDisplay(int displayId)342 void waitForAppTransitionIdleOnDisplay(int displayId) { 343 waitForWithWmState( 344 state -> WindowManagerState.APP_STATE_IDLE.equals( 345 state.getDisplay(displayId).getAppTransitionState()), 346 "***Waiting for app transition idle on Display " + displayId + " ..."); 347 } 348 349 waitAndAssertNavBarShownOnDisplay(int displayId)350 void waitAndAssertNavBarShownOnDisplay(int displayId) { 351 waitForWithWmState( 352 state -> state.getAndAssertSingleNavBarWindowOnDisplay(displayId) != null, 353 "***Waiting for navigation bar #" + displayId + " show..."); 354 final WindowState ws = getWmState().getAndAssertSingleNavBarWindowOnDisplay(displayId); 355 356 assertNotNull(ws); 357 } 358 waitForWithAmState(Predicate<ActivityManagerState> waitCondition, String message)359 public void waitForWithAmState(Predicate<ActivityManagerState> waitCondition, String message) { 360 waitFor((amState, wmState) -> waitCondition.test(amState), message); 361 } 362 waitForWithWmState(Predicate<WindowManagerState> waitCondition, String message)363 public void waitForWithWmState(Predicate<WindowManagerState> waitCondition, String message) { 364 waitFor((amState, wmState) -> waitCondition.test(wmState), message); 365 } 366 waitFor( BiPredicate<ActivityManagerState, WindowManagerState> waitCondition, String message)367 void waitFor( 368 BiPredicate<ActivityManagerState, WindowManagerState> waitCondition, String message) { 369 waitFor(message, () -> { 370 mAmState.computeState(); 371 mWmState.computeState(); 372 return waitCondition.test(mAmState, mWmState); 373 }); 374 } 375 waitFor(String message, BooleanSupplier waitCondition)376 void waitFor(String message, BooleanSupplier waitCondition) { 377 for (int retry = 1; retry <= 5; retry++) { 378 if (waitCondition.getAsBoolean()) { 379 return; 380 } 381 logAlways(message + " retry=" + retry); 382 SystemClock.sleep(1000); 383 } 384 logE(message + " failed"); 385 } 386 387 /** 388 * @return true if should wait for valid stacks state. 389 */ shouldWaitForValidStacks(boolean compareTaskAndStackBounds)390 private boolean shouldWaitForValidStacks(boolean compareTaskAndStackBounds) { 391 if (!taskListsInAmAndWmAreEqual()) { 392 // We want to wait for equal task lists in AM and WM in case we caught them in the 393 // middle of some state change operations. 394 logAlways("***taskListsInAmAndWmAreEqual=false"); 395 return true; 396 } 397 if (!stackBoundsInAMAndWMAreEqual()) { 398 // We want to wait a little for the stacks in AM and WM to have equal bounds as there 399 // might be a transition animation ongoing when we got the states from WM AM separately. 400 logAlways("***stackBoundsInAMAndWMAreEqual=false"); 401 return true; 402 } 403 try { 404 // Temporary fix to avoid catching intermediate state with different task bounds in AM 405 // and WM. 406 assertValidBounds(compareTaskAndStackBounds); 407 } catch (AssertionError e) { 408 logAlways("***taskBoundsInAMAndWMAreEqual=false : " + e.getMessage()); 409 return true; 410 } 411 final int stackCount = mAmState.getStackCount(); 412 if (stackCount == 0) { 413 logAlways("***stackCount=" + stackCount); 414 return true; 415 } 416 final int resumedActivitiesCount = mAmState.getResumedActivitiesCount(); 417 if (!mAmState.getKeyguardControllerState().keyguardShowing && resumedActivitiesCount < 1) { 418 logAlways("***resumedActivitiesCount=" + resumedActivitiesCount); 419 return true; 420 } 421 if (mAmState.getFocusedActivity() == null) { 422 logAlways("***focusedActivity=null"); 423 return true; 424 } 425 return false; 426 } 427 428 /** 429 * @return true if should wait for some activities to become visible. 430 */ shouldWaitForActivities(WaitForValidActivityState... waitForActivitiesVisible)431 private boolean shouldWaitForActivities(WaitForValidActivityState... waitForActivitiesVisible) { 432 if (waitForActivitiesVisible == null || waitForActivitiesVisible.length == 0) { 433 return false; 434 } 435 // If the caller is interested in us waiting for some particular activity windows to be 436 // visible before compute the state. Check for the visibility of those activity windows 437 // and for placing them in correct stacks (if requested). 438 boolean allActivityWindowsVisible = true; 439 boolean tasksInCorrectStacks = true; 440 for (final WaitForValidActivityState state : waitForActivitiesVisible) { 441 final ComponentName activityName = state.activityName; 442 final String windowName = state.windowName; 443 final int stackId = state.stackId; 444 final int windowingMode = state.windowingMode; 445 final int activityType = state.activityType; 446 447 final List<WindowState> matchingWindowStates = 448 mWmState.getMatchingVisibleWindowState(windowName); 449 boolean activityWindowVisible = !matchingWindowStates.isEmpty(); 450 if (!activityWindowVisible) { 451 logAlways("Activity window not visible: " + windowName); 452 allActivityWindowsVisible = false; 453 } else if (activityName != null 454 && !mAmState.isActivityVisible(activityName)) { 455 logAlways("Activity not visible: " + getActivityName(activityName)); 456 allActivityWindowsVisible = false; 457 } else { 458 // Check if window is already the correct state requested by test. 459 boolean windowInCorrectState = false; 460 for (WindowState ws : matchingWindowStates) { 461 if (stackId != INVALID_STACK_ID && ws.getStackId() != stackId) { 462 continue; 463 } 464 if (windowingMode != WINDOWING_MODE_UNDEFINED 465 && ws.getWindowingMode() != windowingMode) { 466 continue; 467 } 468 if (activityType != ACTIVITY_TYPE_UNDEFINED 469 && ws.getActivityType() != activityType) { 470 continue; 471 } 472 windowInCorrectState = true; 473 break; 474 } 475 476 if (!windowInCorrectState) { 477 logAlways("Window in incorrect stack: " + state); 478 tasksInCorrectStacks = false; 479 } 480 } 481 } 482 return !allActivityWindowsVisible || !tasksInCorrectStacks; 483 } 484 485 /** 486 * @return true if should wait valid windows state. 487 */ shouldWaitForWindows()488 private boolean shouldWaitForWindows() { 489 if (mWmState.getFrontWindow() == null) { 490 logAlways("***frontWindow=null"); 491 return true; 492 } 493 if (mWmState.getFocusedWindow() == null) { 494 logAlways("***focusedWindow=null"); 495 return true; 496 } 497 if (mWmState.getFocusedApp() == null) { 498 logAlways("***focusedApp=null"); 499 return true; 500 } 501 502 return false; 503 } 504 shouldWaitForDebuggerWindow(ComponentName activityName)505 private boolean shouldWaitForDebuggerWindow(ComponentName activityName) { 506 List<WindowState> matchingWindowStates = 507 mWmState.getMatchingVisibleWindowState(activityName.getPackageName()); 508 for (WindowState ws : matchingWindowStates) { 509 if (ws.isDebuggerWindow()) { 510 return false; 511 } 512 } 513 logAlways("Debugger window not available yet"); 514 return true; 515 } 516 shouldWaitForActivityRecords(ComponentName... activityNames)517 private boolean shouldWaitForActivityRecords(ComponentName... activityNames) { 518 // Check if the activity records we're looking for is already added. 519 for (final ComponentName activityName : activityNames) { 520 if (!mAmState.isActivityVisible(activityName)) { 521 logAlways("ActivityRecord " + getActivityName(activityName) + " not visible yet"); 522 return true; 523 } 524 } 525 return false; 526 } 527 shouldWaitForSanityCheck(boolean compareTaskAndStackBounds)528 private boolean shouldWaitForSanityCheck(boolean compareTaskAndStackBounds) { 529 try { 530 assertSanity(); 531 assertValidBounds(compareTaskAndStackBounds); 532 } catch (Throwable t) { 533 logAlways("Waiting for sanity check: " + t.toString()); 534 return true; 535 } 536 return false; 537 } 538 getAmState()539 public ActivityManagerState getAmState() { 540 return mAmState; 541 } 542 getWmState()543 public WindowManagerState getWmState() { 544 return mWmState; 545 } 546 assertSanity()547 void assertSanity() { 548 assertThat("Must have stacks", mAmState.getStackCount(), greaterThan(0)); 549 // TODO: Update when keyguard will be shown on multiple displays 550 if (!mAmState.getKeyguardControllerState().keyguardShowing) { 551 assertThat("There should be at least one resumed activity in the system.", 552 mAmState.getResumedActivitiesCount(), greaterThanOrEqualTo(1)); 553 } 554 assertNotNull("Must have focus activity.", mAmState.getFocusedActivity()); 555 556 for (ActivityStack aStack : mAmState.getStacks()) { 557 final int stackId = aStack.mStackId; 558 for (ActivityTask aTask : aStack.getTasks()) { 559 assertEquals("Stack can only contain its own tasks", stackId, aTask.mStackId); 560 } 561 } 562 563 assertNotNull("Must have front window.", mWmState.getFrontWindow()); 564 assertNotNull("Must have focused window.", mWmState.getFocusedWindow()); 565 assertNotNull("Must have app.", mWmState.getFocusedApp()); 566 } 567 assertContainsStack(String msg, int windowingMode, int activityType)568 void assertContainsStack(String msg, int windowingMode, int activityType) { 569 assertTrue(msg, mAmState.containsStack(windowingMode, activityType)); 570 assertTrue(msg, mWmState.containsStack(windowingMode, activityType)); 571 } 572 assertDoesNotContainStack(String msg, int windowingMode, int activityType)573 void assertDoesNotContainStack(String msg, int windowingMode, int activityType) { 574 assertFalse(msg, mAmState.containsStack(windowingMode, activityType)); 575 assertFalse(msg, mWmState.containsStack(windowingMode, activityType)); 576 } 577 assertFrontStack(String msg, int windowingMode, int activityType)578 public void assertFrontStack(String msg, int windowingMode, int activityType) { 579 assertFrontStackOnDisplay(msg, windowingMode, activityType, DEFAULT_DISPLAY); 580 } 581 assertFrontStackOnDisplay(String msg, int windowingMode, int activityType, int displayId)582 void assertFrontStackOnDisplay(String msg, int windowingMode, int activityType, int displayId) { 583 if (windowingMode != WINDOWING_MODE_UNDEFINED) { 584 assertEquals(msg, windowingMode, 585 mAmState.getFrontStackWindowingMode(displayId)); 586 } 587 if (activityType != ACTIVITY_TYPE_UNDEFINED) { 588 assertEquals(msg, activityType, mAmState.getFrontStackActivityType(displayId)); 589 } 590 } 591 assertFrontStackActivityType(String msg, int activityType)592 void assertFrontStackActivityType(String msg, int activityType) { 593 assertEquals(msg, activityType, mAmState.getFrontStackActivityType(DEFAULT_DISPLAY)); 594 assertEquals(msg, activityType, mWmState.getFrontStackActivityType(DEFAULT_DISPLAY)); 595 } 596 assertFocusedStack(String msg, int stackId)597 void assertFocusedStack(String msg, int stackId) { 598 assertEquals(msg, stackId, mAmState.getFocusedStackId()); 599 } 600 assertFocusedStack(String msg, int windowingMode, int activityType)601 void assertFocusedStack(String msg, int windowingMode, int activityType) { 602 if (windowingMode != WINDOWING_MODE_UNDEFINED) { 603 assertEquals(msg, windowingMode, mAmState.getFocusedStackWindowingMode()); 604 } 605 if (activityType != ACTIVITY_TYPE_UNDEFINED) { 606 assertEquals(msg, activityType, mAmState.getFocusedStackActivityType()); 607 } 608 } 609 assertFocusedActivity(final String msg, final ComponentName activityName)610 public void assertFocusedActivity(final String msg, final ComponentName activityName) { 611 final String activityComponentName = getActivityName(activityName); 612 assertEquals(msg, activityComponentName, mAmState.getFocusedActivity()); 613 assertEquals(msg, activityComponentName, mWmState.getFocusedApp()); 614 } 615 assertFocusedAppOnDisplay(final String msg, final ComponentName activityName, final int displayId)616 void assertFocusedAppOnDisplay(final String msg, final ComponentName activityName, 617 final int displayId) { 618 final String activityComponentName = getActivityName(activityName); 619 assertEquals(msg, activityComponentName, mWmState.getDisplay(displayId).getFocusedApp()); 620 } 621 assertNotFocusedActivity(String msg, ComponentName activityName)622 void assertNotFocusedActivity(String msg, ComponentName activityName) { 623 assertNotEquals(msg, mAmState.getFocusedActivity(), getActivityName(activityName)); 624 assertNotEquals(msg, mWmState.getFocusedApp(), getActivityName(activityName)); 625 } 626 assertResumedActivity(final String msg, final ComponentName activityName)627 public void assertResumedActivity(final String msg, final ComponentName activityName) { 628 assertEquals(msg, getActivityName(activityName), 629 mAmState.getFocusedActivity()); 630 } 631 632 /** Asserts that each display has correct resumed activity. */ assertResumedActivities(final String msg, SparseArray<ComponentName> resumedActivities)633 public void assertResumedActivities(final String msg, 634 SparseArray<ComponentName> resumedActivities) { 635 for (int i = 0; i < resumedActivities.size(); i++) { 636 final int displayId = resumedActivities.keyAt(i); 637 final ComponentName activityComponent = resumedActivities.valueAt(i); 638 assertEquals("Error asserting resumed activity on display " + displayId + ": " + msg, 639 activityComponent != null ? getActivityName(activityComponent) : null, 640 mAmState.getResumedActivityOnDisplay(displayId)); 641 } 642 } 643 assertNotResumedActivity(String msg, ComponentName activityName)644 void assertNotResumedActivity(String msg, ComponentName activityName) { 645 assertNotEquals(msg, mAmState.getFocusedActivity(), getActivityName(activityName)); 646 } 647 assertFocusedWindow(String msg, String windowName)648 void assertFocusedWindow(String msg, String windowName) { 649 assertEquals(msg, windowName, mWmState.getFocusedWindow()); 650 } 651 assertNotFocusedWindow(String msg, String windowName)652 void assertNotFocusedWindow(String msg, String windowName) { 653 assertNotEquals(msg, mWmState.getFocusedWindow(), windowName); 654 } 655 assertNotExist(final ComponentName activityName)656 void assertNotExist(final ComponentName activityName) { 657 final String windowName = getWindowName(activityName); 658 assertFalse("Activity=" + getActivityName(activityName) + " must NOT exist.", 659 mAmState.containsActivity(activityName)); 660 assertFalse("Window=" + windowName + " must NOT exits.", 661 mWmState.containsWindow(windowName)); 662 } 663 assertVisibility(final ComponentName activityName, final boolean visible)664 public void assertVisibility(final ComponentName activityName, final boolean visible) { 665 final String windowName = getWindowName(activityName); 666 // Check existence of activity and window. 667 assertTrue("Activity=" + getActivityName(activityName) + " must exist.", 668 mAmState.containsActivity(activityName)); 669 assertTrue("Window=" + windowName + " must exist.", mWmState.containsWindow(windowName)); 670 671 // Check visibility of activity and window. 672 assertEquals("Activity=" + getActivityName(activityName) + " must" + (visible ? "" : " NOT") 673 + " be visible.", visible, mAmState.isActivityVisible(activityName)); 674 assertEquals("Window=" + windowName + " must" + (visible ? "" : " NOT") + " be visible.", 675 visible, mWmState.isWindowVisible(windowName)); 676 } 677 assertHomeActivityVisible(boolean visible)678 void assertHomeActivityVisible(boolean visible) { 679 final ComponentName homeActivity = mAmState.getHomeActivityName(); 680 assertNotNull(homeActivity); 681 assertVisibility(homeActivity, visible); 682 } 683 684 /** 685 * Asserts that the device default display minimim width is larger than the minimum task width. 686 */ assertDeviceDefaultDisplaySize(String errorMessage)687 void assertDeviceDefaultDisplaySize(String errorMessage) { 688 computeState(true); 689 final int minTaskSizePx = defaultMinimalTaskSize(DEFAULT_DISPLAY); 690 final Display display = getWmState().getDisplay(DEFAULT_DISPLAY); 691 final Rect displayRect = display.getDisplayRect(); 692 if (Math.min(displayRect.width(), displayRect.height()) < minTaskSizePx) { 693 fail(errorMessage); 694 } 695 } 696 assertKeyguardShowingAndOccluded()697 public void assertKeyguardShowingAndOccluded() { 698 assertTrue("Keyguard is showing", 699 getAmState().getKeyguardControllerState().keyguardShowing); 700 assertTrue("Keyguard is occluded", 701 getAmState().getKeyguardControllerState().isKeyguardOccluded(DEFAULT_DISPLAY)); 702 } 703 assertKeyguardShowingAndNotOccluded()704 public void assertKeyguardShowingAndNotOccluded() { 705 assertTrue("Keyguard is showing", 706 getAmState().getKeyguardControllerState().keyguardShowing); 707 assertFalse("Keyguard is not occluded", 708 getAmState().getKeyguardControllerState().isKeyguardOccluded(DEFAULT_DISPLAY)); 709 } 710 assertKeyguardGone()711 public void assertKeyguardGone() { 712 assertFalse("Keyguard is not shown", 713 getAmState().getKeyguardControllerState().keyguardShowing); 714 } 715 assertAodShowing()716 public void assertAodShowing() { 717 assertTrue("AOD is showing", 718 getAmState().getKeyguardControllerState().aodShowing); 719 } 720 assertAodNotShowing()721 public void assertAodNotShowing() { 722 assertFalse("AOD is not showing", 723 getAmState().getKeyguardControllerState().aodShowing); 724 } 725 assumePendingActivityContain(ComponentName activity)726 public void assumePendingActivityContain(ComponentName activity) { 727 assumeTrue(getAmState().pendingActivityContain(activity)); 728 } 729 taskListsInAmAndWmAreEqual()730 boolean taskListsInAmAndWmAreEqual() { 731 for (ActivityStack aStack : mAmState.getStacks()) { 732 final int stackId = aStack.mStackId; 733 final WindowStack wStack = mWmState.getStack(stackId); 734 if (wStack == null) { 735 log("Waiting for stack setup in WM, stackId=" + stackId); 736 return false; 737 } 738 739 for (ActivityTask aTask : aStack.getTasks()) { 740 if (wStack.getTask(aTask.mTaskId) == null) { 741 log("Task is in AM but not in WM, waiting for it to settle, taskId=" 742 + aTask.mTaskId); 743 return false; 744 } 745 } 746 747 for (WindowTask wTask : wStack.mTasks) { 748 if (aStack.getTask(wTask.mTaskId) == null) { 749 log("Task is in WM but not in AM, waiting for it to settle, taskId=" 750 + wTask.mTaskId); 751 return false; 752 } 753 } 754 } 755 return true; 756 } 757 758 /** Get the stack position on its display. */ getStackIndexByActivityType(int activityType)759 int getStackIndexByActivityType(int activityType) { 760 int wmStackIndex = mWmState.getStackIndexByActivityType(activityType); 761 int amStackIndex = mAmState.getStackIndexByActivityType(activityType); 762 assertEquals("Window and activity manager must have the same stack position index", 763 amStackIndex, wmStackIndex); 764 return wmStackIndex; 765 } 766 stackBoundsInAMAndWMAreEqual()767 boolean stackBoundsInAMAndWMAreEqual() { 768 for (ActivityStack aStack : mAmState.getStacks()) { 769 final int stackId = aStack.mStackId; 770 final WindowStack wStack = mWmState.getStack(stackId); 771 if (aStack.isFullscreen() != wStack.isFullscreen()) { 772 log("Waiting for correct fullscreen state, stackId=" + stackId); 773 return false; 774 } 775 776 final Rect aStackBounds = aStack.getBounds(); 777 final Rect wStackBounds = wStack.getBounds(); 778 779 if (aStack.isFullscreen()) { 780 if (aStackBounds != null) { 781 log("Waiting for correct stack state in AM, stackId=" + stackId); 782 return false; 783 } 784 } else if (!Objects.equals(aStackBounds, wStackBounds)) { 785 // If stack is not fullscreen - comparing bounds. Not doing it always because 786 // for fullscreen stack bounds in WM can be either null or equal to display size. 787 log("Waiting for stack bound equality in AM and WM, stackId=" + stackId); 788 return false; 789 } 790 } 791 792 return true; 793 } 794 795 /** 796 * Check task bounds when docked to top/left. 797 */ assertDockedTaskBounds(int taskWidth, int taskHeight, ComponentName activityName)798 void assertDockedTaskBounds(int taskWidth, int taskHeight, ComponentName activityName) { 799 // Task size can be affected by default minimal size. 800 int defaultMinimalTaskSize = defaultMinimalTaskSize( 801 mAmState.getStandardStackByWindowingMode( 802 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).mDisplayId); 803 int targetWidth = Math.max(taskWidth, defaultMinimalTaskSize); 804 int targetHeight = Math.max(taskHeight, defaultMinimalTaskSize); 805 806 assertEquals(new Rect(0, 0, targetWidth, targetHeight), 807 mAmState.getTaskByActivity(activityName).getBounds()); 808 } 809 assertValidBounds(boolean compareTaskAndStackBounds)810 void assertValidBounds(boolean compareTaskAndStackBounds) { 811 // Cycle through the stacks and tasks to figure out if the home stack is resizable 812 final ActivityTask homeTask = mAmState.getHomeTask(); 813 final boolean homeStackIsResizable = homeTask != null 814 && homeTask.getResizeMode() == RESIZE_MODE_RESIZEABLE; 815 816 for (ActivityStack aStack : mAmState.getStacks()) { 817 final int stackId = aStack.mStackId; 818 final WindowStack wStack = mWmState.getStack(stackId); 819 assertNotNull("stackId=" + stackId + " in AM but not in WM?", wStack); 820 821 assertEquals("Stack fullscreen state in AM and WM must be equal stackId=" + stackId, 822 aStack.isFullscreen(), wStack.isFullscreen()); 823 824 final Rect aStackBounds = aStack.getBounds(); 825 final Rect wStackBounds = wStack.getBounds(); 826 827 if (aStack.isFullscreen()) { 828 assertNull("Stack bounds in AM must be null stackId=" + stackId, aStackBounds); 829 } else { 830 assertEquals("Stack bounds in AM and WM must be equal stackId=" + stackId, 831 aStackBounds, wStackBounds); 832 } 833 834 for (ActivityTask aTask : aStack.getTasks()) { 835 final int taskId = aTask.mTaskId; 836 final WindowTask wTask = wStack.getTask(taskId); 837 assertNotNull( 838 "taskId=" + taskId + " in AM but not in WM? stackId=" + stackId, wTask); 839 840 final boolean aTaskIsFullscreen = aTask.isFullscreen(); 841 final boolean wTaskIsFullscreen = wTask.isFullscreen(); 842 assertEquals("Task fullscreen state in AM and WM must be equal taskId=" + taskId 843 + ", stackId=" + stackId, aTaskIsFullscreen, wTaskIsFullscreen); 844 845 final Rect aTaskBounds = aTask.getBounds(); 846 final Rect wTaskBounds = wTask.getBounds(); 847 848 if (aTaskIsFullscreen) { 849 assertNull("Task bounds in AM must be null for fullscreen taskId=" + taskId, 850 aTaskBounds); 851 } else if (!homeStackIsResizable && mWmState.isDockedStackMinimized() 852 && !isScreenPortrait(aStack.mDisplayId)) { 853 // When minimized using non-resizable launcher in landscape mode, it will move 854 // the task offscreen in the negative x direction unlike portrait that crops. 855 // The x value in the task bounds will not match the stack bounds since the 856 // only the task was moved. 857 assertEquals("Task bounds in AM and WM must match width taskId=" + taskId 858 + ", stackId" + stackId, aTaskBounds.width(), 859 wTaskBounds.width()); 860 assertEquals("Task bounds in AM and WM must match height taskId=" + taskId 861 + ", stackId" + stackId, aTaskBounds.height(), 862 wTaskBounds.height()); 863 assertEquals("Task bounds must match stack bounds y taskId=" + taskId 864 + ", stackId" + stackId, aTaskBounds.top, 865 wTaskBounds.top); 866 assertEquals("Task and stack bounds must match width taskId=" + taskId 867 + ", stackId" + stackId, aStackBounds.width(), 868 wTaskBounds.width()); 869 assertEquals("Task and stack bounds must match height taskId=" + taskId 870 + ", stackId" + stackId, aStackBounds.height(), 871 wTaskBounds.height()); 872 assertEquals("Task and stack bounds must match y taskId=" + taskId 873 + ", stackId" + stackId, aStackBounds.top, 874 wTaskBounds.top); 875 } else { 876 assertEquals("Task bounds in AM and WM must be equal taskId=" + taskId 877 + ", stackId=" + stackId, aTaskBounds, wTaskBounds); 878 879 if (compareTaskAndStackBounds 880 && aStack.getWindowingMode() != WINDOWING_MODE_FREEFORM) { 881 int aTaskMinWidth = aTask.getMinWidth(); 882 int aTaskMinHeight = aTask.getMinHeight(); 883 884 if (aTaskMinWidth == -1 || aTaskMinHeight == -1) { 885 // Minimal dimension(s) not set for task - it should be using defaults. 886 int defaultMinimalSize = 887 aStack.getWindowingMode() == WINDOWING_MODE_PINNED 888 ? defaultMinimalPinnedTaskSize(aStack.mDisplayId) 889 : defaultMinimalTaskSize(aStack.mDisplayId); 890 891 if (aTaskMinWidth == -1) { 892 aTaskMinWidth = defaultMinimalSize; 893 } 894 if (aTaskMinHeight == -1) { 895 aTaskMinHeight = defaultMinimalSize; 896 } 897 } 898 899 if (aStackBounds.width() >= aTaskMinWidth 900 && aStackBounds.height() >= aTaskMinHeight 901 || aStack.getWindowingMode() == WINDOWING_MODE_PINNED) { 902 // Bounds are not smaller then minimal possible, so stack and task 903 // bounds must be equal. 904 assertEquals("Task bounds must be equal to stack bounds taskId=" 905 + taskId + ", stackId=" + stackId, aStackBounds, wTaskBounds); 906 } else if (aStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY 907 && homeStackIsResizable && mWmState.isDockedStackMinimized()) { 908 // Portrait if the display height is larger than the width 909 if (isScreenPortrait(aStack.mDisplayId)) { 910 assertEquals("Task width must be equal to stack width taskId=" 911 + taskId + ", stackId=" + stackId, 912 aStackBounds.width(), wTaskBounds.width()); 913 assertThat("Task height must be greater than stack height " 914 + "taskId=" + taskId + ", stackId=" + stackId, 915 aStackBounds.height(), lessThan(wTaskBounds.height())); 916 assertEquals("Task and stack x position must be equal taskId=" 917 + taskId + ", stackId=" + stackId, 918 wTaskBounds.left, wStackBounds.left); 919 } else { 920 assertThat("Task width must be greater than stack width taskId=" 921 + taskId + ", stackId=" + stackId, 922 aStackBounds.width(), lessThan(wTaskBounds.width())); 923 assertEquals("Task height must be equal to stack height taskId=" 924 + taskId + ", stackId=" + stackId, 925 aStackBounds.height(), wTaskBounds.height()); 926 assertEquals("Task and stack y position must be equal taskId=" 927 + taskId + ", stackId=" + stackId, wTaskBounds.top, 928 wStackBounds.top); 929 } 930 } else { 931 // Minimal dimensions affect task size, so bounds of task and stack must 932 // be different - will compare dimensions instead. 933 int targetWidth = Math.max(aTaskMinWidth, aStackBounds.width()); 934 assertEquals("Task width must be set according to minimal width" 935 + " taskId=" + taskId + ", stackId=" + stackId, 936 targetWidth, wTaskBounds.width()); 937 int targetHeight = Math.max(aTaskMinHeight, aStackBounds.height()); 938 assertEquals("Task height must be set according to minimal height" 939 + " taskId=" + taskId + ", stackId=" + stackId, 940 targetHeight, wTaskBounds.height()); 941 } 942 } 943 } 944 } 945 } 946 } 947 assertActivityDisplayed(final ComponentName activityName)948 public void assertActivityDisplayed(final ComponentName activityName) throws Exception { 949 assertWindowDisplayed(getWindowName(activityName)); 950 } 951 assertWindowDisplayed(final String windowName)952 public void assertWindowDisplayed(final String windowName) throws Exception { 953 waitForValidState(WaitForValidActivityState.forWindow(windowName)); 954 assertTrue(windowName + "is visible", getWmState().isWindowVisible(windowName)); 955 } 956 waitAndAssertImeWindowShownOnDisplay(int displayId)957 void waitAndAssertImeWindowShownOnDisplay(int displayId) { 958 final WindowManagerState.WindowState imeWinState = waitForValidProduct( 959 this::getImeWindowState, "IME window", 960 w -> w.isShown() && w.getDisplayId() == displayId); 961 assertNotNull("IME window must exist", imeWinState); 962 assertTrue("IME window must be shown", imeWinState.isShown()); 963 assertEquals("IME window must be on the given display", displayId, 964 imeWinState.getDisplayId()); 965 } 966 getImeWindowState()967 WindowManagerState.WindowState getImeWindowState() { 968 final WindowManagerState wmState = getWmState(); 969 wmState.computeState(); 970 return wmState.getInputMethodWindowState(); 971 } 972 isScreenPortrait()973 boolean isScreenPortrait() { 974 final int displayId = mAmState.getStandardStackByWindowingMode( 975 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).mDisplayId; 976 return isScreenPortrait(displayId); 977 } 978 isScreenPortrait(int displayId)979 boolean isScreenPortrait(int displayId) { 980 final Rect displayRect = mWmState.getDisplay(displayId).getDisplayRect(); 981 return displayRect.height() > displayRect.width(); 982 } 983 dpToPx(float dp, int densityDpi)984 static int dpToPx(float dp, int densityDpi) { 985 return (int) (dp * densityDpi / DENSITY_DEFAULT + 0.5f); 986 } 987 defaultMinimalTaskSize(int displayId)988 private int defaultMinimalTaskSize(int displayId) { 989 return dpToPx(DEFAULT_RESIZABLE_TASK_SIZE_DP, mWmState.getDisplay(displayId).getDpi()); 990 } 991 defaultMinimalPinnedTaskSize(int displayId)992 private int defaultMinimalPinnedTaskSize(int displayId) { 993 return dpToPx(DEFAULT_PIP_RESIZABLE_TASK_SIZE_DP, mWmState.getDisplay(displayId).getDpi()); 994 } 995 } 996