TaskStack.java revision 49b80afaf9e71d6b5d4cab26f1459d84d1070f19
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 static android.app.ActivityManager.*; 20import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT; 21import static com.android.server.wm.WindowManagerService.TAG; 22 23import android.annotation.IntDef; 24import android.content.res.Configuration; 25import android.graphics.Rect; 26import android.os.Debug; 27import android.os.RemoteException; 28import android.util.EventLog; 29import android.util.Slog; 30import android.util.SparseArray; 31import android.view.DisplayInfo; 32 33import android.view.Surface; 34import com.android.server.EventLogTags; 35 36import java.io.PrintWriter; 37import java.lang.annotation.Retention; 38import java.lang.annotation.RetentionPolicy; 39import java.util.ArrayList; 40 41public class TaskStack implements DimLayer.DimLayerUser { 42 43 // If the stack should be resized to fullscreen. 44 private static final boolean FULLSCREEN = true; 45 46 /** Unique identifier */ 47 final int mStackId; 48 49 /** The service */ 50 private final WindowManagerService mService; 51 52 /** The display this stack sits under. */ 53 private DisplayContent mDisplayContent; 54 55 /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match 56 * mTaskHistory in the ActivityStack with the same mStackId */ 57 private final ArrayList<Task> mTasks = new ArrayList<>(); 58 59 /** For comparison with DisplayContent bounds. */ 60 private Rect mTmpRect = new Rect(); 61 private Rect TmpRect2 = new Rect(); 62 63 /** Content limits relative to the DisplayContent this sits in. */ 64 private Rect mBounds = new Rect(); 65 66 /** Whether mBounds is fullscreen */ 67 private boolean mFullscreen = true; 68 69 // Device rotation as of the last time {@link #mBounds} was set. 70 int mRotation; 71 72 /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */ 73 DimLayer mAnimationBackgroundSurface; 74 75 /** The particular window with an Animation with non-zero background color. */ 76 WindowStateAnimator mAnimationBackgroundAnimator; 77 78 /** Application tokens that are exiting, but still on screen for animations. */ 79 final AppTokenList mExitingAppTokens = new AppTokenList(); 80 81 /** Detach this stack from its display when animation completes. */ 82 boolean mDeferDetach; 83 84 static final int DOCKED_INVALID = -1; 85 static final int DOCKED_LEFT = 1; 86 static final int DOCKED_TOP = 2; 87 static final int DOCKED_RIGHT = 3; 88 static final int DOCKED_BOTTOM = 4; 89 @IntDef({ 90 DOCKED_INVALID, 91 DOCKED_LEFT, 92 DOCKED_TOP, 93 DOCKED_RIGHT, 94 DOCKED_BOTTOM}) 95 @Retention(RetentionPolicy.SOURCE) 96 @interface DockSide {} 97 98 TaskStack(WindowManagerService service, int stackId) { 99 mService = service; 100 mStackId = stackId; 101 EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId); 102 } 103 104 DisplayContent getDisplayContent() { 105 return mDisplayContent; 106 } 107 108 ArrayList<Task> getTasks() { 109 return mTasks; 110 } 111 112 void resizeWindows() { 113 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 114 mTasks.get(taskNdx).resizeWindows(); 115 } 116 } 117 118 boolean allowTaskResize() { 119 return mStackId == FREEFORM_WORKSPACE_STACK_ID 120 || mStackId == DOCKED_STACK_ID; 121 } 122 123 /** 124 * Set the bounds of the stack and its containing tasks. 125 * @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen. 126 * @param configs Configuration for individual tasks, keyed by task id. 127 * @param taskBounds Bounds for individual tasks, keyed by task id. 128 * @return True if the stack bounds was changed. 129 * */ 130 boolean setBounds( 131 Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds) { 132 if (!setBounds(stackBounds)) { 133 return false; 134 } 135 136 // Update bounds of containing tasks. 137 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 138 final Task task = mTasks.get(taskNdx); 139 Configuration config = configs.get(task.mTaskId); 140 if (config != null) { 141 Rect bounds = taskBounds.get(task.mTaskId); 142 task.setBounds(bounds, config); 143 } else { 144 Slog.wtf(TAG, "No config for task: " + task + ", is there a mismatch with AM?"); 145 } 146 } 147 return true; 148 } 149 150 private boolean setBounds(Rect bounds) { 151 boolean oldFullscreen = mFullscreen; 152 int rotation = Surface.ROTATION_0; 153 if (mDisplayContent != null) { 154 mDisplayContent.getLogicalDisplayRect(mTmpRect); 155 rotation = mDisplayContent.getDisplayInfo().rotation; 156 if (bounds == null) { 157 bounds = mTmpRect; 158 mFullscreen = true; 159 } else { 160 // ensure bounds are entirely within the display rect 161 if (!bounds.intersect(mTmpRect)) { 162 // Can't set bounds outside the containing display.. Sorry! 163 return false; 164 } 165 mFullscreen = mTmpRect.equals(bounds); 166 } 167 } 168 169 if (bounds == null) { 170 // Can't set to fullscreen if we don't have a display to get bounds from... 171 return false; 172 } 173 if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) { 174 return false; 175 } 176 177 mAnimationBackgroundSurface.setBounds(bounds); 178 mBounds.set(bounds); 179 mRotation = rotation; 180 return true; 181 } 182 183 void getBounds(Rect out) { 184 out.set(mBounds); 185 } 186 187 void updateDisplayInfo(Rect bounds) { 188 if (mDisplayContent != null) { 189 if (bounds != null) { 190 setBounds(bounds); 191 } else if (mFullscreen) { 192 setBounds(null); 193 } else { 194 TmpRect2.set(mBounds); 195 mDisplayContent.rotateBounds( 196 mRotation, mDisplayContent.getDisplayInfo().rotation, TmpRect2); 197 setBounds(TmpRect2); 198 } 199 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 200 mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent); 201 } 202 } 203 } 204 205 boolean isAnimating() { 206 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 207 final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens; 208 for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { 209 final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows; 210 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { 211 final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator; 212 if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) { 213 return true; 214 } 215 } 216 } 217 } 218 return false; 219 } 220 221 void addTask(Task task, boolean toTop) { 222 addTask(task, toTop, task.showForAllUsers()); 223 } 224 225 /** 226 * Put a Task in this stack. Used for adding and moving. 227 * @param task The task to add. 228 * @param toTop Whether to add it to the top or bottom. 229 * @param showForAllUsers Whether to show the task regardless of the current user. 230 */ 231 void addTask(Task task, boolean toTop, boolean showForAllUsers) { 232 positionTask(task, toTop ? mTasks.size() : 0, showForAllUsers); 233 } 234 235 void positionTask(Task task, int position, boolean showForAllUsers) { 236 final boolean canShowTask = 237 showForAllUsers || mService.isCurrentProfileLocked(task.mUserId); 238 mTasks.remove(task); 239 int stackSize = mTasks.size(); 240 int minPosition = 0; 241 int maxPosition = stackSize; 242 243 if (canShowTask) { 244 minPosition = computeMinPosition(minPosition, stackSize); 245 } else { 246 maxPosition = computeMaxPosition(maxPosition); 247 } 248 // Reset position based on minimum/maximum possible positions. 249 position = Math.min(Math.max(position, minPosition), maxPosition); 250 251 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, 252 "positionTask: task=" + task + " position=" + position); 253 mTasks.add(position, task); 254 255 task.mStack = this; 256 task.updateDisplayInfo(mDisplayContent); 257 boolean toTop = position == mTasks.size() - 1; 258 if (toTop) { 259 mDisplayContent.moveStack(this, true); 260 } 261 EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, position); 262 } 263 264 /** Calculate the minimum possible position for a task that can be shown to the user. 265 * The minimum position will be above all other tasks that can't be shown. 266 * @param minPosition The minimum position the caller is suggesting. 267 * We will start adjusting up from here. 268 * @param size The size of the current task list. 269 */ 270 private int computeMinPosition(int minPosition, int size) { 271 while (minPosition < size) { 272 final Task tmpTask = mTasks.get(minPosition); 273 final boolean canShowTmpTask = 274 tmpTask.showForAllUsers() 275 || mService.isCurrentProfileLocked(tmpTask.mUserId); 276 if (canShowTmpTask) { 277 break; 278 } 279 minPosition++; 280 } 281 return minPosition; 282 } 283 284 /** Calculate the maximum possible position for a task that can't be shown to the user. 285 * The maximum position will be below all other tasks that can be shown. 286 * @param maxPosition The maximum position the caller is suggesting. 287 * We will start adjusting down from here. 288 */ 289 private int computeMaxPosition(int maxPosition) { 290 while (maxPosition > 0) { 291 final Task tmpTask = mTasks.get(maxPosition - 1); 292 final boolean canShowTmpTask = 293 tmpTask.showForAllUsers() 294 || mService.isCurrentProfileLocked(tmpTask.mUserId); 295 if (!canShowTmpTask) { 296 break; 297 } 298 maxPosition--; 299 } 300 return maxPosition; 301 } 302 303 void moveTaskToTop(Task task) { 304 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers=" 305 + Debug.getCallers(6)); 306 mTasks.remove(task); 307 addTask(task, true); 308 } 309 310 void moveTaskToBottom(Task task) { 311 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task); 312 mTasks.remove(task); 313 addTask(task, false); 314 } 315 316 /** 317 * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the 318 * back. 319 * @param task The Task to delete. 320 */ 321 void removeTask(Task task) { 322 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task); 323 mTasks.remove(task); 324 if (mDisplayContent != null) { 325 if (mTasks.isEmpty()) { 326 mDisplayContent.moveStack(this, false); 327 } 328 mDisplayContent.layoutNeeded = true; 329 } 330 for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) { 331 final AppWindowToken wtoken = mExitingAppTokens.get(appNdx); 332 if (wtoken.mTask == task) { 333 wtoken.mIsExiting = false; 334 mExitingAppTokens.remove(appNdx); 335 } 336 } 337 } 338 339 void attachDisplayContent(DisplayContent displayContent) { 340 if (mDisplayContent != null) { 341 throw new IllegalStateException("attachDisplayContent: Already attached"); 342 } 343 344 mDisplayContent = displayContent; 345 mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId()); 346 347 Rect bounds = null; 348 final boolean dockedStackExists = mService.mStackIdToStack.get(DOCKED_STACK_ID) != null; 349 if (mStackId == DOCKED_STACK_ID || (dockedStackExists 350 && mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID)) { 351 // The existence of a docked stack affects the size of any static stack created since 352 // the docked stack occupies a dedicated region on screen. 353 bounds = new Rect(); 354 displayContent.getLogicalDisplayRect(mTmpRect); 355 getInitialDockedStackBounds(mTmpRect, bounds, mStackId, 356 mDisplayContent.mDividerControllerLocked.getWidth() / 2); 357 } 358 359 updateDisplayInfo(bounds); 360 361 if (mStackId == DOCKED_STACK_ID) { 362 // Attaching a docked stack to the display affects the size of all other static 363 // stacks since the docked stack occupies a dedicated region on screen. 364 // Resize existing static stacks so they are pushed to the side of the docked stack. 365 resizeNonDockedStacks(!FULLSCREEN); 366 } 367 } 368 369 /** 370 * Outputs the initial bounds a stack should be given the presence of a docked stack on the 371 * display. 372 * @param displayRect The bounds of the display the docked stack is on. 373 * @param outBounds Output bounds that should be used for the stack. 374 * @param stackId Id of stack we are calculating the bounds for. 375 * @param adjustment 376 */ 377 private static void getInitialDockedStackBounds(Rect displayRect, Rect outBounds, int stackId, 378 int adjustment) { 379 // Docked stack start off occupying half the screen space. 380 final boolean dockedStack = stackId == DOCKED_STACK_ID; 381 final boolean splitHorizontally = displayRect.width() > displayRect.height(); 382 final boolean topOrLeftCreateMode = 383 WindowManagerService.sDockedStackCreateMode == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; 384 final boolean placeTopOrLeft = (dockedStack && topOrLeftCreateMode) 385 || (!dockedStack && !topOrLeftCreateMode); 386 outBounds.set(displayRect); 387 if (placeTopOrLeft) { 388 if (splitHorizontally) { 389 outBounds.right = displayRect.centerX() - adjustment; 390 } else { 391 outBounds.bottom = displayRect.centerY() - adjustment; 392 } 393 } else { 394 if (splitHorizontally) { 395 outBounds.left = displayRect.centerX() + adjustment; 396 } else { 397 outBounds.top = displayRect.centerY() + adjustment; 398 } 399 } 400 } 401 402 /** Resizes all non-docked stacks in the system to either fullscreen or the appropriate size 403 * based on the presence of a docked stack. 404 * @param fullscreen If true the stacks will be resized to fullscreen, else they will be 405 * resized to the appropriate size based on the presence of a docked stack. 406 */ 407 private void resizeNonDockedStacks(boolean fullscreen) { 408 mDisplayContent.getLogicalDisplayRect(mTmpRect); 409 if (!fullscreen) { 410 getInitialDockedStackBounds(mTmpRect, mTmpRect, FULLSCREEN_WORKSPACE_STACK_ID, 411 mDisplayContent.mDividerControllerLocked.getWidth()); 412 } 413 414 final int count = mService.mStackIdToStack.size(); 415 for (int i = 0; i < count; i++) { 416 final TaskStack otherStack = mService.mStackIdToStack.valueAt(i); 417 final int otherStackId = otherStack.mStackId; 418 if (otherStackId != DOCKED_STACK_ID 419 && otherStackId >= FIRST_STATIC_STACK_ID 420 && otherStackId <= LAST_STATIC_STACK_ID) { 421 try { 422 mService.mActivityManager.resizeStack(otherStackId, mTmpRect); 423 } catch (RemoteException e) { 424 // This will not happen since we are in the same process. 425 } 426 } 427 } 428 } 429 430 void detachDisplay() { 431 EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId); 432 433 boolean doAnotherLayoutPass = false; 434 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 435 final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens; 436 for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) { 437 final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows; 438 for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) { 439 // We are in the middle of changing the state of displays/stacks/tasks. We need 440 // to finish that, before we let layout interfere with it. 441 mService.removeWindowLocked(appWindows.get(winNdx)); 442 doAnotherLayoutPass = true; 443 } 444 } 445 } 446 if (doAnotherLayoutPass) { 447 mService.mWindowPlacerLocked.requestTraversal(); 448 } 449 450 if (mStackId == DOCKED_STACK_ID) { 451 // Docked stack was detached from the display, so we no longer need to restrict the 452 // region of the screen other static stacks occupy. Go ahead and make them fullscreen. 453 resizeNonDockedStacks(FULLSCREEN); 454 } 455 456 close(); 457 } 458 459 void resetAnimationBackgroundAnimator() { 460 mAnimationBackgroundAnimator = null; 461 mAnimationBackgroundSurface.hide(); 462 } 463 464 void setAnimationBackground(WindowStateAnimator winAnimator, int color) { 465 int animLayer = winAnimator.mAnimLayer; 466 if (mAnimationBackgroundAnimator == null 467 || animLayer < mAnimationBackgroundAnimator.mAnimLayer) { 468 mAnimationBackgroundAnimator = winAnimator; 469 animLayer = mService.adjustAnimationBackground(winAnimator); 470 mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM, 471 ((color >> 24) & 0xff) / 255f, 0); 472 } 473 } 474 475 void switchUser() { 476 int top = mTasks.size(); 477 for (int taskNdx = 0; taskNdx < top; ++taskNdx) { 478 Task task = mTasks.get(taskNdx); 479 if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) { 480 mTasks.remove(taskNdx); 481 mTasks.add(task); 482 --top; 483 } 484 } 485 } 486 487 void close() { 488 if (mAnimationBackgroundSurface != null) { 489 mAnimationBackgroundSurface.destroySurface(); 490 mAnimationBackgroundSurface = null; 491 } 492 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 493 mTasks.get(taskNdx).close(); 494 } 495 mDisplayContent = null; 496 } 497 498 public void dump(String prefix, PrintWriter pw) { 499 pw.print(prefix); pw.print("mStackId="); pw.println(mStackId); 500 pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach); 501 for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) { 502 pw.print(prefix); 503 mTasks.get(taskNdx).printTo(prefix + " ", pw); 504 } 505 if (mAnimationBackgroundSurface.isDimming()) { 506 pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:"); 507 mAnimationBackgroundSurface.printTo(prefix + " ", pw); 508 } 509 if (!mExitingAppTokens.isEmpty()) { 510 pw.println(); 511 pw.println(" Exiting application tokens:"); 512 for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) { 513 WindowToken token = mExitingAppTokens.get(i); 514 pw.print(" Exiting App #"); pw.print(i); 515 pw.print(' '); pw.print(token); 516 pw.println(':'); 517 token.dump(pw, " "); 518 } 519 } 520 } 521 522 @Override 523 public boolean isFullscreen() { 524 return mFullscreen; 525 } 526 527 @Override 528 public DisplayInfo getDisplayInfo() { 529 return mDisplayContent.getDisplayInfo(); 530 } 531 532 @Override 533 public String toString() { 534 return "{stackId=" + mStackId + " tasks=" + mTasks + "}"; 535 } 536 537 /** 538 * For docked workspace provides information which side of the screen was the dock anchored. 539 */ 540 @DockSide 541 int getDockSide() { 542 if (mStackId != DOCKED_STACK_ID) { 543 return DOCKED_INVALID; 544 } 545 if (mDisplayContent == null) { 546 return DOCKED_INVALID; 547 } 548 mDisplayContent.getLogicalDisplayRect(mTmpRect); 549 final int orientation = mService.mCurConfiguration.orientation; 550 if (orientation == Configuration.ORIENTATION_PORTRAIT) { 551 // Portrait mode, docked either at the top or the bottom. 552 if (mBounds.top - mTmpRect.top < mTmpRect.bottom - mBounds.bottom) { 553 return DOCKED_TOP; 554 } else { 555 return DOCKED_BOTTOM; 556 } 557 } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { 558 // Landscape mode, docked either on the left or on the right. 559 if (mBounds.left - mTmpRect.left < mTmpRect.right - mBounds.right) { 560 return DOCKED_LEFT; 561 } else { 562 return DOCKED_RIGHT; 563 } 564 } else { 565 return DOCKED_INVALID; 566 } 567 } 568} 569