TaskStack.java revision e3119b7d353e71d1f94ddff932b722b4d285931e
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.graphics.Rect; 23import android.os.Debug; 24import android.util.EventLog; 25import android.util.Slog; 26import android.util.TypedValue; 27import com.android.server.EventLogTags; 28 29import java.io.PrintWriter; 30import java.util.ArrayList; 31 32public class TaskStack { 33 /** Amount of time in milliseconds to animate the dim surface from one value to another, 34 * when no window animation is driving it. */ 35 private static final int DEFAULT_DIM_DURATION = 200; 36 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<Task>(); 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 /** Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND} */ 60 private DimLayer mDimLayer; 61 62 /** The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer. */ 63 WindowStateAnimator mDimWinAnimator; 64 65 /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */ 66 DimLayer mAnimationBackgroundSurface; 67 68 /** The particular window with an Animation with non-zero background color. */ 69 WindowStateAnimator mAnimationBackgroundAnimator; 70 71 /** Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the end 72 * then stop any dimming. */ 73 boolean mDimmingTag; 74 75 /** Application tokens that are exiting, but still on screen for animations. */ 76 final AppTokenList mExitingAppTokens = new AppTokenList(); 77 78 /** Detach this stack from its display when animation completes. */ 79 boolean mDeferDetach; 80 81 TaskStack(WindowManagerService service, int stackId) { 82 mService = service; 83 mStackId = stackId; 84 // TODO: remove bounds from log, they are always 0. 85 EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, mBounds.left, mBounds.top, 86 mBounds.right, mBounds.bottom); 87 } 88 89 DisplayContent getDisplayContent() { 90 return mDisplayContent; 91 } 92 93 ArrayList<Task> getTasks() { 94 return mTasks; 95 } 96 97 void resizeWindows() { 98 final boolean underStatusBar = mBounds.top == 0; 99 100 final ArrayList<WindowState> resizingWindows = mService.mResizingWindows; 101 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 102 final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens; 103 for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { 104 final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows; 105 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { 106 final WindowState win = windows.get(winNdx); 107 if (!resizingWindows.contains(win)) { 108 if (WindowManagerService.DEBUG_RESIZE) Slog.d(TAG, 109 "setBounds: Resizing " + win); 110 resizingWindows.add(win); 111 } 112 win.mUnderStatusBar = underStatusBar; 113 } 114 } 115 } 116 } 117 118 boolean setBounds(Rect bounds) { 119 boolean oldFullscreen = mFullscreen; 120 if (mDisplayContent != null) { 121 mDisplayContent.getLogicalDisplayRect(mTmpRect); 122 mFullscreen = mTmpRect.equals(bounds); 123 } 124 125 if (mBounds.equals(bounds) && oldFullscreen == mFullscreen) { 126 return false; 127 } 128 129 mDimLayer.setBounds(bounds); 130 mAnimationBackgroundSurface.setBounds(bounds); 131 mBounds.set(bounds); 132 133 return true; 134 } 135 136 void getBounds(Rect out) { 137 out.set(mBounds); 138 } 139 140 void updateDisplayInfo() { 141 if (mFullscreen && mDisplayContent != null) { 142 mDisplayContent.getLogicalDisplayRect(mTmpRect); 143 setBounds(mTmpRect); 144 } 145 } 146 147 boolean isFullscreen() { 148 return mFullscreen; 149 } 150 151 boolean isAnimating() { 152 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 153 final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens; 154 for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { 155 final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows; 156 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { 157 final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator; 158 if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) { 159 return true; 160 } 161 } 162 } 163 } 164 return false; 165 } 166 167 /** 168 * Put a Task in this stack. Used for adding and moving. 169 * @param task The task to add. 170 * @param toTop Whether to add it to the top or bottom. 171 */ 172 void addTask(Task task, boolean toTop) { 173 int stackNdx; 174 if (!toTop) { 175 stackNdx = 0; 176 } else { 177 stackNdx = mTasks.size(); 178 if (!mService.isCurrentProfileLocked(task.mUserId)) { 179 // Place the task below all current user tasks. 180 while (--stackNdx >= 0) { 181 if (!mService.isCurrentProfileLocked(mTasks.get(stackNdx).mUserId)) { 182 break; 183 } 184 } 185 // Put it above first non-current user task. 186 ++stackNdx; 187 } 188 } 189 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop 190 + " pos=" + stackNdx); 191 mTasks.add(stackNdx, task); 192 193 task.mStack = this; 194 if (toTop) { 195 mDisplayContent.moveStack(this, true); 196 } 197 EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.taskId, toTop ? 1 : 0, stackNdx); 198 } 199 200 void moveTaskToTop(Task task) { 201 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers=" 202 + Debug.getCallers(6)); 203 mTasks.remove(task); 204 addTask(task, true); 205 } 206 207 void moveTaskToBottom(Task task) { 208 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task); 209 mTasks.remove(task); 210 addTask(task, false); 211 } 212 213 /** 214 * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the 215 * back. 216 * @param task The Task to delete. 217 */ 218 void removeTask(Task task) { 219 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task); 220 mTasks.remove(task); 221 if (mDisplayContent != null) { 222 if (mTasks.isEmpty()) { 223 mDisplayContent.moveStack(this, false); 224 } 225 mDisplayContent.layoutNeeded = true; 226 } 227 final int taskId = task.taskId; 228 for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) { 229 final AppWindowToken wtoken = mExitingAppTokens.get(appNdx); 230 if (wtoken.groupId == taskId) { 231 wtoken.mIsExiting = false; 232 mExitingAppTokens.remove(appNdx); 233 } 234 } 235 } 236 237 void attachDisplayContent(DisplayContent displayContent) { 238 if (mDisplayContent != null) { 239 throw new IllegalStateException("attachDisplayContent: Already attached"); 240 } 241 242 mDisplayContent = displayContent; 243 mDimLayer = new DimLayer(mService, this, displayContent); 244 mAnimationBackgroundSurface = new DimLayer(mService, this, displayContent); 245 updateDisplayInfo(); 246 } 247 248 void detachDisplay() { 249 EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId); 250 251 boolean doAnotherLayoutPass = false; 252 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 253 final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens; 254 for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) { 255 final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows; 256 for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) { 257 mService.removeWindowInnerLocked(null, appWindows.get(winNdx)); 258 doAnotherLayoutPass = true; 259 } 260 } 261 } 262 if (doAnotherLayoutPass) { 263 mService.requestTraversalLocked(); 264 } 265 266 mAnimationBackgroundSurface.destroySurface(); 267 mAnimationBackgroundSurface = null; 268 mDimLayer.destroySurface(); 269 mDimLayer = null; 270 mDisplayContent = null; 271 } 272 273 void resetAnimationBackgroundAnimator() { 274 mAnimationBackgroundAnimator = null; 275 mAnimationBackgroundSurface.hide(); 276 } 277 278 private long getDimBehindFadeDuration(long duration) { 279 TypedValue tv = new TypedValue(); 280 mService.mContext.getResources().getValue( 281 com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true); 282 if (tv.type == TypedValue.TYPE_FRACTION) { 283 duration = (long)tv.getFraction(duration, duration); 284 } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) { 285 duration = tv.data; 286 } 287 return duration; 288 } 289 290 boolean animateDimLayers() { 291 final int dimLayer; 292 final float dimAmount; 293 if (mDimWinAnimator == null) { 294 dimLayer = mDimLayer.getLayer(); 295 dimAmount = 0; 296 } else { 297 dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM; 298 dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount; 299 } 300 final float targetAlpha = mDimLayer.getTargetAlpha(); 301 if (targetAlpha != dimAmount) { 302 if (mDimWinAnimator == null) { 303 mDimLayer.hide(DEFAULT_DIM_DURATION); 304 } else { 305 long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null) 306 ? mDimWinAnimator.mAnimation.computeDurationHint() 307 : DEFAULT_DIM_DURATION; 308 if (targetAlpha > dimAmount) { 309 duration = getDimBehindFadeDuration(duration); 310 } 311 mDimLayer.show(dimLayer, dimAmount, duration); 312 } 313 } else if (mDimLayer.getLayer() != dimLayer) { 314 mDimLayer.setLayer(dimLayer); 315 } 316 if (mDimLayer.isAnimating()) { 317 if (!mService.okToDisplay()) { 318 // Jump to the end of the animation. 319 mDimLayer.show(); 320 } else { 321 return mDimLayer.stepAnimation(); 322 } 323 } 324 return false; 325 } 326 327 void resetDimmingTag() { 328 mDimmingTag = false; 329 } 330 331 void setDimmingTag() { 332 mDimmingTag = true; 333 } 334 335 boolean testDimmingTag() { 336 return mDimmingTag; 337 } 338 339 boolean isDimming() { 340 return mDimLayer.isDimming(); 341 } 342 343 boolean isDimming(WindowStateAnimator winAnimator) { 344 return mDimWinAnimator == winAnimator && mDimLayer.isDimming(); 345 } 346 347 void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) { 348 // Only set dim params on the highest dimmed layer. 349 final WindowStateAnimator existingDimWinAnimator = mDimWinAnimator; 350 // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer. 351 if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator == null 352 || !existingDimWinAnimator.mSurfaceShown 353 || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) { 354 mDimWinAnimator = newWinAnimator; 355 } 356 } 357 358 void stopDimmingIfNeeded() { 359 if (!mDimmingTag && isDimming()) { 360 mDimWinAnimator = null; 361 } 362 } 363 364 void setAnimationBackground(WindowStateAnimator winAnimator, int color) { 365 int animLayer = winAnimator.mAnimLayer; 366 if (mAnimationBackgroundAnimator == null 367 || animLayer < mAnimationBackgroundAnimator.mAnimLayer) { 368 mAnimationBackgroundAnimator = winAnimator; 369 animLayer = mService.adjustAnimationBackground(winAnimator); 370 mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM, 371 ((color >> 24) & 0xff) / 255f, 0); 372 } 373 } 374 375 void switchUser(int userId) { 376 int top = mTasks.size(); 377 for (int taskNdx = 0; taskNdx < top; ++taskNdx) { 378 Task task = mTasks.get(taskNdx); 379 if (mService.isCurrentProfileLocked(task.mUserId)) { 380 mTasks.remove(taskNdx); 381 mTasks.add(task); 382 --top; 383 } 384 } 385 } 386 387 void close() { 388 mDimLayer.mDimSurface.destroy(); 389 mAnimationBackgroundSurface.mDimSurface.destroy(); 390 } 391 392 public void dump(String prefix, PrintWriter pw) { 393 pw.print(prefix); pw.print("mStackId="); pw.println(mStackId); 394 pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach); 395 for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) { 396 pw.print(prefix); pw.println(mTasks.get(taskNdx)); 397 } 398 if (mAnimationBackgroundSurface.isDimming()) { 399 pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:"); 400 mAnimationBackgroundSurface.printTo(prefix + " ", pw); 401 } 402 if (mDimLayer.isDimming()) { 403 pw.print(prefix); pw.println("mDimLayer:"); 404 mDimLayer.printTo(prefix, pw); 405 pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator); 406 } 407 if (!mExitingAppTokens.isEmpty()) { 408 pw.println(); 409 pw.println(" Exiting application tokens:"); 410 for (int i=mExitingAppTokens.size()-1; i>=0; i--) { 411 WindowToken token = mExitingAppTokens.get(i); 412 pw.print(" Exiting App #"); pw.print(i); 413 pw.print(' '); pw.print(token); 414 pw.println(':'); 415 token.dump(pw, " "); 416 } 417 } 418 } 419 420 @Override 421 public String toString() { 422 return "{stackId=" + mStackId + " tasks=" + mTasks + "}"; 423 } 424} 425