DisplayContent.java revision 912d919772b284af0d17160b104902c45bfafa63
1/* 2 * Copyright (C) 2012 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 17package com.android.server.wm; 18 19import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; 20import static android.app.ActivityManager.StackId.HOME_STACK_ID; 21import static android.app.ActivityManager.StackId.PINNED_STACK_ID; 22import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY; 23import static com.android.server.wm.WindowManagerService.TAG; 24import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP; 25 26import android.app.ActivityManager.StackId; 27import android.graphics.Rect; 28import android.graphics.Region; 29import android.util.DisplayMetrics; 30import android.util.Slog; 31import android.view.Display; 32import android.view.DisplayInfo; 33import android.view.Surface; 34 35import java.io.PrintWriter; 36import java.util.ArrayList; 37 38class DisplayContentList extends ArrayList<DisplayContent> { 39} 40 41/** 42 * Utility class for keeping track of the WindowStates and other pertinent contents of a 43 * particular Display. 44 * 45 * IMPORTANT: No method from this class should ever be used without holding 46 * WindowManagerService.mWindowMap. 47 */ 48class DisplayContent { 49 50 /** Unique identifier of this stack. */ 51 private final int mDisplayId; 52 53 /** Z-ordered (bottom-most first) list of all Window objects. Assigned to an element 54 * from mDisplayWindows; */ 55 private final WindowList mWindows = new WindowList(); 56 57 int mInitialDisplayWidth = 0; 58 int mInitialDisplayHeight = 0; 59 int mInitialDisplayDensity = 0; 60 int mBaseDisplayWidth = 0; 61 int mBaseDisplayHeight = 0; 62 int mBaseDisplayDensity = 0; 63 boolean mDisplayScalingDisabled; 64 private final DisplayInfo mDisplayInfo = new DisplayInfo(); 65 private final Display mDisplay; 66 private final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); 67 68 Rect mBaseDisplayRect = new Rect(); 69 Rect mContentRect = new Rect(); 70 71 // Accessed directly by all users. 72 boolean layoutNeeded; 73 int pendingLayoutChanges; 74 final boolean isDefaultDisplay; 75 76 /** Window tokens that are in the process of exiting, but still on screen for animations. */ 77 final ArrayList<WindowToken> mExitingTokens = new ArrayList<>(); 78 79 /** Array containing all TaskStacks on this display. Array 80 * is stored in display order with the current bottom stack at 0. */ 81 private final ArrayList<TaskStack> mStacks = new ArrayList<>(); 82 83 /** A special TaskStack with id==HOME_STACK_ID that moves to the bottom whenever any TaskStack 84 * (except a future lockscreen TaskStack) moves to the top. */ 85 private TaskStack mHomeStack = null; 86 87 /** Detect user tapping outside of current focused task bounds .*/ 88 TaskTapPointerEventListener mTapDetector; 89 90 /** Detect user tapping outside of current focused stack bounds .*/ 91 Region mTouchExcludeRegion = new Region(); 92 93 /** Detect user tapping in a non-resizeable task in docked or fullscreen stack .*/ 94 Region mNonResizeableRegion = new Region(); 95 96 /** Save allocating when calculating rects */ 97 private final Rect mTmpRect = new Rect(); 98 private final Rect mTmpRect2 = new Rect(); 99 private final Region mTmpRegion = new Region(); 100 101 /** For gathering Task objects in order. */ 102 final ArrayList<Task> mTmpTaskHistory = new ArrayList<Task>(); 103 104 final WindowManagerService mService; 105 106 /** Remove this display when animation on it has completed. */ 107 boolean mDeferredRemoval; 108 109 final DockedStackDividerController mDividerControllerLocked; 110 111 final DimLayerController mDimLayerController; 112 113 /** 114 * @param display May not be null. 115 * @param service You know. 116 */ 117 DisplayContent(Display display, WindowManagerService service) { 118 mDisplay = display; 119 mDisplayId = display.getDisplayId(); 120 display.getDisplayInfo(mDisplayInfo); 121 display.getMetrics(mDisplayMetrics); 122 isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY; 123 mService = service; 124 initializeDisplayBaseInfo(); 125 mDividerControllerLocked = new DockedStackDividerController(service.mContext, this); 126 mDimLayerController = new DimLayerController(this); 127 } 128 129 int getDisplayId() { 130 return mDisplayId; 131 } 132 133 WindowList getWindowList() { 134 return mWindows; 135 } 136 137 Display getDisplay() { 138 return mDisplay; 139 } 140 141 DisplayInfo getDisplayInfo() { 142 return mDisplayInfo; 143 } 144 145 DisplayMetrics getDisplayMetrics() { 146 return mDisplayMetrics; 147 } 148 149 DockedStackDividerController getDockedDividerController() { 150 return mDividerControllerLocked; 151 } 152 153 /** 154 * Returns true if the specified UID has access to this display. 155 */ 156 public boolean hasAccess(int uid) { 157 return mDisplay.hasAccess(uid); 158 } 159 160 public boolean isPrivate() { 161 return (mDisplay.getFlags() & Display.FLAG_PRIVATE) != 0; 162 } 163 164 ArrayList<TaskStack> getStacks() { 165 return mStacks; 166 } 167 168 /** 169 * Retrieve the tasks on this display in stack order from the bottommost TaskStack up. 170 * @return All the Tasks, in order, on this display. 171 */ 172 ArrayList<Task> getTasks() { 173 mTmpTaskHistory.clear(); 174 final int numStacks = mStacks.size(); 175 for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { 176 mTmpTaskHistory.addAll(mStacks.get(stackNdx).getTasks()); 177 } 178 return mTmpTaskHistory; 179 } 180 181 TaskStack getHomeStack() { 182 if (mHomeStack == null && mDisplayId == Display.DEFAULT_DISPLAY) { 183 Slog.e(TAG, "getHomeStack: Returning null from this=" + this); 184 } 185 return mHomeStack; 186 } 187 188 void updateDisplayInfo() { 189 mDisplay.getDisplayInfo(mDisplayInfo); 190 mDisplay.getMetrics(mDisplayMetrics); 191 for (int i = mStacks.size() - 1; i >= 0; --i) { 192 mStacks.get(i).updateDisplayInfo(null); 193 } 194 } 195 196 void initializeDisplayBaseInfo() { 197 // Bootstrap the default logical display from the display manager. 198 final DisplayInfo newDisplayInfo = 199 mService.mDisplayManagerInternal.getDisplayInfo(mDisplayId); 200 if (newDisplayInfo != null) { 201 mDisplayInfo.copyFrom(newDisplayInfo); 202 } 203 mBaseDisplayWidth = mInitialDisplayWidth = mDisplayInfo.logicalWidth; 204 mBaseDisplayHeight = mInitialDisplayHeight = mDisplayInfo.logicalHeight; 205 mBaseDisplayDensity = mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi; 206 mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight); 207 } 208 209 void getLogicalDisplayRect(Rect out) { 210 // Uses same calculation as in LogicalDisplay#configureDisplayInTransactionLocked. 211 final int orientation = mDisplayInfo.rotation; 212 boolean rotated = (orientation == Surface.ROTATION_90 213 || orientation == Surface.ROTATION_270); 214 final int physWidth = rotated ? mBaseDisplayHeight : mBaseDisplayWidth; 215 final int physHeight = rotated ? mBaseDisplayWidth : mBaseDisplayHeight; 216 int width = mDisplayInfo.logicalWidth; 217 int left = (physWidth - width) / 2; 218 int height = mDisplayInfo.logicalHeight; 219 int top = (physHeight - height) / 2; 220 out.set(left, top, left + width, top + height); 221 } 222 223 /** Refer to {@link WindowManagerService#attachStack(int, int, boolean)} */ 224 void attachStack(TaskStack stack, boolean onTop) { 225 if (stack.mStackId == HOME_STACK_ID) { 226 if (mHomeStack != null) { 227 throw new IllegalArgumentException("attachStack: HOME_STACK_ID (0) not first."); 228 } 229 mHomeStack = stack; 230 } 231 if (onTop) { 232 mStacks.add(stack); 233 } else { 234 mStacks.add(0, stack); 235 } 236 layoutNeeded = true; 237 } 238 239 void moveStack(TaskStack stack, boolean toTop) { 240 if (stack.mStackId == PINNED_STACK_ID && !toTop) { 241 // Pinned stack is always-on-top silly... 242 Slog.w(TAG, "Ignoring move of always-on-top stack=" + stack + " to bottom"); 243 return; 244 } 245 246 if (!mStacks.remove(stack)) { 247 Slog.wtf(TAG, "moving stack that was not added: " + stack, new Throwable()); 248 } 249 250 int addIndex = toTop ? mStacks.size() : 0; 251 252 if (toTop 253 && mService.isStackVisibleLocked(PINNED_STACK_ID) 254 && stack.mStackId != PINNED_STACK_ID) { 255 // The pinned stack is always the top most stack (always-on-top) when it is visible. 256 // So, stack is moved just below the pinned stack. 257 addIndex--; 258 TaskStack topStack = mStacks.get(addIndex); 259 if (topStack.mStackId != PINNED_STACK_ID) { 260 throw new IllegalStateException("Pinned stack isn't top stack??? " + mStacks); 261 } 262 } 263 mStacks.add(addIndex, stack); 264 } 265 266 void detachStack(TaskStack stack) { 267 mDimLayerController.removeDimLayerUser(stack); 268 mStacks.remove(stack); 269 } 270 271 /** 272 * Propagate the new bounds to all child stacks. 273 * @param contentRect The bounds to apply at the top level. 274 */ 275 void resize(Rect contentRect) { 276 mContentRect.set(contentRect); 277 } 278 279 int taskIdFromPoint(int x, int y) { 280 for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { 281 TaskStack stack = mStacks.get(stackNdx); 282 stack.getBounds(mTmpRect); 283 if (!mTmpRect.contains(x, y)) { 284 continue; 285 } 286 final ArrayList<Task> tasks = stack.getTasks(); 287 for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { 288 final Task task = tasks.get(taskNdx); 289 final WindowState win = task.getTopVisibleAppMainWindow(); 290 if (win == null) { 291 continue; 292 } 293 // We need to use the task's dim bounds (which is derived from the visible 294 // bounds of its apps windows) for any touch-related tests. Can't use 295 // the task's original bounds because it might be adjusted to fit the 296 // content frame. For example, the presence of the IME adjusting the 297 // windows frames when the app window is the IME target. 298 task.getDimBounds(mTmpRect); 299 if (mTmpRect.contains(x, y)) { 300 return task.mTaskId; 301 } 302 } 303 } 304 return -1; 305 } 306 307 /** 308 * Find the task whose outside touch area (for resizing) (x, y) falls within. 309 * Returns null if the touch doesn't fall into a resizing area. 310 */ 311 Task findTaskForControlPoint(int x, int y) { 312 final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics); 313 for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { 314 TaskStack stack = mStacks.get(stackNdx); 315 if (!StackId.isTaskResizeAllowed(stack.mStackId)) { 316 break; 317 } 318 final ArrayList<Task> tasks = stack.getTasks(); 319 for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { 320 final Task task = tasks.get(taskNdx); 321 if (task.isFullscreen()) { 322 return null; 323 } 324 325 // We need to use the task's dim bounds (which is derived from the visible 326 // bounds of its apps windows) for any touch-related tests. Can't use 327 // the task's original bounds because it might be adjusted to fit the 328 // content frame. One example is when the task is put to top-left quadrant, 329 // the actual visible area would not start at (0,0) after it's adjusted 330 // for the status bar. 331 task.getDimBounds(mTmpRect); 332 mTmpRect.inset(-delta, -delta); 333 if (mTmpRect.contains(x, y)) { 334 mTmpRect.inset(delta, delta); 335 if (!mTmpRect.contains(x, y)) { 336 return task; 337 } 338 // User touched inside the task. No need to look further, 339 // focus transfer will be handled in ACTION_UP. 340 return null; 341 } 342 } 343 } 344 return null; 345 } 346 347 void setTouchExcludeRegion(Task focusedTask) { 348 mTouchExcludeRegion.set(mBaseDisplayRect); 349 final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics); 350 boolean addBackFocusedTask = false; 351 mNonResizeableRegion.setEmpty(); 352 for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { 353 TaskStack stack = mStacks.get(stackNdx); 354 final ArrayList<Task> tasks = stack.getTasks(); 355 for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { 356 final Task task = tasks.get(taskNdx); 357 final WindowState win = task.getTopVisibleAppMainWindow(); 358 if (win == null) { 359 continue; 360 } 361 362 /** 363 * Exclusion region is the region that TapDetector doesn't care about. 364 * Here we want to remove all non-focused tasks from the exclusion region. 365 * We also remove the outside touch area for resizing for all freeform 366 * tasks (including the focused). 367 * 368 * (For freeform focused task, the below logic will first remove the enlarged 369 * area, then add back the inner area.) 370 */ 371 final boolean isFreeformed = task.inFreeformWorkspace(); 372 if (task != focusedTask || isFreeformed) { 373 task.getDimBounds(mTmpRect); 374 if (isFreeformed) { 375 // If we're removing a freeform, focused app from the exclusion region, 376 // we need to add back its touchable frame later. Remember the touchable 377 // frame now. 378 if (task == focusedTask) { 379 addBackFocusedTask = true; 380 mTmpRect2.set(mTmpRect); 381 } 382 // If the task is freeformed, enlarge the area to account for outside 383 // touch area for resize. 384 mTmpRect.inset(-delta, -delta); 385 // Intersect with display content rect. If we have system decor (status bar/ 386 // navigation bar), we want to exclude that from the tap detection. 387 // Otherwise, if the app is partially placed under some system button (eg. 388 // Recents, Home), pressing that button would cause a full series of 389 // unwanted transfer focus/resume/pause, before we could go home. 390 mTmpRect.intersect(mContentRect); 391 } 392 mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE); 393 } 394 if (task.isDockedInEffect() && !task.isResizeable()) { 395 stack.getBounds(mTmpRect); 396 mNonResizeableRegion.op(mTmpRect, Region.Op.UNION); 397 break; 398 } 399 } 400 } 401 // If we removed the focused task above, add it back and only leave its 402 // outside touch area in the exclusion. TapDectector is not interested in 403 // any touch inside the focused task itself. 404 if (addBackFocusedTask) { 405 mTouchExcludeRegion.op(mTmpRect2, Region.Op.UNION); 406 } 407 final WindowState inputMethod = mService.mInputMethodWindow; 408 if (inputMethod != null && inputMethod.isVisibleLw()) { 409 // If the input method is visible and the user is typing, we don't want these touch 410 // events to be intercepted and used to change focus. This would likely cause a 411 // disappearance of the input method. 412 inputMethod.getTouchableRegion(mTmpRegion); 413 mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION); 414 } 415 if (mTapDetector != null) { 416 mTapDetector.setTouchExcludeRegion(mTouchExcludeRegion, mNonResizeableRegion); 417 } 418 } 419 420 void switchUserStacks() { 421 final WindowList windows = getWindowList(); 422 for (int i = 0; i < windows.size(); i++) { 423 final WindowState win = windows.get(i); 424 if (win.isHiddenFromUserLocked()) { 425 if (DEBUG_VISIBILITY) Slog.w(TAG, "user changing, hiding " + win 426 + ", attrs=" + win.mAttrs.type + ", belonging to " + win.mOwnerUid); 427 win.hideLw(false); 428 } 429 } 430 431 for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { 432 mStacks.get(stackNdx).switchUser(); 433 } 434 } 435 436 void resetAnimationBackgroundAnimator() { 437 for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { 438 mStacks.get(stackNdx).resetAnimationBackgroundAnimator(); 439 } 440 } 441 442 boolean animateDimLayers() { 443 return mDimLayerController.animateDimLayers(); 444 } 445 446 void resetDimming() { 447 mDimLayerController.resetDimming(); 448 } 449 450 boolean isDimming() { 451 return mDimLayerController.isDimming(); 452 } 453 454 void stopDimmingIfNeeded() { 455 mDimLayerController.stopDimmingIfNeeded(); 456 } 457 458 void close() { 459 mDimLayerController.close(); 460 for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { 461 mStacks.get(stackNdx).close(); 462 } 463 } 464 465 boolean isAnimating() { 466 for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { 467 final TaskStack stack = mStacks.get(stackNdx); 468 if (stack.isAnimating()) { 469 return true; 470 } 471 } 472 return false; 473 } 474 475 void checkForDeferredActions() { 476 boolean animating = false; 477 for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { 478 final TaskStack stack = mStacks.get(stackNdx); 479 if (stack.isAnimating()) { 480 animating = true; 481 } else { 482 if (stack.mDeferDetach) { 483 mService.detachStackLocked(this, stack); 484 } 485 final ArrayList<Task> tasks = stack.getTasks(); 486 for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { 487 final Task task = tasks.get(taskNdx); 488 AppTokenList tokens = task.mAppTokens; 489 for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { 490 AppWindowToken wtoken = tokens.get(tokenNdx); 491 if (wtoken.mIsExiting) { 492 wtoken.removeAppFromTaskLocked(); 493 } 494 } 495 } 496 } 497 } 498 if (!animating && mDeferredRemoval) { 499 mService.onDisplayRemoved(mDisplayId); 500 } 501 } 502 503 void rotateBounds(int oldRotation, int newRotation, Rect bounds) { 504 final int rotationDelta = DisplayContent.deltaRotation(oldRotation, newRotation); 505 getLogicalDisplayRect(mTmpRect); 506 switch (rotationDelta) { 507 case Surface.ROTATION_0: 508 mTmpRect2.set(bounds); 509 break; 510 case Surface.ROTATION_90: 511 mTmpRect2.top = mTmpRect.bottom - bounds.right; 512 mTmpRect2.left = bounds.top; 513 mTmpRect2.right = mTmpRect2.left + bounds.height(); 514 mTmpRect2.bottom = mTmpRect2.top + bounds.width(); 515 break; 516 case Surface.ROTATION_180: 517 mTmpRect2.top = mTmpRect.bottom - bounds.bottom; 518 mTmpRect2.left = mTmpRect.right - bounds.right; 519 mTmpRect2.right = mTmpRect2.left + bounds.width(); 520 mTmpRect2.bottom = mTmpRect2.top + bounds.height(); 521 break; 522 case Surface.ROTATION_270: 523 mTmpRect2.top = bounds.left; 524 mTmpRect2.left = mTmpRect.right - bounds.bottom; 525 mTmpRect2.right = mTmpRect2.left + bounds.height(); 526 mTmpRect2.bottom = mTmpRect2.top + bounds.width(); 527 break; 528 } 529 bounds.set(mTmpRect2); 530 } 531 532 static int deltaRotation(int oldRotation, int newRotation) { 533 int delta = newRotation - oldRotation; 534 if (delta < 0) delta += 4; 535 return delta; 536 } 537 538 public void dump(String prefix, PrintWriter pw) { 539 pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId); 540 final String subPrefix = " " + prefix; 541 pw.print(subPrefix); pw.print("init="); pw.print(mInitialDisplayWidth); pw.print("x"); 542 pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity); 543 pw.print("dpi"); 544 if (mInitialDisplayWidth != mBaseDisplayWidth 545 || mInitialDisplayHeight != mBaseDisplayHeight 546 || mInitialDisplayDensity != mBaseDisplayDensity) { 547 pw.print(" base="); 548 pw.print(mBaseDisplayWidth); pw.print("x"); pw.print(mBaseDisplayHeight); 549 pw.print(" "); pw.print(mBaseDisplayDensity); pw.print("dpi"); 550 } 551 if (mDisplayScalingDisabled) { 552 pw.println(" noscale"); 553 } 554 pw.print(" cur="); 555 pw.print(mDisplayInfo.logicalWidth); 556 pw.print("x"); pw.print(mDisplayInfo.logicalHeight); 557 pw.print(" app="); 558 pw.print(mDisplayInfo.appWidth); 559 pw.print("x"); pw.print(mDisplayInfo.appHeight); 560 pw.print(" rng="); pw.print(mDisplayInfo.smallestNominalAppWidth); 561 pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight); 562 pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth); 563 pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight); 564 pw.print(subPrefix); pw.print("deferred="); pw.print(mDeferredRemoval); 565 pw.print(" layoutNeeded="); pw.println(layoutNeeded); 566 for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { 567 final TaskStack stack = mStacks.get(stackNdx); 568 pw.print(prefix); pw.print("mStacks[" + stackNdx + "]"); pw.println(stack.mStackId); 569 stack.dump(prefix + " ", pw); 570 } 571 pw.println(); 572 pw.println(" Application tokens in top down Z order:"); 573 int ndx = 0; 574 for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { 575 final TaskStack stack = mStacks.get(stackNdx); 576 pw.print(" mStackId="); pw.println(stack.mStackId); 577 ArrayList<Task> tasks = stack.getTasks(); 578 for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { 579 final Task task = tasks.get(taskNdx); 580 pw.print(" mTaskId="); pw.println(task.mTaskId); 581 AppTokenList tokens = task.mAppTokens; 582 for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx, ++ndx) { 583 final AppWindowToken wtoken = tokens.get(tokenNdx); 584 pw.print(" Activity #"); pw.print(tokenNdx); 585 pw.print(' '); pw.print(wtoken); pw.println(":"); 586 wtoken.dump(pw, " "); 587 } 588 } 589 } 590 if (ndx == 0) { 591 pw.println(" None"); 592 } 593 pw.println(); 594 if (!mExitingTokens.isEmpty()) { 595 pw.println(); 596 pw.println(" Exiting tokens:"); 597 for (int i=mExitingTokens.size()-1; i>=0; i--) { 598 WindowToken token = mExitingTokens.get(i); 599 pw.print(" Exiting #"); pw.print(i); 600 pw.print(' '); pw.print(token); 601 pw.println(':'); 602 token.dump(pw, " "); 603 } 604 } 605 pw.println(); 606 mDimLayerController.dump(prefix + " ", pw); 607 } 608 609 @Override 610 public String toString() { 611 return "Display " + mDisplayId + " info=" + mDisplayInfo + " stacks=" + mStacks; 612 } 613 614 TaskStack getDockedStackLocked() { 615 final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID); 616 return (stack != null && stack.isVisibleLocked()) ? stack : null; 617 } 618} 619