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