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      */
computeState(ComponentName... componentNames)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      */
computeState(WaitForValidActivityState... waitForActivitiesVisible)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      */
computeState(boolean compareTaskAndStackBounds, WaitForValidActivityState... waitForActivitiesVisible)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      */
waitForValidState(ComponentName... activityNames)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. */
waitForValidState(WaitForValidActivityState... waitForActivityVisible)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      */
waitForValidState(boolean compareTaskAndStackBounds, WaitForValidActivityState... waitForActivitiesVisible)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      */
waitForAllExitingWindows()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 
waitForAllStoppedActivities()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      */
waitForDebuggerWindowVisible(ComponentName activityName)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 
waitForHomeActivityVisible()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      */
waitForRecentsActivityVisible()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 
waitForKeyguardShowingAndNotOccluded()240     void waitForKeyguardShowingAndNotOccluded() {
241         waitForWithAmState(state -> state.getKeyguardControllerState().keyguardShowing
242                         && !state.getKeyguardControllerState().keyguardOccluded,
243                 "***Waiting for Keyguard showing...");
244     }
245 
waitForKeyguardShowingAndOccluded()246     void waitForKeyguardShowingAndOccluded() {
247         waitForWithAmState(state -> state.getKeyguardControllerState().keyguardShowing
248                         && state.getKeyguardControllerState().keyguardOccluded,
249                 "***Waiting for Keyguard showing and occluded...");
250     }
251 
waitForKeyguardGone()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 */
waitForRotation(int 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      */
waitForLastOrientation(int orientation)267     void waitForLastOrientation(int orientation) {
268         waitForWithWmState(state -> state.getLastOrientation() == orientation,
269                 "***Waiting for LastOrientation: " + orientation);
270     }
271 
waitForDisplayUnfrozen()272     void waitForDisplayUnfrozen() {
273         waitForWithWmState(state -> !state.isDisplayFrozen(),
274                 "***Waiting for Display unfrozen");
275     }
276 
waitForActivityState(ComponentName activityName, String activityState)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
waitForFocusedStack(int stackId)283     void waitForFocusedStack(int stackId) {
284         waitForWithAmState(state -> state.getFocusedStackId() == stackId,
285                 "***Waiting for focused stack...");
286     }
287 
waitForFocusedStack(int windowingMode, int activityType)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 
waitForAppTransitionIdle()297     void waitForAppTransitionIdle() {
298         waitForWithWmState(
299                 state -> WindowManagerState.APP_STATE_IDLE.equals(state.getAppTransitionState()),
300                 "***Waiting for app transition idle...");
301     }
302 
waitForWithAmState(Predicate<ActivityManagerState> waitCondition, String message)303     void waitForWithAmState(Predicate<ActivityManagerState> waitCondition, String message) {
304         waitFor((amState, wmState) -> waitCondition.test(amState), message);
305     }
306 
waitForWithWmState(Predicate<WindowManagerState> waitCondition, String message)307     void waitForWithWmState(Predicate<WindowManagerState> waitCondition, String message) {
308         waitFor((amState, wmState) -> waitCondition.test(wmState), message);
309     }
310 
waitFor( BiPredicate<ActivityManagerState, WindowManagerState> waitCondition, String message)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 
waitFor(String message, BooleanSupplier waitCondition)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      */
shouldWaitForValidStacks(boolean compareTaskAndStackBounds)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      */
shouldWaitForActivities(WaitForValidActivityState... waitForActivitiesVisible)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      */
shouldWaitForWindows()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 
shouldWaitForDebuggerWindow(ComponentName activityName)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 
shouldWaitForActivityRecords(ComponentName... activityNames)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 
shouldWaitForSanityCheck(boolean compareTaskAndStackBounds)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 
getAmState()483     ActivityManagerState getAmState() {
484         return mAmState;
485     }
486 
getWmState()487     public WindowManagerState getWmState() {
488         return mWmState;
489     }
490 
assertSanity()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 
assertContainsStack(String msg, int windowingMode, int activityType)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 
assertDoesNotContainStack(String msg, int windowingMode, int activityType)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 
assertFrontStack(String msg, int stackId)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 
assertFrontStack(String msg, int windowingMode, int activityType)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 
assertFrontStackActivityType(String msg, int activityType)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
assertFocusedStack(String msg, int stackId)542     void assertFocusedStack(String msg, int stackId) {
543         assertEquals(msg, stackId, mAmState.getFocusedStackId());
544     }
545 
assertFocusedStack(String msg, int windowingMode, int activityType)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 
assertFocusedActivity(final String msg, final ComponentName activityName)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 
assertNotFocusedActivity(String msg, ComponentName activityName)561     void assertNotFocusedActivity(String msg, ComponentName activityName) {
562         assertNotEquals(msg, mAmState.getFocusedActivity(), getActivityName(activityName));
563         assertNotEquals(msg, mWmState.getFocusedApp(), getActivityName(activityName));
564     }
565 
assertResumedActivity(final String msg, final ComponentName activityName)566     public void assertResumedActivity(final String msg, final ComponentName activityName) {
567         assertEquals(msg, getActivityName(activityName), mAmState.getResumedActivity());
568     }
569 
assertNotResumedActivity(String msg, ComponentName activityName)570     void assertNotResumedActivity(String msg, ComponentName activityName) {
571         assertNotEquals(msg, mAmState.getResumedActivity(), getActivityName(activityName));
572     }
573 
assertFocusedWindow(String msg, String windowName)574     void assertFocusedWindow(String msg, String windowName) {
575         assertEquals(msg, windowName, mWmState.getFocusedWindow());
576     }
577 
assertNotFocusedWindow(String msg, String windowName)578     void assertNotFocusedWindow(String msg, String windowName) {
579         assertNotEquals(msg, mWmState.getFocusedWindow(), windowName);
580     }
581 
assertFrontWindow(String msg, String windowName)582     void assertFrontWindow(String msg, String windowName) {
583         assertEquals(msg, windowName, mWmState.getFrontWindow());
584     }
585 
assertVisibility(final ComponentName activityName, final boolean visible)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 
assertHomeActivityVisible(boolean visible)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      */
assertDeviceDefaultDisplaySize(String errorMessage)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 
assertKeyguardShowingAndOccluded()616     public void assertKeyguardShowingAndOccluded() {
617         assertTrue("Keyguard is showing",
618                 getAmState().getKeyguardControllerState().keyguardShowing);
619         assertTrue("Keyguard is occluded",
620                 getAmState().getKeyguardControllerState().keyguardOccluded);
621     }
622 
assertKeyguardShowingAndNotOccluded()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 
assertKeyguardGone()630     public void assertKeyguardGone() {
631         assertFalse("Keyguard is not shown",
632                 getAmState().getKeyguardControllerState().keyguardShowing);
633     }
634 
taskListsInAmAndWmAreEqual()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. */
getStackIndexByActivityType(int activityType)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 
stackBoundsInAMAndWMAreEqual()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      */
assertDockedTaskBounds(int taskWidth, int taskHeight, ComponentName activityName)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 
assertValidBounds(boolean compareTaskAndStackBounds)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 
assertActivityDisplayed(final ComponentName activityName)853     public void assertActivityDisplayed(final ComponentName activityName) throws Exception {
854         assertWindowDisplayed(getWindowName(activityName));
855     }
856 
assertWindowDisplayed(final String windowName)857     public void assertWindowDisplayed(final String windowName) throws Exception {
858         waitForValidState(WaitForValidActivityState.forWindow(windowName));
859         assertTrue(windowName + "is visible", getWmState().isWindowVisible(windowName));
860     }
861 
isScreenPortrait()862     boolean isScreenPortrait() {
863         final int displayId = mAmState.getStandardStackByWindowingMode(
864             WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).mDisplayId;
865         return isScreenPortrait(displayId);
866     }
867 
isScreenPortrait(int displayId)868     boolean isScreenPortrait(int displayId) {
869         final Rect displayRect = mWmState.getDisplay(displayId).getDisplayRect();
870         return displayRect.height() > displayRect.width();
871     }
872 
dpToPx(float dp, int densityDpi)873     static int dpToPx(float dp, int densityDpi) {
874         return (int) (dp * densityDpi / DENSITY_DEFAULT + 0.5f);
875     }
876 
defaultMinimalTaskSize(int displayId)877     private int defaultMinimalTaskSize(int displayId) {
878         return dpToPx(DEFAULT_RESIZABLE_TASK_SIZE_DP, mWmState.getDisplay(displayId).getDpi());
879     }
880 
defaultMinimalPinnedTaskSize(int displayId)881     private int defaultMinimalPinnedTaskSize(int displayId) {
882         return dpToPx(DEFAULT_PIP_RESIZABLE_TASK_SIZE_DP, mWmState.getDisplay(displayId).getDpi());
883     }
884 }
885