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