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