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