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