/* * Copyright (C) 2011 QSDN,Inc. * Copyright (C) 2011 Atsushi Konno * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jp.co.qsdn.android.jinbei3d; import android.content.Context; import android.content.Intent; import android.os.Bundle; //import android.os.Debug; import android.service.wallpaper.WallpaperService; import android.util.Log; import android.view.SurfaceHolder; import android.widget.Toast; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGL11; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLSurface; import javax.microedition.khronos.opengles.GL10; import jp.co.qsdn.android.jinbei3d.GLRenderer; import jp.co.qsdn.android.jinbei3d.util.MatrixTrackingGL; public class AtlantisService extends WallpaperService { private static final String TAG = AtlantisService.class.getName(); private static final int RETRY_COUNT = 3; private class AtlantisEngine extends Engine { private final String TAG = AtlantisEngine.class.getName(); private int width = 0; private int height = 0; private boolean binded = false; private boolean mInitialized = false; /* EGL関連は毎回再作成?? */ private MatrixTrackingGL gl10 = null; private EGL10 egl10 = null; private EGLContext eglContext = null; private EGLDisplay eglDisplay = null; private EGLSurface eglSurface = null; private GLRenderer glRenderer = null; private long BASE_TICK = 45410157L; private ExecutorService getExecutor() { if (executor == null) { executor = Executors.newSingleThreadExecutor(); } return executor; } private ExecutorService executor = null; private Runnable drawCommand = null; private void doExecute(Runnable command) { if (command == null) { return; } while(true) { try { getExecutor().execute(command); } catch (RejectedExecutionException e) { if (getExecutor().isShutdown()) { // ignore } else { Log.e(TAG, "command execute failure", e); waitNano(); System.gc(); continue; } } break; } } @Override public void onCreate(final SurfaceHolder holder) { if (Constant.DEBUG) Log.d(TAG, "start onCreate() [" + this + "]"); super.onCreate(holder); /*=====================================================================*/ /* 携帯電話として機能しなくなるので */ /* タッチイベントは無効にしておく. */ /* 画面の空いたところのタッチにだけ反応したいので */ /* Engine.onCommandで対応する */ /*=====================================================================*/ setTouchEventsEnabled(false); if (! isPreview()) { AtlantisNotification.putNotice(AtlantisService.this); } if (Constant.DEBUG) Log.d(TAG, "end onCreate() [" + this + "]"); } @Override public void onDestroy() { if (Constant.DEBUG) Log.d(TAG, "start onDestroy() [" + this + "]"); if (! isPreview()) { AtlantisNotification.removeNotice(getApplicationContext()); } else { } super.onDestroy(); System.gc(); if (Constant.DEBUG) Log.d(TAG, "end onDestroy() [" + this + "]"); } int[][] configSpec = { { // RGB565 color EGL10.EGL_RED_SIZE, 5, EGL10.EGL_GREEN_SIZE, 6, EGL10.EGL_BLUE_SIZE, 5, EGL10.EGL_ALPHA_SIZE, EGL10.EGL_DONT_CARE, EGL10.EGL_DEPTH_SIZE, 24, EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE, // window (and not a pixmap or a pbuffer) EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT, EGL10.EGL_NONE , }, { // RGB565 color EGL10.EGL_RED_SIZE, 5, EGL10.EGL_GREEN_SIZE,6, EGL10.EGL_BLUE_SIZE,5, EGL10.EGL_ALPHA_SIZE, EGL10.EGL_DONT_CARE, EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE, // window (and not a pixmap or a pbuffer) EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT, EGL10.EGL_NONE , }, { EGL10.EGL_NONE, } }; @Override public void onSurfaceCreated(final SurfaceHolder holder) { if (Constant.DEBUG) Log.d(TAG, "start onSurfaceCreated() [" + this + "]"); super.onSurfaceCreated(holder); Runnable surfaceCreatedCommand = new Runnable() { @Override public void run() { doSurfaceCreated(); } protected void doSurfaceCreated() { if (mInitialized) { if (Constant.DEBUG) Log.d(TAG, "already Initialized(surfaceCreatedCommand)"); return; } boolean ret; /* OpenGLの初期化 */ int counter = 0; int specCounter = 0; while(true) { if (Constant.DEBUG) Log.d(TAG, "start EGLContext.getEGL()"); exitEgl(); egl10 = (EGL10) EGLContext.getEGL(); if (Constant.DEBUG) Log.d(TAG, "end EGLContext.getEGL()"); if (Constant.DEBUG) Log.d(TAG, "start eglGetDisplay"); eglDisplay = egl10.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); if (Constant.DEBUG) Log.d(TAG, "end eglGetDisplay"); if (eglDisplay == null || EGL10.EGL_NO_DISPLAY.equals(eglDisplay)) { String errStr = AtlantisService.getErrorString(egl10.eglGetError()); if (Constant.DEBUG) Log.d(TAG, "eglGetDisplayがEGL_NO_DISPLAY [" + errStr + "]"); exitEgl(); if (++counter >= AtlantisService.RETRY_COUNT) { Log.e(TAG, "egl10.eglCreateContextがEGL_NO_DISPLAY"); throw new RuntimeException("OpenGL Error(EGL_NO_DISPLAY) " + errStr + ": " ); } if (Constant.DEBUG) Log.d(TAG, "RETRY"); System.gc(); waitNano(); continue; } { egl10.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); } int[] version = new int[2]; if (! egl10.eglInitialize(eglDisplay, version)) { String errStr = AtlantisService.getErrorString(egl10.eglGetError()); if (Constant.DEBUG) Log.d(TAG, "egl10.eglInitializeがfalse [" + errStr + "]"); exitEgl(); if (++counter >= AtlantisService.RETRY_COUNT) { Log.e(TAG,"egl10.eglInitializeがfalse"); throw new RuntimeException("OpenGL Error(eglInitialize) " + errStr + ": " ); } if (Constant.DEBUG) Log.d(TAG,"RETRY"); System.gc(); waitNano(); continue; } EGLConfig[] configs = new EGLConfig[1]; int[] numConfig = new int[1]; /*-----------------------------------------------------------------*/ /* 条件に見合うEGLConfigを取得 */ /*-----------------------------------------------------------------*/ egl10.eglChooseConfig(eglDisplay, configSpec[specCounter++], configs, 1, numConfig); /*-----------------------------------------------------------------*/ /* もしEGLConfigが取得できなければ */ /*-----------------------------------------------------------------*/ if (numConfig[0] == 0) { if (Constant.DEBUG) Log.d(TAG, "numConfig[0]=" + numConfig[0] + ""); String errStr = AtlantisService.getErrorString(egl10.eglGetError()); errStr += " eglChooseConfig numConfig == 0 "; errStr += " numConfig:[" + numConfig[0] + "]"; errStr += " specCounter:[" + (specCounter - 1) + "]"; exitEgl(); Log.e(TAG,"eglChooseConfig失敗:" + "numConfig:[" + numConfig[0] + "]" + errStr); if (++counter >= AtlantisService.RETRY_COUNT) { throw new RuntimeException("OpenGL Error " + errStr + " :" ); } if (Constant.DEBUG) Log.d(TAG, "RETRY"); System.gc(); waitNano(); continue; } EGLConfig config = configs[0]; /*-----------------------------------------------------------------*/ /* 取得したEGLDisplayとEGLConfigでEGLContext作成 */ /*-----------------------------------------------------------------*/ eglContext = egl10.eglCreateContext(eglDisplay, config, EGL10.EGL_NO_CONTEXT, null); if (eglContext == null || EGL10.EGL_NO_CONTEXT.equals(eglContext)) { String errStr = AtlantisService.getErrorString(egl10.eglGetError()); errStr += "egl10.eglCreateContext == EGL_NO_CONTEXT "; errStr += "specCounter:[" + (specCounter - 1) + "]"; if (Constant.DEBUG) Log.d(TAG, "egl10.eglCreateContext == EGL_NO_CONTEXT [" + errStr + "]"); exitEgl(); if (++counter >= AtlantisService.RETRY_COUNT) { Log.e(TAG, "egl10.eglCreateContextがEGL_NO_CONTEXT"); throw new RuntimeException("OpenGL Error(EGL_NO_CONTEXT) " + errStr + " :" ); } if (Constant.DEBUG) Log.d(TAG, "RETRY"); System.gc(); waitNano(); continue; } if (Constant.DEBUG) Log.v(TAG, "eglCreateContext done."); /*-----------------------------------------------------------------*/ /* 取得したEGLDisplayとEGLConfigでEGLSurface作成 */ /*-----------------------------------------------------------------*/ eglSurface = egl10.eglCreateWindowSurface(eglDisplay, config, holder, null); if (eglSurface == null || EGL10.EGL_NO_SURFACE.equals(eglSurface)) { String errStr = AtlantisService.getErrorString(egl10.eglGetError()); errStr += "egl10.eglCreateWindowSurface == EGL_NO_SURFACE "; errStr += "specCounter:[" + (specCounter - 1) + "]"; if (Constant.DEBUG) Log.d(TAG, "egl10.eglCreateWindowSurface == EGL_NO_SURFACE [" + errStr + "]"); exitEgl(); if (++counter >= AtlantisService.RETRY_COUNT) { Log.e(TAG, "egl10.eglCreateWindowSurfaceがEGL_NO_SURFACE"); throw new RuntimeException("OpenGL Error(EGL_NO_SURFACE) " + errStr + " :" ); } if (Constant.DEBUG) Log.e(TAG, "RETRY"); System.gc(); waitNano(); continue; } if (Constant.DEBUG) Log.v(TAG, "eglCreateWindowSurface done."); /*-----------------------------------------------------------------*/ /* EGLContextとEGLSurfaceを関連付ける(アタッチ) */ /*-----------------------------------------------------------------*/ if (! egl10.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { String errStr = AtlantisService.getErrorString(egl10.eglGetError()); errStr += "egl10.eglMakeCurrent == false "; errStr += "specCounter:[" + (specCounter - 1) + "]"; if (Constant.DEBUG) Log.v(TAG, "egl10.eglMakeCurrent == false [" + errStr + "]"); exitEgl(); if (++counter >= AtlantisService.RETRY_COUNT) { Log.e(TAG,"egl10.eglMakeCurrentがfalse"); throw new RuntimeException("OpenGL Error(eglMakeCurrent) " + errStr + " :" ); } if (Constant.DEBUG) Log.v(TAG,"RETRY"); System.gc(); waitNano(); continue; } if (Constant.DEBUG) Log.v(TAG, "eglMakeCurrent done."); if (Constant.DEBUG) Log.v(TAG, "now create gl10 object"); gl10 = new MatrixTrackingGL((GL10) (eglContext.getGL())); if (Constant.DEBUG) Log.v(TAG, "new MatrixTrakingGL done"); /*-----------------------------------------------------------------*/ /* Rendererの初期化 */ /*-----------------------------------------------------------------*/ glRenderer = GLRenderer.getInstance(getApplicationContext()); if (Constant.DEBUG) Log.v(TAG, "GLRenderer.getInstance()"); synchronized (glRenderer) { glRenderer.onSurfaceCreated(gl10, config, getApplicationContext()); if (Constant.DEBUG) Log.v(TAG, "GLRenderer.onSurfaceCreated()"); } if (Constant.DEBUG) Log.v(TAG, "EGL initalize done."); mInitialized = true; if (drawCommand == null) { drawCommand = new Runnable() { public void run() { doDrawCommand(); } protected void doDrawCommand() { if (Constant.DEBUG) Log.v(TAG, ">>> doDrawCommand()"); if (mInitialized && glRenderer != null && gl10 != null) { synchronized (glRenderer) { long nowTime = System.nanoTime(); if (nowTime - glRenderer.prevTick < BASE_TICK) { try { TimeUnit.NANOSECONDS.sleep(BASE_TICK - (nowTime - glRenderer.prevTick)); } catch (InterruptedException e) { } } glRenderer.onDrawFrame(gl10); glRenderer.prevTick = nowTime; } egl10.eglSwapBuffers(eglDisplay, eglSurface); if (!getExecutor().isShutdown() && isVisible() && egl10.eglGetError() != EGL11.EGL_CONTEXT_LOST) { doExecute(drawCommand); } } if (Constant.DEBUG) Log.v(TAG, "<<< doDrawCommand()"); } }; doExecute(drawCommand); } break; } } }; doExecute(surfaceCreatedCommand); if (Constant.DEBUG) Log.d(TAG, "end onSurfaceCreated() [" + this + "]"); } @Override public void onSurfaceDestroyed(final SurfaceHolder holder) { if (Constant.DEBUG) Log.d(TAG, "start onSurfaceDestroyed() [" + this + "]"); Runnable surfaceDestroyedCommand = new Runnable() { @Override public void run() { doSurfaceDestroyedCommand(); } private void doSurfaceDestroyedCommand() { synchronized (glRenderer) { glRenderer.onSurfaceDestroyed(gl10); } exitEgl(); gl10.shutdown(); gl10 = null; System.gc(); mInitialized = false; } }; doExecute(surfaceDestroyedCommand); getExecutor().shutdown(); try { if (!getExecutor().awaitTermination(60, TimeUnit.SECONDS)) { getExecutor().shutdownNow(); if (!getExecutor().awaitTermination(60, TimeUnit.SECONDS)) { if (Constant.DEBUG) Log.d(TAG,"ExecutorService did not terminate...."); getExecutor().shutdownNow(); Thread.currentThread().interrupt(); } } } catch (InterruptedException e) { executor.shutdownNow(); Thread.currentThread().interrupt(); } drawCommand = null; super.onSurfaceDestroyed(holder); if (Constant.DEBUG) Log.d(TAG, "end onSurfaceDestroyed() [" + this + "]"); } @Override public void onSurfaceChanged(final SurfaceHolder holder, final int format, final int width, final int height) { if (Constant.DEBUG) Log.d(TAG, "start onSurfaceChanged() [" + this + "]"); super.onSurfaceChanged(holder, format, width, height); this.width = width; this.height = height; Runnable surfaceChangedCommand = new Runnable() { public void run() { doSurfaceChanged(); } private void doSurfaceChanged() { if (glRenderer != null && gl10 != null && mInitialized) { synchronized (glRenderer) { glRenderer.onSurfaceChanged(gl10, width, height); } } }; }; doExecute(surfaceChangedCommand); if (Constant.DEBUG) Log.d(TAG, "end onSurfaceChanged() [" + this + "]"); } @Override public void onVisibilityChanged(final boolean visible) { if (Constant.DEBUG) Log.d(TAG, "start onVisibilityChanged()"); super.onVisibilityChanged(visible); /* サーフェスが見えるようになったよ! */ if (visible && drawCommand != null && mInitialized) { /* 設定変更のタイミング */ if (glRenderer != null) { synchronized (glRenderer) { glRenderer.updateSetting(getApplicationContext()); } } doExecute(drawCommand); } if (Constant.DEBUG) Log.d(TAG, "end onVisibilityChanged()"); } @Override public void onOffsetsChanged(final float xOffset, final float yOffset, final float xOffsetStep, final float yOffsetStep, final int xPixelOffset, final int yPixelOffset) { if (Constant.DEBUG) Log.d(TAG, "start onOffsetsChanged()"); super.onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixelOffset, yPixelOffset); if (xOffsetStep == 0.0f && yOffsetStep == 0.0f) { if (Constant.DEBUG) Log.d(TAG, "end onOffsetChanged() no execute"); return; } Runnable offsetsChangedCommand = new Runnable() { public void run() { doOffsetsChanged(); } private void doOffsetsChanged() { if (mInitialized && glRenderer != null && gl10 != null) { synchronized (glRenderer) { glRenderer.onOffsetsChanged(gl10, xOffset, yOffset, xOffsetStep, yOffsetStep, xPixelOffset, yPixelOffset); } } }; }; doExecute(offsetsChangedCommand); if (Constant.DEBUG) Log.d(TAG, "end onOffsetChanged()"); } @Override public Bundle onCommand(final String action, final int x, final int y, final int z, final Bundle extras, final boolean resultRequested){ if (Constant.DEBUG) { Log.d(TAG, "start onCommand " + "action:[" + action + "]:" + "x:[" + x + "]:" + "y:[" + y + "]:" + "z:[" + z + "]:" + "extras:[" + extras + "]:" + "resultRequested:[" + resultRequested + "]:" ); } /*=====================================================================*/ /* 画面の何もないところへのタッチにだけ反応するため */ /* actionがandroid.wallpaper.tapのときだけ処理する */ /*=====================================================================*/ if (action.equals("android.wallpaper.tap")) { Runnable onCommandCommand = new Runnable() { public void run() { doCommandCommand(); } private void doCommandCommand() { if (mInitialized && glRenderer != null && gl10 != null) { synchronized (glRenderer) { glRenderer.onCommand(gl10, action, x, y, z, extras, resultRequested); } } } }; doExecute(onCommandCommand); } Bundle ret = super.onCommand(action, x, y, z, extras, resultRequested); if (Constant.DEBUG) Log.d(TAG, "end onCommand"); return ret; } public void exitEgl() { if (Constant.DEBUG) Log.d(TAG, "start exitEgl"); if (egl10 != null) { if (eglDisplay != null && ! eglDisplay.equals(EGL10.EGL_NO_DISPLAY)) { if (! egl10.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT)) { Log.e(TAG, "eglMakeCurrentがfalse [" + AtlantisService.getErrorString(egl10.eglGetError()) + "]"); } if (eglSurface != null && ! eglSurface.equals(EGL10.EGL_NO_SURFACE)) { if (! egl10.eglDestroySurface(eglDisplay, eglSurface)) { Log.e(TAG, "eglDestroySurfaceがfalse [" + AtlantisService.getErrorString(egl10.eglGetError()) + "]"); } eglSurface = null; } if (eglContext != null && ! eglContext.equals(EGL10.EGL_NO_CONTEXT)) { if (! egl10.eglDestroyContext(eglDisplay, eglContext)) { Log.e(TAG, "eglDestroyContextがfalse [" + AtlantisService.getErrorString(egl10.eglGetError()) + "]"); } eglContext = null; } if (! egl10.eglTerminate(eglDisplay)) { Log.e(TAG, "eglTerminateがfalse [" + AtlantisService.getErrorString(egl10.eglGetError()) + "]"); } eglDisplay = null; } egl10 = null; } if (Constant.DEBUG) Log.d(TAG, "end exitEgl"); } } @Override public Engine onCreateEngine() { if (Constant.DEBUG) Log.d(TAG, "start onCreateEngine()"); AtlantisEngine engine = new AtlantisEngine(); if (Constant.DEBUG) Log.d(TAG, "engine:[" + engine + "]"); if (Constant.DEBUG) Log.d(TAG, "end onCreateEngine()"); return engine; } public void waitNano() { if (Constant.DEBUG) Log.d(TAG, "start waitNano"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { } if (Constant.DEBUG) Log.d(TAG, "end waitNano"); } public static String getErrorString(int err) { switch (err) { case EGL10.EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; case EGL10.EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; case EGL10.EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; case EGL10.EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; case EGL10.EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; case EGL10.EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; case EGL10.EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; case EGL10.EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; case EGL10.EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; case EGL10.EGL_BAD_MATCH: return "EGL_BAD_MATCH"; case EGL10.EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; case EGL10.EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; case EGL10.EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; case EGL11.EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; default: return "OTHER err:[" + err + "]"; } } }