001/* 002 * BridJ - Dynamic and blazing-fast native interop for Java. 003 * http://bridj.googlecode.com/ 004 * 005 * Copyright (c) 2010-2013, Olivier Chafik (http://ochafik.com/) 006 * All rights reserved. 007 * 008 * Redistribution and use in source and binary forms, with or without 009 * modification, are permitted provided that the following conditions are met: 010 * 011 * * Redistributions of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * * Redistributions in binary form must reproduce the above copyright 014 * notice, this list of conditions and the following disclaimer in the 015 * documentation and/or other materials provided with the distribution. 016 * * Neither the name of Olivier Chafik nor the 017 * names of its contributors may be used to endorse or promote products 018 * derived from this software without specific prior written permission. 019 * 020 * THIS SOFTWARE IS PROVIDED BY OLIVIER CHAFIK AND CONTRIBUTORS ``AS IS'' AND ANY 021 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 022 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 023 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 024 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 025 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 026 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 027 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 028 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 029 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 030 */ 031package org.bridj; 032 033import org.bridj.util.ClassDefiner; 034import android.app.Application; 035import android.content.pm.ApplicationInfo; 036import android.os.Environment; 037import java.io.File; 038import java.io.FileNotFoundException; 039import java.io.IOException; 040import java.net.URL; 041import java.util.regex.Matcher; 042import java.util.regex.Pattern; 043 044/** 045 * 046 * @author ochafik 047 */ 048public class AndroidSupport extends PlatformSupport { 049 050 private volatile Application app; 051 052 AndroidSupport() { 053 } 054 055 synchronized void setApp(Application application) { 056 if (this.app != null && application != null && this.app != application) { 057 throw new IllegalArgumentException("Android Application has already been set to a different value : " + this.app); 058 } 059 060 this.app = application; 061 } 062 063 public static void setApplication(Application application) { 064 ((AndroidSupport) PlatformSupport.getInstance()).setApp(application); 065 } 066 067 String adviseToSetApp() { 068 return app == null ? "" : "Please use AndroidSupport.setApplication(Application). "; 069 } 070 volatile AndroidClassDefiner classDefiner; 071 072 synchronized File getCacheDir() throws FileNotFoundException { 073 File cacheDir = null; 074 if (app != null) { 075 cacheDir = app.getCacheDir(); 076 } 077 078 if (cacheDir == null || !cacheDir.isDirectory() || !cacheDir.canWrite()) { 079 throw new FileNotFoundException("Failed to find the cache directory. " + adviseToSetApp()); 080 } 081 082 return cacheDir; 083 } 084 085 @Override 086 public synchronized ClassDefiner getClassDefiner(ClassDefiner defaultDefiner, ClassLoader parentClassLoader) { 087 if (classDefiner == null) { 088 try { 089 classDefiner = new AndroidClassDefiner(getCacheDir(), parentClassLoader); 090 } catch (IOException ex) { 091 throw new RuntimeException("Failed to instantiate the Android class definer : " + ex, ex); 092 } 093 } 094 return classDefiner; 095 } 096 097 String getLibFileName(String libName) { 098 return "lib" + libName + ".so"; 099 } 100 101 synchronized File getNativeLibraryDir(String someBundledNativeLibraryName) throws FileNotFoundException { 102 //String someKnownResource = 103 File f = null; 104 if (app != null) { 105 try { 106 // ApplicationInfo.nativeLibraryDir is only available from API level 9 and later 107 // http://developer.android.com/reference/android/content/pm/ApplicationInfo.html#nativeLibraryDir 108 f = (File) ApplicationInfo.class.getField("nativeLibraryDir").get(app.getApplicationInfo()); 109 } catch (Throwable th) { 110 } 111 } 112 if (f == null) { 113 String someKnownResource = "lib/armeabi/" + getLibFileName(someBundledNativeLibraryName); 114 f = new File(getApplicationDataDir(someKnownResource), "lib"); 115 } 116 117 if (f != null && f.isDirectory()) { 118 return f; 119 } 120 121 throw new FileNotFoundException("Failed to get the native library directory " + (f != null ? "(" + f + " is not a directory). " : ". ") + adviseToSetApp()); 122 } 123 124 synchronized File getApplicationDataDir(String someKnownResource) throws FileNotFoundException { 125 if (app != null) { 126 return new File(app.getApplicationInfo().dataDir); 127 } else { 128 return new File(new File(Environment.getDataDirectory(), "data"), getPackageName(someKnownResource)); 129 } 130 } 131 132 @Override 133 public synchronized NativeLibrary loadNativeLibrary(String name) throws IOException { 134 File f = new File(getNativeLibraryDir(name), getLibFileName(name)); 135 if (f.exists()) { 136 return NativeLibrary.load(f == null ? name : f.toString()); 137 } else { 138 throw new RuntimeException("File not found : " + f); 139 } 140 } 141 142 synchronized String getPackageName(String someKnownResource) throws FileNotFoundException { 143 if (app != null) { 144 return app.getPackageName(); 145 } else { 146 URL resource = Platform.getResource(someKnownResource); 147 if (resource == null) { 148 throw new FileNotFoundException("Resource does not exist : " + someKnownResource); 149 } 150 151 return getAndroidPackageNameFromResourceURL(resource.toString()); 152 } 153 } 154 155 static String getAndroidPackageNameFromResourceURL(String url) { 156 Pattern p = Pattern.compile("jar:file:/data/[^/]+/([^/]*?)\\.apk!.*"); 157 Matcher m = p.matcher(url); 158 String packageName = null; 159 if (!m.matches()) { 160 p = Pattern.compile("jar:file:/.*?/([^/]+)/pkg\\.apk!.*"); 161 m = p.matcher(url); 162 } 163 if (m.matches()) { 164 packageName = m.group(1); 165 if (packageName.matches(".*?-\\d+")) { 166 int i = packageName.lastIndexOf("-"); 167 packageName = packageName.substring(0, i); 168 } 169 } 170 return packageName; 171 } 172}