• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onCreate()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
onTrimMemory(int level)84     public void onTrimMemory(int level) {
85         if (mEngine != null) {
86             mEngine.trimMemory(level);
87         }
88     }
89 
isEmulator()90     private static boolean isEmulator() {
91         return "1".equals(SystemProperties.get(PROPERTY_KERNEL_QEMU, "0"));
92     }
93 
94     @Override
onCreateEngine()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
onReceive(Context context, Intent intent)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 
DrawableEngine()162         public DrawableEngine() {
163             super();
164             setFixedSizeAllowed(true);
165         }
166 
trimMemory(int level)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
onCreate(SurfaceHolder surfaceHolder)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
onDestroy()199         public void onDestroy() {
200             super.onDestroy();
201             if (mReceiver != null) {
202                 unregisterReceiver(mReceiver);
203             }
204         }
205 
206         @Override
onDesiredSizeChanged(int desiredWidth, int desiredHeight)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 
updateSurfaceSize(SurfaceHolder surfaceHolder)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
onVisibilityChanged(boolean visible)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
onTouchEvent(MotionEvent event)242         public void onTouchEvent(MotionEvent event) {
243             super.onTouchEvent(event);
244         }
245 
246         @Override
onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixels, int yPixels)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
onSurfaceChanged(SurfaceHolder holder, int format, int width, int height)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
onSurfaceDestroyed(SurfaceHolder holder)279         public void onSurfaceDestroyed(SurfaceHolder holder) {
280             super.onSurfaceDestroyed(holder);
281             mLastSurfaceWidth = mLastSurfaceHeight = -1;
282         }
283 
284         @Override
onSurfaceCreated(SurfaceHolder holder)285         public void onSurfaceCreated(SurfaceHolder holder) {
286             super.onSurfaceCreated(holder);
287             mLastSurfaceWidth = mLastSurfaceHeight = -1;
288         }
289 
290         @Override
onSurfaceRedrawNeeded(SurfaceHolder holder)291         public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
292             if (DEBUG) {
293                 Log.d(TAG, "onSurfaceRedrawNeeded");
294             }
295             super.onSurfaceRedrawNeeded(holder);
296 
297             drawFrame();
298         }
299 
drawFrame()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