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.graphics.Rect; 23 import android.os.Debug; 24 import android.util.EventLog; 25 import android.util.Slog; 26 import android.util.TypedValue; 27 import com.android.server.EventLogTags; 28 29 import java.io.PrintWriter; 30 import java.util.ArrayList; 31 32 public 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 mDisplayContent.moveStack(this, true); 195 EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.taskId, toTop ? 1 : 0, stackNdx); 196 } 197 198 void moveTaskToTop(Task task) { 199 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers=" 200 + Debug.getCallers(6)); 201 mTasks.remove(task); 202 addTask(task, true); 203 } 204 205 void moveTaskToBottom(Task task) { 206 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task); 207 mTasks.remove(task); 208 addTask(task, false); 209 } 210 211 /** 212 * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the 213 * back. 214 * @param task The Task to delete. 215 */ 216 void removeTask(Task task) { 217 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task); 218 mTasks.remove(task); 219 if (mDisplayContent != null) { 220 if (mTasks.isEmpty()) { 221 mDisplayContent.moveStack(this, false); 222 } 223 mDisplayContent.layoutNeeded = true; 224 } 225 } 226 227 void attachDisplayContent(DisplayContent displayContent) { 228 if (mDisplayContent != null) { 229 throw new IllegalStateException("attachDisplayContent: Already attached"); 230 } 231 232 mDisplayContent = displayContent; 233 mDimLayer = new DimLayer(mService, this, displayContent); 234 mAnimationBackgroundSurface = new DimLayer(mService, this, displayContent); 235 updateDisplayInfo(); 236 } 237 238 void detachDisplay() { 239 EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId); 240 241 boolean doAnotherLayoutPass = false; 242 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 243 final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens; 244 for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) { 245 final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows; 246 for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) { 247 mService.removeWindowInnerLocked(null, appWindows.get(winNdx)); 248 doAnotherLayoutPass = true; 249 } 250 } 251 } 252 if (doAnotherLayoutPass) { 253 mService.requestTraversalLocked(); 254 } 255 256 mAnimationBackgroundSurface.destroySurface(); 257 mAnimationBackgroundSurface = null; 258 mDimLayer.destroySurface(); 259 mDimLayer = null; 260 mDisplayContent = null; 261 } 262 263 void resetAnimationBackgroundAnimator() { 264 mAnimationBackgroundAnimator = null; 265 mAnimationBackgroundSurface.hide(); 266 } 267 268 private long getDimBehindFadeDuration(long duration) { 269 TypedValue tv = new TypedValue(); 270 mService.mContext.getResources().getValue( 271 com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true); 272 if (tv.type == TypedValue.TYPE_FRACTION) { 273 duration = (long)tv.getFraction(duration, duration); 274 } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) { 275 duration = tv.data; 276 } 277 return duration; 278 } 279 280 boolean animateDimLayers() { 281 final int dimLayer; 282 final float dimAmount; 283 if (mDimWinAnimator == null) { 284 dimLayer = mDimLayer.getLayer(); 285 dimAmount = 0; 286 } else { 287 dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM; 288 dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount; 289 } 290 final float targetAlpha = mDimLayer.getTargetAlpha(); 291 if (targetAlpha != dimAmount) { 292 if (mDimWinAnimator == null) { 293 mDimLayer.hide(DEFAULT_DIM_DURATION); 294 } else { 295 long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null) 296 ? mDimWinAnimator.mAnimation.computeDurationHint() 297 : DEFAULT_DIM_DURATION; 298 if (targetAlpha > dimAmount) { 299 duration = getDimBehindFadeDuration(duration); 300 } 301 mDimLayer.show(dimLayer, dimAmount, duration); 302 } 303 } else if (mDimLayer.getLayer() != dimLayer) { 304 mDimLayer.setLayer(dimLayer); 305 } 306 if (mDimLayer.isAnimating()) { 307 if (!mService.okToDisplay()) { 308 // Jump to the end of the animation. 309 mDimLayer.show(); 310 } else { 311 return mDimLayer.stepAnimation(); 312 } 313 } 314 return false; 315 } 316 317 void resetDimmingTag() { 318 mDimmingTag = false; 319 } 320 321 void setDimmingTag() { 322 mDimmingTag = true; 323 } 324 325 boolean testDimmingTag() { 326 return mDimmingTag; 327 } 328 329 boolean isDimming() { 330 return mDimLayer.isDimming(); 331 } 332 333 boolean isDimming(WindowStateAnimator winAnimator) { 334 return mDimWinAnimator == winAnimator && mDimLayer.isDimming(); 335 } 336 337 void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) { 338 // Only set dim params on the highest dimmed layer. 339 final WindowStateAnimator existingDimWinAnimator = mDimWinAnimator; 340 // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer. 341 if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator == null 342 || !existingDimWinAnimator.mSurfaceShown 343 || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) { 344 mDimWinAnimator = newWinAnimator; 345 } 346 } 347 348 void stopDimmingIfNeeded() { 349 if (!mDimmingTag && isDimming()) { 350 mDimWinAnimator = null; 351 } 352 } 353 354 void setAnimationBackground(WindowStateAnimator winAnimator, int color) { 355 int animLayer = winAnimator.mAnimLayer; 356 if (mAnimationBackgroundAnimator == null 357 || animLayer < mAnimationBackgroundAnimator.mAnimLayer) { 358 mAnimationBackgroundAnimator = winAnimator; 359 animLayer = mService.adjustAnimationBackground(winAnimator); 360 mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM, 361 ((color >> 24) & 0xff) / 255f, 0); 362 } 363 } 364 365 void switchUser(int userId) { 366 int top = mTasks.size(); 367 for (int taskNdx = 0; taskNdx < top; ++taskNdx) { 368 Task task = mTasks.get(taskNdx); 369 if (mService.isCurrentProfileLocked(task.mUserId)) { 370 mTasks.remove(taskNdx); 371 mTasks.add(task); 372 --top; 373 } 374 } 375 } 376 377 void close() { 378 mDimLayer.mDimSurface.destroy(); 379 mAnimationBackgroundSurface.mDimSurface.destroy(); 380 } 381 382 public void dump(String prefix, PrintWriter pw) { 383 pw.print(prefix); pw.print("mStackId="); pw.println(mStackId); 384 pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach); 385 for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) { 386 pw.print(prefix); pw.println(mTasks.get(taskNdx)); 387 } 388 if (mAnimationBackgroundSurface.isDimming()) { 389 pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:"); 390 mAnimationBackgroundSurface.printTo(prefix + " ", pw); 391 } 392 if (mDimLayer.isDimming()) { 393 pw.print(prefix); pw.println("mDimLayer:"); 394 mDimLayer.printTo(prefix, pw); 395 pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator); 396 } 397 if (!mExitingAppTokens.isEmpty()) { 398 pw.println(); 399 pw.println(" Exiting application tokens:"); 400 for (int i=mExitingAppTokens.size()-1; i>=0; i--) { 401 WindowToken token = mExitingAppTokens.get(i); 402 pw.print(" Exiting App #"); pw.print(i); 403 pw.print(' '); pw.print(token); 404 pw.println(':'); 405 token.dump(pw, " "); 406 } 407 } 408 } 409 410 @Override 411 public String toString() { 412 return "{stackId=" + mStackId + " tasks=" + mTasks + "}"; 413 } 414 } 415