/** * BlueCove - Java library for Bluetooth * Copyright (C) 2006-2009 Vlad Skarzhevskyy * Copyright (C) 2010 Mina Shokry. * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * * @author vlads * @version $Id$ */ package com.intel.bluetooth; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Hashtable; /** * Load native library from resources. * * * By default Native Library is extracted from from jar to temporary directory * `${java.io.tmpdir}/bluecove_${user.name}_N` and loaded from this location. * <p> * If you wish to load library (.dll) from another location add this system property * `-Dbluecove.native.path=/your/path`. * <p> * If you wish to load library from default location in path e.g. `%SystemRoot%\system32` or any other location in * %PATH% use `-Dbluecove.native.resource=false` * */ public abstract class NativeLibLoader { static final int OS_UNSUPPORTED = -1; static final int OS_LINUX = 1; static final int OS_WINDOWS = 2; static final int OS_WINDOWS_CE = 3; static final int OS_MAC_OS_X = 4; static final int OS_ANDROID_1_X = 5; static final int OS_ANDROID_2_X = 6; private static int os = 0; private static Hashtable libsState = new Hashtable(); private static Object bluecoveDllDir = null; private static class LibState { boolean triedToLoadAlredy = false; boolean libraryAvailable = false; StringBuffer loadErrors = new StringBuffer(); } private NativeLibLoader() { } static int getOS() { if (os != 0) { return os; } String sysName = System.getProperty("os.name"); if (sysName == null) { DebugLog.fatal("Native Library not available on unknown platform"); os = OS_UNSUPPORTED; } else { sysName = sysName.toLowerCase(); if (sysName.indexOf("windows") != -1) { if (sysName.indexOf("ce") != -1) { os = OS_WINDOWS_CE; } else { os = OS_WINDOWS; } } else if (sysName.indexOf("mac os x") != -1) { os = OS_MAC_OS_X; } else if (sysName.indexOf("linux") != -1) { String javaRuntimeName = System.getProperty("java.runtime.name"); if ((javaRuntimeName != null) && (javaRuntimeName.toLowerCase().indexOf("android runtime") != -1)) { try { int androidApiLevel = Class.forName("android.os.Build$VERSION").getField("SDK_INT").getInt(null); // android 2.0 has code 5 if (androidApiLevel >= 5) { // let's consider probability that Android 2.x bluetooth APIs // are available but for some reason, we want to use the native // bluez stack directly. // In this case, user just has not to include // bluecove-android2.jar in classpath. Class.forName("com.intel.bluetooth.BluetoothStackAndroid"); os = OS_ANDROID_2_X; } else { os = OS_ANDROID_1_X; } } catch (Exception ex) { // if field android.os.Build.VERSION.SDK_INT doesn't exist, // we are on android 1.5 or earlier as this field was introduced // in android 1.6 (API Level 4). // also if com.intel.bluetooth.BluetoothStackAndroid class // doesn't exist in classpath, we will use native // bluez implementation os = OS_ANDROID_1_X; } } else { os = OS_LINUX; } } else { DebugLog.fatal("Native Library not available on platform " + sysName); os = OS_UNSUPPORTED; } } return os; } static boolean isAvailable(String name) { return isAvailable(name, null); } static String getLoadErrors(String name) { LibState state = (LibState) libsState.get(name); if ((state == null) || (state.loadErrors == null)) { return ""; } else { return state.loadErrors.toString(); } } static boolean isAvailable(String name, Class stackClass) { return isAvailable(name, stackClass, true); } static boolean isAvailable(String name, Class stackClass, boolean requiredLibrary) { LibState state = (LibState) libsState.get(name); if (state == null) { state = new LibState(); libsState.put(name, state); } if (state.triedToLoadAlredy) { return state.libraryAvailable; } state.loadErrors = new StringBuffer(); String libName = name; String libFileName = libName; // DebugLog.debug("OS:" + System.getProperty("os.name") + "|" + // System.getProperty("os.version") + "|" + // System.getProperty("os.arch")); // DebugLog.debug("Java:" + System.getProperty("java.vendor") + " " + // System.getProperty("java.version")); String sysName = System.getProperty("os.name"); String sysArch = System.getProperty("os.arch"); if (sysArch != null) { sysArch = sysArch.toLowerCase(); } else { sysArch = ""; } switch (getOS()) { case OS_UNSUPPORTED: state.loadErrors.append("Native Library " + name + " not available on [" + sysName + "] platform"); DebugLog.fatal("Native Library " + name + " not available on [" + sysName + "] platform"); state.triedToLoadAlredy = true; state.libraryAvailable = false; return state.libraryAvailable; case OS_WINDOWS_CE: libName += "_ce"; libFileName = libName; libFileName = libFileName + ".dll"; break; case OS_WINDOWS: if ((sysArch.indexOf("amd64") != -1) || (sysArch.indexOf("x86_64") != -1)) { libName += "_x64"; libFileName = libName; } libFileName = libFileName + ".dll"; break; case OS_MAC_OS_X: libFileName = "lib" + libFileName + ".jnilib"; break; case OS_LINUX: if ((sysArch.indexOf("i386") != -1) || (sysArch.length() == 0)) { // regular Intel } else if ((sysArch.indexOf("amd64") != -1) || (sysArch.indexOf("x86_64") != -1)) { libName += "_x64"; } else if ((sysArch.indexOf("x86") != -1)) { // regular Intel under IBM J9 } else { // Any other system libName += "_" + sysArch; } libFileName = libName; libFileName = "lib" + libFileName + ".so"; break; case OS_ANDROID_1_X: libFileName = "lib" + libFileName + ".so"; break; case OS_ANDROID_2_X: // we don't need to load any native libraries with android 2.x state.libraryAvailable = true; break; default: state.loadErrors.append("Native Library " + name + " not available on [" + sysName + "] platform"); DebugLog.fatal("Native Library " + name + " not available on platform " + sysName); state.triedToLoadAlredy = true; state.libraryAvailable = false; return state.libraryAvailable; } String path = System.getProperty(BlueCoveConfigProperties.PROPERTY_NATIVE_PATH); if (path != null) { if (!UtilsJavaSE.ibmJ9midp) { state.libraryAvailable = tryloadPath(path, libFileName, state.loadErrors); } else { // Not working // state.libraryAvailable = tryloadPathIBMj9MIDP(path, // libFileName); } } boolean useResource = true; String d = System.getProperty(BlueCoveConfigProperties.PROPERTY_NATIVE_RESOURCE); if (((d != null) && (d.equalsIgnoreCase("false"))) || (getOS() == OS_ANDROID_1_X || getOS() == OS_ANDROID_2_X)) { useResource = false; } if ((!state.libraryAvailable) && (useResource) && (!UtilsJavaSE.ibmJ9midp)) { state.libraryAvailable = loadAsSystemResource(libFileName, stackClass, state.loadErrors); } // Try load bluecove from package private location installed on the system if ((!state.libraryAvailable) && (getOS() == OS_LINUX) && (!UtilsJavaSE.ibmJ9midp)) { state.libraryAvailable = tryloadPath(createLinuxPackagePath(sysArch), libFileName, state.loadErrors); } if (!state.libraryAvailable) { if (!UtilsJavaSE.ibmJ9midp) { state.libraryAvailable = tryload(libName, state.loadErrors); } else { state.libraryAvailable = tryloadIBMj9MIDP(libName); } } if (!state.libraryAvailable) { if (requiredLibrary) { System.err.println("Native Library " + libName + " not available"); } DebugLog.debug("java.library.path", System.getProperty("java.library.path")); } state.triedToLoadAlredy = true; return state.libraryAvailable; } private static String createLinuxPackagePath(String sysArch) { if (sysArch.indexOf("64") != -1) { return "/usr/lib64/bluecove/" + BlueCoveImpl.version; } else { return "/usr/lib/bluecove/" + BlueCoveImpl.version; } } private static boolean tryload(String name, StringBuffer loadErrors) { try { System.loadLibrary(name); DebugLog.debug("Library loaded", name); } catch (Throwable e) { DebugLog.error("Library " + name + " not loaded ", e); loadErrors.append("\nload [").append(name).append("] ").append(e.getMessage()); return false; } return true; } private static boolean tryloadIBMj9MIDP(String name) { try { IBMJ9Helper.loadLibrary(name); DebugLog.debug("Library loaded", name); } catch (Throwable e) { DebugLog.error("Library " + name + " not loaded ", e); return false; } return true; } private static boolean tryloadPath(String path, String name, StringBuffer loadErrors) { UtilsStringTokenizer tok = new UtilsStringTokenizer(path, File.pathSeparator); while (tok.hasMoreTokens()) { String dirPath = tok.nextToken(); File dir = new File(dirPath); if (dir.isDirectory()) { if (tryloadFile(dir, name, loadErrors)) { return true; } } } return false; } private static boolean tryloadFile(File path, String name, StringBuffer loadErrors) { File f = new File(path, name); if (!f.canRead()) { DebugLog.debug("Native Library " + f.getAbsolutePath() + " not found"); return false; } try { System.load(f.getAbsolutePath()); DebugLog.debug("Library loaded", f.getAbsolutePath()); return true; } catch (Throwable e) { DebugLog.error("Can't load library from path " + path, e); loadErrors.append("\nload [").append(f.getAbsolutePath()).append("] ").append(e.getMessage()); return false; } } private static boolean tryloadPathIBMj9MIDP(String path, String name) { try { IBMJ9Helper.loadLibrary(path + "\\" + name); DebugLog.debug("Library loaded", path + "\\" + name); } catch (Throwable e) { DebugLog.error("Can't load library from path " + path + "\\" + name, e); return false; } return true; } private static boolean loadAsSystemResource(String libFileName, Class stackClass, StringBuffer loadErrors) { InputStream is = null; try { ClassLoader clo = null; try { if (stackClass != null) { clo = stackClass.getClassLoader(); DebugLog.debug("Use stack ClassLoader"); } else { clo = NativeLibLoader.class.getClassLoader(); } } catch (Throwable j9) { } if (clo == null) { DebugLog.debug("Use System ClassLoader"); is = ClassLoader.getSystemResourceAsStream(libFileName); } else { is = clo.getResourceAsStream(libFileName); } } catch (Throwable e) { DebugLog.error("Native Library " + libFileName + " is not a Resource !"); loadErrors.append("\nresource not found ").append(libFileName); return false; } if (is == null) { DebugLog.error("Native Library " + libFileName + " is not a Resource !"); loadErrors.append("\nresource not found ").append(libFileName); return false; } File fd = makeTempName(libFileName); try { if (!copy2File(is, fd)) { loadErrors.append("\ncan't create temp file"); return false; } } finally { try { is.close(); } catch (IOException ignore) { is = null; } } try { fd.deleteOnExit(); } catch (Throwable e) { // Java 1.1 or J9 } // deleteOnExit(fd); try { System.load(fd.getAbsolutePath()); DebugLog.debug("Library loaded from", fd); } catch (Throwable e) { DebugLog.fatal("Can't load library file ", e); loadErrors.append("\nload resource [").append(fd.getAbsolutePath()).append("] ").append(e.getMessage()); File debugFileCreated = new File(fd.getAbsolutePath()); if (!debugFileCreated.canRead()) { DebugLog.fatal("File " + fd.getAbsolutePath() + " magicaly disappeared"); } return false; } return true; } private static boolean copy2File(InputStream is, File fd) { FileOutputStream fos = null; try { fos = new FileOutputStream(fd); byte b[] = new byte[1000]; int len; while ((len = is.read(b)) >= 0) { fos.write(b, 0, len); } return true; } catch (Throwable e) { DebugLog.debug("Can't create temp file ", e); System.err.println("Can't create temp file " + fd.getAbsolutePath()); return false; } finally { if (fos != null) { try { fos.close(); } catch (IOException ignore) { fos = null; } } } } private static File makeTempName(String libFileName) { if (bluecoveDllDir != null) { File f = new File((File)bluecoveDllDir, libFileName); DebugLog.debug("tmp file", f.getAbsolutePath()); return f; } String tmpDir = System.getProperty("java.io.tmpdir"); if ((tmpDir == null) || (tmpDir.length() == 0)) { // CrEme 3.29 tmpDir = "temp"; } String uname = System.getProperty("user.name"); int count = 0; File fd = null; File dir = null; selectDirectory: while (true) { if (count > 10) { DebugLog.debug("Can't create temporary dir " + dir.getAbsolutePath()); return new File(tmpDir, libFileName); } dir = new File(tmpDir, "bluecove_" + uname + "_" + (count++)); if (dir.exists()) { if (!dir.isDirectory()) { continue selectDirectory; } // Remove all files. try { File[] files = dir.listFiles(); for (int i = 0; i < files.length; i++) { if (!files[i].delete()) { continue selectDirectory; } } } catch (Throwable e) { // Java 1.1 or J9 } } if ((!dir.exists()) && (!dir.mkdirs())) { DebugLog.debug("Can't create temporary dir ", dir.getAbsolutePath()); continue selectDirectory; } try { dir.deleteOnExit(); } catch (Throwable e) { // Java 1.1 or J9 } fd = new File(dir, libFileName); if ((fd.exists()) && (!fd.delete())) { continue; } try { if (!fd.createNewFile()) { DebugLog.debug("Can't create file in temporary dir ", fd.getAbsolutePath()); continue; } } catch (IOException e) { DebugLog.debug("Can't create file in temporary dir ", fd.getAbsolutePath()); continue; } catch (Throwable e) { // Java 1.1 or J9 } bluecoveDllDir = dir; DebugLog.debug("set dll dir", dir.getAbsolutePath()); break; } return fd; } // private static void deleteOnExit(final File fd) { // Runnable r = new Runnable() { // public void run() { // if (!fd.delete()) { // System.err.println("Can't remove Native Library " + fd); // } // } // }; // Runtime.getRuntime().addShutdownHook(new Thread(r)); // } }