From 235f8b1cbff8ed13071d5c19c0be492c0b25cb78 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Sat, 17 Mar 2012 21:15:49 +0100
Subject: Add 'asset' URLConnection; IOUtil uses URLConnection / incr.
 effeciency; Android ClassLoaderUtil cleanup;

- Add 'asset' URLConnection
  - Please read API doc 'PiggybackURLConnection' and 'AssetURLConnection'

  - Solves generic resource handling where platform locations may differ,
    ie ClassLoader lookup on Android in the 'assets/' subfolder.

  - New Android 'AssetDexClassLoader' uses 'assets/' folder for findResource(..)

  - aapt.signed (our APK ant task)
    - uses 'assets/' folder

    - adds the 'assetsdir' attribute allowing to copy other assets into the APK

- IOUtil uses URLConnection / incr. effeciency
  - using URLConnection on all getResource(..) since URL
    is connected anyways for validation and URLConnection can be used by caller right away

  - String getRelativeOf(URL, String) -> URL getRelativeOf(URL, String)
    - preserves scheme, authority, etc
    - simple parentOf handling, more efficient

  - reusing new 'asset' protocol impl.

- Android ClassLoaderUtil cleanup;
  - Use createClassLoader(..) impl for build-in static jogamp and user APKs,
    which removes code redundancy

Tests: New code path, especially 'assets' are covered by new unit tests, no regressions on Linux.
---
 .../jogamp/android/launcher/ClassLoaderUtil.java   | 140 ++++++++++-----------
 1 file changed, 68 insertions(+), 72 deletions(-)

(limited to 'src/java/jogamp/android/launcher/ClassLoaderUtil.java')

diff --git a/src/java/jogamp/android/launcher/ClassLoaderUtil.java b/src/java/jogamp/android/launcher/ClassLoaderUtil.java
index 319a0fd..85c0b94 100644
--- a/src/java/jogamp/android/launcher/ClassLoaderUtil.java
+++ b/src/java/jogamp/android/launcher/ClassLoaderUtil.java
@@ -29,89 +29,83 @@
 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.Arrays;
 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
+   // 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;
    
-   public static final String dexPathName= "jogampDex";
+   // 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 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");
-           }
+   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");                
-           }
-           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);
+           }           
+           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");
-           }           
+       }       
+   }
+   
+   public static synchronized ClassLoader createClassLoader(Context ctx, List<String> userPackageNames, boolean addLibPath) {       
+       return createClassLoader(ctx, userPackageNames, addLibPath, null); 
+   }
+   
+   public static synchronized ClassLoader createClassLoader(Context ctx, List<String> userPackageNames, 
+                                                            boolean addLibPath, ClassLoader parent) {
+       init(ctx);
+       
+       if(null==jogAmpClassLoader) {           
+           jogAmpClassLoader = createClassLoaderImpl(ctx, Arrays.asList(packagesJogAmp), true, 
+                                                     (null != parent ) ? parent : ctx.getClassLoader());
        }
+       parent =  jogAmpClassLoader;
        
-       StringBuilder userAPKs = new StringBuilder();
-       int numUserAPKs = 0;
+       return createClassLoaderImpl(ctx, userPackageNames, addLibPath, 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<String> userPackageNames, 
+                                                                 boolean addLibPath, 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
+       
        for(Iterator<String> i=userPackageNames.iterator(); i.hasNext(); ) {
            lastUserPackageName = i.next();
            String userAPK = null;
@@ -121,32 +115,33 @@ public class ClassLoaderUtil {
                Log.d(TAG, "error: "+e, e);
            }
            if(null != userAPK) {
-               numUserAPKs++;
-               if(numUserAPKs>0) {
-                   userAPKs.append(":");
+               if(apkCount>0) {
+                   apks.append(ELEM_SEP);
+                   if(addLibPath) {
+                       libs.append(ELEM_SEP);
+                   }
+               }
+               apks.append(userAPK);
+               if(addLibPath) {
+                   libs.append(appDir).append(PATH_SEP).append(lastUserPackageName).append(PATH_SEP).append(libSub).append(PATH_SEP);
                }
-               userAPKs.append(userAPK);
                Log.d(TAG, "APK found: <"+lastUserPackageName+"> -> <"+userAPK+">");
+               apkCount++;
            } else {
                Log.d(TAG, "APK not found: <"+lastUserPackageName+">");
            }
        }
-       if( userPackageNames.size()!=numUserAPKs ) {
+       if( userPackageNames.size()!=apkCount ) {
            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;
+       // 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 {
@@ -188,6 +183,7 @@ public class ClassLoaderUtil {
            }
        }
        return null;
-   }   
+   }
+   */   
 
 }
-- 
cgit v1.2.3