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