1 /*
2  * Copyright (C) 2014 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.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
20 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
21 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
22 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
24 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
25 
26 import android.graphics.PixelFormat;
27 import android.graphics.Rect;
28 import android.os.SystemClock;
29 import android.util.Slog;
30 import android.view.DisplayInfo;
31 import android.view.SurfaceControl;
32 
33 import java.io.PrintWriter;
34 
35 public class DimLayer {
36     private static final String TAG = TAG_WITH_CLASS_NAME ? "DimLayer" : TAG_WM;
37     private final WindowManagerService mService;
38 
39     /** Actual surface that dims */
40     private SurfaceControl mDimSurface;
41 
42     /** Last value passed to mDimSurface.setAlpha() */
43     private float mAlpha = 0;
44 
45     /** Last value passed to mDimSurface.setLayer() */
46     private int mLayer = -1;
47 
48     /** Next values to pass to mDimSurface.setPosition() and mDimSurface.setSize() */
49     private final Rect mBounds = new Rect();
50 
51     /** Last values passed to mDimSurface.setPosition() and mDimSurface.setSize() */
52     private final Rect mLastBounds = new Rect();
53 
54     /** True after mDimSurface.show() has been called, false after mDimSurface.hide(). */
55     private boolean mShowing = false;
56 
57     /** Value of mAlpha when beginning transition to mTargetAlpha */
58     private float mStartAlpha = 0;
59 
60     /** Final value of mAlpha following transition */
61     private float mTargetAlpha = 0;
62 
63     /** Time in units of SystemClock.uptimeMillis() at which the current transition started */
64     private long mStartTime;
65 
66     /** Time in milliseconds to take to transition from mStartAlpha to mTargetAlpha */
67     private long mDuration;
68 
69     private boolean mDestroyed = false;
70 
71     private final int mDisplayId;
72 
73 
74     /** Interface implemented by users of the dim layer */
75     interface DimLayerUser {
76         /** Returns true if the  dim should be fullscreen. */
dimFullscreen()77         boolean dimFullscreen();
78         /** Returns the display info. of the dim layer user. */
getDisplayInfo()79         DisplayInfo getDisplayInfo();
80         /** Returns true if the dim layer user is currently attached to a display */
isAttachedToDisplay()81         boolean isAttachedToDisplay();
82         /** Gets the bounds of the dim layer user. */
getDimBounds(Rect outBounds)83         void getDimBounds(Rect outBounds);
toShortString()84         String toShortString();
85     }
86     /** The user of this dim layer. */
87     private final DimLayerUser mUser;
88 
89     private final String mName;
90 
DimLayer(WindowManagerService service, DimLayerUser user, int displayId, String name)91     DimLayer(WindowManagerService service, DimLayerUser user, int displayId, String name) {
92         mUser = user;
93         mDisplayId = displayId;
94         mService = service;
95         mName = name;
96         if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: displayId=" + displayId);
97     }
98 
constructSurface(WindowManagerService service)99     private void constructSurface(WindowManagerService service) {
100         service.openSurfaceTransaction();
101         try {
102             if (DEBUG_SURFACE_TRACE) {
103                 mDimSurface = new WindowSurfaceController.SurfaceTrace(service.mFxSession,
104                     "DimSurface",
105                     16, 16, PixelFormat.OPAQUE,
106                     SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
107             } else {
108                 mDimSurface = new SurfaceControl(service.mFxSession, mName,
109                     16, 16, PixelFormat.OPAQUE,
110                     SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
111             }
112             if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
113                     "  DIM " + mDimSurface + ": CREATE");
114             mDimSurface.setLayerStack(mDisplayId);
115             adjustBounds();
116             adjustAlpha(mAlpha);
117             adjustLayer(mLayer);
118         } catch (Exception e) {
119             Slog.e(TAG_WM, "Exception creating Dim surface", e);
120         } finally {
121             service.closeSurfaceTransaction();
122         }
123     }
124 
125     /** Return true if dim layer is showing */
isDimming()126     boolean isDimming() {
127         return mTargetAlpha != 0;
128     }
129 
130     /** Return true if in a transition period */
isAnimating()131     boolean isAnimating() {
132         return mTargetAlpha != mAlpha;
133     }
134 
getTargetAlpha()135     float getTargetAlpha() {
136         return mTargetAlpha;
137     }
138 
setLayer(int layer)139     void setLayer(int layer) {
140         if (mLayer == layer) {
141             return;
142         }
143         mLayer = layer;
144         adjustLayer(layer);
145     }
146 
adjustLayer(int layer)147     private void adjustLayer(int layer) {
148         if (mDimSurface != null) {
149             mDimSurface.setLayer(layer);
150         }
151     }
152 
getLayer()153     int getLayer() {
154         return mLayer;
155     }
156 
setAlpha(float alpha)157     private void setAlpha(float alpha) {
158         if (mAlpha == alpha) {
159             return;
160         }
161         mAlpha = alpha;
162         adjustAlpha(alpha);
163     }
164 
adjustAlpha(float alpha)165     private void adjustAlpha(float alpha) {
166         if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha alpha=" + alpha);
167         try {
168             if (mDimSurface != null) {
169                 mDimSurface.setAlpha(alpha);
170             }
171             if (alpha == 0 && mShowing) {
172                 if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha hiding");
173                 if (mDimSurface != null) {
174                     mDimSurface.hide();
175                     mShowing = false;
176                 }
177             } else if (alpha > 0 && !mShowing) {
178                 if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha showing");
179                 if (mDimSurface != null) {
180                     mDimSurface.show();
181                     mShowing = true;
182                 }
183             }
184         } catch (RuntimeException e) {
185             Slog.w(TAG, "Failure setting alpha immediately", e);
186         }
187     }
188 
189     /**
190      * NOTE: Must be called with Surface transaction open.
191      */
adjustBounds()192     private void adjustBounds() {
193         if (mUser.dimFullscreen()) {
194             getBoundsForFullscreen(mBounds);
195         }
196 
197         if (mDimSurface != null) {
198             mDimSurface.setPosition(mBounds.left, mBounds.top);
199             mDimSurface.setSize(mBounds.width(), mBounds.height());
200             if (DEBUG_DIM_LAYER) Slog.v(TAG,
201                     "adjustBounds user=" + mUser.toShortString() + " mBounds=" + mBounds);
202         }
203 
204         mLastBounds.set(mBounds);
205     }
206 
getBoundsForFullscreen(Rect outBounds)207     private void getBoundsForFullscreen(Rect outBounds) {
208         final int dw, dh;
209         final float xPos, yPos;
210         // Set surface size to screen size.
211         final DisplayInfo info = mUser.getDisplayInfo();
212         // Multiply by 1.5 so that rotating a frozen surface that includes this does not expose
213         // a corner.
214         dw = (int) (info.logicalWidth * 1.5);
215         dh = (int) (info.logicalHeight * 1.5);
216         // back off position so 1/4 of Surface is before and 1/4 is after.
217         xPos = -1 * dw / 6;
218         yPos = -1 * dh / 6;
219         outBounds.set((int) xPos, (int) yPos, (int) xPos + dw, (int) yPos + dh);
220     }
221 
setBoundsForFullscreen()222     void setBoundsForFullscreen() {
223         getBoundsForFullscreen(mBounds);
224         setBounds(mBounds);
225     }
226 
227     /** @param bounds The new bounds to set */
setBounds(Rect bounds)228     void setBounds(Rect bounds) {
229         mBounds.set(bounds);
230         if (isDimming() && !mLastBounds.equals(bounds)) {
231             try {
232                 mService.openSurfaceTransaction();
233                 adjustBounds();
234             } catch (RuntimeException e) {
235                 Slog.w(TAG, "Failure setting size", e);
236             } finally {
237                 mService.closeSurfaceTransaction();
238             }
239         }
240     }
241 
242     /**
243      * @param duration The time to test.
244      * @return True if the duration would lead to an earlier end to the current animation.
245      */
durationEndsEarlier(long duration)246     private boolean durationEndsEarlier(long duration) {
247         return SystemClock.uptimeMillis() + duration < mStartTime + mDuration;
248     }
249 
250     /** Jump to the end of the animation.
251      * NOTE: Must be called with Surface transaction open. */
show()252     void show() {
253         if (isAnimating()) {
254             if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: immediate");
255             show(mLayer, mTargetAlpha, 0);
256         }
257     }
258 
259     /**
260      * Begin an animation to a new dim value.
261      * NOTE: Must be called with Surface transaction open.
262      *
263      * @param layer The layer to set the surface to.
264      * @param alpha The dim value to end at.
265      * @param duration How long to take to get there in milliseconds.
266      */
show(int layer, float alpha, long duration)267     void show(int layer, float alpha, long duration) {
268         if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: layer=" + layer + " alpha=" + alpha
269                 + " duration=" + duration + ", mDestroyed=" + mDestroyed);
270         if (mDestroyed) {
271             Slog.e(TAG, "show: no Surface");
272             // Make sure isAnimating() returns false.
273             mTargetAlpha = mAlpha = 0;
274             return;
275         }
276 
277         if (mDimSurface == null) {
278             constructSurface(mService);
279         }
280 
281         if (!mLastBounds.equals(mBounds)) {
282             adjustBounds();
283         }
284         setLayer(layer);
285 
286         long curTime = SystemClock.uptimeMillis();
287         final boolean animating = isAnimating();
288         if ((animating && (mTargetAlpha != alpha || durationEndsEarlier(duration)))
289                 || (!animating && mAlpha != alpha)) {
290             if (duration <= 0) {
291                 // No animation required, just set values.
292                 setAlpha(alpha);
293             } else {
294                 // Start or continue animation with new parameters.
295                 mStartAlpha = mAlpha;
296                 mStartTime = curTime;
297                 mDuration = duration;
298             }
299         }
300         mTargetAlpha = alpha;
301         if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: mStartAlpha=" + mStartAlpha + " mStartTime="
302                 + mStartTime + " mTargetAlpha=" + mTargetAlpha);
303     }
304 
305     /** Immediate hide.
306      * NOTE: Must be called with Surface transaction open. */
hide()307     void hide() {
308         if (mShowing) {
309             if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: immediate");
310             hide(0);
311         }
312     }
313 
314     /**
315      * Gradually fade to transparent.
316      * NOTE: Must be called with Surface transaction open.
317      *
318      * @param duration Time to fade in milliseconds.
319      */
hide(long duration)320     void hide(long duration) {
321         if (mShowing && (mTargetAlpha != 0 || durationEndsEarlier(duration))) {
322             if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: duration=" + duration);
323             show(mLayer, 0, duration);
324         }
325     }
326 
327     /**
328      * Advance the dimming per the last #show(int, float, long) call.
329      * NOTE: Must be called with Surface transaction open.
330      *
331      * @return True if animation is still required after this step.
332      */
stepAnimation()333     boolean stepAnimation() {
334         if (mDestroyed) {
335             Slog.e(TAG, "stepAnimation: surface destroyed");
336             // Ensure that isAnimating() returns false;
337             mTargetAlpha = mAlpha = 0;
338             return false;
339         }
340         if (isAnimating()) {
341             final long curTime = SystemClock.uptimeMillis();
342             final float alphaDelta = mTargetAlpha - mStartAlpha;
343             float alpha = mStartAlpha + alphaDelta * (curTime - mStartTime) / mDuration;
344             if (alphaDelta > 0 && alpha > mTargetAlpha ||
345                     alphaDelta < 0 && alpha < mTargetAlpha) {
346                 // Don't exceed limits.
347                 alpha = mTargetAlpha;
348             }
349             if (DEBUG_DIM_LAYER) Slog.v(TAG, "stepAnimation: curTime=" + curTime + " alpha=" + alpha);
350             setAlpha(alpha);
351         }
352 
353         return isAnimating();
354     }
355 
356     /** Cleanup */
destroySurface()357     void destroySurface() {
358         if (DEBUG_DIM_LAYER) Slog.v(TAG, "destroySurface.");
359         if (mDimSurface != null) {
360             mDimSurface.destroy();
361             mDimSurface = null;
362         }
363         mDestroyed = true;
364     }
365 
printTo(String prefix, PrintWriter pw)366     public void printTo(String prefix, PrintWriter pw) {
367         pw.print(prefix); pw.print("mDimSurface="); pw.print(mDimSurface);
368                 pw.print(" mLayer="); pw.print(mLayer);
369                 pw.print(" mAlpha="); pw.println(mAlpha);
370         pw.print(prefix); pw.print("mLastBounds="); pw.print(mLastBounds.toShortString());
371                 pw.print(" mBounds="); pw.println(mBounds.toShortString());
372         pw.print(prefix); pw.print("Last animation: ");
373                 pw.print(" mDuration="); pw.print(mDuration);
374                 pw.print(" mStartTime="); pw.print(mStartTime);
375                 pw.print(" curTime="); pw.println(SystemClock.uptimeMillis());
376         pw.print(prefix); pw.print(" mStartAlpha="); pw.print(mStartAlpha);
377                 pw.print(" mTargetAlpha="); pw.println(mTargetAlpha);
378     }
379 }
380