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