/** * 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.util.Arrays; import java.util.Iterator; import java.util.List; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.util.Log; public class ClassLoaderUtil { private static final String TAG = "JogampClassLoader"; // FIXME: Need to generalize this .. (Note: native lib resources must be cached!) private static final String[] packagesJogAmp = { "com.jogamp.common", "javax.media.opengl" }; private static ClassLoader jogAmpClassLoader = null; // location where optimized dex files will be written private static final String dexPathName= "jogampDex"; private static File dexPath = null; private static LauncherTempFileCache tmpFileCache = null; private static final String PATH_SEP = "/"; private static final String ELEM_SEP = ":"; private static synchronized void init(Context ctx) { if(null == tmpFileCache) { if(!LauncherTempFileCache.initSingleton(ctx)) { throw new InternalError("TempFileCache initialization error"); } tmpFileCache = new LauncherTempFileCache(); if(!tmpFileCache.isValid()) { throw new InternalError("TempFileCache instantiation error"); } dexPath = new File(tmpFileCache.getTempDir(), dexPathName); Log.d(TAG, "jogamp dexPath: " + dexPath.getAbsolutePath()); dexPath.mkdir(); } } public static synchronized ClassLoader createClassLoader(Context ctx, List userPackageNames, boolean addUserLibPath, List apkNames) { return createClassLoader(ctx, userPackageNames, addUserLibPath, apkNames, null); } public static synchronized ClassLoader createClassLoader(Context ctx, List userPackageNames, boolean addUserLibPath, List apkNames, ClassLoader parent) { init(ctx); if(null==jogAmpClassLoader) { jogAmpClassLoader = createClassLoaderImpl(ctx, Arrays.asList(packagesJogAmp), true, null, (null != parent ) ? parent : ctx.getClassLoader()); } parent = jogAmpClassLoader; return createClassLoaderImpl(ctx, userPackageNames, addUserLibPath, apkNames, jogAmpClassLoader); } /** * * @param ctx * @param userPackageNames list of user package names, the last entry shall reflect the Activity * @return */ private static synchronized ClassLoader createClassLoaderImpl(Context ctx, List userPackageNames, boolean addUserLibPath, List apkNames, ClassLoader parent) { final ApplicationInfo appInfo = ctx.getApplicationInfo(); final String appDir = new File(appInfo.dataDir).getParent(); final String libSub = appInfo.nativeLibraryDir.substring(appInfo.nativeLibraryDir.lastIndexOf('/')+1); Log.d(TAG, "S: userPackageName: "+userPackageNames+"; appName "+appInfo.name+", appDir "+appDir+", nativeLibraryDir: "+appInfo.nativeLibraryDir+"; dataDir: "+appInfo.dataDir+", libSub "+libSub); StringBuilder apks = new StringBuilder(); StringBuilder libs = new StringBuilder(); int apkCount = 0; String lastUserPackageName = null; // the very last one reflects the Activity if( null != userPackageNames ) { 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) { if(apkCount>0) { apks.append(ELEM_SEP); if(addUserLibPath) { libs.append(ELEM_SEP); } } apks.append(userAPK); Log.d(TAG, "APK["+apkCount+"] found: <"+lastUserPackageName+"> -> <"+userAPK+">"); Log.d(TAG, "APK["+apkCount+"] apks: <"+apks.toString()+">"); if(addUserLibPath) { libs.append(appDir).append(PATH_SEP).append(lastUserPackageName).append(PATH_SEP).append(libSub).append(PATH_SEP); Log.d(TAG, "APK["+apkCount+"] libs: <"+libs.toString()+">"); } apkCount++; } else { Log.d(TAG, "APK not found: <"+lastUserPackageName+">"); } } if( userPackageNames.size() != apkCount ) { Log.d(TAG, "User APKs incomplete, abort (1)"); return null; } } final int userAPKCount = apkCount; if( null != apkNames ) { for(Iterator i=apkNames.iterator(); i.hasNext(); ) { String userAPK = i.next(); if(apkCount>0) { apks.append(ELEM_SEP); } apks.append(userAPK); Log.d(TAG, "APK added: <"+userAPK+">"); apkCount++; } if( apkNames.size() != apkCount - userAPKCount ) { Log.d(TAG, "Framework APKs incomplete, abort (2)"); return null; } } // return new TraceDexClassLoader(apks.toString(), dexPath.getAbsolutePath(), libs.toString(), parent); return new AssetDexClassLoader(apks.toString(), dexPath.getAbsolutePath(), libs.toString(), parent); } /*** * 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; } */ }