Task.java revision 112eb8c1f76fff6a670d7c5f85e8c3d656cd3aa8
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.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION; 24import static com.android.server.wm.WindowManagerService.TAG; 25import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE; 26import static com.android.server.wm.WindowManagerService.DEBUG_STACK; 27import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK; 28 29import android.app.ActivityManager.StackId; 30import android.content.res.Configuration; 31import android.graphics.Rect; 32import android.util.EventLog; 33import android.util.Slog; 34import android.view.DisplayInfo; 35import android.view.Surface; 36 37import com.android.server.EventLogTags; 38 39import java.io.PrintWriter; 40import java.util.ArrayList; 41 42class Task implements DimLayer.DimLayerUser { 43 // Return value from {@link setBounds} indicating no change was made to the Task bounds. 44 static final int BOUNDS_CHANGE_NONE = 0; 45 // Return value from {@link setBounds} indicating the position of the Task bounds changed. 46 static final int BOUNDS_CHANGE_POSITION = 1; 47 // Return value from {@link setBounds} indicating the size of the Task bounds changed. 48 static final int BOUNDS_CHANGE_SIZE = 1 << 1; 49 50 TaskStack mStack; 51 final AppTokenList mAppTokens = new AppTokenList(); 52 final int mTaskId; 53 final int mUserId; 54 boolean mDeferRemoval = false; 55 final WindowManagerService mService; 56 57 // Content limits relative to the DisplayContent this sits in. 58 private Rect mBounds = new Rect(); 59 60 // Device rotation as of the last time {@link #mBounds} was set. 61 int mRotation; 62 63 // Whether mBounds is fullscreen 64 private boolean mFullscreen = true; 65 66 // Contains configurations settings that are different from the global configuration due to 67 // stack specific operations. E.g. {@link #setBounds}. 68 Configuration mOverrideConfig; 69 70 // For comparison with DisplayContent bounds. 71 private Rect mTmpRect = new Rect(); 72 // For handling display rotations. 73 private Rect mTmpRect2 = new Rect(); 74 75 // Whether the task is currently being drag-resized 76 private boolean mDragResizing; 77 78 Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds, 79 Configuration config) { 80 mTaskId = taskId; 81 mStack = stack; 82 mUserId = userId; 83 mService = service; 84 setBounds(bounds, config); 85 } 86 87 DisplayContent getDisplayContent() { 88 return mStack.getDisplayContent(); 89 } 90 91 void addAppToken(int addPos, AppWindowToken wtoken) { 92 final int lastPos = mAppTokens.size(); 93 if (addPos >= lastPos) { 94 addPos = lastPos; 95 } else { 96 for (int pos = 0; pos < lastPos && pos < addPos; ++pos) { 97 if (mAppTokens.get(pos).removed) { 98 // addPos assumes removed tokens are actually gone. 99 ++addPos; 100 } 101 } 102 } 103 mAppTokens.add(addPos, wtoken); 104 wtoken.mTask = this; 105 mDeferRemoval = false; 106 } 107 108 void removeLocked() { 109 if (!mAppTokens.isEmpty() && mStack.isAnimating()) { 110 if (DEBUG_STACK) Slog.i(TAG, "removeTask: deferring removing taskId=" + mTaskId); 111 mDeferRemoval = true; 112 return; 113 } 114 if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId); 115 EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "removeTask"); 116 mDeferRemoval = false; 117 DisplayContent content = getDisplayContent(); 118 if (content != null) { 119 content.mDimLayerController.removeDimLayerUser(this); 120 } 121 mStack.removeTask(this); 122 mService.mTaskIdToTask.delete(mTaskId); 123 } 124 125 void moveTaskToStack(TaskStack stack, boolean toTop) { 126 if (stack == mStack) { 127 return; 128 } 129 if (DEBUG_STACK) Slog.i(TAG, "moveTaskToStack: removing taskId=" + mTaskId 130 + " from stack=" + mStack); 131 EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "moveTask"); 132 if (mStack != null) { 133 mStack.removeTask(this); 134 } 135 stack.addTask(this, toTop); 136 } 137 138 void positionTaskInStack(TaskStack stack, int position) { 139 if (mStack != null && stack != mStack) { 140 if (DEBUG_STACK) Slog.i(TAG, "positionTaskInStack: removing taskId=" + mTaskId 141 + " from stack=" + mStack); 142 EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "moveTask"); 143 mStack.removeTask(this); 144 } 145 stack.positionTask(this, position, showForAllUsers()); 146 } 147 148 boolean removeAppToken(AppWindowToken wtoken) { 149 boolean removed = mAppTokens.remove(wtoken); 150 if (mAppTokens.size() == 0) { 151 EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, 152 "removeAppToken: last token"); 153 if (mDeferRemoval) { 154 removeLocked(); 155 } 156 } 157 wtoken.mTask = null; 158 /* Leave mTaskId for now, it might be useful for debug 159 wtoken.mTaskId = -1; 160 */ 161 return removed; 162 } 163 164 void setSendingToBottom(boolean toBottom) { 165 for (int appTokenNdx = 0; appTokenNdx < mAppTokens.size(); appTokenNdx++) { 166 mAppTokens.get(appTokenNdx).sendingToBottom = toBottom; 167 } 168 } 169 170 /** Set the task bounds. Passing in null sets the bounds to fullscreen. */ 171 int setBounds(Rect bounds, Configuration config) { 172 if (config == null) { 173 config = Configuration.EMPTY; 174 } 175 if (bounds == null && !Configuration.EMPTY.equals(config)) { 176 throw new IllegalArgumentException("null bounds but non empty configuration: " 177 + config); 178 } 179 if (bounds != null && Configuration.EMPTY.equals(config)) { 180 throw new IllegalArgumentException("non null bounds, but empty configuration"); 181 } 182 boolean oldFullscreen = mFullscreen; 183 int rotation = Surface.ROTATION_0; 184 final DisplayContent displayContent = mStack.getDisplayContent(); 185 if (displayContent != null) { 186 displayContent.getLogicalDisplayRect(mTmpRect); 187 rotation = displayContent.getDisplayInfo().rotation; 188 if (bounds == null) { 189 bounds = mTmpRect; 190 mFullscreen = true; 191 } else { 192 if ((mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID 193 && mStack.mStackId != PINNED_STACK_ID) || bounds.isEmpty()) { 194 // ensure bounds are entirely within the display rect 195 if (!bounds.intersect(mTmpRect)) { 196 // Can't set bounds outside the containing display...Sorry! 197 return BOUNDS_CHANGE_NONE; 198 } 199 } 200 mFullscreen = mTmpRect.equals(bounds); 201 } 202 } 203 204 if (bounds == null) { 205 // Can't set to fullscreen if we don't have a display to get bounds from... 206 return BOUNDS_CHANGE_NONE; 207 } 208 if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) { 209 return BOUNDS_CHANGE_NONE; 210 } 211 212 int boundsChange = BOUNDS_CHANGE_NONE; 213 if (mBounds.left != bounds.left || mBounds.right != bounds.right) { 214 boundsChange |= BOUNDS_CHANGE_POSITION; 215 } 216 if (mBounds.width() != bounds.width() || mBounds.height() != bounds.height()) { 217 boundsChange |= BOUNDS_CHANGE_SIZE; 218 } 219 220 mBounds.set(bounds); 221 mRotation = rotation; 222 if (displayContent != null) { 223 displayContent.mDimLayerController.updateDimLayer(this); 224 } 225 mOverrideConfig = mFullscreen ? Configuration.EMPTY : config; 226 return boundsChange; 227 } 228 229 boolean resizeLocked(Rect bounds, Configuration configuration, boolean forced) { 230 int boundsChanged = setBounds(bounds, configuration); 231 if (forced) { 232 boundsChanged |= BOUNDS_CHANGE_SIZE; 233 } 234 if (boundsChanged == BOUNDS_CHANGE_NONE) { 235 return false; 236 } 237 if ((boundsChanged & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) { 238 resizeWindows(); 239 } 240 return true; 241 } 242 243 /** Return true if the current bound can get outputted to the rest of the system as-is. */ 244 private boolean useCurrentBounds() { 245 final DisplayContent displayContent = mStack.getDisplayContent(); 246 if (mFullscreen 247 || !StackId.isTaskResizeableByDockedStack(mStack.mStackId) 248 || displayContent == null 249 || displayContent.getDockedStackLocked() != null) { 250 return true; 251 } 252 return false; 253 } 254 255 /** Bounds of the task with other system factors taken into consideration. */ 256 @Override 257 public void getBounds(Rect out) { 258 if (useCurrentBounds()) { 259 // No need to adjust the output bounds if fullscreen or the docked stack is visible 260 // since it is already what we want to represent to the rest of the system. 261 out.set(mBounds); 262 return; 263 } 264 265 // The bounds has been adjusted to accommodate for a docked stack, but the docked stack 266 // is not currently visible. Go ahead a represent it as fullscreen to the rest of the 267 // system. 268 mStack.getDisplayContent().getLogicalDisplayRect(out); 269 } 270 271 void setDragResizing(boolean dragResizing) { 272 mDragResizing = dragResizing; 273 } 274 275 boolean isDragResizing() { 276 return mDragResizing; 277 } 278 279 void updateDisplayInfo(final DisplayContent displayContent) { 280 if (displayContent == null) { 281 return; 282 } 283 if (mFullscreen) { 284 setBounds(null, Configuration.EMPTY); 285 return; 286 } 287 final int newRotation = displayContent.getDisplayInfo().rotation; 288 if (mRotation == newRotation) { 289 return; 290 } 291 292 // Device rotation changed. We don't want the task to move around on the screen when 293 // this happens, so update the task bounds so it stays in the same place. 294 mTmpRect2.set(mBounds); 295 displayContent.rotateBounds(mRotation, newRotation, mTmpRect2); 296 if (setBounds(mTmpRect2, mOverrideConfig) != BOUNDS_CHANGE_NONE) { 297 // Post message to inform activity manager of the bounds change simulating 298 // a one-way call. We do this to prevent a deadlock between window manager 299 // lock and activity manager lock been held. Only tasks within the freeform stack 300 // are resizeable independently of their stack resizing. 301 if (mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID) { 302 mService.mH.sendMessage(mService.mH.obtainMessage( 303 RESIZE_TASK, mTaskId, RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mBounds)); 304 } 305 } 306 } 307 308 void resizeWindows() { 309 final ArrayList<WindowState> resizingWindows = mService.mResizingWindows; 310 for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) { 311 final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows; 312 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { 313 final WindowState win = windows.get(winNdx); 314 if (!resizingWindows.contains(win)) { 315 if (DEBUG_RESIZE) Slog.d(TAG, "setBounds: Resizing " + win); 316 resizingWindows.add(win); 317 } 318 } 319 } 320 } 321 322 /** 323 * Cancels any running app transitions associated with the task. 324 */ 325 void cancelTaskWindowTransition() { 326 for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) { 327 mAppTokens.get(activityNdx).mAppAnimator.clearAnimation(); 328 } 329 } 330 331 boolean showForAllUsers() { 332 final int tokensCount = mAppTokens.size(); 333 return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers; 334 } 335 336 boolean inHomeStack() { 337 return mStack != null && mStack.mStackId == HOME_STACK_ID; 338 } 339 340 boolean inFreeformWorkspace() { 341 return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID; 342 } 343 344 boolean inDockedWorkspace() { 345 return mStack != null && mStack.mStackId == DOCKED_STACK_ID; 346 } 347 348 WindowState getTopAppMainWindow() { 349 final int tokensCount = mAppTokens.size(); 350 return tokensCount > 0 ? mAppTokens.get(tokensCount - 1).findMainWindow() : null; 351 } 352 353 AppWindowToken getTopAppWindowToken() { 354 final int tokensCount = mAppTokens.size(); 355 return tokensCount > 0 ? mAppTokens.get(tokensCount - 1) : null; 356 } 357 358 @Override 359 public boolean isFullscreen() { 360 if (useCurrentBounds()) { 361 return mFullscreen; 362 } 363 // The bounds has been adjusted to accommodate for a docked stack, but the docked stack 364 // is not currently visible. Go ahead a represent it as fullscreen to the rest of the 365 // system. 366 return true; 367 } 368 369 @Override 370 public DisplayInfo getDisplayInfo() { 371 return mStack.getDisplayContent().getDisplayInfo(); 372 } 373 374 @Override 375 public String toString() { 376 return "{taskId=" + mTaskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}"; 377 } 378 379 @Override 380 public String toShortString() { 381 return "Task=" + mTaskId; 382 } 383 384 public void printTo(String prefix, PrintWriter pw) { 385 pw.print(prefix); pw.print("taskId="); pw.print(mTaskId); 386 pw.print(prefix); pw.print("appTokens="); pw.print(mAppTokens); 387 pw.print(prefix); pw.print("mdr="); pw.println(mDeferRemoval); 388 } 389} 390