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