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