Task.java revision bd0d937303ae54d8a5bb5f08080c4164302daefc
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.StackId.DOCKED_STACK_ID; 20import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; 21import static android.app.ActivityManager.StackId.HOME_STACK_ID; 22import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; 23import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION; 24import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 25import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE; 26import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; 27import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK; 28import static com.android.server.wm.WindowManagerService.H.SHOW_NON_RESIZEABLE_DOCK_TOAST; 29import static android.view.WindowManager.DOCKED_INVALID; 30import static android.view.WindowManager.DOCKED_LEFT; 31import static android.view.WindowManager.DOCKED_RIGHT; 32import static android.view.WindowManager.DOCKED_TOP; 33 34import android.app.ActivityManager.StackId; 35import android.content.res.Configuration; 36import android.graphics.Rect; 37import android.util.EventLog; 38import android.util.Slog; 39import android.view.DisplayInfo; 40import android.view.Surface; 41 42import com.android.server.EventLogTags; 43 44import java.io.PrintWriter; 45import java.util.ArrayList; 46 47class Task implements DimLayer.DimLayerUser { 48 // Return value from {@link setBounds} indicating no change was made to the Task bounds. 49 static final int BOUNDS_CHANGE_NONE = 0; 50 // Return value from {@link setBounds} indicating the position of the Task bounds changed. 51 static final int BOUNDS_CHANGE_POSITION = 1; 52 // Return value from {@link setBounds} indicating the size of the Task bounds changed. 53 static final int BOUNDS_CHANGE_SIZE = 1 << 1; 54 55 TaskStack mStack; 56 final AppTokenList mAppTokens = new AppTokenList(); 57 final int mTaskId; 58 final int mUserId; 59 boolean mDeferRemoval = false; 60 final WindowManagerService mService; 61 62 // Content limits relative to the DisplayContent this sits in. 63 private Rect mBounds = new Rect(); 64 65 // Device rotation as of the last time {@link #mBounds} was set. 66 int mRotation; 67 68 // Whether mBounds is fullscreen 69 private boolean mFullscreen = true; 70 71 // Contains configurations settings that are different from the global configuration due to 72 // stack specific operations. E.g. {@link #setBounds}. 73 Configuration mOverrideConfig; 74 75 // For comparison with DisplayContent bounds. 76 private Rect mTmpRect = new Rect(); 77 // For handling display rotations. 78 private Rect mTmpRect2 = new Rect(); 79 80 // Whether the task is resizeable 81 private boolean mResizeable; 82 83 // Whether we need to show toast about the app being non-resizeable when it becomes visible. 84 // This flag is set when a non-resizeable task is docked (or side-by-side). It's cleared 85 // after we show the toast. 86 private boolean mShowNonResizeableDockToast; 87 88 // Whether the task is currently being drag-resized 89 private boolean mDragResizing; 90 91 Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds, 92 Configuration config) { 93 mTaskId = taskId; 94 mStack = stack; 95 mUserId = userId; 96 mService = service; 97 setBounds(bounds, config); 98 } 99 100 DisplayContent getDisplayContent() { 101 return mStack.getDisplayContent(); 102 } 103 104 void setShowNonResizeableDockToast() { 105 mShowNonResizeableDockToast = true; 106 } 107 108 void scheduleShowNonResizeableDockToastIfNeeded() { 109 if (!mShowNonResizeableDockToast) { 110 return; 111 } 112 final DisplayContent displayContent = mStack.getDisplayContent(); 113 // If docked stack is not yet visible, we don't want to show the toast yet, 114 // since we need the visible rect of the docked task to position the toast. 115 if (displayContent == null || displayContent.getDockedStackLocked() == null) { 116 return; 117 } 118 119 mShowNonResizeableDockToast = false; 120 121 final int dockSide = mStack.getDockSide(); 122 int xOffset = 0; 123 int yOffset = 0; 124 if (dockSide != DOCKED_INVALID) { 125 mStack.getBounds(mTmpRect); 126 displayContent.getLogicalDisplayRect(mTmpRect2); 127 128 if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) { 129 xOffset = mTmpRect.centerX() - mTmpRect2.centerX(); 130 } else if (dockSide == DOCKED_TOP) { 131 // We don't adjust for DOCKED_BOTTOM case since it's already at the bottom. 132 yOffset = mTmpRect2.bottom - mTmpRect.bottom; 133 } 134 mService.mH.obtainMessage( 135 SHOW_NON_RESIZEABLE_DOCK_TOAST, xOffset, yOffset).sendToTarget(); 136 } 137 } 138 139 void addAppToken(int addPos, AppWindowToken wtoken) { 140 final int lastPos = mAppTokens.size(); 141 if (addPos >= lastPos) { 142 addPos = lastPos; 143 } else { 144 for (int pos = 0; pos < lastPos && pos < addPos; ++pos) { 145 if (mAppTokens.get(pos).removed) { 146 // addPos assumes removed tokens are actually gone. 147 ++addPos; 148 } 149 } 150 } 151 mAppTokens.add(addPos, wtoken); 152 wtoken.mTask = this; 153 mDeferRemoval = false; 154 } 155 156 void removeLocked() { 157 if (!mAppTokens.isEmpty() && mStack.isAnimating()) { 158 if (DEBUG_STACK) Slog.i(TAG_WM, "removeTask: deferring removing taskId=" + mTaskId); 159 mDeferRemoval = true; 160 return; 161 } 162 if (DEBUG_STACK) Slog.i(TAG_WM, "removeTask: removing taskId=" + mTaskId); 163 EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "removeTask"); 164 mDeferRemoval = false; 165 DisplayContent content = getDisplayContent(); 166 if (content != null) { 167 content.mDimLayerController.removeDimLayerUser(this); 168 } 169 mStack.removeTask(this); 170 mService.mTaskIdToTask.delete(mTaskId); 171 } 172 173 void moveTaskToStack(TaskStack stack, boolean toTop) { 174 if (stack == mStack) { 175 return; 176 } 177 if (DEBUG_STACK) Slog.i(TAG_WM, "moveTaskToStack: removing taskId=" + mTaskId 178 + " from stack=" + mStack); 179 EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "moveTask"); 180 if (mStack != null) { 181 mStack.removeTask(this); 182 } 183 stack.addTask(this, toTop); 184 } 185 186 void positionTaskInStack(TaskStack stack, int position, Rect bounds, Configuration config) { 187 if (mStack != null && stack != mStack) { 188 if (DEBUG_STACK) Slog.i(TAG_WM, "positionTaskInStack: removing taskId=" + mTaskId 189 + " from stack=" + mStack); 190 EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "moveTask"); 191 mStack.removeTask(this); 192 } 193 stack.positionTask(this, position, showForAllUsers()); 194 setBounds(bounds, config); 195 } 196 197 boolean removeAppToken(AppWindowToken wtoken) { 198 boolean removed = mAppTokens.remove(wtoken); 199 if (mAppTokens.size() == 0) { 200 EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, 201 "removeAppToken: last token"); 202 if (mDeferRemoval) { 203 removeLocked(); 204 } 205 } 206 wtoken.mTask = null; 207 /* Leave mTaskId for now, it might be useful for debug 208 wtoken.mTaskId = -1; 209 */ 210 return removed; 211 } 212 213 void setSendingToBottom(boolean toBottom) { 214 for (int appTokenNdx = 0; appTokenNdx < mAppTokens.size(); appTokenNdx++) { 215 mAppTokens.get(appTokenNdx).sendingToBottom = toBottom; 216 } 217 } 218 219 /** Set the task bounds. Passing in null sets the bounds to fullscreen. */ 220 int setBounds(Rect bounds, Configuration config) { 221 if (config == null) { 222 config = Configuration.EMPTY; 223 } 224 if (bounds == null && !Configuration.EMPTY.equals(config)) { 225 throw new IllegalArgumentException("null bounds but non empty configuration: " 226 + config); 227 } 228 if (bounds != null && Configuration.EMPTY.equals(config)) { 229 throw new IllegalArgumentException("non null bounds, but empty configuration"); 230 } 231 boolean oldFullscreen = mFullscreen; 232 int rotation = Surface.ROTATION_0; 233 final DisplayContent displayContent = mStack.getDisplayContent(); 234 if (displayContent != null) { 235 displayContent.getLogicalDisplayRect(mTmpRect); 236 rotation = displayContent.getDisplayInfo().rotation; 237 if (bounds == null) { 238 bounds = mTmpRect; 239 mFullscreen = true; 240 } else { 241 mFullscreen = mTmpRect.equals(bounds); 242 } 243 } 244 245 if (bounds == null) { 246 // Can't set to fullscreen if we don't have a display to get bounds from... 247 return BOUNDS_CHANGE_NONE; 248 } 249 if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) { 250 return BOUNDS_CHANGE_NONE; 251 } 252 253 int boundsChange = BOUNDS_CHANGE_NONE; 254 if (mBounds.left != bounds.left || mBounds.top != bounds.top) { 255 boundsChange |= BOUNDS_CHANGE_POSITION; 256 } 257 if (mBounds.width() != bounds.width() || mBounds.height() != bounds.height()) { 258 boundsChange |= BOUNDS_CHANGE_SIZE; 259 } 260 261 mBounds.set(bounds); 262 mRotation = rotation; 263 if (displayContent != null) { 264 displayContent.mDimLayerController.updateDimLayer(this); 265 } 266 mOverrideConfig = mFullscreen ? Configuration.EMPTY : config; 267 return boundsChange; 268 } 269 270 void setResizeable(boolean resizeable) { 271 mResizeable = resizeable; 272 } 273 274 boolean isResizeable() { 275 return mResizeable; 276 } 277 278 boolean resizeLocked(Rect bounds, Configuration configuration, boolean forced) { 279 int boundsChanged = setBounds(bounds, configuration); 280 if (forced) { 281 boundsChanged |= BOUNDS_CHANGE_SIZE; 282 } 283 if (boundsChanged == BOUNDS_CHANGE_NONE) { 284 return false; 285 } 286 if ((boundsChanged & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) { 287 resizeWindows(); 288 } else { 289 moveWindows(); 290 } 291 return true; 292 } 293 294 boolean scrollLocked(Rect bounds) { 295 // shift the task bound if it doesn't fully cover the stack area 296 mStack.getDimBounds(mTmpRect); 297 if (mService.mCurConfiguration.orientation == ORIENTATION_LANDSCAPE) { 298 if (bounds.left > mTmpRect.left) { 299 bounds.left = mTmpRect.left; 300 bounds.right = mTmpRect.left + mBounds.width(); 301 } else if (bounds.right < mTmpRect.right) { 302 bounds.left = mTmpRect.right - mBounds.width(); 303 bounds.right = mTmpRect.right; 304 } 305 } else { 306 if (bounds.top > mTmpRect.top) { 307 bounds.top = mTmpRect.top; 308 bounds.bottom = mTmpRect.top + mBounds.height(); 309 } else if (bounds.bottom < mTmpRect.bottom) { 310 bounds.top = mTmpRect.bottom - mBounds.height(); 311 bounds.bottom = mTmpRect.bottom; 312 } 313 } 314 315 if (bounds.equals(mBounds)) { 316 return false; 317 } 318 // Normal setBounds() does not allow non-null bounds for fullscreen apps. 319 // We only change bounds for the scrolling case without change it size, 320 // on resizing path we should still want the validation. 321 mBounds.set(bounds); 322 for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) { 323 final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows; 324 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { 325 final WindowState win = windows.get(winNdx); 326 win.mXOffset = bounds.left; 327 win.mYOffset = bounds.top; 328 } 329 } 330 return true; 331 } 332 333 /** Return true if the current bound can get outputted to the rest of the system as-is. */ 334 private boolean useCurrentBounds() { 335 final DisplayContent displayContent = mStack.getDisplayContent(); 336 if (mFullscreen 337 || !StackId.isTaskResizeableByDockedStack(mStack.mStackId) 338 || displayContent == null 339 || displayContent.getDockedStackLocked() != null) { 340 return true; 341 } 342 return false; 343 } 344 345 /** Original bounds of the task if applicable, otherwise fullscreen rect. */ 346 public void getBounds(Rect out) { 347 if (useCurrentBounds()) { 348 // No need to adjust the output bounds if fullscreen or the docked stack is visible 349 // since it is already what we want to represent to the rest of the system. 350 out.set(mBounds); 351 return; 352 } 353 354 // The bounds has been adjusted to accommodate for a docked stack, but the docked stack 355 // is not currently visible. Go ahead a represent it as fullscreen to the rest of the 356 // system. 357 mStack.getDisplayContent().getLogicalDisplayRect(out); 358 } 359 360 361 /** 362 * Calculate the maximum visible area of this task. If the task has only one app, 363 * the result will be visible frame of that app. If the task has more than one apps, 364 * we search from top down if the next app got different visible area. 365 * 366 * This effort is to handle the case where some task (eg. GMail composer) might pop up 367 * a dialog that's different in size from the activity below, in which case we should 368 * be dimming the entire task area behind the dialog. 369 * 370 * @param out Rect containing the max visible bounds. 371 * @return true if the task has some visible app windows; false otherwise. 372 */ 373 boolean getMaxVisibleBounds(Rect out) { 374 boolean foundTop = false; 375 for (int i = mAppTokens.size() - 1; i >= 0; i--) { 376 final AppWindowToken token = mAppTokens.get(i); 377 // skip hidden (or about to hide) apps 378 if (token.mIsExiting || token.clientHidden || token.hiddenRequested) { 379 continue; 380 } 381 final WindowState win = token.findMainWindow(); 382 if (win == null) { 383 continue; 384 } 385 if (!foundTop) { 386 out.set(win.mVisibleFrame); 387 foundTop = true; 388 continue; 389 } 390 if (win.mVisibleFrame.left < out.left) { 391 out.left = win.mVisibleFrame.left; 392 } 393 if (win.mVisibleFrame.top < out.top) { 394 out.top = win.mVisibleFrame.top; 395 } 396 if (win.mVisibleFrame.right > out.right) { 397 out.right = win.mVisibleFrame.right; 398 } 399 if (win.mVisibleFrame.bottom > out.bottom) { 400 out.bottom = win.mVisibleFrame.bottom; 401 } 402 } 403 return foundTop; 404 } 405 406 /** Bounds of the task to be used for dimming, as well as touch related tests. */ 407 @Override 408 public void getDimBounds(Rect out) { 409 if (useCurrentBounds()) { 410 if (inFreeformWorkspace() && getMaxVisibleBounds(out)) { 411 return; 412 } 413 414 out.set(mBounds); 415 return; 416 } 417 418 // The bounds has been adjusted to accommodate for a docked stack, but the docked stack 419 // is not currently visible. Go ahead a represent it as fullscreen to the rest of the 420 // system. 421 mStack.getDisplayContent().getLogicalDisplayRect(out); 422 } 423 424 void setDragResizing(boolean dragResizing) { 425 mDragResizing = dragResizing; 426 } 427 428 boolean isDragResizing() { 429 return mDragResizing; 430 } 431 432 void updateDisplayInfo(final DisplayContent displayContent) { 433 if (displayContent == null) { 434 return; 435 } 436 if (mFullscreen) { 437 setBounds(null, Configuration.EMPTY); 438 return; 439 } 440 final int newRotation = displayContent.getDisplayInfo().rotation; 441 if (mRotation == newRotation) { 442 return; 443 } 444 445 // Device rotation changed. We don't want the task to move around on the screen when 446 // this happens, so update the task bounds so it stays in the same place. 447 mTmpRect2.set(mBounds); 448 displayContent.rotateBounds(mRotation, newRotation, mTmpRect2); 449 if (setBounds(mTmpRect2, mOverrideConfig) != BOUNDS_CHANGE_NONE) { 450 // Post message to inform activity manager of the bounds change simulating 451 // a one-way call. We do this to prevent a deadlock between window manager 452 // lock and activity manager lock been held. Only tasks within the freeform stack 453 // are resizeable independently of their stack resizing. 454 if (mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID) { 455 mService.mH.sendMessage(mService.mH.obtainMessage( 456 RESIZE_TASK, mTaskId, RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mBounds)); 457 } 458 } 459 } 460 461 void resizeWindows() { 462 final ArrayList<WindowState> resizingWindows = mService.mResizingWindows; 463 for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) { 464 final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows; 465 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { 466 final WindowState win = windows.get(winNdx); 467 if (!resizingWindows.contains(win)) { 468 if (DEBUG_RESIZE) Slog.d(TAG_WM, "resizeWindows: Resizing " + win); 469 resizingWindows.add(win); 470 } 471 } 472 } 473 } 474 475 void moveWindows() { 476 for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) { 477 final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows; 478 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { 479 final WindowState win = windows.get(winNdx); 480 if (DEBUG_RESIZE) Slog.d(TAG_WM, "moveWindows: Moving " + win); 481 win.mMovedByResize = true; 482 } 483 } 484 } 485 486 /** 487 * Cancels any running app transitions associated with the task. 488 */ 489 void cancelTaskWindowTransition() { 490 for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) { 491 mAppTokens.get(activityNdx).mAppAnimator.clearAnimation(); 492 } 493 } 494 495 /** 496 * Cancels any running thumbnail transitions associated with the task. 497 */ 498 void cancelTaskThumbnailTransition() { 499 for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) { 500 mAppTokens.get(activityNdx).mAppAnimator.clearThumbnail(); 501 } 502 } 503 504 boolean showForAllUsers() { 505 final int tokensCount = mAppTokens.size(); 506 return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers; 507 } 508 509 boolean inHomeStack() { 510 return mStack != null && mStack.mStackId == HOME_STACK_ID; 511 } 512 513 boolean inFreeformWorkspace() { 514 return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID; 515 } 516 517 boolean inDockedWorkspace() { 518 return mStack != null && mStack.mStackId == DOCKED_STACK_ID; 519 } 520 521 boolean isResizeableByDockedStack() { 522 return mStack != null && getDisplayContent().getDockedStackLocked() != null && 523 StackId.isTaskResizeableByDockedStack(mStack.mStackId); 524 } 525 526 /** 527 * Whether the task should be treated as if it's docked. Returns true if the task 528 * is currently in docked workspace, or it's side-by-side to a docked task. 529 */ 530 boolean isDockedInEffect() { 531 return inDockedWorkspace() || isResizeableByDockedStack(); 532 } 533 534 WindowState getTopVisibleAppMainWindow() { 535 final AppWindowToken token = getTopVisibleAppToken(); 536 return token != null ? token.findMainWindow() : null; 537 } 538 539 AppWindowToken getTopVisibleAppToken() { 540 for (int i = mAppTokens.size() - 1; i >= 0; i--) { 541 final AppWindowToken token = mAppTokens.get(i); 542 // skip hidden (or about to hide) apps 543 if (!token.mIsExiting && !token.clientHidden && !token.hiddenRequested) { 544 return token; 545 } 546 } 547 return null; 548 } 549 550 @Override 551 public boolean isFullscreen() { 552 if (useCurrentBounds()) { 553 return mFullscreen; 554 } 555 // The bounds has been adjusted to accommodate for a docked stack, but the docked stack 556 // is not currently visible. Go ahead a represent it as fullscreen to the rest of the 557 // system. 558 return true; 559 } 560 561 @Override 562 public DisplayInfo getDisplayInfo() { 563 return mStack.getDisplayContent().getDisplayInfo(); 564 } 565 566 @Override 567 public String toString() { 568 return "{taskId=" + mTaskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}"; 569 } 570 571 @Override 572 public String toShortString() { 573 return "Task=" + mTaskId; 574 } 575 576 public void printTo(String prefix, PrintWriter pw) { 577 pw.print(prefix); pw.print("taskId="); pw.println(mTaskId); 578 pw.print(prefix + prefix); pw.print("mFullscreen="); pw.println(mFullscreen); 579 pw.print(prefix + prefix); pw.print("mBounds="); pw.println(mBounds.toShortString()); 580 pw.print(prefix + prefix); pw.print("mdr="); pw.println(mDeferRemoval); 581 pw.print(prefix + prefix); pw.print("appTokens="); pw.println(mAppTokens); 582 } 583} 584