1/*
2 * Copyright (C) 2009 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.systemui;
18
19import static android.opengl.GLES20.*;
20import static javax.microedition.khronos.egl.EGL10.*;
21
22import android.app.ActivityManager;
23import android.app.WallpaperManager;
24import android.content.BroadcastReceiver;
25import android.content.ComponentCallbacks2;
26import android.content.Context;
27import android.content.Intent;
28import android.graphics.Bitmap;
29import android.graphics.Canvas;
30import android.graphics.Rect;
31import android.graphics.Region.Op;
32import android.opengl.GLUtils;
33import android.os.SystemProperties;
34import android.renderscript.Matrix4f;
35import android.service.wallpaper.WallpaperService;
36import android.util.Log;
37import android.view.MotionEvent;
38import android.view.SurfaceHolder;
39import android.view.WindowManager;
40
41import java.io.IOException;
42import java.nio.ByteBuffer;
43import java.nio.ByteOrder;
44import java.nio.FloatBuffer;
45
46import javax.microedition.khronos.egl.EGL10;
47import javax.microedition.khronos.egl.EGLConfig;
48import javax.microedition.khronos.egl.EGLContext;
49import javax.microedition.khronos.egl.EGLDisplay;
50import javax.microedition.khronos.egl.EGLSurface;
51
52/**
53 * Default built-in wallpaper that simply shows a static image.
54 */
55@SuppressWarnings({"UnusedDeclaration"})
56public class ImageWallpaper extends WallpaperService {
57    private static final String TAG = "ImageWallpaper";
58    private static final String GL_LOG_TAG = "ImageWallpaperGL";
59    private static final boolean DEBUG = false;
60    private static final String PROPERTY_KERNEL_QEMU = "ro.kernel.qemu";
61
62    static final boolean FIXED_SIZED_SURFACE = true;
63    static final boolean USE_OPENGL = true;
64
65    WallpaperManager mWallpaperManager;
66
67    DrawableEngine mEngine;
68
69    boolean mIsHwAccelerated;
70
71    @Override
72    public void onCreate() {
73        super.onCreate();
74        mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
75
76        //noinspection PointlessBooleanExpression,ConstantConditions
77        if (FIXED_SIZED_SURFACE && USE_OPENGL) {
78            if (!isEmulator()) {
79                mIsHwAccelerated = ActivityManager.isHighEndGfx();
80            }
81        }
82    }
83
84    @Override
85    public void onTrimMemory(int level) {
86        if (mEngine != null) {
87            mEngine.trimMemory(level);
88        }
89    }
90
91    private static boolean isEmulator() {
92        return "1".equals(SystemProperties.get(PROPERTY_KERNEL_QEMU, "0"));
93    }
94
95    @Override
96    public Engine onCreateEngine() {
97        mEngine = new DrawableEngine();
98        return mEngine;
99    }
100
101    class DrawableEngine extends Engine {
102        static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
103        static final int EGL_OPENGL_ES2_BIT = 4;
104
105        // TODO: Not currently used, keeping around until we know we don't need it
106        @SuppressWarnings({"UnusedDeclaration"})
107        private WallpaperObserver mReceiver;
108
109        Bitmap mBackground;
110        int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
111        int mLastRotation = -1;
112        float mXOffset;
113        float mYOffset;
114
115        boolean mVisible = true;
116        boolean mRedrawNeeded;
117        boolean mOffsetsChanged;
118        int mLastXTranslation;
119        int mLastYTranslation;
120
121        private EGL10 mEgl;
122        private EGLDisplay mEglDisplay;
123        private EGLConfig mEglConfig;
124        private EGLContext mEglContext;
125        private EGLSurface mEglSurface;
126
127        private static final String sSimpleVS =
128                "attribute vec4 position;\n" +
129                "attribute vec2 texCoords;\n" +
130                "varying vec2 outTexCoords;\n" +
131                "uniform mat4 projection;\n" +
132                "\nvoid main(void) {\n" +
133                "    outTexCoords = texCoords;\n" +
134                "    gl_Position = projection * position;\n" +
135                "}\n\n";
136        private static final String sSimpleFS =
137                "precision mediump float;\n\n" +
138                "varying vec2 outTexCoords;\n" +
139                "uniform sampler2D texture;\n" +
140                "\nvoid main(void) {\n" +
141                "    gl_FragColor = texture2D(texture, outTexCoords);\n" +
142                "}\n\n";
143
144        private static final int FLOAT_SIZE_BYTES = 4;
145        private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
146        private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
147        private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
148
149        class WallpaperObserver extends BroadcastReceiver {
150            @Override
151            public void onReceive(Context context, Intent intent) {
152                if (DEBUG) {
153                    Log.d(TAG, "onReceive");
154                }
155
156                mLastSurfaceWidth = mLastSurfaceHeight = -1;
157                mBackground = null;
158                mRedrawNeeded = true;
159                drawFrame();
160            }
161        }
162
163        public DrawableEngine() {
164            super();
165            setFixedSizeAllowed(true);
166        }
167
168        public void trimMemory(int level) {
169            if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW &&
170                    mBackground != null && mIsHwAccelerated) {
171                if (DEBUG) {
172                    Log.d(TAG, "trimMemory");
173                }
174                mBackground.recycle();
175                mBackground = null;
176                mWallpaperManager.forgetLoadedWallpaper();
177            }
178        }
179
180        @Override
181        public void onCreate(SurfaceHolder surfaceHolder) {
182            if (DEBUG) {
183                Log.d(TAG, "onCreate");
184            }
185
186            super.onCreate(surfaceHolder);
187
188            // TODO: Don't need this currently because the wallpaper service
189            // will restart the image wallpaper whenever the image changes.
190            //IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
191            //mReceiver = new WallpaperObserver();
192            //registerReceiver(mReceiver, filter, null, mHandler);
193
194            updateSurfaceSize(surfaceHolder);
195
196            setOffsetNotificationsEnabled(false);
197        }
198
199        @Override
200        public void onDestroy() {
201            super.onDestroy();
202            if (mReceiver != null) {
203                unregisterReceiver(mReceiver);
204            }
205        }
206
207        @Override
208        public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
209            super.onDesiredSizeChanged(desiredWidth, desiredHeight);
210            SurfaceHolder surfaceHolder = getSurfaceHolder();
211            if (surfaceHolder != null) {
212                updateSurfaceSize(surfaceHolder);
213            }
214        }
215
216        void updateSurfaceSize(SurfaceHolder surfaceHolder) {
217            if (FIXED_SIZED_SURFACE) {
218                // Used a fixed size surface, because we are special.  We can do
219                // this because we know the current design of window animations doesn't
220                // cause this to break.
221                surfaceHolder.setFixedSize(getDesiredMinimumWidth(), getDesiredMinimumHeight());
222            } else {
223                surfaceHolder.setSizeFromLayout();
224            }
225        }
226
227        @Override
228        public void onVisibilityChanged(boolean visible) {
229            if (DEBUG) {
230                Log.d(TAG, "onVisibilityChanged: mVisible, visible=" + mVisible + ", " + visible);
231            }
232
233            if (mVisible != visible) {
234                if (DEBUG) {
235                    Log.d(TAG, "Visibility changed to visible=" + visible);
236                }
237                mVisible = visible;
238                drawFrame();
239            }
240        }
241
242        @Override
243        public void onTouchEvent(MotionEvent event) {
244            super.onTouchEvent(event);
245        }
246
247        @Override
248        public void onOffsetsChanged(float xOffset, float yOffset,
249                float xOffsetStep, float yOffsetStep,
250                int xPixels, int yPixels) {
251            if (DEBUG) {
252                Log.d(TAG, "onOffsetsChanged: xOffset=" + xOffset + ", yOffset=" + yOffset
253                        + ", xOffsetStep=" + xOffsetStep + ", yOffsetStep=" + yOffsetStep
254                        + ", xPixels=" + xPixels + ", yPixels=" + yPixels);
255            }
256
257            if (mXOffset != xOffset || mYOffset != yOffset) {
258                if (DEBUG) {
259                    Log.d(TAG, "Offsets changed to (" + xOffset + "," + yOffset + ").");
260                }
261                mXOffset = xOffset;
262                mYOffset = yOffset;
263                mOffsetsChanged = true;
264            }
265            drawFrame();
266        }
267
268        @Override
269        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
270            if (DEBUG) {
271                Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height);
272            }
273
274            super.onSurfaceChanged(holder, format, width, height);
275
276            drawFrame();
277        }
278
279        @Override
280        public void onSurfaceDestroyed(SurfaceHolder holder) {
281            super.onSurfaceDestroyed(holder);
282            mLastSurfaceWidth = mLastSurfaceHeight = -1;
283        }
284
285        @Override
286        public void onSurfaceCreated(SurfaceHolder holder) {
287            super.onSurfaceCreated(holder);
288            mLastSurfaceWidth = mLastSurfaceHeight = -1;
289        }
290
291        @Override
292        public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
293            if (DEBUG) {
294                Log.d(TAG, "onSurfaceRedrawNeeded");
295            }
296            super.onSurfaceRedrawNeeded(holder);
297
298            drawFrame();
299        }
300
301        void drawFrame() {
302            SurfaceHolder sh = getSurfaceHolder();
303            final Rect frame = sh.getSurfaceFrame();
304            final int dw = frame.width();
305            final int dh = frame.height();
306            int newRotation = ((WindowManager) getSystemService(WINDOW_SERVICE)).
307                    getDefaultDisplay().getRotation();
308            boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth || dh != mLastSurfaceHeight;
309
310            boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation;
311            if (!redrawNeeded && !mOffsetsChanged) {
312                if (DEBUG) {
313                    Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
314                            + "and offsets have not changed.");
315                }
316                return;
317            }
318            mLastRotation = newRotation;
319
320            // Load bitmap if it is not yet loaded or if it was loaded at a different size
321            if (mBackground == null || surfaceDimensionsChanged) {
322                if (DEBUG) {
323                    Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = " +
324                            mBackground + ", " +
325                            ((mBackground == null) ? 0 : mBackground.getWidth()) + ", " +
326                            ((mBackground == null) ? 0 : mBackground.getHeight()) + ", " +
327                            dw + ", " + dh);
328                }
329                mWallpaperManager.forgetLoadedWallpaper();
330                updateWallpaperLocked();
331                if (mBackground == null) {
332                    if (DEBUG) {
333                        Log.d(TAG, "Unable to load bitmap");
334                    }
335                    return;
336                }
337                if (DEBUG) {
338                    if (dw != mBackground.getWidth() || dh != mBackground.getHeight()) {
339                        Log.d(TAG, "Surface != bitmap dimensions: surface w/h, bitmap w/h: " +
340                                dw + ", " + dh + ", " + mBackground.getWidth() + ", " +
341                                mBackground.getHeight());
342                    }
343                }
344            }
345
346            final int availw = dw - mBackground.getWidth();
347            final int availh = dh - mBackground.getHeight();
348            int xPixels = availw < 0 ? (int)(availw * mXOffset + .5f) : (availw / 2);
349            int yPixels = availh < 0 ? (int)(availh * mYOffset + .5f) : (availh / 2);
350
351            mOffsetsChanged = false;
352            mRedrawNeeded = false;
353            if (surfaceDimensionsChanged) {
354                mLastSurfaceWidth = dw;
355                mLastSurfaceHeight = dh;
356            }
357            mLastXTranslation = xPixels;
358            mLastYTranslation = yPixels;
359            if (!redrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
360                if (DEBUG) {
361                    Log.d(TAG, "Suppressed drawFrame since the image has not "
362                            + "actually moved an integral number of pixels.");
363                }
364                return;
365            }
366
367            if (DEBUG) {
368                Log.d(TAG, "Redrawing wallpaper");
369            }
370
371            if (mIsHwAccelerated) {
372                if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
373                    drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
374                }
375            } else {
376                drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
377                if (FIXED_SIZED_SURFACE) {
378                    // If the surface is fixed-size, we should only need to
379                    // draw it once and then we'll let the window manager
380                    // position it appropriately.  As such, we no longer needed
381                    // the loaded bitmap.  Yay!
382                    // hw-accelerated path retains bitmap for faster rotation
383                    mBackground = null;
384                    mWallpaperManager.forgetLoadedWallpaper();
385                }
386            }
387
388        }
389
390        private void updateWallpaperLocked() {
391            Throwable exception = null;
392            try {
393                mBackground = null;
394                mBackground = mWallpaperManager.getBitmap();
395            } catch (RuntimeException e) {
396                exception = e;
397            } catch (OutOfMemoryError e) {
398                exception = e;
399            }
400
401            if (exception != null) {
402                mBackground = null;
403                // Note that if we do fail at this, and the default wallpaper can't
404                // be loaded, we will go into a cycle.  Don't do a build where the
405                // default wallpaper can't be loaded.
406                Log.w(TAG, "Unable to load wallpaper!", exception);
407                try {
408                    mWallpaperManager.clear();
409                } catch (IOException ex) {
410                    // now we're really screwed.
411                    Log.w(TAG, "Unable reset to default wallpaper!", ex);
412                }
413            }
414        }
415
416        private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int x, int y) {
417            Canvas c = sh.lockCanvas();
418            if (c != null) {
419                try {
420                    if (DEBUG) {
421                        Log.d(TAG, "Redrawing: x=" + x + ", y=" + y);
422                    }
423
424                    c.translate(x, y);
425                    if (w < 0 || h < 0) {
426                        c.save(Canvas.CLIP_SAVE_FLAG);
427                        c.clipRect(0, 0, mBackground.getWidth(), mBackground.getHeight(),
428                                Op.DIFFERENCE);
429                        c.drawColor(0xff000000);
430                        c.restore();
431                    }
432                    if (mBackground != null) {
433                        c.drawBitmap(mBackground, 0, 0, null);
434                    }
435                } finally {
436                    sh.unlockCanvasAndPost(c);
437                }
438            }
439        }
440
441        private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) {
442            if (!initGL(sh)) return false;
443
444            final float right = left + mBackground.getWidth();
445            final float bottom = top + mBackground.getHeight();
446
447            final Rect frame = sh.getSurfaceFrame();
448            final Matrix4f ortho = new Matrix4f();
449            ortho.loadOrtho(0.0f, frame.width(), frame.height(), 0.0f, -1.0f, 1.0f);
450
451            final FloatBuffer triangleVertices = createMesh(left, top, right, bottom);
452
453            final int texture = loadTexture(mBackground);
454            final int program = buildProgram(sSimpleVS, sSimpleFS);
455
456            final int attribPosition = glGetAttribLocation(program, "position");
457            final int attribTexCoords = glGetAttribLocation(program, "texCoords");
458            final int uniformTexture = glGetUniformLocation(program, "texture");
459            final int uniformProjection = glGetUniformLocation(program, "projection");
460
461            checkGlError();
462
463            glViewport(0, 0, frame.width(), frame.height());
464            glBindTexture(GL_TEXTURE_2D, texture);
465
466            glUseProgram(program);
467            glEnableVertexAttribArray(attribPosition);
468            glEnableVertexAttribArray(attribTexCoords);
469            glUniform1i(uniformTexture, 0);
470            glUniformMatrix4fv(uniformProjection, 1, false, ortho.getArray(), 0);
471
472            checkGlError();
473
474            if (w < 0 || h < 0) {
475                glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
476                glClear(GL_COLOR_BUFFER_BIT);
477            }
478
479            // drawQuad
480            triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
481            glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
482                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
483
484            triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
485            glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
486                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
487
488            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
489
490            boolean status = mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
491            checkEglError();
492
493            finishGL();
494
495            return status;
496        }
497
498        private FloatBuffer createMesh(int left, int top, float right, float bottom) {
499            final float[] verticesData = {
500                    // X, Y, Z, U, V
501                     left,  bottom, 0.0f, 0.0f, 1.0f,
502                     right, bottom, 0.0f, 1.0f, 1.0f,
503                     left,  top,    0.0f, 0.0f, 0.0f,
504                     right, top,    0.0f, 1.0f, 0.0f,
505            };
506
507            final int bytes = verticesData.length * FLOAT_SIZE_BYTES;
508            final FloatBuffer triangleVertices = ByteBuffer.allocateDirect(bytes).order(
509                    ByteOrder.nativeOrder()).asFloatBuffer();
510            triangleVertices.put(verticesData).position(0);
511            return triangleVertices;
512        }
513
514        private int loadTexture(Bitmap bitmap) {
515            int[] textures = new int[1];
516
517            glActiveTexture(GL_TEXTURE0);
518            glGenTextures(1, textures, 0);
519            checkGlError();
520
521            int texture = textures[0];
522            glBindTexture(GL_TEXTURE_2D, texture);
523            checkGlError();
524
525            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
526            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
527
528            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
529            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
530
531            GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
532            checkGlError();
533
534            return texture;
535        }
536
537        private int buildProgram(String vertex, String fragment) {
538            int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
539            if (vertexShader == 0) return 0;
540
541            int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
542            if (fragmentShader == 0) return 0;
543
544            int program = glCreateProgram();
545            glAttachShader(program, vertexShader);
546            checkGlError();
547
548            glAttachShader(program, fragmentShader);
549            checkGlError();
550
551            glLinkProgram(program);
552            checkGlError();
553
554            int[] status = new int[1];
555            glGetProgramiv(program, GL_LINK_STATUS, status, 0);
556            if (status[0] != GL_TRUE) {
557                String error = glGetProgramInfoLog(program);
558                Log.d(GL_LOG_TAG, "Error while linking program:\n" + error);
559                glDeleteShader(vertexShader);
560                glDeleteShader(fragmentShader);
561                glDeleteProgram(program);
562                return 0;
563            }
564
565            return program;
566        }
567
568        private int buildShader(String source, int type) {
569            int shader = glCreateShader(type);
570
571            glShaderSource(shader, source);
572            checkGlError();
573
574            glCompileShader(shader);
575            checkGlError();
576
577            int[] status = new int[1];
578            glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
579            if (status[0] != GL_TRUE) {
580                String error = glGetShaderInfoLog(shader);
581                Log.d(GL_LOG_TAG, "Error while compiling shader:\n" + error);
582                glDeleteShader(shader);
583                return 0;
584            }
585
586            return shader;
587        }
588
589        private void checkEglError() {
590            int error = mEgl.eglGetError();
591            if (error != EGL_SUCCESS) {
592                Log.w(GL_LOG_TAG, "EGL error = " + GLUtils.getEGLErrorString(error));
593            }
594        }
595
596        private void checkGlError() {
597            int error = glGetError();
598            if (error != GL_NO_ERROR) {
599                Log.w(GL_LOG_TAG, "GL error = 0x" + Integer.toHexString(error), new Throwable());
600            }
601        }
602
603        private void finishGL() {
604            mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
605            mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
606            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
607            mEgl.eglTerminate(mEglDisplay);
608        }
609
610        private boolean initGL(SurfaceHolder surfaceHolder) {
611            mEgl = (EGL10) EGLContext.getEGL();
612
613            mEglDisplay = mEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
614            if (mEglDisplay == EGL_NO_DISPLAY) {
615                throw new RuntimeException("eglGetDisplay failed " +
616                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
617            }
618
619            int[] version = new int[2];
620            if (!mEgl.eglInitialize(mEglDisplay, version)) {
621                throw new RuntimeException("eglInitialize failed " +
622                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
623            }
624
625            mEglConfig = chooseEglConfig();
626            if (mEglConfig == null) {
627                throw new RuntimeException("eglConfig not initialized");
628            }
629
630            mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
631            if (mEglContext == EGL_NO_CONTEXT) {
632                throw new RuntimeException("createContext failed " +
633                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
634            }
635
636            int attribs[] = {
637                EGL_WIDTH, 1,
638                EGL_HEIGHT, 1,
639                EGL_NONE
640            };
641            EGLSurface tmpSurface = mEgl.eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
642            mEgl.eglMakeCurrent(mEglDisplay, tmpSurface, tmpSurface, mEglContext);
643
644            int[] maxSize = new int[1];
645            Rect frame = surfaceHolder.getSurfaceFrame();
646            glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxSize, 0);
647
648            mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
649            mEgl.eglDestroySurface(mEglDisplay, tmpSurface);
650
651            if(frame.width() > maxSize[0] || frame.height() > maxSize[0]) {
652                mEgl.eglDestroyContext(mEglDisplay, mEglContext);
653                mEgl.eglTerminate(mEglDisplay);
654                Log.e(GL_LOG_TAG, "requested  texture size " +
655                    frame.width() + "x" + frame.height() + " exceeds the support maximum of " +
656                    maxSize[0] + "x" + maxSize[0]);
657                return false;
658            }
659
660            mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null);
661            if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
662                int error = mEgl.eglGetError();
663                if (error == EGL_BAD_NATIVE_WINDOW || error == EGL_BAD_ALLOC) {
664                    Log.e(GL_LOG_TAG, "createWindowSurface returned " +
665                                         GLUtils.getEGLErrorString(error) + ".");
666                    return false;
667                }
668                throw new RuntimeException("createWindowSurface failed " +
669                        GLUtils.getEGLErrorString(error));
670            }
671
672            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
673                throw new RuntimeException("eglMakeCurrent failed " +
674                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
675            }
676
677            return true;
678        }
679
680
681        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
682            int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
683            return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attrib_list);
684        }
685
686        private EGLConfig chooseEglConfig() {
687            int[] configsCount = new int[1];
688            EGLConfig[] configs = new EGLConfig[1];
689            int[] configSpec = getConfig();
690            if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
691                throw new IllegalArgumentException("eglChooseConfig failed " +
692                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
693            } else if (configsCount[0] > 0) {
694                return configs[0];
695            }
696            return null;
697        }
698
699        private int[] getConfig() {
700            return new int[] {
701                    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
702                    EGL_RED_SIZE, 8,
703                    EGL_GREEN_SIZE, 8,
704                    EGL_BLUE_SIZE, 8,
705                    EGL_ALPHA_SIZE, 0,
706                    EGL_DEPTH_SIZE, 0,
707                    EGL_STENCIL_SIZE, 0,
708                    EGL_CONFIG_CAVEAT, EGL_NONE,
709                    EGL_NONE
710            };
711        }
712    }
713}
714