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