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.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.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.view.Display.DEFAULT_DISPLAY;
32 
33 import static org.hamcrest.Matchers.greaterThan;
34 import static org.hamcrest.Matchers.lessThan;
35 import static org.junit.Assert.assertEquals;
36 import static org.junit.Assert.assertNotNull;
37 import static org.junit.Assert.assertNull;
38 import static org.junit.Assert.assertThat;
39 
40 import android.content.ComponentName;
41 import android.graphics.Rect;
42 import android.os.SystemClock;
43 import android.server.am.ActivityManagerState.ActivityStack;
44 import android.server.am.ActivityManagerState.ActivityTask;
45 import android.server.am.WindowManagerState.WindowStack;
46 import android.server.am.WindowManagerState.WindowState;
47 import android.server.am.WindowManagerState.WindowTask;
48 
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.List;
52 import java.util.Objects;
53 import java.util.function.BiPredicate;
54 import java.util.function.BooleanSupplier;
55 import java.util.function.Predicate;
56 
57 /**
58  * Combined state of the activity manager and window manager.
59  */
60 public class ActivityAndWindowManagersState {
61 
62     // Clone of android DisplayMetrics.DENSITY_DEFAULT (DENSITY_MEDIUM)
63     // (Needed in host-side tests to convert dp to px.)
64     private static final int DISPLAY_DENSITY_DEFAULT = 160;
65 
66     // Default minimal size of resizable task, used if none is set explicitly.
67     // Must be kept in sync with 'default_minimal_size_resizable_task' dimen from frameworks/base.
68     private static final int DEFAULT_RESIZABLE_TASK_SIZE_DP = 220;
69 
70     // Default minimal size of a resizable PiP task, used if none is set explicitly.
71     // Must be kept in sync with 'default_minimal_size_pip_resizable_task' dimen from
72     // frameworks/base.
73     private static final int DEFAULT_PIP_RESIZABLE_TASK_SIZE_DP = 108;
74 
75     private ActivityManagerState mAmState = new ActivityManagerState();
76     private WindowManagerState mWmState = new WindowManagerState();
77 
78     /**
79      * Compute AM and WM state of device, check sanity and bounds.
80      * WM state will include only visible windows, stack and task bounds will be compared.
81      *
82      * @param waitForActivitiesVisible array of activity names to wait for.
83      */
computeState(WaitForValidActivityState... waitForActivitiesVisible)84     public void computeState(WaitForValidActivityState... waitForActivitiesVisible) {
85         waitForValidState(true /* compareTaskAndStackBounds */, waitForActivitiesVisible);
86     }
87 
88     /**
89      * Compute AM and WM state of device, check sanity and bounds.
90      *
91      * @param compareTaskAndStackBounds pass 'true' if stack and task bounds should be compared,
92      *                                  'false' otherwise.
93      * @param waitForActivitiesVisible  array of activity states to wait for.
94      */
computeState(boolean compareTaskAndStackBounds, WaitForValidActivityState... waitForActivitiesVisible)95     void computeState(boolean compareTaskAndStackBounds,
96             WaitForValidActivityState... waitForActivitiesVisible) {
97         waitForValidState(compareTaskAndStackBounds, waitForActivitiesVisible);
98     }
99 
100     /** Wait for the activity to appear and for valid state in AM and WM. */
waitForValidState(WaitForValidActivityState... waitForActivityVisible)101     void waitForValidState(WaitForValidActivityState... waitForActivityVisible) {
102         waitForValidState(false /* compareTaskAndStackBounds */, waitForActivityVisible);
103     }
104 
105     /**
106      * Wait for the activities to appear in proper stacks and for valid state in AM and WM.
107      *
108      * @param compareTaskAndStackBounds flag indicating if we should compare task and stack bounds
109      *                                  for equality.
110      * @param waitForActivitiesVisible  array of activity states to wait for.
111      */
waitForValidState(boolean compareTaskAndStackBounds, WaitForValidActivityState... waitForActivitiesVisible)112     private void waitForValidState(boolean compareTaskAndStackBounds,
113             WaitForValidActivityState... waitForActivitiesVisible) {
114         for (int retry = 1; retry <= 5; retry++) {
115             // TODO: Get state of AM and WM at the same time to avoid mismatches caused by
116             // requesting dump in some intermediate state.
117             mAmState.computeState();
118             mWmState.computeState();
119             if (shouldWaitForSanityCheck(compareTaskAndStackBounds)
120                     || shouldWaitForValidStacks(compareTaskAndStackBounds)
121                     || shouldWaitForActivities(waitForActivitiesVisible)
122                     || shouldWaitForWindows()) {
123                 logAlways("***Waiting for valid stacks and activities states... retry=" + retry);
124                 SystemClock.sleep(1000);
125             } else {
126                 return;
127             }
128         }
129         logE("***Waiting for states failed: " + Arrays.toString(waitForActivitiesVisible));
130     }
131 
waitForHomeActivityVisible()132     boolean waitForHomeActivityVisible() {
133         ComponentName homeActivity = mAmState.getHomeActivityName();
134         // Sometimes this function is called before we know what Home Activity is
135         if (homeActivity == null) {
136             log("Computing state to determine Home Activity");
137             computeState(true);
138             homeActivity = mAmState.getHomeActivityName();
139         }
140         assertNotNull("homeActivity should not be null", homeActivity);
141         waitForValidState(new WaitForValidActivityState(homeActivity));
142         return mAmState.isHomeActivityVisible();
143     }
144 
waitForKeyguardShowingAndNotOccluded()145     public void waitForKeyguardShowingAndNotOccluded() {
146         waitForWithAmState(state -> state.getKeyguardControllerState().keyguardShowing
147                         && !state.getKeyguardControllerState().isKeyguardOccluded(DEFAULT_DISPLAY),
148                 "***Waiting for Keyguard showing...");
149     }
150 
151     @Deprecated
waitForFocusedStack(int stackId)152     void waitForFocusedStack(int stackId) {
153         waitForWithAmState(state -> state.getFocusedStackId() == stackId,
154                 "***Waiting for focused stack...");
155     }
156 
waitForWithAmState(Predicate<ActivityManagerState> waitCondition, String message)157     void waitForWithAmState(Predicate<ActivityManagerState> waitCondition, String message) {
158         waitFor((amState, wmState) -> waitCondition.test(amState), message);
159     }
160 
waitFor( BiPredicate<ActivityManagerState, WindowManagerState> waitCondition, String message)161     void waitFor(
162             BiPredicate<ActivityManagerState, WindowManagerState> waitCondition, String message) {
163         waitFor(message, () -> {
164             mAmState.computeState();
165             mWmState.computeState();
166             return waitCondition.test(mAmState, mWmState);
167         });
168     }
169 
waitFor(String message, BooleanSupplier waitCondition)170     void waitFor(String message, BooleanSupplier waitCondition) {
171         for (int retry = 1; retry <= 5; retry++) {
172             if (waitCondition.getAsBoolean()) {
173                 return;
174             }
175             logAlways(message + " retry=" + retry);
176             SystemClock.sleep(1000);
177         }
178         logE(message + " failed");
179     }
180 
181     /**
182      * @return true if should wait for valid stacks state.
183      */
shouldWaitForValidStacks(boolean compareTaskAndStackBounds)184     private boolean shouldWaitForValidStacks(boolean compareTaskAndStackBounds) {
185         if (!taskListsInAmAndWmAreEqual()) {
186             // We want to wait for equal task lists in AM and WM in case we caught them in the
187             // middle of some state change operations.
188             logAlways("***taskListsInAmAndWmAreEqual=false");
189             return true;
190         }
191         if (!stackBoundsInAMAndWMAreEqual()) {
192             // We want to wait a little for the stacks in AM and WM to have equal bounds as there
193             // might be a transition animation ongoing when we got the states from WM AM separately.
194             logAlways("***stackBoundsInAMAndWMAreEqual=false");
195             return true;
196         }
197         try {
198             // Temporary fix to avoid catching intermediate state with different task bounds in AM
199             // and WM.
200             assertValidBounds(compareTaskAndStackBounds);
201         } catch (AssertionError e) {
202             logAlways("***taskBoundsInAMAndWMAreEqual=false : " + e.getMessage());
203             return true;
204         }
205         final int stackCount = mAmState.getStackCount();
206         if (stackCount == 0) {
207             logAlways("***stackCount=" + stackCount);
208             return true;
209         }
210         final int resumedActivitiesCount = mAmState.getResumedActivitiesCount();
211         if (!mAmState.getKeyguardControllerState().keyguardShowing && resumedActivitiesCount != 1) {
212             logAlways("***resumedActivitiesCount=" + resumedActivitiesCount);
213             return true;
214         }
215         if (mAmState.getFocusedActivity() == null) {
216             logAlways("***focusedActivity=null");
217             return true;
218         }
219         return false;
220     }
221 
222     /**
223      * @return true if should wait for some activities to become visible.
224      */
shouldWaitForActivities(WaitForValidActivityState... waitForActivitiesVisible)225     private boolean shouldWaitForActivities(WaitForValidActivityState... waitForActivitiesVisible) {
226         if (waitForActivitiesVisible == null || waitForActivitiesVisible.length == 0) {
227             return false;
228         }
229         // If the caller is interested in us waiting for some particular activity windows to be
230         // visible before compute the state. Check for the visibility of those activity windows
231         // and for placing them in correct stacks (if requested).
232         boolean allActivityWindowsVisible = true;
233         boolean tasksInCorrectStacks = true;
234         List<WindowState> matchingWindowStates = new ArrayList<>();
235         for (final WaitForValidActivityState state : waitForActivitiesVisible) {
236             final ComponentName activityName = state.activityName;
237             final String windowName = state.windowName;
238             final int stackId = state.stackId;
239             final int windowingMode = state.windowingMode;
240             final int activityType = state.activityType;
241 
242             mWmState.getMatchingVisibleWindowState(windowName, matchingWindowStates);
243             boolean activityWindowVisible = !matchingWindowStates.isEmpty();
244             if (!activityWindowVisible) {
245                 logAlways("Activity window not visible: " + windowName);
246                 allActivityWindowsVisible = false;
247             } else if (activityName != null
248                     && !mAmState.isActivityVisible(activityName)) {
249                 logAlways("Activity not visible: " + getActivityName(activityName));
250                 allActivityWindowsVisible = false;
251             } else {
252                 // Check if window is already the correct state requested by test.
253                 boolean windowInCorrectState = false;
254                 for (WindowState ws : matchingWindowStates) {
255                     if (stackId != INVALID_STACK_ID && ws.getStackId() != stackId) {
256                         continue;
257                     }
258                     if (windowingMode != WINDOWING_MODE_UNDEFINED
259                             && ws.getWindowingMode() != windowingMode) {
260                         continue;
261                     }
262                     if (activityType != ACTIVITY_TYPE_UNDEFINED
263                             && ws.getActivityType() != activityType) {
264                         continue;
265                     }
266                     windowInCorrectState = true;
267                     break;
268                 }
269 
270                 if (!windowInCorrectState) {
271                     logAlways("Window in incorrect stack: " + state);
272                     tasksInCorrectStacks = false;
273                 }
274             }
275         }
276         return !allActivityWindowsVisible || !tasksInCorrectStacks;
277     }
278 
279     /**
280      * @return true if should wait valid windows state.
281      */
shouldWaitForWindows()282     private boolean shouldWaitForWindows() {
283         if (mWmState.getFrontWindow() == null) {
284             logAlways("***frontWindow=null");
285             return true;
286         }
287         if (mWmState.getFocusedWindow() == null) {
288             logAlways("***focusedWindow=null");
289             return true;
290         }
291         if (mWmState.getFocusedApp() == null) {
292             logAlways("***focusedApp=null");
293             return true;
294         }
295 
296         return false;
297     }
298 
shouldWaitForSanityCheck(boolean compareTaskAndStackBounds)299     private boolean shouldWaitForSanityCheck(boolean compareTaskAndStackBounds) {
300         try {
301             assertSanity();
302             assertValidBounds(compareTaskAndStackBounds);
303         } catch (Throwable t) {
304             logAlways("Waiting for sanity check: " + t.toString());
305             return true;
306         }
307         return false;
308     }
309 
getWmState()310     public WindowManagerState getWmState() {
311         return mWmState;
312     }
313 
assertSanity()314     void assertSanity() {
315         assertThat("Must have stacks", mAmState.getStackCount(), greaterThan(0));
316         if (!mAmState.getKeyguardControllerState().keyguardShowing) {
317             assertEquals("There should be one and only one resumed activity in the system.",
318                     1, mAmState.getResumedActivitiesCount());
319         }
320         assertNotNull("Must have focus activity.", mAmState.getFocusedActivity());
321 
322         for (ActivityStack aStack : mAmState.getStacks()) {
323             final int stackId = aStack.mStackId;
324             for (ActivityTask aTask : aStack.getTasks()) {
325                 assertEquals("Stack can only contain its own tasks", stackId, aTask.mStackId);
326             }
327         }
328 
329         assertNotNull("Must have front window.", mWmState.getFrontWindow());
330         assertNotNull("Must have focused window.", mWmState.getFocusedWindow());
331         assertNotNull("Must have app.", mWmState.getFocusedApp());
332     }
333 
334     @Deprecated
assertFocusedStack(String msg, int stackId)335     void assertFocusedStack(String msg, int stackId) {
336         assertEquals(msg, stackId, mAmState.getFocusedStackId());
337     }
338 
assertVisibility(final ComponentName activityName, final boolean visible)339     public void assertVisibility(final ComponentName activityName, final boolean visible) {
340         final String windowName = getWindowName(activityName);
341         final boolean activityVisible = mAmState.isActivityVisible(activityName);
342         final boolean windowVisible = mWmState.isWindowVisible(windowName);
343 
344         assertEquals("Activity=" + getActivityName(activityName) + " must" + (visible ? "" : " NOT")
345                 + " be visible.", visible, activityVisible);
346         assertEquals("Window=" + windowName + " must" + (visible ? "" : " NOT") + " be visible.",
347                 visible, windowVisible);
348     }
349 
taskListsInAmAndWmAreEqual()350     boolean taskListsInAmAndWmAreEqual() {
351         for (ActivityStack aStack : mAmState.getStacks()) {
352             final int stackId = aStack.mStackId;
353             final WindowStack wStack = mWmState.getStack(stackId);
354             if (wStack == null) {
355                 log("Waiting for stack setup in WM, stackId=" + stackId);
356                 return false;
357             }
358 
359             for (ActivityTask aTask : aStack.getTasks()) {
360                 if (wStack.getTask(aTask.mTaskId) == null) {
361                     log("Task is in AM but not in WM, waiting for it to settle, taskId="
362                             + aTask.mTaskId);
363                     return false;
364                 }
365             }
366 
367             for (WindowTask wTask : wStack.mTasks) {
368                 if (aStack.getTask(wTask.mTaskId) == null) {
369                     log("Task is in WM but not in AM, waiting for it to settle, taskId="
370                             + wTask.mTaskId);
371                     return false;
372                 }
373             }
374         }
375         return true;
376     }
377 
stackBoundsInAMAndWMAreEqual()378     boolean stackBoundsInAMAndWMAreEqual() {
379         for (ActivityStack aStack : mAmState.getStacks()) {
380             final int stackId = aStack.mStackId;
381             final WindowStack wStack = mWmState.getStack(stackId);
382             if (aStack.isFullscreen() != wStack.isFullscreen()) {
383                 log("Waiting for correct fullscreen state, stackId=" + stackId);
384                 return false;
385             }
386 
387             final Rect aStackBounds = aStack.getBounds();
388             final Rect wStackBounds = wStack.getBounds();
389 
390             if (aStack.isFullscreen()) {
391                 if (aStackBounds != null) {
392                     log("Waiting for correct stack state in AM, stackId=" + stackId);
393                     return false;
394                 }
395             } else if (!Objects.equals(aStackBounds, wStackBounds)) {
396                 // If stack is not fullscreen - comparing bounds. Not doing it always because
397                 // for fullscreen stack bounds in WM can be either null or equal to display size.
398                 log("Waiting for stack bound equality in AM and WM, stackId=" + stackId);
399                 return false;
400             }
401         }
402 
403         return true;
404     }
405 
assertValidBounds(boolean compareTaskAndStackBounds)406     void assertValidBounds(boolean compareTaskAndStackBounds) {
407         // Cycle through the stacks and tasks to figure out if the home stack is resizable
408         final ActivityTask homeTask = mAmState.getHomeTask();
409         final boolean homeStackIsResizable = homeTask != null
410                 && homeTask.getResizeMode() == RESIZE_MODE_RESIZEABLE;
411 
412         for (ActivityStack aStack : mAmState.getStacks()) {
413             final int stackId = aStack.mStackId;
414             final WindowStack wStack = mWmState.getStack(stackId);
415             assertNotNull("stackId=" + stackId + " in AM but not in WM?", wStack);
416 
417             assertEquals("Stack fullscreen state in AM and WM must be equal stackId=" + stackId,
418                     aStack.isFullscreen(), wStack.isFullscreen());
419 
420             final Rect aStackBounds = aStack.getBounds();
421             final Rect wStackBounds = wStack.getBounds();
422 
423             if (aStack.isFullscreen()) {
424                 assertNull("Stack bounds in AM must be null stackId=" + stackId, aStackBounds);
425             } else {
426                 assertEquals("Stack bounds in AM and WM must be equal stackId=" + stackId,
427                         aStackBounds, wStackBounds);
428             }
429 
430             for (ActivityTask aTask : aStack.getTasks()) {
431                 final int taskId = aTask.mTaskId;
432                 final WindowTask wTask = wStack.getTask(taskId);
433                 assertNotNull(
434                         "taskId=" + taskId + " in AM but not in WM? stackId=" + stackId, wTask);
435 
436                 final boolean aTaskIsFullscreen = aTask.isFullscreen();
437                 final boolean wTaskIsFullscreen = wTask.isFullscreen();
438                 assertEquals("Task fullscreen state in AM and WM must be equal taskId=" + taskId
439                         + ", stackId=" + stackId, aTaskIsFullscreen, wTaskIsFullscreen);
440 
441                 final Rect aTaskBounds = aTask.getBounds();
442                 final Rect wTaskBounds = wTask.getBounds();
443 
444                 if (aTaskIsFullscreen) {
445                     assertNull("Task bounds in AM must be null for fullscreen taskId=" + taskId,
446                             aTaskBounds);
447                 } else if (!homeStackIsResizable && mWmState.isDockedStackMinimized()
448                         && !isScreenPortrait(aStack.mDisplayId)) {
449                     // When minimized using non-resizable launcher in landscape mode, it will move
450                     // the task offscreen in the negative x direction unlike portrait that crops.
451                     // The x value in the task bounds will not match the stack bounds since the
452                     // only the task was moved.
453                     assertEquals("Task bounds in AM and WM must match width taskId=" + taskId
454                                     + ", stackId" + stackId, aTaskBounds.width(),
455                             wTaskBounds.width());
456                     assertEquals("Task bounds in AM and WM must match height taskId=" + taskId
457                                     + ", stackId" + stackId, aTaskBounds.height(),
458                             wTaskBounds.height());
459                     assertEquals("Task bounds must match stack bounds y taskId=" + taskId
460                                     + ", stackId" + stackId, aTaskBounds.top,
461                             wTaskBounds.top);
462                     assertEquals("Task and stack bounds must match width taskId=" + taskId
463                                     + ", stackId" + stackId, aStackBounds.width(),
464                             wTaskBounds.width());
465                     assertEquals("Task and stack bounds must match height taskId=" + taskId
466                                     + ", stackId" + stackId, aStackBounds.height(),
467                             wTaskBounds.height());
468                     assertEquals("Task and stack bounds must match y taskId=" + taskId
469                                     + ", stackId" + stackId, aStackBounds.top,
470                             wTaskBounds.top);
471                 } else {
472                     assertEquals("Task bounds in AM and WM must be equal taskId=" + taskId
473                             + ", stackId=" + stackId, aTaskBounds, wTaskBounds);
474 
475                     if (compareTaskAndStackBounds
476                             && aStack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
477                         int aTaskMinWidth = aTask.getMinWidth();
478                         int aTaskMinHeight = aTask.getMinHeight();
479 
480                         if (aTaskMinWidth == -1 || aTaskMinHeight == -1) {
481                             // Minimal dimension(s) not set for task - it should be using defaults.
482                             int defaultMinimalSize =
483                                     aStack.getWindowingMode() == WINDOWING_MODE_PINNED
484                                     ? defaultMinimalPinnedTaskSize(aStack.mDisplayId)
485                                     : defaultMinimalTaskSize(aStack.mDisplayId);
486 
487                             if (aTaskMinWidth == -1) {
488                                 aTaskMinWidth = defaultMinimalSize;
489                             }
490                             if (aTaskMinHeight == -1) {
491                                 aTaskMinHeight = defaultMinimalSize;
492                             }
493                         }
494 
495                         if (aStackBounds.width() >= aTaskMinWidth
496                                 && aStackBounds.height() >= aTaskMinHeight
497                                 || aStack.getWindowingMode() == WINDOWING_MODE_PINNED) {
498                             // Bounds are not smaller then minimal possible, so stack and task
499                             // bounds must be equal.
500                             assertEquals("Task bounds must be equal to stack bounds taskId="
501                                     + taskId + ", stackId=" + stackId, aStackBounds, wTaskBounds);
502                         } else if (aStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
503                                 && homeStackIsResizable && mWmState.isDockedStackMinimized()) {
504                             // Portrait if the display height is larger than the width
505                             if (isScreenPortrait(aStack.mDisplayId)) {
506                                 assertEquals("Task width must be equal to stack width taskId="
507                                                 + taskId + ", stackId=" + stackId,
508                                         aStackBounds.width(), wTaskBounds.width());
509                                 assertThat("Task height must be greater than stack height "
510                                                 + "taskId=" + taskId + ", stackId=" + stackId,
511                                         aStackBounds.height(), lessThan(wTaskBounds.height()));
512                                 assertEquals("Task and stack x position must be equal taskId="
513                                                 + taskId + ", stackId=" + stackId,
514                                         wTaskBounds.left, wStackBounds.left);
515                             } else {
516                                 assertThat("Task width must be greater than stack width taskId="
517                                                 + taskId + ", stackId=" + stackId,
518                                         aStackBounds.width(), lessThan(wTaskBounds.width()));
519                                 assertEquals("Task height must be equal to stack height taskId="
520                                                 + taskId + ", stackId=" + stackId,
521                                         aStackBounds.height(), wTaskBounds.height());
522                                 assertEquals("Task and stack y position must be equal taskId="
523                                                 + taskId + ", stackId=" + stackId, wTaskBounds.top,
524                                         wStackBounds.top);
525                             }
526                         } else {
527                             // Minimal dimensions affect task size, so bounds of task and stack must
528                             // be different - will compare dimensions instead.
529                             int targetWidth = (int) Math.max(aTaskMinWidth,
530                                     aStackBounds.width());
531                             assertEquals("Task width must be set according to minimal width"
532                                             + " taskId=" + taskId + ", stackId=" + stackId,
533                                     targetWidth, (int) wTaskBounds.width());
534                             int targetHeight = (int) Math.max(aTaskMinHeight,
535                                     aStackBounds.height());
536                             assertEquals("Task height must be set according to minimal height"
537                                             + " taskId=" + taskId + ", stackId=" + stackId,
538                                     targetHeight, (int) wTaskBounds.height());
539                         }
540                     }
541                 }
542             }
543         }
544     }
545 
isScreenPortrait(int displayId)546     boolean isScreenPortrait(int displayId) {
547         final Rect displayRect = mWmState.getDisplay(displayId).getDisplayRect();
548         return displayRect.height() > displayRect.width();
549     }
550 
dpToPx(float dp, int densityDpi)551     static int dpToPx(float dp, int densityDpi) {
552         return (int) (dp * densityDpi / DISPLAY_DENSITY_DEFAULT + 0.5f);
553     }
554 
defaultMinimalTaskSize(int displayId)555     private int defaultMinimalTaskSize(int displayId) {
556         return dpToPx(DEFAULT_RESIZABLE_TASK_SIZE_DP, mWmState.getDisplay(displayId).getDpi());
557     }
558 
defaultMinimalPinnedTaskSize(int displayId)559     private int defaultMinimalPinnedTaskSize(int displayId) {
560         return dpToPx(DEFAULT_PIP_RESIZABLE_TASK_SIZE_DP, mWmState.getDisplay(displayId).getDpi());
561     }
562 }
563