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