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