TaskStack.java revision ddc1cb2c15549ed23dce9d416680a009fa6ae23c
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 com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT; 20import static com.android.server.wm.WindowManagerService.TAG; 21 22import android.content.res.Configuration; 23import android.graphics.Rect; 24import android.os.Debug; 25import android.util.EventLog; 26import android.util.IntArray; 27import android.util.Slog; 28import android.view.DisplayInfo; 29 30import com.android.server.EventLogTags; 31 32import java.io.PrintWriter; 33import java.util.ArrayList; 34import java.util.List; 35 36public class TaskStack implements DimLayer.DimLayerUser { 37 /** Unique identifier */ 38 final int mStackId; 39 40 /** The service */ 41 private final WindowManagerService mService; 42 43 /** The display this stack sits under. */ 44 private DisplayContent mDisplayContent; 45 46 /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match 47 * mTaskHistory in the ActivityStack with the same mStackId */ 48 private final ArrayList<Task> mTasks = new ArrayList<>(); 49 50 /** For comparison with DisplayContent bounds. */ 51 private Rect mTmpRect = new Rect(); 52 53 /** Content limits relative to the DisplayContent this sits in. */ 54 private Rect mBounds = new Rect(); 55 56 /** Whether mBounds is fullscreen */ 57 private boolean mFullscreen = true; 58 59 /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */ 60 DimLayer mAnimationBackgroundSurface; 61 62 /** The particular window with an Animation with non-zero background color. */ 63 WindowStateAnimator mAnimationBackgroundAnimator; 64 65 /** Application tokens that are exiting, but still on screen for animations. */ 66 final AppTokenList mExitingAppTokens = new AppTokenList(); 67 68 /** Detach this stack from its display when animation completes. */ 69 boolean mDeferDetach; 70 71 TaskStack(WindowManagerService service, int stackId) { 72 mService = service; 73 mStackId = stackId; 74 EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId); 75 } 76 77 DisplayContent getDisplayContent() { 78 return mDisplayContent; 79 } 80 81 ArrayList<Task> getTasks() { 82 return mTasks; 83 } 84 85 void resizeWindows() { 86 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 87 mTasks.get(taskNdx).resizeWindows(); 88 } 89 } 90 91 /** 92 * Set the bounds of the stack and its containing tasks. 93 * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen. 94 * @param changedTaskIds Output list of Ids of tasks that changed in bounds. 95 * @param newTaskConfigs Output list of new Configuation of the tasks that changed. 96 * @return True if the stack bounds was changed. 97 * */ 98 boolean setBounds(Rect bounds, IntArray changedTaskIds, List<Configuration> newTaskConfigs) { 99 if (!setBounds(bounds)) { 100 return false; 101 } 102 103 // Update bounds of containing tasks. 104 final Rect newBounds = mFullscreen ? null : mBounds; 105 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 106 final Task task = mTasks.get(taskNdx); 107 if (task.setBounds(newBounds)) { 108 changedTaskIds.add(task.mTaskId); 109 newTaskConfigs.add(task.mOverrideConfig); 110 } 111 } 112 return true; 113 } 114 115 private boolean setBounds(Rect bounds) { 116 boolean oldFullscreen = mFullscreen; 117 if (mDisplayContent != null) { 118 mDisplayContent.getLogicalDisplayRect(mTmpRect); 119 if (bounds == null) { 120 bounds = mTmpRect; 121 mFullscreen = true; 122 } else { 123 // ensure bounds are entirely within the display rect 124 if (!bounds.intersect(mTmpRect)) { 125 // Can't set bounds outside the containing display.. Sorry! 126 return false; 127 } 128 mFullscreen = mTmpRect.equals(bounds); 129 } 130 } 131 132 if (bounds == null) { 133 // Can't set to fullscreen if we don't have a display to get bounds from... 134 return false; 135 } 136 if (mBounds.equals(bounds) && oldFullscreen == mFullscreen) { 137 return false; 138 } 139 140 mAnimationBackgroundSurface.setBounds(bounds); 141 mBounds.set(bounds); 142 return true; 143 } 144 145 void getBounds(Rect out) { 146 out.set(mBounds); 147 } 148 149 void updateDisplayInfo() { 150 if (mDisplayContent != null) { 151 mDisplayContent.getLogicalDisplayRect(mTmpRect); 152 mAnimationBackgroundSurface.setBounds(mTmpRect); 153 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 154 mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent); 155 } 156 } 157 } 158 159 boolean isAnimating() { 160 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 161 final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens; 162 for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { 163 final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows; 164 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { 165 final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator; 166 if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) { 167 return true; 168 } 169 } 170 } 171 } 172 return false; 173 } 174 175 void addTask(Task task, boolean toTop) { 176 addTask(task, toTop, task.showForAllUsers()); 177 } 178 179 /** 180 * Put a Task in this stack. Used for adding and moving. 181 * @param task The task to add. 182 * @param toTop Whether to add it to the top or bottom. 183 * @param showForAllUsers Whether to show the task regardless of the current user. 184 */ 185 void addTask(Task task, boolean toTop, boolean showForAllUsers) { 186 positionTask(task, toTop ? mTasks.size() : 0, showForAllUsers); 187 } 188 189 void positionTask(Task task, int position, boolean showForAllUsers) { 190 final boolean canShowTask = 191 showForAllUsers || mService.isCurrentProfileLocked(task.mUserId); 192 mTasks.remove(task); 193 int stackSize = mTasks.size(); 194 int minPosition = 0; 195 int maxPosition = stackSize; 196 197 if (canShowTask) { 198 minPosition = computeMinPosition(minPosition, stackSize); 199 } else { 200 maxPosition = computeMaxPosition(maxPosition); 201 } 202 // Reset position based on minimum/maximum possible positions. 203 position = Math.min(Math.max(position, minPosition), maxPosition); 204 205 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, 206 "positionTask: task=" + task + " position=" + position); 207 mTasks.add(position, task); 208 209 task.mStack = this; 210 task.updateDisplayInfo(mDisplayContent); 211 boolean toTop = position == mTasks.size() - 1; 212 if (toTop) { 213 mDisplayContent.moveStack(this, true); 214 } 215 EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, position); 216 } 217 218 /** Calculate the minimum possible position for a task that can be shown to the user. 219 * The minimum position will be above all other tasks that can't be shown. 220 * @param minPosition The minimum position the caller is suggesting. 221 * We will start adjusting up from here. 222 * @param size The size of the current task list. 223 */ 224 private int computeMinPosition(int minPosition, int size) { 225 while (minPosition < size) { 226 final Task tmpTask = mTasks.get(minPosition); 227 final boolean canShowTmpTask = 228 tmpTask.showForAllUsers() 229 || mService.isCurrentProfileLocked(tmpTask.mUserId); 230 if (canShowTmpTask) { 231 break; 232 } 233 minPosition++; 234 } 235 return minPosition; 236 } 237 238 /** Calculate the maximum possible position for a task that can't be shown to the user. 239 * The maximum position will be below all other tasks that can be shown. 240 * @param maxPosition The maximum position the caller is suggesting. 241 * We will start adjusting down from here. 242 */ 243 private int computeMaxPosition(int maxPosition) { 244 while (maxPosition > 0) { 245 final Task tmpTask = mTasks.get(maxPosition - 1); 246 final boolean canShowTmpTask = 247 tmpTask.showForAllUsers() 248 || mService.isCurrentProfileLocked(tmpTask.mUserId); 249 if (!canShowTmpTask) { 250 break; 251 } 252 maxPosition--; 253 } 254 return maxPosition; 255 } 256 257 void moveTaskToTop(Task task) { 258 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers=" 259 + Debug.getCallers(6)); 260 mTasks.remove(task); 261 addTask(task, true); 262 } 263 264 void moveTaskToBottom(Task task) { 265 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task); 266 mTasks.remove(task); 267 addTask(task, false); 268 } 269 270 /** 271 * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the 272 * back. 273 * @param task The Task to delete. 274 */ 275 void removeTask(Task task) { 276 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task); 277 mTasks.remove(task); 278 if (mDisplayContent != null) { 279 if (mTasks.isEmpty()) { 280 mDisplayContent.moveStack(this, false); 281 } 282 mDisplayContent.layoutNeeded = true; 283 } 284 for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) { 285 final AppWindowToken wtoken = mExitingAppTokens.get(appNdx); 286 if (wtoken.mTask == task) { 287 wtoken.mIsExiting = false; 288 mExitingAppTokens.remove(appNdx); 289 } 290 } 291 } 292 293 void attachDisplayContent(DisplayContent displayContent) { 294 if (mDisplayContent != null) { 295 throw new IllegalStateException("attachDisplayContent: Already attached"); 296 } 297 298 mDisplayContent = displayContent; 299 mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId()); 300 updateDisplayInfo(); 301 } 302 303 void detachDisplay() { 304 EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId); 305 306 boolean doAnotherLayoutPass = false; 307 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 308 final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens; 309 for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) { 310 final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows; 311 for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) { 312 mService.removeWindowInnerLocked(appWindows.get(winNdx)); 313 doAnotherLayoutPass = true; 314 } 315 } 316 } 317 if (doAnotherLayoutPass) { 318 mService.requestTraversalLocked(); 319 } 320 321 close(); 322 323 mDisplayContent = null; 324 } 325 326 void resetAnimationBackgroundAnimator() { 327 mAnimationBackgroundAnimator = null; 328 mAnimationBackgroundSurface.hide(); 329 } 330 331 void setAnimationBackground(WindowStateAnimator winAnimator, int color) { 332 int animLayer = winAnimator.mAnimLayer; 333 if (mAnimationBackgroundAnimator == null 334 || animLayer < mAnimationBackgroundAnimator.mAnimLayer) { 335 mAnimationBackgroundAnimator = winAnimator; 336 animLayer = mService.adjustAnimationBackground(winAnimator); 337 mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM, 338 ((color >> 24) & 0xff) / 255f, 0); 339 } 340 } 341 342 void switchUser() { 343 int top = mTasks.size(); 344 for (int taskNdx = 0; taskNdx < top; ++taskNdx) { 345 Task task = mTasks.get(taskNdx); 346 if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) { 347 mTasks.remove(taskNdx); 348 mTasks.add(task); 349 --top; 350 } 351 } 352 } 353 354 void close() { 355 if (mAnimationBackgroundSurface != null) { 356 mAnimationBackgroundSurface.destroySurface(); 357 mAnimationBackgroundSurface = null; 358 } 359 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 360 mTasks.get(taskNdx).close(); 361 } 362 } 363 364 public void dump(String prefix, PrintWriter pw) { 365 pw.print(prefix); pw.print("mStackId="); pw.println(mStackId); 366 pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach); 367 for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) { 368 pw.print(prefix); 369 mTasks.get(taskNdx).printTo(prefix + " ", pw); 370 } 371 if (mAnimationBackgroundSurface.isDimming()) { 372 pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:"); 373 mAnimationBackgroundSurface.printTo(prefix + " ", pw); 374 } 375 if (!mExitingAppTokens.isEmpty()) { 376 pw.println(); 377 pw.println(" Exiting application tokens:"); 378 for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) { 379 WindowToken token = mExitingAppTokens.get(i); 380 pw.print(" Exiting App #"); pw.print(i); 381 pw.print(' '); pw.print(token); 382 pw.println(':'); 383 token.dump(pw, " "); 384 } 385 } 386 } 387 388 @Override 389 public boolean isFullscreen() { 390 return mFullscreen; 391 } 392 393 @Override 394 public DisplayInfo getDisplayInfo() { 395 return mDisplayContent.getDisplayInfo(); 396 } 397 398 @Override 399 public String toString() { 400 return "{stackId=" + mStackId + " tasks=" + mTasks + "}"; 401 } 402} 403