blob: 80cf75a72d0753fa8f3550b60a4ad613a98f7bbd [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package android.server.am;
import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.server.am.ComponentNameUtils.getActivityName;
import static android.server.am.ComponentNameUtils.getWindowName;
import static android.server.am.StateLogger.log;
import static android.server.am.StateLogger.logAlways;
import static android.server.am.StateLogger.logE;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.Display.DEFAULT_DISPLAY;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import android.content.ComponentName;
import android.graphics.Rect;
import android.os.SystemClock;
import android.server.am.ActivityManagerState.ActivityStack;
import android.server.am.ActivityManagerState.ActivityTask;
import android.server.am.WindowManagerState.Display;
import android.server.am.WindowManagerState.WindowStack;
import android.server.am.WindowManagerState.WindowState;
import android.server.am.WindowManagerState.WindowTask;
import android.util.SparseArray;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.BiPredicate;
import java.util.function.BooleanSupplier;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* Combined state of the activity manager and window manager.
*/
public class ActivityAndWindowManagersState {
// Default minimal size of resizable task, used if none is set explicitly.
// Must be kept in sync with 'default_minimal_size_resizable_task' dimen from frameworks/base.
private static final int DEFAULT_RESIZABLE_TASK_SIZE_DP = 220;
// Default minimal size of a resizable PiP task, used if none is set explicitly.
// Must be kept in sync with 'default_minimal_size_pip_resizable_task' dimen from
// frameworks/base.
private static final int DEFAULT_PIP_RESIZABLE_TASK_SIZE_DP = 108;
private final ActivityManagerState mAmState = new ActivityManagerState();
private final WindowManagerState mWmState = new WindowManagerState();
/**
* Compute AM and WM state of device, check sanity and bounds.
* WM state will include only visible windows, stack and task bounds will be compared.
*
* @param componentNames array of activity names to wait for.
*/
public void computeState(ComponentName... componentNames) {
waitForValidState(true /* compareTaskAndStackBounds */,
Arrays.stream(componentNames)
.map(WaitForValidActivityState::new)
.toArray(WaitForValidActivityState[]::new));
}
/**
* Compute AM and WM state of device, check sanity and bounds.
* WM state will include only visible windows, stack and task bounds will be compared.
*
* @param waitForActivitiesVisible array of activity names to wait for.
*/
public void computeState(WaitForValidActivityState... waitForActivitiesVisible) {
waitForValidState(true /* compareTaskAndStackBounds */, waitForActivitiesVisible);
}
/**
* Compute AM and WM state of device, check sanity and bounds.
*
* @param compareTaskAndStackBounds pass 'true' if stack and task bounds should be compared,
* 'false' otherwise.
* @param waitForActivitiesVisible array of activity states to wait for.
*/
void computeState(boolean compareTaskAndStackBounds,
WaitForValidActivityState... waitForActivitiesVisible) {
waitForValidState(compareTaskAndStackBounds, waitForActivitiesVisible);
}
/**
* Wait for the activities to appear and for valid state in AM and WM.
*
* @param activityNames name list of activities to wait for.
*/
public void waitForValidState(ComponentName... activityNames) {
waitForValidState(false /* compareTaskAndStackBounds */,
Arrays.stream(activityNames)
.map(WaitForValidActivityState::new)
.toArray(WaitForValidActivityState[]::new));
}
/** Wait for the activity to appear and for valid state in AM and WM. */
void waitForValidState(WaitForValidActivityState... waitForActivityVisible) {
waitForValidState(false /* compareTaskAndStackBounds */, waitForActivityVisible);
}
/**
* Wait for the activities to appear in proper stacks and for valid state in AM and WM.
*
* @param compareTaskAndStackBounds flag indicating if we should compare task and stack bounds
* for equality.
* @param waitForActivitiesVisible array of activity states to wait for.
*/
private void waitForValidState(boolean compareTaskAndStackBounds,
WaitForValidActivityState... waitForActivitiesVisible) {
for (int retry = 1; retry <= 5; retry++) {
// TODO: Get state of AM and WM at the same time to avoid mismatches caused by
// requesting dump in some intermediate state.
mAmState.computeState();
mWmState.computeState();
if (shouldWaitForSanityCheck(compareTaskAndStackBounds)
|| shouldWaitForValidStacks(compareTaskAndStackBounds)
|| shouldWaitForActivities(waitForActivitiesVisible)
|| shouldWaitForWindows()) {
logAlways("***Waiting for valid stacks and activities states... retry=" + retry);
SystemClock.sleep(1000);
} else {
return;
}
}
logE("***Waiting for states failed: " + Arrays.toString(waitForActivitiesVisible));
}
/**
* Ensures all exiting windows have been removed.
*/
void waitForAllExitingWindows() {
List<WindowState> exitingWindows = null;
for (int retry = 1; retry <= 5; retry++) {
mWmState.computeState();
exitingWindows = mWmState.getExitingWindows();
if (exitingWindows.isEmpty()) {
return;
}
logAlways("***Waiting for all exiting windows have been removed... retry=" + retry);
SystemClock.sleep(1000);
}
fail("All exiting windows have been removed, actual=" + exitingWindows.stream()
.map(WindowState::getName)
.collect(Collectors.joining(",")));
}
void waitForAllStoppedActivities() {
for (int retry = 1; retry <= 5; retry++) {
mAmState.computeState();
if (!mAmState.containsStartedActivities()) {
return;
}
logAlways("***Waiting for all started activities have been removed... retry=" + retry);
SystemClock.sleep(1500);
}
fail("All started activities have been removed");
}
/**
* Compute AM and WM state of device, wait for the activity records to be added, and
* wait for debugger window to show up.
*
* This should only be used when starting with -D (debugger) option, where we pop up the
* waiting-for-debugger window, but real activity window won't show up since we're waiting
* for debugger.
*/
void waitForDebuggerWindowVisible(ComponentName activityName) {
for (int retry = 1; retry <= 5; retry++) {
mAmState.computeState();
mWmState.computeState();
if (shouldWaitForDebuggerWindow(activityName)
|| shouldWaitForActivityRecords(activityName)) {
logAlways("***Waiting for debugger window... retry=" + retry);
SystemClock.sleep(1000);
} else {
return;
}
}
logE("***Waiting for debugger window failed");
}
<T> T waitForValidProduct(Supplier<T> supplier, String productName, Predicate<T> tester) {
T product = null;
for (int retry = 1; retry <= 5; retry++) {
product = supplier.get();
if (product != null) {
if (tester.test(product)) {
break;
}
}
logAlways("***Waiting for valid " + productName + "... retry=" + retry);
SystemClock.sleep(1000);
}
return product;
}
void waitForHomeActivityVisible() {
ComponentName homeActivity = mAmState.getHomeActivityName();
// Sometimes this function is called before we know what Home Activity is
if (homeActivity == null) {
logAlways("Computing state to determine Home Activity");
computeState(true);
homeActivity = mAmState.getHomeActivityName();
}
assertNotNull("homeActivity should not be null", homeActivity);
waitForValidState(homeActivity);
}
void waitForRecentsActivityVisible() {
if (mAmState.isHomeRecentsComponent()) {
waitForHomeActivityVisible();
} else {
waitForWithAmState(ActivityManagerState::isRecentsActivityVisible,
"***Waiting for recents activity to be visible...");
}
}
void waitForKeyguardShowingAndNotOccluded() {
waitForWithAmState(state -> state.getKeyguardControllerState().keyguardShowing
&& !state.getKeyguardControllerState().isKeyguardOccluded(DEFAULT_DISPLAY),
"***Waiting for Keyguard showing...");
}
void waitForKeyguardShowingAndOccluded() {
waitForWithAmState(state -> state.getKeyguardControllerState().keyguardShowing
&& state.getKeyguardControllerState().isKeyguardOccluded(DEFAULT_DISPLAY),
"***Waiting for Keyguard showing and occluded...");
}
void waitForKeyguardGone() {
waitForWithAmState(state -> !state.getKeyguardControllerState().keyguardShowing,
"***Waiting for Keyguard gone...");
}
/** Wait for specific rotation for the default display. Values are Surface#Rotation */
void waitForRotation(int rotation) {
waitForWithWmState(state -> state.getRotation() == rotation,
"***Waiting for Rotation: " + rotation);
}
/**
* Wait for specific orientation for the default display.
* Values are ActivityInfo.ScreenOrientation
*/
void waitForLastOrientation(int orientation) {
waitForWithWmState(state -> state.getLastOrientation() == orientation,
"***Waiting for LastOrientation: " + orientation);
}
/**
* Wait for orientation for the Activity
*/
void waitForActivityOrientation(ComponentName activityName, int orientation) {
waitForWithAmState(amState -> {
final ActivityTask task = amState.getTaskByActivity(activityName);
if (task == null) {
return false;
}
return task.mFullConfiguration.orientation == orientation;
}, "***Waiting for Activity orientation: " + orientation);
}
void waitForDisplayUnfrozen() {
waitForWithWmState(state -> !state.isDisplayFrozen(),
"***Waiting for Display unfrozen");
}
public void waitForActivityState(ComponentName activityName, String activityState) {
waitForWithAmState(state -> state.hasActivityState(activityName, activityState),
"***Waiting for Activity State: " + activityState);
}
public void waitForActivityRemoved(ComponentName activityName) {
waitForWithAmState((state) -> !state.containsActivity(activityName),
"Waiting for activity to be removed");
waitForWithWmState((state) -> !state.containsWindow(getWindowName(activityName)),
"Waiting for activity window to be gone");
}
@Deprecated
void waitForFocusedStack(int stackId) {
waitForWithAmState(state -> state.getFocusedStackId() == stackId,
"***Waiting for focused stack...");
}
void waitForFocusedStack(int windowingMode, int activityType) {
waitForWithAmState(state ->
(activityType == ACTIVITY_TYPE_UNDEFINED
|| state.getFocusedStackActivityType() == activityType)
&& (windowingMode == WINDOWING_MODE_UNDEFINED
|| state.getFocusedStackWindowingMode() == windowingMode),
"***Waiting for focused stack...");
}
void waitForPendingActivityContain(ComponentName activity) {
waitForWithAmState(state -> state.pendingActivityContain(activity),
"***Waiting for activity in pending list...");
}
void waitForAppTransitionIdleOnDisplay(int displayId) {
waitForWithWmState(
state -> WindowManagerState.APP_STATE_IDLE.equals(
state.getDisplay(displayId).getAppTransitionState()),
"***Waiting for app transition idle on Display " + displayId + " ...");
}
void waitAndAssertNavBarShownOnDisplay(int displayId) {
waitForWithWmState(
state -> state.getAndAssertSingleNavBarWindowOnDisplay(displayId) != null,
"***Waiting for navigation bar #" + displayId + " show...");
final WindowState ws = getWmState().getAndAssertSingleNavBarWindowOnDisplay(displayId);
assertNotNull(ws);
}
public void waitForWithAmState(Predicate<ActivityManagerState> waitCondition, String message) {
waitFor((amState, wmState) -> waitCondition.test(amState), message);
}
public void waitForWithWmState(Predicate<WindowManagerState> waitCondition, String message) {
waitFor((amState, wmState) -> waitCondition.test(wmState), message);
}
void waitFor(
BiPredicate<ActivityManagerState, WindowManagerState> waitCondition, String message) {
waitFor(message, () -> {
mAmState.computeState();
mWmState.computeState();
return waitCondition.test(mAmState, mWmState);
});
}
void waitFor(String message, BooleanSupplier waitCondition) {
for (int retry = 1; retry <= 5; retry++) {
if (waitCondition.getAsBoolean()) {
return;
}
logAlways(message + " retry=" + retry);
SystemClock.sleep(1000);
}
logE(message + " failed");
}
/**
* @return true if should wait for valid stacks state.
*/
private boolean shouldWaitForValidStacks(boolean compareTaskAndStackBounds) {
if (!taskListsInAmAndWmAreEqual()) {
// We want to wait for equal task lists in AM and WM in case we caught them in the
// middle of some state change operations.
logAlways("***taskListsInAmAndWmAreEqual=false");
return true;
}
if (!stackBoundsInAMAndWMAreEqual()) {
// We want to wait a little for the stacks in AM and WM to have equal bounds as there
// might be a transition animation ongoing when we got the states from WM AM separately.
logAlways("***stackBoundsInAMAndWMAreEqual=false");
return true;
}
try {
// Temporary fix to avoid catching intermediate state with different task bounds in AM
// and WM.
assertValidBounds(compareTaskAndStackBounds);
} catch (AssertionError e) {
logAlways("***taskBoundsInAMAndWMAreEqual=false : " + e.getMessage());
return true;
}
final int stackCount = mAmState.getStackCount();
if (stackCount == 0) {
logAlways("***stackCount=" + stackCount);
return true;
}
final int resumedActivitiesCount = mAmState.getResumedActivitiesCount();
if (!mAmState.getKeyguardControllerState().keyguardShowing && resumedActivitiesCount < 1) {
logAlways("***resumedActivitiesCount=" + resumedActivitiesCount);
return true;
}
if (mAmState.getFocusedActivity() == null) {
logAlways("***focusedActivity=null");
return true;
}
return false;
}
/**
* @return true if should wait for some activities to become visible.
*/
private boolean shouldWaitForActivities(WaitForValidActivityState... waitForActivitiesVisible) {
if (waitForActivitiesVisible == null || waitForActivitiesVisible.length == 0) {
return false;
}
// If the caller is interested in us waiting for some particular activity windows to be
// visible before compute the state. Check for the visibility of those activity windows
// and for placing them in correct stacks (if requested).
boolean allActivityWindowsVisible = true;
boolean tasksInCorrectStacks = true;
for (final WaitForValidActivityState state : waitForActivitiesVisible) {
final ComponentName activityName = state.activityName;
final String windowName = state.windowName;
final int stackId = state.stackId;
final int windowingMode = state.windowingMode;
final int activityType = state.activityType;
final List<WindowState> matchingWindowStates =
mWmState.getMatchingVisibleWindowState(windowName);
boolean activityWindowVisible = !matchingWindowStates.isEmpty();
if (!activityWindowVisible) {
logAlways("Activity window not visible: " + windowName);
allActivityWindowsVisible = false;
} else if (activityName != null
&& !mAmState.isActivityVisible(activityName)) {
logAlways("Activity not visible: " + getActivityName(activityName));
allActivityWindowsVisible = false;
} else {
// Check if window is already the correct state requested by test.
boolean windowInCorrectState = false;
for (WindowState ws : matchingWindowStates) {
if (stackId != INVALID_STACK_ID && ws.getStackId() != stackId) {
continue;
}
if (windowingMode != WINDOWING_MODE_UNDEFINED
&& ws.getWindowingMode() != windowingMode) {
continue;
}
if (activityType != ACTIVITY_TYPE_UNDEFINED
&& ws.getActivityType() != activityType) {
continue;
}
windowInCorrectState = true;
break;
}
if (!windowInCorrectState) {
logAlways("Window in incorrect stack: " + state);
tasksInCorrectStacks = false;
}
}
}
return !allActivityWindowsVisible || !tasksInCorrectStacks;
}
/**
* @return true if should wait valid windows state.
*/
private boolean shouldWaitForWindows() {
if (mWmState.getFrontWindow() == null) {
logAlways("***frontWindow=null");
return true;
}
if (mWmState.getFocusedWindow() == null) {
logAlways("***focusedWindow=null");
return true;
}
if (mWmState.getFocusedApp() == null) {
logAlways("***focusedApp=null");
return true;
}
return false;
}
private boolean shouldWaitForDebuggerWindow(ComponentName activityName) {
List<WindowState> matchingWindowStates =
mWmState.getMatchingVisibleWindowState(activityName.getPackageName());
for (WindowState ws : matchingWindowStates) {
if (ws.isDebuggerWindow()) {
return false;
}
}
logAlways("Debugger window not available yet");
return true;
}
private boolean shouldWaitForActivityRecords(ComponentName... activityNames) {
// Check if the activity records we're looking for is already added.
for (final ComponentName activityName : activityNames) {
if (!mAmState.isActivityVisible(activityName)) {
logAlways("ActivityRecord " + getActivityName(activityName) + " not visible yet");
return true;
}
}
return false;
}
private boolean shouldWaitForSanityCheck(boolean compareTaskAndStackBounds) {
try {
assertSanity();
assertValidBounds(compareTaskAndStackBounds);
} catch (Throwable t) {
logAlways("Waiting for sanity check: " + t.toString());
return true;
}
return false;
}
public ActivityManagerState getAmState() {
return mAmState;
}
public WindowManagerState getWmState() {
return mWmState;
}
void assertSanity() {
assertThat("Must have stacks", mAmState.getStackCount(), greaterThan(0));
// TODO: Update when keyguard will be shown on multiple displays
if (!mAmState.getKeyguardControllerState().keyguardShowing) {
assertThat("There should be at least one resumed activity in the system.",
mAmState.getResumedActivitiesCount(), greaterThanOrEqualTo(1));
}
assertNotNull("Must have focus activity.", mAmState.getFocusedActivity());
for (ActivityStack aStack : mAmState.getStacks()) {
final int stackId = aStack.mStackId;
for (ActivityTask aTask : aStack.getTasks()) {
assertEquals("Stack can only contain its own tasks", stackId, aTask.mStackId);
}
}
assertNotNull("Must have front window.", mWmState.getFrontWindow());
assertNotNull("Must have focused window.", mWmState.getFocusedWindow());
assertNotNull("Must have app.", mWmState.getFocusedApp());
}
void assertContainsStack(String msg, int windowingMode, int activityType) {
assertTrue(msg, mAmState.containsStack(windowingMode, activityType));
assertTrue(msg, mWmState.containsStack(windowingMode, activityType));
}
void assertDoesNotContainStack(String msg, int windowingMode, int activityType) {
assertFalse(msg, mAmState.containsStack(windowingMode, activityType));
assertFalse(msg, mWmState.containsStack(windowingMode, activityType));
}
public void assertFrontStack(String msg, int windowingMode, int activityType) {
assertFrontStackOnDisplay(msg, windowingMode, activityType, DEFAULT_DISPLAY);
}
void assertFrontStackOnDisplay(String msg, int windowingMode, int activityType, int displayId) {
if (windowingMode != WINDOWING_MODE_UNDEFINED) {
assertEquals(msg, windowingMode,
mAmState.getFrontStackWindowingMode(displayId));
}
if (activityType != ACTIVITY_TYPE_UNDEFINED) {
assertEquals(msg, activityType, mAmState.getFrontStackActivityType(displayId));
}
}
void assertFrontStackActivityType(String msg, int activityType) {
assertEquals(msg, activityType, mAmState.getFrontStackActivityType(DEFAULT_DISPLAY));
assertEquals(msg, activityType, mWmState.getFrontStackActivityType(DEFAULT_DISPLAY));
}
void assertFocusedStack(String msg, int stackId) {
assertEquals(msg, stackId, mAmState.getFocusedStackId());
}
void assertFocusedStack(String msg, int windowingMode, int activityType) {
if (windowingMode != WINDOWING_MODE_UNDEFINED) {
assertEquals(msg, windowingMode, mAmState.getFocusedStackWindowingMode());
}
if (activityType != ACTIVITY_TYPE_UNDEFINED) {
assertEquals(msg, activityType, mAmState.getFocusedStackActivityType());
}
}
public void assertFocusedActivity(final String msg, final ComponentName activityName) {
final String activityComponentName = getActivityName(activityName);
assertEquals(msg, activityComponentName, mAmState.getFocusedActivity());
assertEquals(msg, activityComponentName, mWmState.getFocusedApp());
}
void assertFocusedAppOnDisplay(final String msg, final ComponentName activityName,
final int displayId) {
final String activityComponentName = getActivityName(activityName);
assertEquals(msg, activityComponentName, mWmState.getDisplay(displayId).getFocusedApp());
}
void assertNotFocusedActivity(String msg, ComponentName activityName) {
assertNotEquals(msg, mAmState.getFocusedActivity(), getActivityName(activityName));
assertNotEquals(msg, mWmState.getFocusedApp(), getActivityName(activityName));
}
public void assertResumedActivity(final String msg, final ComponentName activityName) {
assertEquals(msg, getActivityName(activityName),
mAmState.getFocusedActivity());
}
/** Asserts that each display has correct resumed activity. */
public void assertResumedActivities(final String msg,
SparseArray<ComponentName> resumedActivities) {
for (int i = 0; i < resumedActivities.size(); i++) {
final int displayId = resumedActivities.keyAt(i);
final ComponentName activityComponent = resumedActivities.valueAt(i);
assertEquals("Error asserting resumed activity on display " + displayId + ": " + msg,
activityComponent != null ? getActivityName(activityComponent) : null,
mAmState.getResumedActivityOnDisplay(displayId));
}
}
void assertNotResumedActivity(String msg, ComponentName activityName) {
assertNotEquals(msg, mAmState.getFocusedActivity(), getActivityName(activityName));
}
void assertFocusedWindow(String msg, String windowName) {
assertEquals(msg, windowName, mWmState.getFocusedWindow());
}
void assertNotFocusedWindow(String msg, String windowName) {
assertNotEquals(msg, mWmState.getFocusedWindow(), windowName);
}
void assertNotExist(final ComponentName activityName) {
final String windowName = getWindowName(activityName);
assertFalse("Activity=" + getActivityName(activityName) + " must NOT exist.",
mAmState.containsActivity(activityName));
assertFalse("Window=" + windowName + " must NOT exits.",
mWmState.containsWindow(windowName));
}
public void assertVisibility(final ComponentName activityName, final boolean visible) {
final String windowName = getWindowName(activityName);
// Check existence of activity and window.
assertTrue("Activity=" + getActivityName(activityName) + " must exist.",
mAmState.containsActivity(activityName));
assertTrue("Window=" + windowName + " must exist.", mWmState.containsWindow(windowName));
// Check visibility of activity and window.
assertEquals("Activity=" + getActivityName(activityName) + " must" + (visible ? "" : " NOT")
+ " be visible.", visible, mAmState.isActivityVisible(activityName));
assertEquals("Window=" + windowName + " must" + (visible ? "" : " NOT") + " be visible.",
visible, mWmState.isWindowVisible(windowName));
}
void assertHomeActivityVisible(boolean visible) {
final ComponentName homeActivity = mAmState.getHomeActivityName();
assertNotNull(homeActivity);
assertVisibility(homeActivity, visible);
}
/**
* Asserts that the device default display minimim width is larger than the minimum task width.
*/
void assertDeviceDefaultDisplaySize(String errorMessage) {
computeState(true);
final int minTaskSizePx = defaultMinimalTaskSize(DEFAULT_DISPLAY);
final Display display = getWmState().getDisplay(DEFAULT_DISPLAY);
final Rect displayRect = display.getDisplayRect();
if (Math.min(displayRect.width(), displayRect.height()) < minTaskSizePx) {
fail(errorMessage);
}
}
public void assertKeyguardShowingAndOccluded() {
assertTrue("Keyguard is showing",
getAmState().getKeyguardControllerState().keyguardShowing);
assertTrue("Keyguard is occluded",
getAmState().getKeyguardControllerState().isKeyguardOccluded(DEFAULT_DISPLAY));
}
public void assertKeyguardShowingAndNotOccluded() {
assertTrue("Keyguard is showing",
getAmState().getKeyguardControllerState().keyguardShowing);
assertFalse("Keyguard is not occluded",
getAmState().getKeyguardControllerState().isKeyguardOccluded(DEFAULT_DISPLAY));
}
public void assertKeyguardGone() {
assertFalse("Keyguard is not shown",
getAmState().getKeyguardControllerState().keyguardShowing);
}
public void assumePendingActivityContain(ComponentName activity) {
assumeTrue(getAmState().pendingActivityContain(activity));
}
boolean taskListsInAmAndWmAreEqual() {
for (ActivityStack aStack : mAmState.getStacks()) {
final int stackId = aStack.mStackId;
final WindowStack wStack = mWmState.getStack(stackId);
if (wStack == null) {
log("Waiting for stack setup in WM, stackId=" + stackId);
return false;
}
for (ActivityTask aTask : aStack.getTasks()) {
if (wStack.getTask(aTask.mTaskId) == null) {
log("Task is in AM but not in WM, waiting for it to settle, taskId="
+ aTask.mTaskId);
return false;
}
}
for (WindowTask wTask : wStack.mTasks) {
if (aStack.getTask(wTask.mTaskId) == null) {
log("Task is in WM but not in AM, waiting for it to settle, taskId="
+ wTask.mTaskId);
return false;
}
}
}
return true;
}
/** Get the stack position on its display. */
int getStackIndexByActivityType(int activityType) {
int wmStackIndex = mWmState.getStackIndexByActivityType(activityType);
int amStackIndex = mAmState.getStackIndexByActivityType(activityType);
assertEquals("Window and activity manager must have the same stack position index",
amStackIndex, wmStackIndex);
return wmStackIndex;
}
boolean stackBoundsInAMAndWMAreEqual() {
for (ActivityStack aStack : mAmState.getStacks()) {
final int stackId = aStack.mStackId;
final WindowStack wStack = mWmState.getStack(stackId);
if (aStack.isFullscreen() != wStack.isFullscreen()) {
log("Waiting for correct fullscreen state, stackId=" + stackId);
return false;
}
final Rect aStackBounds = aStack.getBounds();
final Rect wStackBounds = wStack.getBounds();
if (aStack.isFullscreen()) {
if (aStackBounds != null) {
log("Waiting for correct stack state in AM, stackId=" + stackId);
return false;
}
} else if (!Objects.equals(aStackBounds, wStackBounds)) {
// If stack is not fullscreen - comparing bounds. Not doing it always because
// for fullscreen stack bounds in WM can be either null or equal to display size.
log("Waiting for stack bound equality in AM and WM, stackId=" + stackId);
return false;
}
}
return true;
}
/**
* Check task bounds when docked to top/left.
*/
void assertDockedTaskBounds(int taskWidth, int taskHeight, ComponentName activityName) {
// Task size can be affected by default minimal size.
int defaultMinimalTaskSize = defaultMinimalTaskSize(
mAmState.getStandardStackByWindowingMode(
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).mDisplayId);
int targetWidth = Math.max(taskWidth, defaultMinimalTaskSize);
int targetHeight = Math.max(taskHeight, defaultMinimalTaskSize);
assertEquals(new Rect(0, 0, targetWidth, targetHeight),
mAmState.getTaskByActivity(activityName).getBounds());
}
void assertValidBounds(boolean compareTaskAndStackBounds) {
// Cycle through the stacks and tasks to figure out if the home stack is resizable
final ActivityTask homeTask = mAmState.getHomeTask();
final boolean homeStackIsResizable = homeTask != null
&& homeTask.getResizeMode() == RESIZE_MODE_RESIZEABLE;
for (ActivityStack aStack : mAmState.getStacks()) {
final int stackId = aStack.mStackId;
final WindowStack wStack = mWmState.getStack(stackId);
assertNotNull("stackId=" + stackId + " in AM but not in WM?", wStack);
assertEquals("Stack fullscreen state in AM and WM must be equal stackId=" + stackId,
aStack.isFullscreen(), wStack.isFullscreen());
final Rect aStackBounds = aStack.getBounds();
final Rect wStackBounds = wStack.getBounds();
if (aStack.isFullscreen()) {
assertNull("Stack bounds in AM must be null stackId=" + stackId, aStackBounds);
} else {
assertEquals("Stack bounds in AM and WM must be equal stackId=" + stackId,
aStackBounds, wStackBounds);
}
for (ActivityTask aTask : aStack.getTasks()) {
final int taskId = aTask.mTaskId;
final WindowTask wTask = wStack.getTask(taskId);
assertNotNull(
"taskId=" + taskId + " in AM but not in WM? stackId=" + stackId, wTask);
final boolean aTaskIsFullscreen = aTask.isFullscreen();
final boolean wTaskIsFullscreen = wTask.isFullscreen();
assertEquals("Task fullscreen state in AM and WM must be equal taskId=" + taskId
+ ", stackId=" + stackId, aTaskIsFullscreen, wTaskIsFullscreen);
final Rect aTaskBounds = aTask.getBounds();
final Rect wTaskBounds = wTask.getBounds();
if (aTaskIsFullscreen) {
assertNull("Task bounds in AM must be null for fullscreen taskId=" + taskId,
aTaskBounds);
} else if (!homeStackIsResizable && mWmState.isDockedStackMinimized()
&& !isScreenPortrait(aStack.mDisplayId)) {
// When minimized using non-resizable launcher in landscape mode, it will move
// the task offscreen in the negative x direction unlike portrait that crops.
// The x value in the task bounds will not match the stack bounds since the
// only the task was moved.
assertEquals("Task bounds in AM and WM must match width taskId=" + taskId
+ ", stackId" + stackId, aTaskBounds.width(),
wTaskBounds.width());
assertEquals("Task bounds in AM and WM must match height taskId=" + taskId
+ ", stackId" + stackId, aTaskBounds.height(),
wTaskBounds.height());
assertEquals("Task bounds must match stack bounds y taskId=" + taskId
+ ", stackId" + stackId, aTaskBounds.top,
wTaskBounds.top);
assertEquals("Task and stack bounds must match width taskId=" + taskId
+ ", stackId" + stackId, aStackBounds.width(),
wTaskBounds.width());
assertEquals("Task and stack bounds must match height taskId=" + taskId
+ ", stackId" + stackId, aStackBounds.height(),
wTaskBounds.height());
assertEquals("Task and stack bounds must match y taskId=" + taskId
+ ", stackId" + stackId, aStackBounds.top,
wTaskBounds.top);
} else {
assertEquals("Task bounds in AM and WM must be equal taskId=" + taskId
+ ", stackId=" + stackId, aTaskBounds, wTaskBounds);
if (compareTaskAndStackBounds
&& aStack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
int aTaskMinWidth = aTask.getMinWidth();
int aTaskMinHeight = aTask.getMinHeight();
if (aTaskMinWidth == -1 || aTaskMinHeight == -1) {
// Minimal dimension(s) not set for task - it should be using defaults.
int defaultMinimalSize =
aStack.getWindowingMode() == WINDOWING_MODE_PINNED
? defaultMinimalPinnedTaskSize(aStack.mDisplayId)
: defaultMinimalTaskSize(aStack.mDisplayId);
if (aTaskMinWidth == -1) {
aTaskMinWidth = defaultMinimalSize;
}
if (aTaskMinHeight == -1) {
aTaskMinHeight = defaultMinimalSize;
}
}
if (aStackBounds.width() >= aTaskMinWidth
&& aStackBounds.height() >= aTaskMinHeight
|| aStack.getWindowingMode() == WINDOWING_MODE_PINNED) {
// Bounds are not smaller then minimal possible, so stack and task
// bounds must be equal.
assertEquals("Task bounds must be equal to stack bounds taskId="
+ taskId + ", stackId=" + stackId, aStackBounds, wTaskBounds);
} else if (aStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
&& homeStackIsResizable && mWmState.isDockedStackMinimized()) {
// Portrait if the display height is larger than the width
if (isScreenPortrait(aStack.mDisplayId)) {
assertEquals("Task width must be equal to stack width taskId="
+ taskId + ", stackId=" + stackId,
aStackBounds.width(), wTaskBounds.width());
assertThat("Task height must be greater than stack height "
+ "taskId=" + taskId + ", stackId=" + stackId,
aStackBounds.height(), lessThan(wTaskBounds.height()));
assertEquals("Task and stack x position must be equal taskId="
+ taskId + ", stackId=" + stackId,
wTaskBounds.left, wStackBounds.left);
} else {
assertThat("Task width must be greater than stack width taskId="
+ taskId + ", stackId=" + stackId,
aStackBounds.width(), lessThan(wTaskBounds.width()));
assertEquals("Task height must be equal to stack height taskId="
+ taskId + ", stackId=" + stackId,
aStackBounds.height(), wTaskBounds.height());
assertEquals("Task and stack y position must be equal taskId="
+ taskId + ", stackId=" + stackId, wTaskBounds.top,
wStackBounds.top);
}
} else {
// Minimal dimensions affect task size, so bounds of task and stack must
// be different - will compare dimensions instead.
int targetWidth = Math.max(aTaskMinWidth, aStackBounds.width());
assertEquals("Task width must be set according to minimal width"
+ " taskId=" + taskId + ", stackId=" + stackId,
targetWidth, wTaskBounds.width());
int targetHeight = Math.max(aTaskMinHeight, aStackBounds.height());
assertEquals("Task height must be set according to minimal height"
+ " taskId=" + taskId + ", stackId=" + stackId,
targetHeight, wTaskBounds.height());
}
}
}
}
}
}
public void assertActivityDisplayed(final ComponentName activityName) throws Exception {
assertWindowDisplayed(getWindowName(activityName));
}
public void assertWindowDisplayed(final String windowName) throws Exception {
waitForValidState(WaitForValidActivityState.forWindow(windowName));
assertTrue(windowName + "is visible", getWmState().isWindowVisible(windowName));
}
void waitAndAssertImeWindowShownOnDisplay(int displayId) {
final WindowManagerState.WindowState imeWinState = waitForValidProduct(
this::getImeWindowState, "IME window",
w -> w.isShown() && w.getDisplayId() == displayId);
assertNotNull("IME window must exist", imeWinState);
assertTrue("IME window must be shown", imeWinState.isShown());
assertEquals("IME window must be on the given display", displayId,
imeWinState.getDisplayId());
}
WindowManagerState.WindowState getImeWindowState() {
final WindowManagerState wmState = getWmState();
wmState.computeState();
return wmState.getInputMethodWindowState();
}
boolean isScreenPortrait() {
final int displayId = mAmState.getStandardStackByWindowingMode(
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).mDisplayId;
return isScreenPortrait(displayId);
}
boolean isScreenPortrait(int displayId) {
final Rect displayRect = mWmState.getDisplay(displayId).getDisplayRect();
return displayRect.height() > displayRect.width();
}
static int dpToPx(float dp, int densityDpi) {
return (int) (dp * densityDpi / DENSITY_DEFAULT + 0.5f);
}
private int defaultMinimalTaskSize(int displayId) {
return dpToPx(DEFAULT_RESIZABLE_TASK_SIZE_DP, mWmState.getDisplay(displayId).getDpi());
}
private int defaultMinimalPinnedTaskSize(int displayId) {
return dpToPx(DEFAULT_PIP_RESIZABLE_TASK_SIZE_DP, mWmState.getDisplay(displayId).getDpi());
}
}