1 package com.android.server.wm; 2 3 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER; 4 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 5 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 6 import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM; 7 8 import android.graphics.Rect; 9 import android.util.ArrayMap; 10 import android.util.Slog; 11 import android.util.TypedValue; 12 13 import com.android.server.wm.DimLayer.DimLayerUser; 14 15 import java.io.PrintWriter; 16 17 /** 18 * Centralizes the control of dim layers used for 19 * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND} 20 * as well as other use cases (such as dimming above a dead window). 21 */ 22 class DimLayerController { 23 private static final String TAG_LOCAL = "DimLayerController"; 24 private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM; 25 26 /** Amount of time in milliseconds to animate the dim surface from one value to another, 27 * when no window animation is driving it. */ 28 private static final int DEFAULT_DIM_DURATION = 200; 29 30 /** 31 * The default amount of dim applied over a dead window 32 */ 33 private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f; 34 35 // Shared dim layer for fullscreen users. {@link DimLayerState#dimLayer} will point to this 36 // instead of creating a new object per fullscreen task on a display. 37 private DimLayer mSharedFullScreenDimLayer; 38 39 private ArrayMap<DimLayer.DimLayerUser, DimLayerState> mState = new ArrayMap<>(); 40 41 private DisplayContent mDisplayContent; 42 43 private Rect mTmpBounds = new Rect(); 44 DimLayerController(DisplayContent displayContent)45 DimLayerController(DisplayContent displayContent) { 46 mDisplayContent = displayContent; 47 } 48 49 /** Updates the dim layer bounds, recreating it if needed. */ updateDimLayer(DimLayer.DimLayerUser dimLayerUser)50 void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) { 51 final DimLayerState state = getOrCreateDimLayerState(dimLayerUser); 52 final boolean previousFullscreen = state.dimLayer != null 53 && state.dimLayer == mSharedFullScreenDimLayer; 54 DimLayer newDimLayer; 55 final int displayId = mDisplayContent.getDisplayId(); 56 if (dimLayerUser.dimFullscreen()) { 57 if (previousFullscreen && mSharedFullScreenDimLayer != null) { 58 // Update the bounds for fullscreen in case of rotation. 59 mSharedFullScreenDimLayer.setBoundsForFullscreen(); 60 return; 61 } 62 // Use shared fullscreen dim layer 63 newDimLayer = mSharedFullScreenDimLayer; 64 if (newDimLayer == null) { 65 if (state.dimLayer != null) { 66 // Re-purpose the previous dim layer. 67 newDimLayer = state.dimLayer; 68 } else { 69 // Create new full screen dim layer. 70 newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId, 71 getDimLayerTag(dimLayerUser)); 72 } 73 dimLayerUser.getDimBounds(mTmpBounds); 74 newDimLayer.setBounds(mTmpBounds); 75 mSharedFullScreenDimLayer = newDimLayer; 76 } else if (state.dimLayer != null) { 77 state.dimLayer.destroySurface(); 78 } 79 } else { 80 newDimLayer = (state.dimLayer == null || previousFullscreen) 81 ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId, 82 getDimLayerTag(dimLayerUser)) 83 : state.dimLayer; 84 dimLayerUser.getDimBounds(mTmpBounds); 85 newDimLayer.setBounds(mTmpBounds); 86 } 87 state.dimLayer = newDimLayer; 88 } 89 getDimLayerTag(DimLayerUser dimLayerUser)90 private static String getDimLayerTag(DimLayerUser dimLayerUser) { 91 return TAG_LOCAL + "/" + dimLayerUser.toShortString(); 92 } 93 getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser)94 private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) { 95 if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser=" 96 + dimLayerUser.toShortString()); 97 DimLayerState state = mState.get(dimLayerUser); 98 if (state == null) { 99 state = new DimLayerState(); 100 mState.put(dimLayerUser, state); 101 } 102 return state; 103 } 104 setContinueDimming(DimLayer.DimLayerUser dimLayerUser)105 private void setContinueDimming(DimLayer.DimLayerUser dimLayerUser) { 106 DimLayerState state = mState.get(dimLayerUser); 107 if (state == null) { 108 if (DEBUG_DIM_LAYER) Slog.w(TAG, "setContinueDimming, no state for: " 109 + dimLayerUser.toShortString()); 110 return; 111 } 112 state.continueDimming = true; 113 } 114 isDimming()115 boolean isDimming() { 116 for (int i = mState.size() - 1; i >= 0; i--) { 117 DimLayerState state = mState.valueAt(i); 118 if (state.dimLayer != null && state.dimLayer.isDimming()) { 119 return true; 120 } 121 } 122 return false; 123 } 124 resetDimming()125 void resetDimming() { 126 for (int i = mState.size() - 1; i >= 0; i--) { 127 mState.valueAt(i).continueDimming = false; 128 } 129 } 130 getContinueDimming(DimLayer.DimLayerUser dimLayerUser)131 private boolean getContinueDimming(DimLayer.DimLayerUser dimLayerUser) { 132 DimLayerState state = mState.get(dimLayerUser); 133 return state != null && state.continueDimming; 134 } 135 startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator newWinAnimator, boolean aboveApp)136 void startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser, 137 WindowStateAnimator newWinAnimator, boolean aboveApp) { 138 // Only set dim params on the highest dimmed layer. 139 // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer. 140 DimLayerState state = getOrCreateDimLayerState(dimLayerUser); 141 state.dimAbove = aboveApp; 142 if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded," 143 + " dimLayerUser=" + dimLayerUser.toShortString() 144 + " newWinAnimator=" + newWinAnimator 145 + " state.animator=" + state.animator); 146 if (newWinAnimator.getShown() && (state.animator == null 147 || !state.animator.getShown() 148 || state.animator.mAnimLayer <= newWinAnimator.mAnimLayer)) { 149 state.animator = newWinAnimator; 150 if (state.animator.mWin.mAppToken == null && !dimLayerUser.dimFullscreen()) { 151 // Dim should cover the entire screen for system windows. 152 mDisplayContent.getLogicalDisplayRect(mTmpBounds); 153 } else { 154 dimLayerUser.getDimBounds(mTmpBounds); 155 } 156 state.dimLayer.setBounds(mTmpBounds); 157 } 158 } 159 stopDimmingIfNeeded()160 void stopDimmingIfNeeded() { 161 if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded, mState.size()=" + mState.size()); 162 for (int i = mState.size() - 1; i >= 0; i--) { 163 DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i); 164 stopDimmingIfNeeded(dimLayerUser); 165 } 166 } 167 stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser)168 private void stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser) { 169 // No need to check if state is null, we know the key has a value. 170 DimLayerState state = mState.get(dimLayerUser); 171 if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded," 172 + " dimLayerUser=" + dimLayerUser.toShortString() 173 + " state.continueDimming=" + state.continueDimming 174 + " state.dimLayer.isDimming=" + state.dimLayer.isDimming()); 175 if (state.animator != null && state.animator.mWin.mWillReplaceWindow) { 176 return; 177 } 178 179 if (!state.continueDimming && state.dimLayer.isDimming()) { 180 state.animator = null; 181 dimLayerUser.getDimBounds(mTmpBounds); 182 state.dimLayer.setBounds(mTmpBounds); 183 } 184 } 185 animateDimLayers()186 boolean animateDimLayers() { 187 int fullScreen = -1; 188 int fullScreenAndDimming = -1; 189 boolean result = false; 190 191 for (int i = mState.size() - 1; i >= 0; i--) { 192 DimLayer.DimLayerUser user = mState.keyAt(i); 193 DimLayerState state = mState.valueAt(i); 194 // We have to check that we are actually the shared fullscreen layer 195 // for this path. If we began as non fullscreen and became fullscreen 196 // (e.g. Docked stack closing), then we may not be the shared layer 197 // and we have to make sure we always animate the layer. 198 if (user.dimFullscreen() && state.dimLayer == mSharedFullScreenDimLayer) { 199 fullScreen = i; 200 if (mState.valueAt(i).continueDimming) { 201 fullScreenAndDimming = i; 202 } 203 } else { 204 // We always want to animate the non fullscreen windows, they don't share their 205 // dim layers. 206 result |= animateDimLayers(user); 207 } 208 } 209 // For the shared, full screen dim layer, we prefer the animation that is causing it to 210 // appear. 211 if (fullScreenAndDimming != -1) { 212 result |= animateDimLayers(mState.keyAt(fullScreenAndDimming)); 213 } else if (fullScreen != -1) { 214 // If there is no animation for the full screen dim layer to appear, we can use any of 215 // the animators that will cause it to disappear. 216 result |= animateDimLayers(mState.keyAt(fullScreen)); 217 } 218 return result; 219 } 220 animateDimLayers(DimLayer.DimLayerUser dimLayerUser)221 private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) { 222 DimLayerState state = mState.get(dimLayerUser); 223 if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers," 224 + " dimLayerUser=" + dimLayerUser.toShortString() 225 + " state.animator=" + state.animator 226 + " state.continueDimming=" + state.continueDimming); 227 final int dimLayer; 228 final float dimAmount; 229 if (state.animator == null) { 230 dimLayer = state.dimLayer.getLayer(); 231 dimAmount = 0; 232 } else { 233 if (state.dimAbove) { 234 dimLayer = state.animator.mAnimLayer + LAYER_OFFSET_DIM; 235 dimAmount = DEFAULT_DIM_AMOUNT_DEAD_WINDOW; 236 } else { 237 dimLayer = state.animator.mAnimLayer - LAYER_OFFSET_DIM; 238 dimAmount = state.animator.mWin.mAttrs.dimAmount; 239 } 240 } 241 final float targetAlpha = state.dimLayer.getTargetAlpha(); 242 if (targetAlpha != dimAmount) { 243 if (state.animator == null) { 244 state.dimLayer.hide(DEFAULT_DIM_DURATION); 245 } else { 246 long duration = (state.animator.mAnimating && state.animator.mAnimation != null) 247 ? state.animator.mAnimation.computeDurationHint() 248 : DEFAULT_DIM_DURATION; 249 if (targetAlpha > dimAmount) { 250 duration = getDimLayerFadeDuration(duration); 251 } 252 state.dimLayer.show(dimLayer, dimAmount, duration); 253 } 254 } else if (state.dimLayer.getLayer() != dimLayer) { 255 state.dimLayer.setLayer(dimLayer); 256 } 257 if (state.dimLayer.isAnimating()) { 258 if (!mDisplayContent.mService.okToDisplay()) { 259 // Jump to the end of the animation. 260 state.dimLayer.show(); 261 } else { 262 return state.dimLayer.stepAnimation(); 263 } 264 } 265 return false; 266 } 267 isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator)268 boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) { 269 DimLayerState state = mState.get(dimLayerUser); 270 return state != null && state.animator == winAnimator && state.dimLayer.isDimming(); 271 } 272 getDimLayerFadeDuration(long duration)273 private long getDimLayerFadeDuration(long duration) { 274 TypedValue tv = new TypedValue(); 275 mDisplayContent.mService.mContext.getResources().getValue( 276 com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true); 277 if (tv.type == TypedValue.TYPE_FRACTION) { 278 duration = (long) tv.getFraction(duration, duration); 279 } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) { 280 duration = tv.data; 281 } 282 return duration; 283 } 284 close()285 void close() { 286 for (int i = mState.size() - 1; i >= 0; i--) { 287 DimLayerState state = mState.valueAt(i); 288 state.dimLayer.destroySurface(); 289 } 290 mState.clear(); 291 mSharedFullScreenDimLayer = null; 292 } 293 removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser)294 void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) { 295 DimLayerState state = mState.get(dimLayerUser); 296 if (state != null) { 297 // Destroy the surface, unless it's the shared fullscreen dim. 298 if (state.dimLayer != mSharedFullScreenDimLayer) { 299 state.dimLayer.destroySurface(); 300 } 301 mState.remove(dimLayerUser); 302 } 303 } 304 applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator)305 void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) { 306 applyDim(dimLayerUser, animator, false /* aboveApp */); 307 } 308 applyDimAbove(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator)309 void applyDimAbove(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) { 310 applyDim(dimLayerUser, animator, true /* aboveApp */); 311 } 312 applyDim( DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp)313 void applyDim( 314 DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) { 315 if (dimLayerUser == null) { 316 Slog.e(TAG, "Trying to apply dim layer for: " + this 317 + ", but no dim layer user found."); 318 return; 319 } 320 if (!getContinueDimming(dimLayerUser)) { 321 setContinueDimming(dimLayerUser); 322 if (!isDimming(dimLayerUser, animator)) { 323 if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming."); 324 startDimmingIfNeeded(dimLayerUser, animator, aboveApp); 325 } 326 } 327 } 328 329 private static class DimLayerState { 330 // The particular window requesting a dim layer. If null, hide dimLayer. 331 WindowStateAnimator animator; 332 // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the 333 // end then stop any dimming. 334 boolean continueDimming; 335 DimLayer dimLayer; 336 boolean dimAbove; 337 } 338 dump(String prefix, PrintWriter pw)339 void dump(String prefix, PrintWriter pw) { 340 pw.println(prefix + "DimLayerController"); 341 final String doubleSpace = " "; 342 final String prefixPlusDoubleSpace = prefix + doubleSpace; 343 344 for (int i = 0, n = mState.size(); i < n; i++) { 345 pw.println(prefixPlusDoubleSpace + mState.keyAt(i).toShortString()); 346 DimLayerState state = mState.valueAt(i); 347 pw.println(prefixPlusDoubleSpace + doubleSpace + "dimLayer=" 348 + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" : state.dimLayer) 349 + ", animator=" + state.animator + ", continueDimming=" + state.continueDimming); 350 if (state.dimLayer != null) { 351 state.dimLayer.printTo(prefixPlusDoubleSpace + doubleSpace, pw); 352 } 353 } 354 } 355 } 356