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