TaskStack.java revision 7998e48ce3240d09f28cd365fb6ba58f8987470f
1/* 2 * Copyright (C) 2013 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 android.app.ActivityManager.StackId; 20import android.content.res.Configuration; 21import android.graphics.Rect; 22import android.os.Debug; 23import android.os.RemoteException; 24import android.util.EventLog; 25import android.util.Slog; 26import android.util.SparseArray; 27import android.view.DisplayInfo; 28import android.view.Surface; 29 30import com.android.internal.policy.DividerSnapAlgorithm; 31import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; 32import com.android.internal.policy.DockedDividerUtils; 33import com.android.server.EventLogTags; 34 35import java.io.PrintWriter; 36import java.util.ArrayList; 37 38import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; 39import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; 40import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; 41import static android.app.ActivityManager.StackId.PINNED_STACK_ID; 42import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 43import static android.view.WindowManager.DOCKED_BOTTOM; 44import static android.view.WindowManager.DOCKED_INVALID; 45import static android.view.WindowManager.DOCKED_LEFT; 46import static android.view.WindowManager.DOCKED_RIGHT; 47import static android.view.WindowManager.DOCKED_TOP; 48import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT; 49import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK; 50import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 51 52public class TaskStack implements DimLayer.DimLayerUser, 53 BoundsAnimationController.AnimateBoundsUser { 54 55 // If the stack should be resized to fullscreen. 56 private static final boolean FULLSCREEN = true; 57 58 /** Unique identifier */ 59 final int mStackId; 60 61 /** The service */ 62 private final WindowManagerService mService; 63 64 /** The display this stack sits under. */ 65 private DisplayContent mDisplayContent; 66 67 /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match 68 * mTaskHistory in the ActivityStack with the same mStackId */ 69 private final ArrayList<Task> mTasks = new ArrayList<>(); 70 71 /** For comparison with DisplayContent bounds. */ 72 private Rect mTmpRect = new Rect(); 73 private Rect mTmpRect2 = new Rect(); 74 75 /** Content limits relative to the DisplayContent this sits in. */ 76 private Rect mBounds = new Rect(); 77 78 /** Screen content area excluding IM windows, etc. */ 79 private final Rect mContentBounds = new Rect(); 80 81 /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */ 82 private final Rect mAdjustedBounds = new Rect(); 83 84 /** Whether mBounds is fullscreen */ 85 private boolean mFullscreen = true; 86 87 // Device rotation as of the last time {@link #mBounds} was set. 88 int mRotation; 89 90 /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */ 91 DimLayer mAnimationBackgroundSurface; 92 93 /** The particular window with an Animation with non-zero background color. */ 94 WindowStateAnimator mAnimationBackgroundAnimator; 95 96 /** Application tokens that are exiting, but still on screen for animations. */ 97 final AppTokenList mExitingAppTokens = new AppTokenList(); 98 99 /** Detach this stack from its display when animation completes. */ 100 boolean mDeferDetach; 101 private boolean mUpdateBoundsAfterRotation = false; 102 103 // Whether the stack and all its tasks is currently being drag-resized 104 private boolean mDragResizing; 105 106 TaskStack(WindowManagerService service, int stackId) { 107 mService = service; 108 mStackId = stackId; 109 EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId); 110 } 111 112 DisplayContent getDisplayContent() { 113 return mDisplayContent; 114 } 115 116 ArrayList<Task> getTasks() { 117 return mTasks; 118 } 119 120 /** 121 * Set the bounds of the stack and its containing tasks. 122 * @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen. 123 * @param configs Configuration for individual tasks, keyed by task id. 124 * @param taskBounds Bounds for individual tasks, keyed by task id. 125 * @return True if the stack bounds was changed. 126 * */ 127 boolean setBounds( 128 Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds, 129 SparseArray<Rect> taskTempInsetBounds) { 130 if (!setBounds(stackBounds)) { 131 return false; 132 } 133 134 // Update bounds of containing tasks. 135 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 136 final Task task = mTasks.get(taskNdx); 137 Configuration config = configs.get(task.mTaskId); 138 if (config != null) { 139 Rect bounds = taskBounds.get(task.mTaskId); 140 if (task.isTwoFingerScrollMode()) { 141 // This is a non-resizeable task that's docked (or side-by-side to the docked 142 // stack). It might have been scrolled previously, and after the stack resizing, 143 // it might no longer fully cover the stack area. 144 // Save the old bounds and re-apply the scroll. This adjusts the bounds to 145 // fit the new stack bounds. 146 task.resizeLocked(bounds, config, false /* forced */); 147 task.getBounds(mTmpRect); 148 task.scrollLocked(mTmpRect); 149 } else { 150 task.resizeLocked(bounds, config, false /* forced */); 151 task.setTempInsetBounds( 152 taskTempInsetBounds != null ? taskTempInsetBounds.get(task.mTaskId) 153 : null); 154 } 155 } else { 156 Slog.wtf(TAG_WM, "No config for task: " + task + ", is there a mismatch with AM?"); 157 } 158 } 159 return true; 160 } 161 162 void prepareFreezingTaskBounds() { 163 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 164 final Task task = mTasks.get(taskNdx); 165 task.prepareFreezingBounds(); 166 } 167 } 168 169 boolean isFullscreenBounds(Rect bounds) { 170 if (mDisplayContent == null || bounds == null) { 171 return true; 172 } 173 mDisplayContent.getLogicalDisplayRect(mTmpRect); 174 return mTmpRect.equals(bounds); 175 } 176 177 void alignTasksToAdjustedBounds(final Rect adjustedBounds) { 178 if (mFullscreen) { 179 return; 180 } 181 // Update bounds of containing tasks. 182 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 183 final Task task = mTasks.get(taskNdx); 184 if (task.isTwoFingerScrollMode()) { 185 // If we're scrolling we don't care about your bounds or configs, 186 // they should be null as if we were in fullscreen. 187 task.resizeLocked(null, null, false /* forced */); 188 task.getBounds(mTmpRect2); 189 task.scrollLocked(mTmpRect2); 190 } else if (task.isResizeable()) { 191 task.getBounds(mTmpRect2); 192 mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top); 193 task.resizeLocked(mTmpRect2, task.mOverrideConfig, false /* forced */); 194 } 195 } 196 } 197 198 void adjustForIME(final WindowState imeWin) { 199 final int dockedSide = getDockSide(); 200 final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM; 201 final Rect adjustedBounds = mAdjustedBounds; 202 if (imeWin == null || !dockedTopOrBottom) { 203 // If mContentBounds is already empty, it means we're not applying 204 // any adjustments, so nothing to do; otherwise clear any adjustments. 205 if (!mContentBounds.isEmpty()) { 206 mContentBounds.setEmpty(); 207 adjustedBounds.set(mBounds); 208 alignTasksToAdjustedBounds(adjustedBounds); 209 } 210 return; 211 } 212 213 final Rect displayContentRect = mTmpRect; 214 final Rect contentBounds = mTmpRect2; 215 216 // Calculate the content bounds excluding the area occupied by IME 217 mDisplayContent.getContentRect(displayContentRect); 218 contentBounds.set(displayContentRect); 219 int imeTop = Math.max(imeWin.getDisplayFrameLw().top, contentBounds.top); 220 imeTop += imeWin.getGivenContentInsetsLw().top; 221 if (contentBounds.bottom > imeTop) { 222 contentBounds.bottom = imeTop; 223 } 224 225 // If content bounds not changing, nothing to do. 226 if (mContentBounds.equals(contentBounds)) { 227 return; 228 } 229 230 // Content bounds changed, need to apply adjustments depending on dock sides. 231 mContentBounds.set(contentBounds); 232 adjustedBounds.set(mBounds); 233 final int yOffset = displayContentRect.bottom - contentBounds.bottom; 234 235 if (dockedSide == DOCKED_TOP) { 236 // If this stack is docked on top, we make it smaller so the bottom stack is not 237 // occluded by IME. We shift its bottom up by the height of the IME (capped by 238 // the display content rect). Note that we don't change the task bounds. 239 adjustedBounds.bottom = Math.max( 240 adjustedBounds.bottom - yOffset, displayContentRect.top); 241 } else { 242 // If this stack is docked on bottom, we shift it up so that it's not occluded by 243 // IME. We try to move it up by the height of the IME window (although the best 244 // we could do is to make the top stack fully collapsed). 245 final int dividerWidth = mDisplayContent.mDividerControllerLocked.getContentWidth(); 246 adjustedBounds.top = Math.max( 247 adjustedBounds.top - yOffset, displayContentRect.top + dividerWidth); 248 adjustedBounds.bottom = adjustedBounds.top + mBounds.height(); 249 250 // We also move the member tasks together, taking care not to resize them. 251 // Resizing might cause relaunch, and IME window may not come back after that. 252 alignTasksToAdjustedBounds(adjustedBounds); 253 } 254 } 255 256 private boolean setBounds(Rect bounds) { 257 boolean oldFullscreen = mFullscreen; 258 int rotation = Surface.ROTATION_0; 259 if (mDisplayContent != null) { 260 mDisplayContent.getLogicalDisplayRect(mTmpRect); 261 rotation = mDisplayContent.getDisplayInfo().rotation; 262 mFullscreen = bounds == null; 263 if (mFullscreen) { 264 bounds = mTmpRect; 265 } 266 } 267 268 if (bounds == null) { 269 // Can't set to fullscreen if we don't have a display to get bounds from... 270 return false; 271 } 272 if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) { 273 return false; 274 } 275 276 if (mDisplayContent != null) { 277 mDisplayContent.mDimLayerController.updateDimLayer(this); 278 mAnimationBackgroundSurface.setBounds(bounds); 279 } 280 281 mBounds.set(bounds); 282 mRotation = rotation; 283 284 // Clear the adjusted content bounds as they're no longer valid. 285 // If IME is still visible, these will be re-applied. 286 // Note that we don't clear mContentBounds here, so that we know the last IME 287 // adjust we applied. 288 // If user starts dragging the dock divider while IME is visible, the new bounds 289 // we received are based on the actual screen location of the divider. It already 290 // accounted for the IME window, so we don't want to adjust again. 291 mAdjustedBounds.set(mBounds); 292 293 return true; 294 } 295 296 /** Bounds of the stack without adjusting for other factors in the system like visibility 297 * of docked stack. 298 * Most callers should be using {@link #getBounds} as it take into consideration other system 299 * factors. */ 300 void getRawBounds(Rect out) { 301 out.set(mBounds); 302 } 303 304 /** Return true if the current bound can get outputted to the rest of the system as-is. */ 305 private boolean useCurrentBounds() { 306 if (mFullscreen 307 || !StackId.isResizeableByDockedStack(mStackId) 308 || mDisplayContent == null 309 || mDisplayContent.getDockedStackLocked() != null) { 310 return true; 311 } 312 return false; 313 } 314 315 public void getBounds(Rect out) { 316 if (useCurrentBounds()) { 317 // If we're currently adjusting for IME, we use the adjusted bounds; otherwise, 318 // no need to adjust the output bounds if fullscreen or the docked stack is visible 319 // since it is already what we want to represent to the rest of the system. 320 if (!mContentBounds.isEmpty()) { 321 out.set(mAdjustedBounds); 322 } else { 323 out.set(mBounds); 324 } 325 return; 326 } 327 328 // The bounds has been adjusted to accommodate for a docked stack, but the docked stack 329 // is not currently visible. Go ahead a represent it as fullscreen to the rest of the 330 // system. 331 mDisplayContent.getLogicalDisplayRect(out); 332 } 333 334 /** Bounds of the stack with other system factors taken into consideration. */ 335 @Override 336 public void getDimBounds(Rect out) { 337 getBounds(out); 338 } 339 340 void updateDisplayInfo(Rect bounds) { 341 mUpdateBoundsAfterRotation = false; 342 if (mDisplayContent != null) { 343 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 344 mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent); 345 } 346 if (bounds != null) { 347 setBounds(bounds); 348 } else if (mFullscreen) { 349 setBounds(null); 350 } else { 351 mUpdateBoundsAfterRotation = true; 352 mTmpRect2.set(mBounds); 353 final int newRotation = mDisplayContent.getDisplayInfo().rotation; 354 if (mRotation == newRotation) { 355 setBounds(mTmpRect2); 356 } 357 358 // If the rotation changes, we'll handle it in updateBoundsAfterRotation 359 } 360 } 361 } 362 363 /** 364 * Updates the bounds after rotating the screen. We can't handle it in 365 * {@link #updateDisplayInfo} because at that point the configuration might not be fully updated 366 * yet. 367 */ 368 void updateBoundsAfterRotation() { 369 if (!mUpdateBoundsAfterRotation) { 370 return; 371 } 372 mUpdateBoundsAfterRotation = false; 373 final int newRotation = getDisplayInfo().rotation; 374 mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2); 375 if (mStackId == DOCKED_STACK_ID) { 376 snapDockedStackAfterRotation(mTmpRect2); 377 } 378 379 // Post message to inform activity manager of the bounds change simulating 380 // a one-way call. We do this to prevent a deadlock between window manager 381 // lock and activity manager lock been held. 382 mService.mH.sendMessage(mService.mH.obtainMessage( 383 RESIZE_STACK, mStackId, 0 /*allowResizeInDockedMode*/, mTmpRect2)); 384 } 385 386 /** 387 * Snaps the bounds after rotation to the closest snap target for the docked stack. 388 */ 389 private void snapDockedStackAfterRotation(Rect outBounds) { 390 391 // Calculate the current position. 392 final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo(); 393 final int dividerSize = mService.getDefaultDisplayContentLocked() 394 .getDockedDividerController().getContentWidth(); 395 final int dockSide = getDockSide(outBounds); 396 final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds, 397 dockSide, dividerSize); 398 final int displayWidth = mDisplayContent.getDisplayInfo().logicalWidth; 399 final int displayHeight = mDisplayContent.getDisplayInfo().logicalHeight; 400 401 // Snap the position to a target. 402 final int rotation = displayInfo.rotation; 403 final int orientation = mService.mCurConfiguration.orientation; 404 mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight, outBounds); 405 final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm( 406 mService.mContext.getResources(), displayWidth, displayHeight, 407 dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds); 408 final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition); 409 410 // Recalculate the bounds based on the position of the target. 411 DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide, 412 outBounds, displayInfo.logicalWidth, displayInfo.logicalHeight, 413 dividerSize); 414 } 415 416 boolean isAnimating() { 417 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 418 final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens; 419 for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { 420 final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows; 421 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { 422 final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator; 423 if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) { 424 return true; 425 } 426 } 427 } 428 } 429 return false; 430 } 431 432 void addTask(Task task, boolean toTop) { 433 addTask(task, toTop, task.showForAllUsers()); 434 } 435 436 /** 437 * Put a Task in this stack. Used for adding and moving. 438 * @param task The task to add. 439 * @param toTop Whether to add it to the top or bottom. 440 * @param showForAllUsers Whether to show the task regardless of the current user. 441 */ 442 void addTask(Task task, boolean toTop, boolean showForAllUsers) { 443 positionTask(task, toTop ? mTasks.size() : 0, showForAllUsers); 444 } 445 446 void positionTask(Task task, int position, boolean showForAllUsers) { 447 final boolean canShowTask = 448 showForAllUsers || mService.isCurrentProfileLocked(task.mUserId); 449 mTasks.remove(task); 450 int stackSize = mTasks.size(); 451 int minPosition = 0; 452 int maxPosition = stackSize; 453 454 if (canShowTask) { 455 minPosition = computeMinPosition(minPosition, stackSize); 456 } else { 457 maxPosition = computeMaxPosition(maxPosition); 458 } 459 // Reset position based on minimum/maximum possible positions. 460 position = Math.min(Math.max(position, minPosition), maxPosition); 461 462 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, 463 "positionTask: task=" + task + " position=" + position); 464 mTasks.add(position, task); 465 466 // If we are moving the task across stacks, the scroll is no longer valid. 467 if (task.mStack != this) { 468 task.resetScrollLocked(); 469 } 470 task.mStack = this; 471 task.updateDisplayInfo(mDisplayContent); 472 boolean toTop = position == mTasks.size() - 1; 473 if (toTop) { 474 mDisplayContent.moveStack(this, true); 475 } 476 EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, position); 477 } 478 479 /** Calculate the minimum possible position for a task that can be shown to the user. 480 * The minimum position will be above all other tasks that can't be shown. 481 * @param minPosition The minimum position the caller is suggesting. 482 * We will start adjusting up from here. 483 * @param size The size of the current task list. 484 */ 485 private int computeMinPosition(int minPosition, int size) { 486 while (minPosition < size) { 487 final Task tmpTask = mTasks.get(minPosition); 488 final boolean canShowTmpTask = 489 tmpTask.showForAllUsers() 490 || mService.isCurrentProfileLocked(tmpTask.mUserId); 491 if (canShowTmpTask) { 492 break; 493 } 494 minPosition++; 495 } 496 return minPosition; 497 } 498 499 /** Calculate the maximum possible position for a task that can't be shown to the user. 500 * The maximum position will be below all other tasks that can be shown. 501 * @param maxPosition The maximum position the caller is suggesting. 502 * We will start adjusting down from here. 503 */ 504 private int computeMaxPosition(int maxPosition) { 505 while (maxPosition > 0) { 506 final Task tmpTask = mTasks.get(maxPosition - 1); 507 final boolean canShowTmpTask = 508 tmpTask.showForAllUsers() 509 || mService.isCurrentProfileLocked(tmpTask.mUserId); 510 if (!canShowTmpTask) { 511 break; 512 } 513 maxPosition--; 514 } 515 return maxPosition; 516 } 517 518 void moveTaskToTop(Task task) { 519 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "moveTaskToTop: task=" + task + " Callers=" 520 + Debug.getCallers(6)); 521 mTasks.remove(task); 522 addTask(task, true); 523 } 524 525 void moveTaskToBottom(Task task) { 526 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "moveTaskToBottom: task=" + task); 527 mTasks.remove(task); 528 addTask(task, false); 529 } 530 531 /** 532 * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the 533 * back. 534 * @param task The Task to delete. 535 */ 536 void removeTask(Task task) { 537 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "removeTask: task=" + task); 538 mTasks.remove(task); 539 if (mDisplayContent != null) { 540 if (mTasks.isEmpty()) { 541 mDisplayContent.moveStack(this, false); 542 } 543 mDisplayContent.layoutNeeded = true; 544 } 545 for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) { 546 final AppWindowToken wtoken = mExitingAppTokens.get(appNdx); 547 if (wtoken.mTask == task) { 548 wtoken.mIsExiting = false; 549 mExitingAppTokens.remove(appNdx); 550 } 551 } 552 } 553 554 void attachDisplayContent(DisplayContent displayContent) { 555 if (mDisplayContent != null) { 556 throw new IllegalStateException("attachDisplayContent: Already attached"); 557 } 558 559 mDisplayContent = displayContent; 560 mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId()); 561 562 Rect bounds = null; 563 final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID); 564 if (mStackId == DOCKED_STACK_ID 565 || (dockedStack != null && StackId.isResizeableByDockedStack(mStackId))) { 566 // The existence of a docked stack affects the size of other static stack created since 567 // the docked stack occupies a dedicated region on screen. 568 bounds = new Rect(); 569 displayContent.getLogicalDisplayRect(mTmpRect); 570 mTmpRect2.setEmpty(); 571 if (dockedStack != null) { 572 dockedStack.getRawBounds(mTmpRect2); 573 } 574 final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode 575 == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; 576 getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2, 577 mDisplayContent.mDividerControllerLocked.getContentWidth(), 578 dockedOnTopOrLeft); 579 } 580 581 updateDisplayInfo(bounds); 582 583 if (mStackId == DOCKED_STACK_ID) { 584 // Attaching a docked stack to the display affects the size of all other static 585 // stacks since the docked stack occupies a dedicated region on screen. 586 // Resize existing static stacks so they are pushed to the side of the docked stack. 587 resizeNonDockedStacks(!FULLSCREEN, mBounds); 588 } 589 } 590 591 void getStackDockedModeBoundsLocked(Rect outBounds, boolean ignoreVisibility) { 592 if ((mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) 593 || mDisplayContent == null) { 594 outBounds.set(mBounds); 595 return; 596 } 597 598 final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID); 599 if (dockedStack == null) { 600 // Not sure why you are calling this method when there is no docked stack... 601 throw new IllegalStateException( 602 "Calling getStackDockedModeBoundsLocked() when there is no docked stack."); 603 } 604 if (!ignoreVisibility && !dockedStack.isVisibleLocked()) { 605 // The docked stack is being dismissed, but we caught before it finished being 606 // dismissed. In that case we want to treat it as if it is not occupying any space and 607 // let others occupy the whole display. 608 mDisplayContent.getLogicalDisplayRect(outBounds); 609 return; 610 } 611 612 final int dockedSide = dockedStack.getDockSide(); 613 if (dockedSide == DOCKED_INVALID) { 614 // Not sure how you got here...Only thing we can do is return current bounds. 615 Slog.e(TAG_WM, "Failed to get valid docked side for docked stack=" + dockedStack); 616 outBounds.set(mBounds); 617 return; 618 } 619 620 mDisplayContent.getLogicalDisplayRect(mTmpRect); 621 dockedStack.getRawBounds(mTmpRect2); 622 final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT; 623 getStackDockedModeBounds(mTmpRect, outBounds, mStackId, mTmpRect2, 624 mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft); 625 626 } 627 628 /** 629 * Outputs the bounds a stack should be given the presence of a docked stack on the display. 630 * @param displayRect The bounds of the display the docked stack is on. 631 * @param outBounds Output bounds that should be used for the stack. 632 * @param stackId Id of stack we are calculating the bounds for. 633 * @param dockedBounds Bounds of the docked stack. 634 * @param dockDividerWidth We need to know the width of the divider make to the output bounds 635 * close to the side of the dock. 636 * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen. 637 */ 638 private void getStackDockedModeBounds( 639 Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int dockDividerWidth, 640 boolean dockOnTopOrLeft) { 641 final boolean dockedStack = stackId == DOCKED_STACK_ID; 642 final boolean splitHorizontally = displayRect.width() > displayRect.height(); 643 644 outBounds.set(displayRect); 645 if (dockedStack) { 646 if (mService.mDockedStackCreateBounds != null) { 647 outBounds.set(mService.mDockedStackCreateBounds); 648 return; 649 } 650 651 // The initial bounds of the docked stack when it is created about half the screen space 652 // and its bounds can be adjusted after that. The bounds of all other stacks are 653 // adjusted to occupy whatever screen space the docked stack isn't occupying. 654 final DisplayInfo di = mDisplayContent.getDisplayInfo(); 655 mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, 656 mTmpRect2); 657 final int position = new DividerSnapAlgorithm(mService.mContext.getResources(), 658 di.logicalWidth, 659 di.logicalHeight, 660 dockDividerWidth, 661 mService.mCurConfiguration.orientation == ORIENTATION_PORTRAIT, 662 mTmpRect2).getMiddleTarget().position; 663 664 if (dockOnTopOrLeft) { 665 if (splitHorizontally) { 666 outBounds.right = position; 667 } else { 668 outBounds.bottom = position; 669 } 670 } else { 671 if (splitHorizontally) { 672 outBounds.left = position - dockDividerWidth; 673 } else { 674 outBounds.top = position - dockDividerWidth; 675 } 676 } 677 return; 678 } 679 680 // Other stacks occupy whatever space is left by the docked stack. 681 if (!dockOnTopOrLeft) { 682 if (splitHorizontally) { 683 outBounds.right = dockedBounds.left - dockDividerWidth; 684 } else { 685 outBounds.bottom = dockedBounds.top - dockDividerWidth; 686 } 687 } else { 688 if (splitHorizontally) { 689 outBounds.left = dockedBounds.right + dockDividerWidth; 690 } else { 691 outBounds.top = dockedBounds.bottom + dockDividerWidth; 692 } 693 } 694 DockedDividerUtils.sanitizeStackBounds(outBounds); 695 } 696 697 /** Resizes all non-docked stacks in the system to either fullscreen or the appropriate size 698 * based on the presence of a docked stack. 699 * @param fullscreen If true the stacks will be resized to fullscreen, else they will be 700 * resized to the appropriate size based on the presence of a docked stack. 701 * @param dockedBounds Bounds of the docked stack. 702 */ 703 private void resizeNonDockedStacks(boolean fullscreen, Rect dockedBounds) { 704 // Not using mTmpRect because we are posting the object in a message. 705 final Rect bounds = new Rect(); 706 mDisplayContent.getLogicalDisplayRect(bounds); 707 if (!fullscreen) { 708 final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode 709 == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; 710 getStackDockedModeBounds(bounds, bounds, FULLSCREEN_WORKSPACE_STACK_ID, dockedBounds, 711 mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft); 712 } 713 714 final int count = mService.mStackIdToStack.size(); 715 for (int i = 0; i < count; i++) { 716 final TaskStack otherStack = mService.mStackIdToStack.valueAt(i); 717 final int otherStackId = otherStack.mStackId; 718 if (StackId.isResizeableByDockedStack(otherStackId) 719 && !otherStack.mBounds.equals(bounds)) { 720 mService.mH.sendMessage( 721 mService.mH.obtainMessage(RESIZE_STACK, otherStackId, 722 1 /*allowResizeInDockedMode*/, fullscreen ? null : bounds)); 723 } 724 } 725 } 726 727 void resetDockedStackToMiddle() { 728 if (mStackId != DOCKED_STACK_ID) { 729 throw new IllegalStateException("Not a docked stack=" + this); 730 } 731 732 mService.mDockedStackCreateBounds = null; 733 734 final Rect bounds = new Rect(); 735 getStackDockedModeBoundsLocked(bounds, true /*ignoreVisibility*/); 736 mService.mH.obtainMessage(RESIZE_STACK, DOCKED_STACK_ID, 737 1 /*allowResizeInDockedMode*/, bounds).sendToTarget(); 738 } 739 740 void detachDisplay() { 741 EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId); 742 743 boolean doAnotherLayoutPass = false; 744 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 745 final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens; 746 for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) { 747 final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows; 748 for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) { 749 // We are in the middle of changing the state of displays/stacks/tasks. We need 750 // to finish that, before we let layout interfere with it. 751 mService.removeWindowLocked(appWindows.get(winNdx)); 752 doAnotherLayoutPass = true; 753 } 754 } 755 } 756 if (doAnotherLayoutPass) { 757 mService.mWindowPlacerLocked.requestTraversal(); 758 } 759 760 if (mStackId == DOCKED_STACK_ID) { 761 // Docked stack was detached from the display, so we no longer need to restrict the 762 // region of the screen other static stacks occupy. Go ahead and make them fullscreen. 763 resizeNonDockedStacks(FULLSCREEN, null); 764 } 765 766 close(); 767 } 768 769 void resetAnimationBackgroundAnimator() { 770 mAnimationBackgroundAnimator = null; 771 mAnimationBackgroundSurface.hide(); 772 } 773 774 void setAnimationBackground(WindowStateAnimator winAnimator, int color) { 775 int animLayer = winAnimator.mAnimLayer; 776 if (mAnimationBackgroundAnimator == null 777 || animLayer < mAnimationBackgroundAnimator.mAnimLayer) { 778 mAnimationBackgroundAnimator = winAnimator; 779 animLayer = mService.adjustAnimationBackground(winAnimator); 780 mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM, 781 ((color >> 24) & 0xff) / 255f, 0); 782 } 783 } 784 785 void switchUser() { 786 int top = mTasks.size(); 787 for (int taskNdx = 0; taskNdx < top; ++taskNdx) { 788 Task task = mTasks.get(taskNdx); 789 if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) { 790 mTasks.remove(taskNdx); 791 mTasks.add(task); 792 --top; 793 } 794 } 795 } 796 797 void close() { 798 if (mAnimationBackgroundSurface != null) { 799 mAnimationBackgroundSurface.destroySurface(); 800 mAnimationBackgroundSurface = null; 801 } 802 mDisplayContent = null; 803 } 804 805 public void dump(String prefix, PrintWriter pw) { 806 pw.println(prefix + "mStackId=" + mStackId); 807 pw.println(prefix + "mDeferDetach=" + mDeferDetach); 808 pw.println(prefix + "mFullscreen=" + mFullscreen); 809 pw.println(prefix + "mBounds=" + mBounds.toShortString()); 810 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; taskNdx--) { 811 mTasks.get(taskNdx).dump(prefix + " ", pw); 812 } 813 if (mAnimationBackgroundSurface.isDimming()) { 814 pw.println(prefix + "mWindowAnimationBackgroundSurface:"); 815 mAnimationBackgroundSurface.printTo(prefix + " ", pw); 816 } 817 if (!mExitingAppTokens.isEmpty()) { 818 pw.println(); 819 pw.println(" Exiting application tokens:"); 820 for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) { 821 WindowToken token = mExitingAppTokens.get(i); 822 pw.print(" Exiting App #"); pw.print(i); 823 pw.print(' '); pw.print(token); 824 pw.println(':'); 825 token.dump(pw, " "); 826 } 827 } 828 } 829 830 /** Fullscreen status of the stack without adjusting for other factors in the system like 831 * visibility of docked stack. 832 * Most callers should be using {@link #isFullscreen} as it take into consideration other 833 * system factors. */ 834 boolean getRawFullscreen() { 835 return mFullscreen; 836 } 837 838 @Override 839 public boolean isFullscreen() { 840 if (useCurrentBounds()) { 841 return mFullscreen; 842 } 843 // The bounds has been adjusted to accommodate for a docked stack, but the docked stack 844 // is not currently visible. Go ahead a represent it as fullscreen to the rest of the 845 // system. 846 return true; 847 } 848 849 @Override 850 public DisplayInfo getDisplayInfo() { 851 return mDisplayContent.getDisplayInfo(); 852 } 853 854 @Override 855 public String toString() { 856 return "{stackId=" + mStackId + " tasks=" + mTasks + "}"; 857 } 858 859 @Override 860 public String toShortString() { 861 return "Stack=" + mStackId; 862 } 863 864 /** 865 * For docked workspace (or workspace that's side-by-side to the docked), provides 866 * information which side of the screen was the dock anchored. 867 */ 868 int getDockSide() { 869 return getDockSide(mBounds); 870 } 871 872 int getDockSide(Rect bounds) { 873 if (mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) { 874 return DOCKED_INVALID; 875 } 876 if (mDisplayContent == null) { 877 return DOCKED_INVALID; 878 } 879 mDisplayContent.getLogicalDisplayRect(mTmpRect); 880 final int orientation = mService.mCurConfiguration.orientation; 881 if (orientation == Configuration.ORIENTATION_PORTRAIT) { 882 // Portrait mode, docked either at the top or the bottom. 883 if (bounds.top - mTmpRect.top <= mTmpRect.bottom - bounds.bottom) { 884 return DOCKED_TOP; 885 } else { 886 return DOCKED_BOTTOM; 887 } 888 } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { 889 // Landscape mode, docked either on the left or on the right. 890 if (bounds.left - mTmpRect.left <= mTmpRect.right - bounds.right) { 891 return DOCKED_LEFT; 892 } else { 893 return DOCKED_RIGHT; 894 } 895 } else { 896 return DOCKED_INVALID; 897 } 898 } 899 900 boolean isVisibleLocked() { 901 final boolean keyguardOn = mService.mPolicy.isKeyguardShowingOrOccluded() 902 && !mService.mAnimator.mKeyguardGoingAway; 903 if (keyguardOn && !StackId.isAllowedOverLockscreen(mStackId)) { 904 // The keyguard is showing and the stack shouldn't show on top of the keyguard. 905 return false; 906 } 907 908 for (int i = mTasks.size() - 1; i >= 0; i--) { 909 Task task = mTasks.get(i); 910 for (int j = task.mAppTokens.size() - 1; j >= 0; j--) { 911 if (!task.mAppTokens.get(j).hidden) { 912 return true; 913 } 914 } 915 } 916 return false; 917 } 918 919 boolean isDragResizing() { 920 return mDragResizing; 921 } 922 923 private void setDragResizingLocked(boolean resizing) { 924 if (mDragResizing == resizing) { 925 return; 926 } 927 mDragResizing = resizing; 928 for (int i = mTasks.size() - 1; i >= 0 ; i--) { 929 mTasks.get(i).resetDragResizingChangeReported(); 930 } 931 } 932 933 @Override // AnimatesBounds 934 public boolean setSize(Rect bounds) { 935 synchronized (mService.mWindowMap) { 936 if (mDisplayContent == null) { 937 return false; 938 } 939 } 940 try { 941 mService.mActivityManager.resizeStack(mStackId, bounds, false, true, false); 942 } catch (RemoteException e) { 943 } 944 return true; 945 } 946 947 @Override // AnimatesBounds 948 public void onAnimationStart() { 949 synchronized (mService.mWindowMap) { 950 setDragResizingLocked(true); 951 } 952 } 953 954 @Override // AnimatesBounds 955 public void onAnimationEnd() { 956 synchronized (mService.mWindowMap) { 957 setDragResizingLocked(false); 958 mService.requestTraversal(); 959 } 960 if (mStackId == PINNED_STACK_ID) { 961 try { 962 mService.mActivityManager.notifyPinnedStackAnimationEnded(); 963 } catch (RemoteException e) { 964 // I don't believe you... 965 } 966 } 967 } 968 969 @Override 970 public void moveToFullscreen() { 971 try { 972 mService.mActivityManager.moveTasksToFullscreenStack(mStackId, true); 973 } catch (RemoteException e) { 974 e.printStackTrace(); 975 } 976 } 977 978 @Override 979 public void getFullScreenBounds(Rect bounds) { 980 getDisplayContent().getContentRect(bounds); 981 } 982}