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.display;
18 
19 import static com.android.internal.policy.TransitionAnimation.hasProtectedContent;
20 
21 import android.content.Context;
22 import android.graphics.BLASTBufferQueue;
23 import android.graphics.PixelFormat;
24 import android.graphics.SurfaceTexture;
25 import android.hardware.display.DisplayManagerInternal;
26 import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
27 import android.opengl.EGL14;
28 import android.opengl.EGLConfig;
29 import android.opengl.EGLContext;
30 import android.opengl.EGLDisplay;
31 import android.opengl.EGLSurface;
32 import android.opengl.GLES11Ext;
33 import android.opengl.GLES20;
34 import android.util.Slog;
35 import android.view.Display;
36 import android.view.DisplayInfo;
37 import android.view.Surface;
38 import android.view.Surface.OutOfResourcesException;
39 import android.view.SurfaceControl;
40 import android.view.SurfaceControl.Transaction;
41 import android.window.ScreenCapture;
42 
43 import com.android.server.LocalServices;
44 import com.android.server.policy.WindowManagerPolicy;
45 
46 import libcore.io.Streams;
47 
48 import java.io.IOException;
49 import java.io.InputStream;
50 import java.io.InputStreamReader;
51 import java.io.PrintWriter;
52 import java.nio.ByteBuffer;
53 import java.nio.ByteOrder;
54 import java.nio.FloatBuffer;
55 
56 /**
57  * <p>
58  * Animates a screen transition from on to off or off to on by applying
59  * some GL transformations to a screenshot.
60  * </p><p>
61  * This component must only be created or accessed by the {@link Looper} thread
62  * that belongs to the {@link DisplayPowerController}.
63  * </p>
64  */
65 final class ColorFade {
66     private static final String TAG = "ColorFade";
67 
68     private static final boolean DEBUG = false;
69 
70     // The layer for the electron beam surface.
71     // This is currently hardcoded to be one layer above the boot animation.
72     private static final int COLOR_FADE_LAYER = WindowManagerPolicy.COLOR_FADE_LAYER;
73 
74     // The number of frames to draw when preparing the animation so that it will
75     // be ready to run smoothly.  We use 3 frames because we are triple-buffered.
76     // See code for details.
77     private static final int DEJANK_FRAMES = 3;
78 
79     private static final int EGL_GL_COLORSPACE_KHR = 0x309D;
80     private static final int EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490;
81     private static final int EGL_PROTECTED_CONTENT_EXT = 0x32C0;
82 
83     private final int mDisplayId;
84 
85     // Set to true when the animation context has been fully prepared.
86     private boolean mPrepared;
87     private boolean mCreatedResources;
88     private int mMode;
89 
90     private final DisplayManagerInternal mDisplayManagerInternal;
91     private int mDisplayLayerStack; // layer stack associated with primary display
92     private int mDisplayWidth;      // real width, not rotated
93     private int mDisplayHeight;     // real height, not rotated
94     private SurfaceControl mSurfaceControl;
95     private Surface mSurface;
96     private SurfaceControl mBLASTSurfaceControl;
97     private BLASTBufferQueue mBLASTBufferQueue;
98     private NaturalSurfaceLayout mSurfaceLayout;
99     private EGLDisplay mEglDisplay;
100     private EGLConfig mEglConfig;
101     private EGLContext mEglContext;
102     private EGLSurface mEglSurface;
103     private boolean mSurfaceVisible;
104     private float mSurfaceAlpha;
105     private boolean mLastWasWideColor;
106     private boolean mLastWasProtectedContent;
107 
108     // Texture names.  We only use one texture, which contains the screenshot.
109     private final int[] mTexNames = new int[1];
110     private boolean mTexNamesGenerated;
111     private final float mTexMatrix[] = new float[16];
112     private final float mProjMatrix[] = new float[16];
113     private final int[] mGLBuffers = new int[2];
114     private int mTexCoordLoc, mVertexLoc, mTexUnitLoc, mProjMatrixLoc, mTexMatrixLoc;
115     private int mOpacityLoc, mGammaLoc;
116     private int mProgram;
117 
118     // Vertex and corresponding texture coordinates.
119     // We have 4 2D vertices, so 8 elements.  The vertices form a quad.
120     private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8);
121     private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8);
122 
123     private final Transaction mTransaction = new Transaction();
124 
125     /**
126      * Animates an color fade warming up.
127      */
128     public static final int MODE_WARM_UP = 0;
129 
130     /**
131      * Animates an color fade shutting off.
132      */
133     public static final int MODE_COOL_DOWN = 1;
134 
135     /**
136      * Animates a simple dim layer to fade the contents of the screen in or out progressively.
137      */
138     public static final int MODE_FADE = 2;
139 
ColorFade(int displayId)140     public ColorFade(int displayId) {
141         mDisplayId = displayId;
142         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
143     }
144 
145     /**
146      * Warms up the color fade in preparation for turning on or off.
147      * This method prepares a GL context, and captures a screen shot.
148      *
149      * @param mode The desired mode for the upcoming animation.
150      * @return True if the color fade is ready, false if it is uncontrollable.
151      */
prepare(Context context, int mode)152     public boolean prepare(Context context, int mode) {
153         if (DEBUG) {
154             Slog.d(TAG, "prepare: mode=" + mode);
155         }
156 
157         mMode = mode;
158 
159         DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId);
160         if (displayInfo == null) {
161             // displayInfo can be null if the associated display has been removed. There
162             // is a delay between the display being removed and ColorFade being dismissed.
163             return false;
164         }
165 
166         // Get the display size and layer stack.
167         // This is not expected to change while the color fade surface is showing.
168         mDisplayLayerStack = displayInfo.layerStack;
169         mDisplayWidth = displayInfo.getNaturalWidth();
170         mDisplayHeight = displayInfo.getNaturalHeight();
171 
172         final boolean isWideColor = displayInfo.colorMode == Display.COLOR_MODE_DISPLAY_P3;
173         // Set mPrepared here so if initialization fails, resources can be cleaned up.
174         mPrepared = true;
175 
176         final ScreenCapture.ScreenshotHardwareBuffer hardwareBuffer = captureScreen();
177         if (hardwareBuffer == null) {
178             dismiss();
179             return false;
180         }
181 
182         final boolean isProtected = hasProtectedContent(hardwareBuffer.getHardwareBuffer());
183         if (!createSurfaceControl(hardwareBuffer.containsSecureLayers())) {
184             dismiss();
185             return false;
186         }
187 
188         // MODE_FADE use ColorLayer to implement.
189         if (mMode == MODE_FADE) {
190             return true;
191         }
192 
193         if (!(createEglContext(isProtected) && createEglSurface(isProtected, isWideColor)
194                 && setScreenshotTextureAndSetViewport(hardwareBuffer, displayInfo.rotation))) {
195             dismiss();
196             return false;
197         }
198 
199         // Init GL
200         if (!attachEglContext()) {
201             return false;
202         }
203         try {
204             if (!initGLShaders(context) || !initGLBuffers() || checkGlErrors("prepare")) {
205                 detachEglContext();
206                 dismiss();
207                 return false;
208             }
209         } finally {
210             detachEglContext();
211         }
212 
213         // Done.
214         mCreatedResources = true;
215         mLastWasProtectedContent = isProtected;
216         mLastWasWideColor = isWideColor;
217 
218         // Dejanking optimization.
219         // Some GL drivers can introduce a lot of lag in the first few frames as they
220         // initialize their state and allocate graphics buffers for rendering.
221         // Work around this problem by rendering the first frame of the animation a few
222         // times.  The rest of the animation should run smoothly thereafter.
223         // The frames we draw here aren't visible because we are essentially just
224         // painting the screenshot as-is.
225         if (mode == MODE_COOL_DOWN) {
226             for (int i = 0; i < DEJANK_FRAMES; i++) {
227                 draw(1.0f);
228             }
229         }
230         return true;
231     }
232 
readFile(Context context, int resourceId)233     private String readFile(Context context, int resourceId) {
234         try{
235             InputStream stream = context.getResources().openRawResource(resourceId);
236             return new String(Streams.readFully(new InputStreamReader(stream)));
237         }
238         catch (IOException e) {
239             Slog.e(TAG, "Unrecognized shader " + Integer.toString(resourceId));
240             throw new RuntimeException(e);
241         }
242     }
243 
loadShader(Context context, int resourceId, int type)244     private int loadShader(Context context, int resourceId, int type) {
245         String source = readFile(context, resourceId);
246 
247         int shader = GLES20.glCreateShader(type);
248 
249         GLES20.glShaderSource(shader, source);
250         GLES20.glCompileShader(shader);
251 
252         int[] compiled = new int[1];
253         GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
254         if (compiled[0] == 0) {
255             Slog.e(TAG, "Could not compile shader " + shader + ", " + type + ":");
256             Slog.e(TAG, GLES20.glGetShaderSource(shader));
257             Slog.e(TAG, GLES20.glGetShaderInfoLog(shader));
258             GLES20.glDeleteShader(shader);
259             shader = 0;
260         }
261 
262         return shader;
263     }
264 
initGLShaders(Context context)265     private boolean initGLShaders(Context context) {
266         int vshader = loadShader(context, com.android.internal.R.raw.color_fade_vert,
267                 GLES20.GL_VERTEX_SHADER);
268         int fshader = loadShader(context, com.android.internal.R.raw.color_fade_frag,
269                 GLES20.GL_FRAGMENT_SHADER);
270         GLES20.glReleaseShaderCompiler();
271         if (vshader == 0 || fshader == 0) return false;
272 
273         mProgram = GLES20.glCreateProgram();
274 
275         GLES20.glAttachShader(mProgram, vshader);
276         GLES20.glAttachShader(mProgram, fshader);
277         GLES20.glDeleteShader(vshader);
278         GLES20.glDeleteShader(fshader);
279 
280         GLES20.glLinkProgram(mProgram);
281 
282         mVertexLoc = GLES20.glGetAttribLocation(mProgram, "position");
283         mTexCoordLoc = GLES20.glGetAttribLocation(mProgram, "uv");
284 
285         mProjMatrixLoc = GLES20.glGetUniformLocation(mProgram, "proj_matrix");
286         mTexMatrixLoc = GLES20.glGetUniformLocation(mProgram, "tex_matrix");
287 
288         mOpacityLoc = GLES20.glGetUniformLocation(mProgram, "opacity");
289         mGammaLoc = GLES20.glGetUniformLocation(mProgram, "gamma");
290         mTexUnitLoc = GLES20.glGetUniformLocation(mProgram, "texUnit");
291 
292         GLES20.glUseProgram(mProgram);
293         GLES20.glUniform1i(mTexUnitLoc, 0);
294         GLES20.glUseProgram(0);
295 
296         return true;
297     }
298 
destroyGLShaders()299     private void destroyGLShaders() {
300         GLES20.glDeleteProgram(mProgram);
301         checkGlErrors("glDeleteProgram");
302     }
303 
initGLBuffers()304     private boolean initGLBuffers() {
305         //Fill vertices
306         setQuad(mVertexBuffer, 0, 0, mDisplayWidth, mDisplayHeight);
307 
308         // Setup GL Textures
309         GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTexNames[0]);
310         GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
311                 GLES20.GL_NEAREST);
312         GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
313                 GLES20.GL_NEAREST);
314         GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
315                 GLES20.GL_CLAMP_TO_EDGE);
316         GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
317                 GLES20.GL_CLAMP_TO_EDGE);
318         GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
319 
320         // Setup GL Buffers
321         GLES20.glGenBuffers(2, mGLBuffers, 0);
322 
323         // fill vertex buffer
324         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLBuffers[0]);
325         GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mVertexBuffer.capacity() * 4,
326                             mVertexBuffer, GLES20.GL_STATIC_DRAW);
327 
328         // fill tex buffer
329         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLBuffers[1]);
330         GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mTexCoordBuffer.capacity() * 4,
331                             mTexCoordBuffer, GLES20.GL_STATIC_DRAW);
332 
333         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
334 
335         return true;
336     }
337 
destroyGLBuffers()338     private void destroyGLBuffers() {
339         GLES20.glDeleteBuffers(2, mGLBuffers, 0);
340         checkGlErrors("glDeleteBuffers");
341     }
342 
setQuad(FloatBuffer vtx, float x, float y, float w, float h)343     private static void setQuad(FloatBuffer vtx, float x, float y, float w, float h) {
344         if (DEBUG) {
345             Slog.d(TAG, "setQuad: x=" + x + ", y=" + y + ", w=" + w + ", h=" + h);
346         }
347         vtx.put(0, x);
348         vtx.put(1, y);
349         vtx.put(2, x);
350         vtx.put(3, y + h);
351         vtx.put(4, x + w);
352         vtx.put(5, y + h);
353         vtx.put(6, x + w);
354         vtx.put(7, y);
355     }
356 
357     /**
358      * Dismisses the color fade animation resources.
359      *
360      * This function destroys the resources that are created for the color fade
361      * animation but does not clean up the surface.
362      */
dismissResources()363     public void dismissResources() {
364         if (DEBUG) {
365             Slog.d(TAG, "dismissResources");
366         }
367 
368         if (mCreatedResources) {
369             attachEglContext();
370             try {
371                 destroyScreenshotTexture();
372                 destroyGLShaders();
373                 destroyGLBuffers();
374                 destroyEglSurface();
375             } finally {
376                 detachEglContext();
377             }
378             // This is being called with no active context so shouldn't be
379             // needed but is safer to not change for now.
380             GLES20.glFlush();
381             mCreatedResources = false;
382         }
383     }
384 
385     /**
386      * Dismisses the color fade animation surface and cleans up.
387      *
388      * To prevent stray photons from leaking out after the color fade has been
389      * turned off, it is a good idea to defer dismissing the animation until the
390      * color fade has been turned back on fully.
391      */
dismiss()392     public void dismiss() {
393         if (DEBUG) {
394             Slog.d(TAG, "dismiss");
395         }
396 
397         if (mPrepared) {
398             dismissResources();
399             destroySurface();
400             mPrepared = false;
401         }
402     }
403 
404     /**
405      * Draws an animation frame showing the color fade activated at the
406      * specified level.
407      *
408      * @param level The color fade level.
409      * @return True if successful.
410      */
draw(float level)411     public boolean draw(float level) {
412         if (DEBUG) {
413             Slog.d(TAG, "drawFrame: level=" + level);
414         }
415 
416         if (!mPrepared) {
417             return false;
418         }
419 
420         if (mMode == MODE_FADE) {
421             return showSurface(1.0f - level);
422         }
423 
424         if (!attachEglContext()) {
425             return false;
426         }
427         try {
428             // Clear frame to solid black.
429             GLES20.glClearColor(0f, 0f, 0f, 1f);
430             GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
431 
432             // Draw the frame.
433             double one_minus_level = 1 - level;
434             double cos = Math.cos(Math.PI * one_minus_level);
435             double sign = cos < 0 ? -1 : 1;
436             float opacity = (float) -Math.pow(one_minus_level, 2) + 1;
437             float gamma = (float) ((0.5d * sign * Math.pow(cos, 2) + 0.5d) * 0.9d + 0.1d);
438             drawFaded(opacity, 1.f / gamma);
439             if (checkGlErrors("drawFrame")) {
440                 return false;
441             }
442 
443             EGL14.eglSwapBuffers(mEglDisplay, mEglSurface);
444         } finally {
445             detachEglContext();
446         }
447         return showSurface(1.0f);
448     }
449 
450     private void drawFaded(float opacity, float gamma) {
451         if (DEBUG) {
452             Slog.d(TAG, "drawFaded: opacity=" + opacity + ", gamma=" + gamma);
453         }
454         // Use shaders
455         GLES20.glUseProgram(mProgram);
456 
457         // Set Uniforms
458         GLES20.glUniformMatrix4fv(mProjMatrixLoc, 1, false, mProjMatrix, 0);
459         GLES20.glUniformMatrix4fv(mTexMatrixLoc, 1, false, mTexMatrix, 0);
460         GLES20.glUniform1f(mOpacityLoc, opacity);
461         GLES20.glUniform1f(mGammaLoc, gamma);
462 
463         // Use textures
464         GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
465         GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTexNames[0]);
466 
467         // draw the plane
468         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLBuffers[0]);
469         GLES20.glEnableVertexAttribArray(mVertexLoc);
470         GLES20.glVertexAttribPointer(mVertexLoc, 2, GLES20.GL_FLOAT, false, 0, 0);
471 
472         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLBuffers[1]);
473         GLES20.glEnableVertexAttribArray(mTexCoordLoc);
474         GLES20.glVertexAttribPointer(mTexCoordLoc, 2, GLES20.GL_FLOAT, false, 0, 0);
475 
476         GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 4);
477 
478         // clean up
479         GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
480         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
481     }
482 
483     private void ortho(float left, float right, float bottom, float top, float znear, float zfar) {
484         mProjMatrix[0] = 2f / (right - left);
485         mProjMatrix[1] = 0;
486         mProjMatrix[2] = 0;
487         mProjMatrix[3] = 0;
488         mProjMatrix[4] = 0;
489         mProjMatrix[5] = 2f / (top - bottom);
490         mProjMatrix[6] = 0;
491         mProjMatrix[7] = 0;
492         mProjMatrix[8] = 0;
493         mProjMatrix[9] = 0;
494         mProjMatrix[10] = -2f / (zfar - znear);
495         mProjMatrix[11] = 0;
496         mProjMatrix[12] = -(right + left) / (right - left);
497         mProjMatrix[13] = -(top + bottom) / (top - bottom);
498         mProjMatrix[14] = -(zfar + znear) / (zfar - znear);
499         mProjMatrix[15] = 1f;
500     }
501 
502     private boolean setScreenshotTextureAndSetViewport(
503             ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer,
504             @Surface.Rotation int rotation) {
505         if (!attachEglContext()) {
506             return false;
507         }
508         try {
509             if (!mTexNamesGenerated) {
510                 GLES20.glGenTextures(1, mTexNames, 0);
511                 if (checkGlErrors("glGenTextures")) {
512                     return false;
513                 }
514                 mTexNamesGenerated = true;
515             }
516 
517             final SurfaceTexture st = new SurfaceTexture(mTexNames[0]);
518             final Surface s = new Surface(st);
519             try {
520                 s.attachAndQueueBufferWithColorSpace(screenshotBuffer.getHardwareBuffer(),
521                         screenshotBuffer.getColorSpace());
522 
523                 st.updateTexImage();
524                 st.getTransformMatrix(mTexMatrix);
525             } finally {
526                 s.release();
527                 st.release();
528             }
529             // if screen is rotated, map texture starting different corner
530             int indexDelta = (rotation == Surface.ROTATION_90) ? 2
531                             : (rotation == Surface.ROTATION_180) ? 4
532                             : (rotation == Surface.ROTATION_270) ? 6 : 0;
533 
534             // Set up texture coordinates for a quad.
535             // We might need to change this if the texture ends up being
536             // a different size from the display for some reason.
537             mTexCoordBuffer.put(indexDelta, 0f);
538             mTexCoordBuffer.put(indexDelta + 1, 0f);
539             mTexCoordBuffer.put((indexDelta + 2) % 8, 0f);
540             mTexCoordBuffer.put((indexDelta + 3) % 8, 1f);
541             mTexCoordBuffer.put((indexDelta + 4) % 8, 1f);
542             mTexCoordBuffer.put((indexDelta + 5) % 8, 1f);
543             mTexCoordBuffer.put((indexDelta + 6) % 8, 1f);
544             mTexCoordBuffer.put((indexDelta + 7) % 8, 0f);
545 
546             // Set up our viewport.
547             GLES20.glViewport(0, 0, mDisplayWidth, mDisplayHeight);
548             ortho(0, mDisplayWidth, 0, mDisplayHeight, -1, 1);
549         } finally {
550             detachEglContext();
551         }
552         return true;
553     }
554 
555     private void destroyScreenshotTexture() {
556         if (mTexNamesGenerated) {
557             mTexNamesGenerated = false;
558             GLES20.glDeleteTextures(1, mTexNames, 0);
559             checkGlErrors("glDeleteTextures");
560         }
561     }
562 
563     private ScreenCapture.ScreenshotHardwareBuffer captureScreen() {
564         ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
565                 mDisplayManagerInternal.systemScreenshot(mDisplayId);
566         if (screenshotBuffer == null) {
567             Slog.e(TAG, "Failed to take screenshot. Buffer is null");
568             return null;
569         }
570         return screenshotBuffer;
571     }
572 
573     private boolean createSurfaceControl(boolean isSecure) {
574         if (mSurfaceControl != null) {
575             mTransaction.setSecure(mSurfaceControl, isSecure).apply();
576             return true;
577         }
578 
579         try {
580             final SurfaceControl.Builder builder = new SurfaceControl.Builder()
581                     .setName("ColorFade")
582                     .setSecure(isSecure)
583                     .setCallsite("ColorFade.createSurface");
584             if (mMode == MODE_FADE) {
585                 builder.setColorLayer();
586             } else {
587                 builder.setContainerLayer();
588             }
589             mSurfaceControl = builder.build();
590         } catch (OutOfResourcesException ex) {
591             Slog.e(TAG, "Unable to create surface.", ex);
592             return false;
593         }
594 
595         mTransaction.setLayerStack(mSurfaceControl, mDisplayLayerStack);
596         mTransaction.setWindowCrop(mSurfaceControl, mDisplayWidth, mDisplayHeight);
597         mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal, mDisplayId,
598                 mSurfaceControl);
599         mSurfaceLayout.onDisplayTransaction(mTransaction);
600         mTransaction.apply();
601 
602         if (mMode != MODE_FADE) {
603             final SurfaceControl.Builder b = new SurfaceControl.Builder()
604                     .setName("ColorFade BLAST")
605                     .setParent(mSurfaceControl)
606                     .setHidden(false)
607                     .setSecure(isSecure)
608                     .setBLASTLayer();
609             mBLASTSurfaceControl = b.build();
610             mBLASTBufferQueue = new BLASTBufferQueue("ColorFade", mBLASTSurfaceControl,
611                     mDisplayWidth, mDisplayHeight, PixelFormat.TRANSLUCENT);
612             mSurface = mBLASTBufferQueue.createSurface();
613         }
614         return true;
615     }
616 
617     private boolean createEglContext(boolean isProtected) {
618         if (mEglDisplay == null) {
619             mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
620             if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {
621                 logEglError("eglGetDisplay");
622                 return false;
623             }
624 
625             int[] version = new int[2];
626             if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {
627                 mEglDisplay = null;
628                 logEglError("eglInitialize");
629                 return false;
630             }
631         }
632 
633         if (mEglConfig == null) {
634             int[] eglConfigAttribList = new int[] {
635                     EGL14.EGL_RENDERABLE_TYPE,
636                     EGL14.EGL_OPENGL_ES2_BIT,
637                     EGL14.EGL_RED_SIZE, 8,
638                     EGL14.EGL_GREEN_SIZE, 8,
639                     EGL14.EGL_BLUE_SIZE, 8,
640                     EGL14.EGL_ALPHA_SIZE, 8,
641                     EGL14.EGL_NONE
642             };
643             int[] numEglConfigs = new int[1];
644             EGLConfig[] eglConfigs = new EGLConfig[1];
645             if (!EGL14.eglChooseConfig(mEglDisplay, eglConfigAttribList, 0,
646                     eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) {
647                 logEglError("eglChooseConfig");
648                 return false;
649             }
650             if (numEglConfigs[0] <= 0) {
651                 Slog.e(TAG, "no valid config found");
652                 return false;
653             }
654 
655             mEglConfig = eglConfigs[0];
656         }
657 
658         // The old context needs to be destroyed if the protected flag has changed. The context will
659         // be recreated based on the protected flag
660         if (mEglContext != null && isProtected != mLastWasProtectedContent) {
661             EGL14.eglDestroyContext(mEglDisplay, mEglContext);
662             mEglContext = null;
663         }
664 
665         if (mEglContext == null) {
666             int[] eglContextAttribList = new int[] {
667                     EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
668                     EGL14.EGL_NONE, EGL14.EGL_NONE,
669                     EGL14.EGL_NONE
670             };
671             if (isProtected) {
672                 eglContextAttribList[2] = EGL_PROTECTED_CONTENT_EXT;
673                 eglContextAttribList[3] = EGL14.EGL_TRUE;
674             }
675             mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, EGL14.EGL_NO_CONTEXT,
676                     eglContextAttribList, 0);
677             if (mEglContext == null) {
678                 logEglError("eglCreateContext");
679                 return false;
680             }
681         }
682         return true;
683     }
684 
685     private boolean createEglSurface(boolean isProtected, boolean isWideColor) {
686         // The old surface needs to be destroyed if either the protected flag or wide color flag has
687         // changed. The surface will be recreated based on the new flags.
688         boolean didContentAttributesChange =
689                 isProtected != mLastWasProtectedContent || isWideColor != mLastWasWideColor;
690         if (mEglSurface != null && didContentAttributesChange) {
691             EGL14.eglDestroySurface(mEglDisplay, mEglSurface);
692             mEglSurface = null;
693         }
694 
695         if (mEglSurface == null) {
696             int[] eglSurfaceAttribList = new int[] {
697                     EGL14.EGL_NONE,
698                     EGL14.EGL_NONE,
699                     EGL14.EGL_NONE,
700                     EGL14.EGL_NONE,
701                     EGL14.EGL_NONE
702             };
703 
704             int index = 0;
705             // If the current display is in wide color, then so is the screenshot.
706             if (isWideColor) {
707                 eglSurfaceAttribList[index++] = EGL_GL_COLORSPACE_KHR;
708                 eglSurfaceAttribList[index++] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
709             }
710             if (isProtected) {
711                 eglSurfaceAttribList[index++] = EGL_PROTECTED_CONTENT_EXT;
712                 eglSurfaceAttribList[index] = EGL14.EGL_TRUE;
713             }
714             // turn our SurfaceControl into a Surface
715             mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface,
716                     eglSurfaceAttribList, 0);
717             if (mEglSurface == null) {
718                 logEglError("eglCreateWindowSurface");
719                 return false;
720             }
721         }
722         return true;
723     }
724 
725     private void destroyEglSurface() {
726         if (mEglSurface != null) {
727             if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) {
728                 logEglError("eglDestroySurface");
729             }
730             mEglSurface = null;
731         }
732     }
733 
734     private void destroySurface() {
735         if (mSurfaceControl != null) {
736             mSurfaceLayout.dispose();
737             mSurfaceLayout = null;
738             mTransaction.remove(mSurfaceControl).apply();
739             if (mSurface != null) {
740                 mSurface.release();
741                 mSurface = null;
742             }
743 
744             if (mBLASTSurfaceControl != null) {
745                 mBLASTSurfaceControl.release();
746                 mBLASTSurfaceControl = null;
747                 mBLASTBufferQueue.destroy();
748                 mBLASTBufferQueue = null;
749             }
750 
751             mSurfaceControl = null;
752             mSurfaceVisible = false;
753             mSurfaceAlpha = 0f;
754         }
755     }
756 
757     private boolean showSurface(float alpha) {
758         if (!mSurfaceVisible || mSurfaceAlpha != alpha) {
759             mTransaction.setLayer(mSurfaceControl, COLOR_FADE_LAYER)
760                     .setAlpha(mSurfaceControl, alpha)
761                     .show(mSurfaceControl)
762                     .apply();
763             mSurfaceVisible = true;
764             mSurfaceAlpha = alpha;
765         }
766         return true;
767     }
768 
769     private boolean attachEglContext() {
770         if (mEglSurface == null) {
771             return false;
772         }
773         if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
774             logEglError("eglMakeCurrent");
775             return false;
776         }
777         return true;
778     }
779 
780     private void detachEglContext() {
781         if (mEglDisplay != null) {
782             EGL14.eglMakeCurrent(mEglDisplay,
783                     EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
784         }
785     }
786 
787     private static FloatBuffer createNativeFloatBuffer(int size) {
788         ByteBuffer bb = ByteBuffer.allocateDirect(size * 4);
789         bb.order(ByteOrder.nativeOrder());
790         return bb.asFloatBuffer();
791     }
792 
793     private static void logEglError(String func) {
794         Slog.e(TAG, func + " failed: error " + EGL14.eglGetError(), new Throwable());
795     }
796 
797     private static boolean checkGlErrors(String func) {
798         return checkGlErrors(func, true);
799     }
800 
801     private static boolean checkGlErrors(String func, boolean log) {
802         boolean hadError = false;
803         int error;
804         while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
805             if (log) {
806                 Slog.e(TAG, func + " failed: error " + error, new Throwable());
807             }
808             hadError = true;
809         }
810         return hadError;
811     }
812 
813     public void dump(PrintWriter pw) {
814         pw.println();
815         pw.println("Color Fade State:");
816         pw.println("  mPrepared=" + mPrepared);
817         pw.println("  mMode=" + mMode);
818         pw.println("  mDisplayLayerStack=" + mDisplayLayerStack);
819         pw.println("  mDisplayWidth=" + mDisplayWidth);
820         pw.println("  mDisplayHeight=" + mDisplayHeight);
821         pw.println("  mSurfaceVisible=" + mSurfaceVisible);
822         pw.println("  mSurfaceAlpha=" + mSurfaceAlpha);
823     }
824 
825     /**
826      * Keeps a surface aligned with the natural orientation of the device.
827      * Updates the position and transformation of the matrix whenever the display
828      * is rotated.  This is a little tricky because the display transaction
829      * callback can be invoked on any thread, not necessarily the thread that
830      * owns the color fade.
831      */
832     private static final class NaturalSurfaceLayout implements DisplayTransactionListener {
833         private final DisplayManagerInternal mDisplayManagerInternal;
834         private final int mDisplayId;
835         private SurfaceControl mSurfaceControl;
836 
837         public NaturalSurfaceLayout(DisplayManagerInternal displayManagerInternal,
838                 int displayId, SurfaceControl surfaceControl) {
839             mDisplayManagerInternal = displayManagerInternal;
840             mDisplayId = displayId;
841             mSurfaceControl = surfaceControl;
842             mDisplayManagerInternal.registerDisplayTransactionListener(this);
843         }
844 
845         public void dispose() {
846             synchronized (this) {
847                 mSurfaceControl = null;
848             }
849             mDisplayManagerInternal.unregisterDisplayTransactionListener(this);
850         }
851 
852         @Override
853         public void onDisplayTransaction(Transaction t) {
854             synchronized (this) {
855                 if (mSurfaceControl == null) {
856                     return;
857                 }
858 
859                 DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId);
860                 if (displayInfo == null) {
861                     // displayInfo can be null if the associated display has been removed. There
862                     // is a delay between the display being removed and ColorFade being dismissed.
863                     return;
864                 }
865 
866                 switch (displayInfo.rotation) {
867                     case Surface.ROTATION_0:
868                         t.setPosition(mSurfaceControl, 0, 0);
869                         t.setMatrix(mSurfaceControl, 1, 0, 0, 1);
870                         break;
871                     case Surface.ROTATION_90:
872                         t.setPosition(mSurfaceControl, 0, displayInfo.logicalHeight);
873                         t.setMatrix(mSurfaceControl, 0, -1, 1, 0);
874                         break;
875                     case Surface.ROTATION_180:
876                         t.setPosition(mSurfaceControl, displayInfo.logicalWidth,
877                                 displayInfo.logicalHeight);
878                         t.setMatrix(mSurfaceControl, -1, 0, 0, -1);
879                         break;
880                     case Surface.ROTATION_270:
881                         t.setPosition(mSurfaceControl, displayInfo.logicalWidth, 0);
882                         t.setMatrix(mSurfaceControl, 0, 1, -1, 0);
883                         break;
884                 }
885             }
886         }
887     }
888 }
889