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