diff options
author | Sven Gothel <[email protected]> | 2012-03-14 23:07:21 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2012-03-14 23:07:21 +0100 |
commit | 0cfc7847c58b51c9a26b50d905b592d1fc4c8578 (patch) | |
tree | 2764affabade9825b3e1b13035632b4934e3a690 /src/java/jogamp/android/launcher/ClassLoaderUtil.java | |
parent | eedb4b530fb83fc59a26962bcf7847a1404092a0 (diff) |
Android: New ActivityLauncher (jogamp.android-launcher.apk)
ActivityLauncher provides delegating Activities, allowing the user to:
- daisy chain custom APK classes and native libraries to the classpath
- name one custom activity which gets delegated to, the downstream activity
Overview:
[User:a1] -- (usr-data) --> [Launcher] -> [User:a2] + using [other packages..]
[User APK] - The user provided APK
[JogAmp APK] - JogAmp APKs
[User:a1] - The initial user activity, which starts the [Launcher].
Providing data to [Launcher]: [User:a2], [User APK]
Resides in [User APK]
[User:a2] - The actual downstream 'real' activity, spoiled w/ full fledged ClassLoader
having access to all packages as requested, ie. [User APK], ..
Resides in [User APK]
[Launcher] - The launcher activity.
Gets called by [User:a1].
Creates a new ClassLoader, daisy chainging all requested APKs.
Instantiates [User:a2] w/ new ClassLoader.
Delegates all calls to [User:a2].
Resides in [JogAmp APK].
Diffstat (limited to 'src/java/jogamp/android/launcher/ClassLoaderUtil.java')
-rw-r--r-- | src/java/jogamp/android/launcher/ClassLoaderUtil.java | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/src/java/jogamp/android/launcher/ClassLoaderUtil.java b/src/java/jogamp/android/launcher/ClassLoaderUtil.java new file mode 100644 index 0000000..319a0fd --- /dev/null +++ b/src/java/jogamp/android/launcher/ClassLoaderUtil.java @@ -0,0 +1,193 @@ +/** + * 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<String> 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<String> 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<String,?> map = (HashMap<String,?>) 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; + } + +} |