/** * Copyright 2011 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ package jogamp.android.launcher; import java.io.File; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Iterator; import java.util.List; import android.app.Activity; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.util.Log; import dalvik.system.DexClassLoader; public class ClassLoaderUtil { private static final String TAG = "JogampClassLoader"; public static final String packageGlueGen = "com.jogamp.common"; public static final String packageJogl = "javax.media.opengl"; // FIXME: a 'performance' hack public static final String dexPathName= "jogampDex"; private static LauncherTempFileCache tmpFileCache = null; private static ClassLoader jogAmpClassLoader = null; /** * * @param ctx * @param userPackageNames list of user package names, the last entry shall reflect the Activity * @return */ public static synchronized ClassLoader createJogampClassLoaderSingleton(Context ctx, List userPackageNames) { if(null==jogAmpClassLoader) { if(null!=tmpFileCache) { throw new InternalError("XXX0"); } if(!LauncherTempFileCache.initSingleton(ctx)) { throw new InternalError("TempFileCache initialization error"); } tmpFileCache = new LauncherTempFileCache(); if(!tmpFileCache.isValid()) { throw new InternalError("TempFileCache instantiation error"); } final ApplicationInfo ai = ctx.getApplicationInfo(); Log.d(TAG, "S: userPackageName: "+userPackageNames+", dataDir: "+ai.dataDir+", nativeLibraryDir: "+ai.nativeLibraryDir); final String appDir = new File(ai.dataDir).getParent(); final String libSub = ai.nativeLibraryDir.substring(ai.nativeLibraryDir.lastIndexOf('/')+1); Log.d(TAG, "S: appDir: "+appDir+", libSub: "+libSub); final String libPathName = appDir + "/" + packageGlueGen + "/" + libSub + "/:" + appDir + "/" + packageJogl + "/" + libSub + "/" ; Log.d(TAG, "S: libPath: "+libPathName); String apkGlueGen = null; String apkJogl = null; try { apkGlueGen = ctx.getPackageManager().getApplicationInfo(packageGlueGen,0).sourceDir; apkJogl = ctx.getPackageManager().getApplicationInfo(packageJogl,0).sourceDir; } catch (PackageManager.NameNotFoundException e) { Log.d(TAG, "error: "+e, e); } if(null == apkGlueGen || null == apkJogl) { Log.d(TAG, "not found: gluegen <"+apkGlueGen+">, jogl <"+apkJogl+">"); return null; } final String cp = apkGlueGen + ":" + apkJogl ; Log.d(TAG, "jogamp cp: " + cp); final File dexPath = new File(tmpFileCache.getTempDir(), dexPathName); Log.d(TAG, "jogamp dexPath: " + dexPath.getAbsolutePath()); dexPath.mkdir(); jogAmpClassLoader = new DexClassLoader(cp, dexPath.getAbsolutePath(), libPathName, ctx.getClassLoader()); } else { if(null==tmpFileCache) { throw new InternalError("XXX1"); } } StringBuilder userAPKs = new StringBuilder(); int numUserAPKs = 0; String lastUserPackageName = null; // the very last one reflects the Activity for(Iterator i=userPackageNames.iterator(); i.hasNext(); ) { lastUserPackageName = i.next(); String userAPK = null; try { userAPK = ctx.getPackageManager().getApplicationInfo(lastUserPackageName,0).sourceDir; } catch (PackageManager.NameNotFoundException e) { Log.d(TAG, "error: "+e, e); } if(null != userAPK) { numUserAPKs++; if(numUserAPKs>0) { userAPKs.append(":"); } userAPKs.append(userAPK); Log.d(TAG, "APK found: <"+lastUserPackageName+"> -> <"+userAPK+">"); } else { Log.d(TAG, "APK not found: <"+lastUserPackageName+">"); } } if( userPackageNames.size()!=numUserAPKs ) { Log.d(TAG, "APKs incomplete, abort"); return null; } final String userAPKs_s = userAPKs.toString(); Log.d(TAG, "user cp: " + userAPKs_s); final File dexPath = new File(tmpFileCache.getTempDir(), lastUserPackageName); Log.d(TAG, "user dexPath: " + dexPath.getAbsolutePath()); dexPath.mkdir(); ClassLoader cl = new DexClassLoader(userAPKs_s, dexPath.getAbsolutePath(), null, jogAmpClassLoader); Log.d(TAG, "cl: " + cl); return cl; } public boolean setAPKClassLoader(String activityPackageName, ClassLoader classLoader) { try { Field mMainThread = getField(Activity.class, "mMainThread"); Object mainThread = mMainThread.get(this); Class threadClass = mainThread.getClass(); Field mPackages = getField(threadClass, "mPackages"); @SuppressWarnings("unchecked") HashMap map = (HashMap) mPackages.get(mainThread); WeakReference ref = (WeakReference) map.get(activityPackageName); Object apk = ref.get(); Class apkClass = apk.getClass(); Field mClassLoader = getField(apkClass, "mClassLoader"); mClassLoader.set(apk, classLoader); Log.d(TAG, "setAPKClassLoader: OK"); return true; } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } Log.d(TAG, "setAPKClassLoader: FAILED"); return false; } private Field getField(Class cls, String name) { for (Field field: cls.getDeclaredFields()) { if (!field.isAccessible()) { field.setAccessible(true); } if (field.getName().equals(name)) { return field; } } return null; } }