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