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