TaskStack.java revision 9d3de4cfb42519fefe9d8b03c38ba440bd6bc886
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.DisplayMetrics; 26import android.util.EventLog; 27import android.util.Slog; 28import android.util.TypedValue; 29 30import com.android.server.EventLogTags; 31 32import java.io.PrintWriter; 33import java.util.ArrayList; 34 35public class TaskStack { 36 /** Amount of time in milliseconds to animate the dim surface from one value to another, 37 * when no window animation is driving it. */ 38 private static final int DEFAULT_DIM_DURATION = 200; 39 40 /** Unique identifier */ 41 final int mStackId; 42 43 /** The service */ 44 private final WindowManagerService mService; 45 46 /** The display this stack sits under. */ 47 private DisplayContent mDisplayContent; 48 49 /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match 50 * mTaskHistory in the ActivityStack with the same mStackId */ 51 private final ArrayList<Task> mTasks = new ArrayList<Task>(); 52 53 /** For comparison with DisplayContent bounds. */ 54 private Rect mTmpRect = new Rect(); 55 56 /** Content limits relative to the DisplayContent this sits in. */ 57 private Rect mBounds = new Rect(); 58 59 /** Whether mBounds is fullscreen */ 60 private boolean mFullscreen = true; 61 62 /** Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND} */ 63 private DimLayer mDimLayer; 64 65 /** The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer. */ 66 WindowStateAnimator mDimWinAnimator; 67 68 /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */ 69 DimLayer mAnimationBackgroundSurface; 70 71 /** The particular window with an Animation with non-zero background color. */ 72 WindowStateAnimator mAnimationBackgroundAnimator; 73 74 /** Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the end 75 * then stop any dimming. */ 76 boolean mDimmingTag; 77 78 /** Application tokens that are exiting, but still on screen for animations. */ 79 final AppTokenList mExitingAppTokens = new AppTokenList(); 80 81 /** Detach this stack from its display when animation completes. */ 82 boolean mDeferDetach; 83 84 // Contains configurations settings that are different from the global configuration due to 85 // stack specific operations. E.g. {@link #setBounds}. 86 Configuration mOverrideConfig; 87 // True if the stack was forced to fullscreen disregarding the override configuration. 88 private boolean mForceFullscreen; 89 // The {@link #mBounds} before the stack was forced to fullscreen. Will be restored as the 90 // stack bounds once the stack is no longer forced to fullscreen. 91 final private Rect mPreForceFullscreenBounds; 92 93 TaskStack(WindowManagerService service, int stackId) { 94 mService = service; 95 mStackId = stackId; 96 mOverrideConfig = Configuration.EMPTY; 97 mForceFullscreen = false; 98 mPreForceFullscreenBounds = new Rect(); 99 // TODO: remove bounds from log, they are always 0. 100 EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, mBounds.left, mBounds.top, 101 mBounds.right, mBounds.bottom); 102 } 103 104 DisplayContent getDisplayContent() { 105 return mDisplayContent; 106 } 107 108 ArrayList<Task> getTasks() { 109 return mTasks; 110 } 111 112 void resizeWindows() { 113 final boolean underStatusBar = mBounds.top == 0; 114 115 final ArrayList<WindowState> resizingWindows = mService.mResizingWindows; 116 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 117 final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens; 118 for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { 119 final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows; 120 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { 121 final WindowState win = windows.get(winNdx); 122 if (!resizingWindows.contains(win)) { 123 if (WindowManagerService.DEBUG_RESIZE) Slog.d(TAG, 124 "setBounds: Resizing " + win); 125 resizingWindows.add(win); 126 } 127 win.mUnderStatusBar = underStatusBar; 128 } 129 } 130 } 131 } 132 133 /** Set the stack bounds. Passing in null sets the bounds to fullscreen. */ 134 boolean setBounds(Rect bounds) { 135 boolean oldFullscreen = mFullscreen; 136 if (mDisplayContent != null) { 137 mDisplayContent.getLogicalDisplayRect(mTmpRect); 138 if (bounds == null) { 139 bounds = mTmpRect; 140 mFullscreen = true; 141 } else { 142 bounds.intersect(mTmpRect); // ensure bounds are entirely within the display rect 143 mFullscreen = mTmpRect.equals(bounds); 144 } 145 } 146 147 if (bounds == null) { 148 // Can set to fullscreen if we don't have a display to get bounds from... 149 return false; 150 } 151 if (mBounds.equals(bounds) && oldFullscreen == mFullscreen) { 152 return false; 153 } 154 155 mDimLayer.setBounds(bounds); 156 mAnimationBackgroundSurface.setBounds(bounds); 157 mBounds.set(bounds); 158 updateOverrideConfiguration(); 159 return true; 160 } 161 162 void getBounds(Rect out) { 163 out.set(mBounds); 164 } 165 166 private void updateOverrideConfiguration() { 167 final Configuration serviceConfig = mService.mCurConfiguration; 168 if (mFullscreen) { 169 mOverrideConfig = Configuration.EMPTY; 170 return; 171 } 172 173 if (mOverrideConfig == Configuration.EMPTY) { 174 mOverrideConfig = new Configuration(); 175 } 176 177 // TODO(multidisplay): Update Dp to that of display stack is on. 178 final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE; 179 mOverrideConfig.screenWidthDp = 180 Math.min((int)(mBounds.width() / density), serviceConfig.screenWidthDp); 181 mOverrideConfig.screenHeightDp = 182 Math.min((int)(mBounds.height() / density), serviceConfig.screenHeightDp); 183 mOverrideConfig.smallestScreenWidthDp = 184 Math.min(mOverrideConfig.screenWidthDp, mOverrideConfig.screenHeightDp); 185 } 186 187 void updateDisplayInfo() { 188 if (mFullscreen && mDisplayContent != null) { 189 mDisplayContent.getLogicalDisplayRect(mTmpRect); 190 setBounds(mTmpRect); 191 } 192 } 193 194 boolean isFullscreen() { 195 return mFullscreen; 196 } 197 198 /** Forces the stack to fullscreen if input is true, else un-forces the stack from fullscreen. 199 * Returns true if something happened. 200 */ 201 boolean forceFullscreen(boolean forceFullscreen) { 202 if (mForceFullscreen == forceFullscreen) { 203 return false; 204 } 205 mForceFullscreen = forceFullscreen; 206 if (forceFullscreen) { 207 if (mFullscreen) { 208 return false; 209 } 210 mPreForceFullscreenBounds.set(mBounds); 211 return setBounds(null); 212 } else { 213 if (!mFullscreen || mPreForceFullscreenBounds.isEmpty()) { 214 return false; 215 } 216 return setBounds(mPreForceFullscreenBounds); 217 } 218 } 219 220 boolean isAnimating() { 221 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 222 final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens; 223 for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { 224 final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows; 225 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { 226 final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator; 227 if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) { 228 return true; 229 } 230 } 231 } 232 } 233 return false; 234 } 235 236 /** 237 * Put a Task in this stack. Used for adding and moving. 238 * @param task The task to add. 239 * @param toTop Whether to add it to the top or bottom. 240 */ 241 void addTask(Task task, boolean toTop) { 242 int stackNdx; 243 if (!toTop) { 244 stackNdx = 0; 245 } else { 246 stackNdx = mTasks.size(); 247 if (!mService.isCurrentProfileLocked(task.mUserId)) { 248 // Place the task below all current user tasks. 249 while (--stackNdx >= 0) { 250 if (!mService.isCurrentProfileLocked(mTasks.get(stackNdx).mUserId)) { 251 break; 252 } 253 } 254 // Put it above first non-current user task. 255 ++stackNdx; 256 } 257 } 258 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop 259 + " pos=" + stackNdx); 260 mTasks.add(stackNdx, task); 261 262 task.mStack = this; 263 if (toTop) { 264 mDisplayContent.moveStack(this, true); 265 } 266 EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, stackNdx); 267 } 268 269 void moveTaskToTop(Task task) { 270 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers=" 271 + Debug.getCallers(6)); 272 mTasks.remove(task); 273 addTask(task, true); 274 } 275 276 void moveTaskToBottom(Task task) { 277 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task); 278 mTasks.remove(task); 279 addTask(task, false); 280 } 281 282 /** 283 * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the 284 * back. 285 * @param task The Task to delete. 286 */ 287 void removeTask(Task task) { 288 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task); 289 mTasks.remove(task); 290 if (mDisplayContent != null) { 291 if (mTasks.isEmpty()) { 292 mDisplayContent.moveStack(this, false); 293 } 294 mDisplayContent.layoutNeeded = true; 295 } 296 for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) { 297 final AppWindowToken wtoken = mExitingAppTokens.get(appNdx); 298 if (wtoken.mTask == task) { 299 wtoken.mIsExiting = false; 300 mExitingAppTokens.remove(appNdx); 301 } 302 } 303 } 304 305 void attachDisplayContent(DisplayContent displayContent) { 306 if (mDisplayContent != null) { 307 throw new IllegalStateException("attachDisplayContent: Already attached"); 308 } 309 310 mDisplayContent = displayContent; 311 mDimLayer = new DimLayer(mService, this, displayContent); 312 mAnimationBackgroundSurface = new DimLayer(mService, this, displayContent); 313 updateDisplayInfo(); 314 } 315 316 void detachDisplay() { 317 EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId); 318 319 boolean doAnotherLayoutPass = false; 320 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 321 final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens; 322 for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) { 323 final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows; 324 for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) { 325 mService.removeWindowInnerLocked(null, appWindows.get(winNdx)); 326 doAnotherLayoutPass = true; 327 } 328 } 329 } 330 if (doAnotherLayoutPass) { 331 mService.requestTraversalLocked(); 332 } 333 334 mAnimationBackgroundSurface.destroySurface(); 335 mAnimationBackgroundSurface = null; 336 mDimLayer.destroySurface(); 337 mDimLayer = null; 338 mDisplayContent = null; 339 } 340 341 void resetAnimationBackgroundAnimator() { 342 mAnimationBackgroundAnimator = null; 343 mAnimationBackgroundSurface.hide(); 344 } 345 346 private long getDimBehindFadeDuration(long duration) { 347 TypedValue tv = new TypedValue(); 348 mService.mContext.getResources().getValue( 349 com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true); 350 if (tv.type == TypedValue.TYPE_FRACTION) { 351 duration = (long)tv.getFraction(duration, duration); 352 } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) { 353 duration = tv.data; 354 } 355 return duration; 356 } 357 358 boolean animateDimLayers() { 359 final int dimLayer; 360 final float dimAmount; 361 if (mDimWinAnimator == null) { 362 dimLayer = mDimLayer.getLayer(); 363 dimAmount = 0; 364 } else { 365 dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM; 366 dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount; 367 } 368 final float targetAlpha = mDimLayer.getTargetAlpha(); 369 if (targetAlpha != dimAmount) { 370 if (mDimWinAnimator == null) { 371 mDimLayer.hide(DEFAULT_DIM_DURATION); 372 } else { 373 long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null) 374 ? mDimWinAnimator.mAnimation.computeDurationHint() 375 : DEFAULT_DIM_DURATION; 376 if (targetAlpha > dimAmount) { 377 duration = getDimBehindFadeDuration(duration); 378 } 379 mDimLayer.show(dimLayer, dimAmount, duration); 380 } 381 } else if (mDimLayer.getLayer() != dimLayer) { 382 mDimLayer.setLayer(dimLayer); 383 } 384 if (mDimLayer.isAnimating()) { 385 if (!mService.okToDisplay()) { 386 // Jump to the end of the animation. 387 mDimLayer.show(); 388 } else { 389 return mDimLayer.stepAnimation(); 390 } 391 } 392 return false; 393 } 394 395 void resetDimmingTag() { 396 mDimmingTag = false; 397 } 398 399 void setDimmingTag() { 400 mDimmingTag = true; 401 } 402 403 boolean testDimmingTag() { 404 return mDimmingTag; 405 } 406 407 boolean isDimming() { 408 return mDimLayer.isDimming(); 409 } 410 411 boolean isDimming(WindowStateAnimator winAnimator) { 412 return mDimWinAnimator == winAnimator && mDimLayer.isDimming(); 413 } 414 415 void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) { 416 // Only set dim params on the highest dimmed layer. 417 final WindowStateAnimator existingDimWinAnimator = mDimWinAnimator; 418 // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer. 419 if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator == null 420 || !existingDimWinAnimator.mSurfaceShown 421 || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) { 422 mDimWinAnimator = newWinAnimator; 423 } 424 } 425 426 void stopDimmingIfNeeded() { 427 if (!mDimmingTag && isDimming()) { 428 mDimWinAnimator = null; 429 } 430 } 431 432 void setAnimationBackground(WindowStateAnimator winAnimator, int color) { 433 int animLayer = winAnimator.mAnimLayer; 434 if (mAnimationBackgroundAnimator == null 435 || animLayer < mAnimationBackgroundAnimator.mAnimLayer) { 436 mAnimationBackgroundAnimator = winAnimator; 437 animLayer = mService.adjustAnimationBackground(winAnimator); 438 mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM, 439 ((color >> 24) & 0xff) / 255f, 0); 440 } 441 } 442 443 void switchUser(int userId) { 444 int top = mTasks.size(); 445 for (int taskNdx = 0; taskNdx < top; ++taskNdx) { 446 Task task = mTasks.get(taskNdx); 447 if (mService.isCurrentProfileLocked(task.mUserId)) { 448 mTasks.remove(taskNdx); 449 mTasks.add(task); 450 --top; 451 } 452 } 453 } 454 455 void close() { 456 mDimLayer.mDimSurface.destroy(); 457 mAnimationBackgroundSurface.mDimSurface.destroy(); 458 } 459 460 public void dump(String prefix, PrintWriter pw) { 461 pw.print(prefix); pw.print("mStackId="); pw.println(mStackId); 462 pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach); 463 for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) { 464 pw.print(prefix); pw.println(mTasks.get(taskNdx)); 465 } 466 if (mAnimationBackgroundSurface.isDimming()) { 467 pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:"); 468 mAnimationBackgroundSurface.printTo(prefix + " ", pw); 469 } 470 if (mDimLayer.isDimming()) { 471 pw.print(prefix); pw.println("mDimLayer:"); 472 mDimLayer.printTo(prefix, pw); 473 pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator); 474 } 475 if (!mExitingAppTokens.isEmpty()) { 476 pw.println(); 477 pw.println(" Exiting application tokens:"); 478 for (int i=mExitingAppTokens.size()-1; i>=0; i--) { 479 WindowToken token = mExitingAppTokens.get(i); 480 pw.print(" Exiting App #"); pw.print(i); 481 pw.print(' '); pw.print(token); 482 pw.println(':'); 483 token.dump(pw, " "); 484 } 485 } 486 } 487 488 @Override 489 public String toString() { 490 return "{stackId=" + mStackId + " tasks=" + mTasks + "}"; 491 } 492} 493