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