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