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