001    /*
002     * To change this template, choose Tools | Templates
003     * and open the template in the editor.
004     */
005    package org.bridj;
006    
007    import org.bridj.util.ClassDefiner;
008    import android.app.Application;
009    import android.content.pm.ApplicationInfo;
010    import android.os.Environment;
011    import java.io.File;
012    import java.io.FileNotFoundException;
013    import java.io.IOException;
014    import java.net.URL;
015    import java.util.regex.Matcher;
016    import java.util.regex.Pattern;
017    
018    /**
019     *
020     * @author ochafik
021     */
022    public class AndroidSupport extends PlatformSupport {
023        private volatile Application app;
024        
025        AndroidSupport() {}
026        
027        
028        synchronized void setApp(Application application) {
029            if (this.app != null && application != null && this.app != application)
030                throw new IllegalArgumentException("Android Application has already been set to a different value : " + this.app);
031            
032            this.app = application;
033        }
034    
035        public static void setApplication(Application application) {
036            ((AndroidSupport)PlatformSupport.getInstance()).setApp(application);
037        }
038        
039        String adviseToSetApp() {
040            return app == null ? "" : "Please use AndroidSupport.setApplication(Application). ";
041        }
042        
043    
044        volatile AndroidClassDefiner classDefiner;
045    
046        synchronized File getCacheDir() throws FileNotFoundException {
047            File cacheDir = null;
048            if (app != null)
049                cacheDir = app.getCacheDir();
050            
051            if (cacheDir == null || !cacheDir.isDirectory() || !cacheDir.canWrite())
052                throw new FileNotFoundException("Failed to find the cache directory. " + adviseToSetApp());
053                        
054            return cacheDir;
055        }
056        @Override
057        public synchronized ClassDefiner getClassDefiner(ClassDefiner defaultDefiner, ClassLoader parentClassLoader) {
058            if (classDefiner == null)
059                try {
060                    classDefiner = new AndroidClassDefiner(getCacheDir(), parentClassLoader);
061                } catch (IOException ex) {
062                    throw new RuntimeException("Failed to instantiate the Android class definer : " + ex, ex);
063                }
064            return classDefiner;
065        }
066    
067        String getLibFileName(String libName) {
068            return "lib" + libName + ".so";
069        }
070        synchronized File getNativeLibraryDir(String someBundledNativeLibraryName) throws FileNotFoundException {
071            //String someKnownResource = 
072            File f = null;
073            if (app != null) {
074                try {
075                    // ApplicationInfo.nativeLibraryDir is only available from API level 9 and later
076                    // http://developer.android.com/reference/android/content/pm/ApplicationInfo.html#nativeLibraryDir
077                    f = (File)ApplicationInfo.class.getField("nativeLibraryDir").get(app.getApplicationInfo());
078                } catch (Throwable th) {}
079            }
080            if (f == null) {
081                String someKnownResource = "lib/armeabi/" + getLibFileName(someBundledNativeLibraryName);
082                f = new File(getApplicationDataDir(someKnownResource), "lib");
083            }
084            
085            if (f != null && f.isDirectory())
086                return f;
087        
088            throw new FileNotFoundException("Failed to get the native library directory " + (f != null ? "(" + f + " is not a directory). " : ". ") + adviseToSetApp());
089        }
090        
091            synchronized File getApplicationDataDir(String someKnownResource) throws FileNotFoundException {
092                    if (app != null)
093                            return new File(app.getApplicationInfo().dataDir);
094                    else
095                            return new File(new File(Environment.getDataDirectory(), "data"), getPackageName(someKnownResource));
096            }
097        
098        @Override
099        public synchronized NativeLibrary loadNativeLibrary(String name) throws IOException {
100            File f = new File(getNativeLibraryDir(name), getLibFileName(name));
101            if (f.exists()) {
102                return NativeLibrary.load(f == null ? name : f.toString());
103            } else {
104                throw new RuntimeException("File not found : " + f);
105            }
106        }
107        
108        synchronized String getPackageName(String someKnownResource) throws FileNotFoundException {
109            if (app != null)
110                return app.getPackageName();
111            else {
112                URL resource = Platform.getResource(someKnownResource);
113                if (resource == null)
114                    throw new FileNotFoundException("Resource does not exist : " + someKnownResource);
115    
116                return getAndroidPackageNameFromResourceURL(resource.toString());
117            }
118        }
119        
120        static String getAndroidPackageNameFromResourceURL(String url) {
121                    Pattern p = Pattern.compile("jar:file:/data/[^/]+/([^/]*?)\\.apk!.*");
122                    Matcher m = p.matcher(url);
123                    String packageName = null;
124                    if (!m.matches()) {
125                            p = Pattern.compile("jar:file:/.*?/([^/]+)/pkg\\.apk!.*");
126                            m = p.matcher(url);
127                    }
128                    if (m.matches()) {
129                            packageName = m.group(1);
130                            if (packageName.matches(".*?-\\d+")) {
131                                    int i = packageName.lastIndexOf("-");
132                                    packageName = packageName.substring(0, i);
133                            }
134                    }
135                    return packageName;
136        }
137        
138    }