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