/** * BlueCove - Java library for Bluetooth * Copyright (C) 2004 Intel Corporation * 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.security.AccessControlContext; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import javax.bluetooth.BluetoothStateException; import com.intel.bluetooth.BluetoothStack.LibraryInformation; /** * * Singleton class used as holder for BluetoothStack. * * Under security manager all you need to do is initialize BlueCoveImpl inside Privileged * context. * <p> * If automatic Bluetooth Stack detection is not enough Java System property * "bluecove.stack" can be used to force desired Stack Initialization. Values "widcomm", * "bluesoleil" or "winsock". By default winsock is selected if available. * <p> * Another property "bluecove.stack.first" is used optimize stack detection. If * -Dbluecove.stack.first=widcomm then widcomm (bluecove.dll) stack is loaded first and if * not available then BlueCove will switch to winsock. By default intelbth.dll is loaded * first. * <p> * If multiple stacks are detected they are selected in following order: "winsock", * "widcomm", "bluesoleil". Since BlueCove v2.0.1 "bluecove.stack.first" will alter the * order of stack selection. * <p> * To use jsr-82 emulator set "bluecove.stack" value to "emulator". * <p> * If System property is not an option (e.g. when running in Webstart) create text file * "bluecove.stack" or "bluecove.stack.first" containing stack name and add this file to * BlueCove or Application jar. (Since v2.0.1) * <p> * Use `LocalDevice.getProperty("bluecove.stack")` to find out which stack is used. * */ public class BlueCoveImpl { public static final String BLUETOOTH_API_VERSION = "1.1.1"; public static final String OBEX_API_VERSION = BLUETOOTH_API_VERSION; public static final int versionMajor1 = 2; public static final int versionMajor2 = 1; public static final int versionMinor = 1; public static final int versionBuild = 0; public static final String versionSufix = "-SNAPSHOT"; // SNAPSHOT public static final String version = String.valueOf(versionMajor1) + "." + String.valueOf(versionMajor2) + "." + String.valueOf(versionMinor) + versionSufix; public static final int nativeLibraryVersionExpected = versionMajor1 * 1000000 + versionMajor2 * 10000 + versionMinor * 100 + versionBuild; public static final String STACK_WINSOCK = "winsock"; public static final String STACK_WIDCOMM = "widcomm"; public static final String STACK_BLUESOLEIL = "bluesoleil"; public static final String STACK_TOSHIBA = "toshiba"; public static final String STACK_BLUEZ = "bluez"; public static final String STACK_BLUEZ_DBUS = "bluez-dbus"; public static final String STACK_OSX = "mac"; public static final String STACK_EMULATOR = "emulator"; public static final String STACK_ANDROID_2_X = "android_2.x"; // We can't use the same DLL on windows for all implementations. // Since WIDCOMM need to be compile /MD using VC6 and winsock /MT using // VC2005 // This variable can be used to simplify development/test builds private static final boolean oneDLLbuild = false; public static final String NATIVE_LIB_MS = "intelbth"; public static final String NATIVE_LIB_WIDCOMM = oneDLLbuild ? NATIVE_LIB_MS : "bluecove"; public static final String NATIVE_LIB_TOSHIBA = "bluecove"; public static final String NATIVE_LIB_BLUEZ = "bluecove"; public static final String NATIVE_LIB_OSX = "bluecove"; /** * To work on BlueSoleil version 2.3 we need to compile C++ code /MT the same as * winsock. */ public static final String NATIVE_LIB_BLUESOLEIL = NATIVE_LIB_MS; static final int BLUECOVE_STACK_DETECT_MICROSOFT = 1; static final int BLUECOVE_STACK_DETECT_WIDCOMM = 1 << 1; static final int BLUECOVE_STACK_DETECT_BLUESOLEIL = 1 << 2; static final int BLUECOVE_STACK_DETECT_TOSHIBA = 1 << 3; static final int BLUECOVE_STACK_DETECT_OSX = 1 << 4; public static final int BLUECOVE_STACK_DETECT_BLUEZ = 1 << 5; public static final int BLUECOVE_STACK_DETECT_EMULATOR = 1 << 6; public static final int BLUECOVE_STACK_DETECT_BLUEZ_DBUS = 1 << 7; public static final int BLUECOVE_STACK_DETECT_ANDROID_1_X = 1 << 8; public static final int BLUECOVE_STACK_DETECT_ANDROID_2_X = 1 << 9; static final String TRUE = "true"; static final String FALSE = "false"; private static final String FQCN = BlueCoveImpl.class.getName(); private static final Vector fqcnSet = new Vector(); /* The context to be used when loading native DLL */ private Object accessControlContext; private static ShutdownHookThread shutdownHookRegistered; private static BlueCoveImpl instance; private static BluetoothStackHolder singleStack; private static ThreadLocalWrapper threadStack; private static BluetoothStackHolder threadStackIDDefault; private static Hashtable resourceConfigProperties = new Hashtable(); private static Hashtable/* <BluetoothStack, BluetoothStackHolder> */stacks = new Hashtable(); private static Vector initializationProperties = new Vector(); static { fqcnSet.addElement(FQCN); for (int i = 0; i < BlueCoveConfigProperties.INITIALIZATION_PROPERTIES.length; i++) { initializationProperties.addElement(BlueCoveConfigProperties.INITIALIZATION_PROPERTIES[i]); } } /** * Enables the use of Multiple Adapters and Bluetooth Stacks in parallel. */ private static class BluetoothStackHolder { private BluetoothStack bluetoothStack; Hashtable configProperties = new Hashtable(); private static BluetoothStack getBluetoothStack() throws BluetoothStateException { return instance().getBluetoothStack(); } public String toString() { if (bluetoothStack == null) { return "not initialized"; } return bluetoothStack.toString(); } } /** * bluetoothStack.destroy(); May stuck forever. Exit JVM anyway after timeout. */ private class AsynchronousShutdownThread extends Thread { final Object monitor = new Object(); int shutdownStart = 0; AsynchronousShutdownThread() { super("BluecoveAsynchronousShutdownThread"); } public void run() { synchronized (monitor) { while (shutdownStart == 0) { try { monitor.wait(); } catch (InterruptedException e) { return; } } } if (shutdownStart == -1) { return; } if (!stacks.isEmpty()) { for (Enumeration en = stacks.elements(); en.hasMoreElements();) { BluetoothStackHolder s = (BluetoothStackHolder) en.nextElement(); if (s.bluetoothStack != null) { try { s.bluetoothStack.destroy(); } finally { s.bluetoothStack = null; } } } stacks.clear(); System.out.println("BlueCove stack shutdown completed"); } synchronized (monitor) { monitor.notifyAll(); } } void deRegister() { shutdownStart = -1; synchronized (monitor) { monitor.notifyAll(); } } } private class ShutdownHookThread extends Thread { AsynchronousShutdownThread shutdownHookThread; ShutdownHookThread(AsynchronousShutdownThread shutdownHookThread) { super("BluecoveShutdownHookThread"); this.shutdownHookThread = shutdownHookThread; } public void run() { final Object monitor = shutdownHookThread.monitor; synchronized (monitor) { shutdownHookThread.shutdownStart = 1; monitor.notifyAll(); if (!stacks.isEmpty()) { try { monitor.wait(7000); } catch (InterruptedException e) { } } } } void deRegister() { shutdownHookRegistered = null; UtilsJavaSE.runtimeRemoveShutdownHook(this); shutdownHookThread.deRegister(); } } /** * Applications should not used this function. Allow default initialization. In Secure * environment instance() should be called initially from secure context. * * @return Instance of the class, getBluetoothStack() can be called. */ public static synchronized BlueCoveImpl instance() { if (instance == null) { instance = new BlueCoveImpl(); } return instance; } private BlueCoveImpl() { try { accessControlContext = AccessController.getContext(); } catch (Throwable javaME) { } // Initialization in WebStart. DebugLog.isDebugEnabled(); copySystemProperties(null); } static int getNativeLibraryVersion() { return nativeLibraryVersionExpected; } private synchronized void createShutdownHook() { if (shutdownHookRegistered != null) { return; } AsynchronousShutdownThread shutdownHookThread = new AsynchronousShutdownThread(); if (UtilsJavaSE.runtimeAddShutdownHook(shutdownHookRegistered = new ShutdownHookThread(shutdownHookThread))) { UtilsJavaSE.threadSetDaemon(shutdownHookThread); shutdownHookThread.start(); } } private int getStackId(String stack) { if (STACK_WIDCOMM.equalsIgnoreCase(stack)) { return BLUECOVE_STACK_DETECT_WIDCOMM; } else if (STACK_BLUESOLEIL.equalsIgnoreCase(stack)) { return BLUECOVE_STACK_DETECT_BLUESOLEIL; } else if (STACK_TOSHIBA.equalsIgnoreCase(stack)) { return BLUECOVE_STACK_DETECT_TOSHIBA; } else if (STACK_WINSOCK.equalsIgnoreCase(stack)) { return BLUECOVE_STACK_DETECT_MICROSOFT; } else if (STACK_BLUEZ.equalsIgnoreCase(stack)) { return BLUECOVE_STACK_DETECT_BLUEZ; } else if (STACK_BLUEZ_DBUS.equalsIgnoreCase(stack)) { return BLUECOVE_STACK_DETECT_BLUEZ_DBUS; } else if (STACK_WINSOCK.equalsIgnoreCase(stack)) { return BLUECOVE_STACK_DETECT_OSX; } else if (STACK_EMULATOR.equalsIgnoreCase(stack)) { return BLUECOVE_STACK_DETECT_EMULATOR; } else { return 0; } } private Class loadStackClass(String classPropertyName, String classNamesDefault) throws BluetoothStateException { String classNames = getConfigProperty(classPropertyName); if (classNames == null) { classNames = classNamesDefault; } UtilsStringTokenizer tok = new UtilsStringTokenizer(classNames, "|"); while (tok.hasMoreTokens()) { String className = tok.nextToken(); try { return Class.forName(className); } catch (ClassNotFoundException e) { DebugLog.error(className, e); } } throw new BluetoothStateException("BlueCove " + classNames + " not available"); } private BluetoothStack newStackInstance(Class ctackClass) throws BluetoothStateException { String className = ctackClass.getName(); try { return (BluetoothStack) ctackClass.newInstance(); } catch (InstantiationException e) { DebugLog.error(className, e); } catch (IllegalAccessException e) { DebugLog.error(className, e); } throw new BluetoothStateException("BlueCove " + className + " can't instantiate"); } private BluetoothStack loadStack(String classPropertyName, String classNameDefault) throws BluetoothStateException { return newStackInstance(loadStackClass(classPropertyName, classNameDefault)); } static void loadNativeLibraries(BluetoothStack stack) throws BluetoothStateException { // Check is libraries already loaded try { if ((UtilsJavaSE.canCallNotLoadedNativeMethod) && (stack.isNativeCodeLoaded())) { return; } } catch (Error e) { // We caught UnsatisfiedLinkError } LibraryInformation[] libs = stack.requireNativeLibraries(); if ((libs == null) || (libs.length == 0)) { // No native libs for this stack return; } for (int i = 0; i < libs.length; i++) { Class c = libs[i].stackClass; if (c == null) { c = stack.getClass(); } if (!NativeLibLoader.isAvailable(libs[i].libraryName, c, libs[i].required)) { if (libs[i].required) { throw new BluetoothStateException("BlueCove library " + libs[i].libraryName + " not available;" + NativeLibLoader.getLoadErrors(libs[i].libraryName)); } else { DebugLog.debug("library " + libs[i].libraryName + " not available"); } } } } private static boolean isNativeLibrariesAvailable(BluetoothStack stack) { try { if (UtilsJavaSE.canCallNotLoadedNativeMethod) { return stack.isNativeCodeLoaded(); } } catch (Error e) { // We caught UnsatisfiedLinkError } LibraryInformation[] libs = stack.requireNativeLibraries(); if ((libs == null) || (libs.length == 0)) { // No native libs for this stack return true; } for (int i = 0; i < libs.length; i++) { Class c = libs[i].stackClass; if (c == null) { c = stack.getClass(); } if (!NativeLibLoader.isAvailable(libs[i].libraryName, c)) { return false; } } return true; } private BluetoothStack detectStack() throws BluetoothStateException { BluetoothStack detectorStack = null; String stackFirstDetector = getConfigProperty(BlueCoveConfigProperties.PROPERTY_STACK_FIRST); String stackSelected = getConfigProperty(BlueCoveConfigProperties.PROPERTY_STACK); if (stackFirstDetector == null) { stackFirstDetector = stackSelected; } if (STACK_EMULATOR.equals(stackSelected)) { detectorStack = loadStack(BlueCoveConfigProperties.PROPERTY_EMULATOR_CLASS, "com.intel.bluetooth.BluetoothEmulator"); } else { switch (NativeLibLoader.getOS()) { case NativeLibLoader.OS_LINUX: case NativeLibLoader.OS_ANDROID_1_X: Class stackClass = loadStackClass(BlueCoveConfigProperties.PROPERTY_BLUEZ_CLASS, "com.intel.bluetooth.BluetoothStackBlueZ|com.intel.bluetooth.BluetoothStackBlueZDBus"); detectorStack = newStackInstance(stackClass); loadNativeLibraries(detectorStack); stackSelected = detectorStack.getStackID(); break; case NativeLibLoader.OS_ANDROID_2_X: String androidBluetoothStack = "com.intel.bluetooth.BluetoothStackAndroid"; try { Class androidStackClass = Class.forName(androidBluetoothStack); detectorStack = newStackInstance(androidStackClass); stackSelected = detectorStack.getStackID(); break; } catch (ClassNotFoundException ex) { throw new BluetoothStateException("BlueCove " + androidBluetoothStack + " not available"); } case NativeLibLoader.OS_MAC_OS_X: detectorStack = new BluetoothStackOSX(); loadNativeLibraries(detectorStack); stackSelected = detectorStack.getStackID(); break; case NativeLibLoader.OS_WINDOWS: case NativeLibLoader.OS_WINDOWS_CE: detectorStack = createDetectorOnWindows(stackFirstDetector); if (DebugLog.isDebugEnabled()) { detectorStack.enableNativeDebug(DebugLog.class, true); } break; default: throw new BluetoothStateException("BlueCove not available"); } } int libraryVersion = detectorStack.getLibraryVersion(); if (nativeLibraryVersionExpected != libraryVersion) { DebugLog.fatal("BlueCove native library version mismatch " + libraryVersion + " expected " + nativeLibraryVersionExpected); throw new BluetoothStateException("BlueCove native library version mismatch"); } if (stackSelected == null) { // auto detect int aval = detectorStack.detectBluetoothStack(); DebugLog.debug("BluetoothStack detected", aval); int detectorID = getStackId(detectorStack.getStackID()); if ((aval & detectorID) != 0) { stackSelected = detectorStack.getStackID(); } else if ((aval & BLUECOVE_STACK_DETECT_MICROSOFT) != 0) { stackSelected = STACK_WINSOCK; } else if ((aval & BLUECOVE_STACK_DETECT_WIDCOMM) != 0) { stackSelected = STACK_WIDCOMM; } else if ((aval & BLUECOVE_STACK_DETECT_BLUESOLEIL) != 0) { stackSelected = STACK_BLUESOLEIL; } else if ((aval & BLUECOVE_STACK_DETECT_TOSHIBA) != 0) { stackSelected = STACK_TOSHIBA; } else if ((aval & BLUECOVE_STACK_DETECT_OSX) != 0) { stackSelected = STACK_OSX; } else { DebugLog.fatal("BluetoothStack not detected"); throw new BluetoothStateException("BluetoothStack not detected"); } } else { DebugLog.debug("BluetoothStack selected", stackSelected); } BluetoothStack stack = setBluetoothStack(stackSelected, detectorStack); stackSelected = stack.getStackID(); copySystemProperties(stack); if (!stackSelected.equals(STACK_EMULATOR)) { System.out.println("BlueCove version " + version + " on " + stackSelected); } return stack; } /** * List the local adapters that can be initialized using configuration property * "bluecove.deviceID". (Linux BlueZ and Emulator) * * The first stack/adapter would be initialized if not initialized already, you can * exclude it from the list. * * The function return empty list on non BlueZ environment. * * @return List of Strings * @throws BluetoothStateException * if stack interface can't be initialized * @see com.intel.bluetooth.BlueCoveConfigProperties#PROPERTY_LOCAL_DEVICE_ID * @see com.intel.bluetooth.BlueCoveLocalDeviceProperties#LOCAL_DEVICE_PROPERTY_DEVICE_ID */ public static Vector getLocalDevicesID() throws BluetoothStateException { Vector v = new Vector(); String ids = BluetoothStackHolder.getBluetoothStack().getLocalDeviceProperty(BlueCoveLocalDeviceProperties.LOCAL_DEVICE_DEVICES_LIST); if (ids != null) { UtilsStringTokenizer tok = new UtilsStringTokenizer(ids, ","); while (tok.hasMoreTokens()) { v.addElement(tok.nextToken()); } } return v; } /** * API that enables the use of Multiple Adapters and Bluetooth Stacks in parallel in * the same JVM. Each thread should call setThreadBluetoothStackID() before using * JSR-82 API. * * Affects the following JSR-82 API methods: * * <pre> * LocalDevice.getLocalDevice(); * LocalDevice.getProperty(String); * Connector.open(...); * methods of RemoteDevice instance created by user. * </pre> * * <STRONG>Example</STRONG> * <P> * * <pre> * BlueCoveImpl.useThreadLocalBluetoothStack(); * // On Windows * BlueCoveImpl.setConfigProperty("bluecove.stack", "widcomm"); * // On Linux or in Emulator * // BlueCoveImpl.setConfigProperty("bluecove.deviceID", "0"); * * final Object id1 = BlueCoveImpl.getThreadBluetoothStackID(); * ... do some work with stack 1 * * // Illustrates attaching thread to already initialized stack interface * Thread t1 = new Thread() { * public void run() { * BlueCoveImpl.setThreadBluetoothStackID(id1); * agent = LocalDevice.getLocalDevice().getDiscoveryAgent(); * agent.startInquiry(...); * ..... * } * }; * t1.start(); * * // Illustrates initialization of new/different stack interface in new thread * // Start another thread that is using different stack * Thread t2 = new Thread() { * public void run() { * // On Windows * BlueCoveImpl.setConfigProperty("bluecove.stack", "winsock"); * // On Linux or in Emulator * // BlueCoveImpl.setConfigProperty("bluecove.deviceID", "1"); * agent = LocalDevice.getLocalDevice().getDiscoveryAgent(); * agent.startInquiry(...); * ..... * } * } * t2.start(); * * Thread t3 = new Thread() { * public void run() { * // Wrong, will produce error: Thread StackID not configured * Connector.open("btspp://12345678:1"); * ..... * } * }; * t3.start(); * * </pre> * * @see #setConfigProperty */ public static synchronized void useThreadLocalBluetoothStack() { if (threadStack == null) { threadStack = new ThreadLocalWrapper(); } BluetoothStackHolder s = ((BluetoothStackHolder) threadStack.get()); if (s == null) { // Move initialized single stack to this thread if (singleStack != null) { s = singleStack; singleStack = null; } else { s = new BluetoothStackHolder(); } threadStack.set(s); } } /** * Initialize BluetoothStack if not already done and returns the ID to be used in * other threads accessing the same stack. * * @return an object that represents Adapter/BluetoothStack, stackID to be used in * call to <code>setThreadBluetoothStackID</code> * @throws BluetoothStateException * if the Bluetooth system could not be initialized */ public static synchronized Object getThreadBluetoothStackID() throws BluetoothStateException { useThreadLocalBluetoothStack(); BluetoothStackHolder.getBluetoothStack(); return threadStack.get(); } /** * Returns the ID to be used in other threads accessing the same stack. * * @return an object that represents Adapter/BluetoothStack, stackID to be used in * call to <code>setThreadBluetoothStackID</code> or * <code>null<code> if ThreadLocalBluetoothStack not used. */ public static synchronized Object getCurrentThreadBluetoothStackID() { if (threadStack == null) { return null; } return threadStack.get(); } /** * Updates the current Thread BluetoothStack. Updating is possible only if * <code>stackID</code> was obtained using the * <code>getThreadBluetoothStackID()</code> method. Should be called before connection * is made or LocalDevice received from LocalDevice.getLocalDevice(). * * @param stackID * stackID to use or <code>null</code> to detach the current Thread */ public static synchronized void setThreadBluetoothStackID(Object stackID) { if ((stackID != null) && (!(stackID instanceof BluetoothStackHolder))) { throw new IllegalArgumentException("stackID is not valid"); } if (threadStack == null) { throw new IllegalArgumentException("ThreadLocal configuration is not initialized"); } threadStack.set(stackID); } /** * Detach BluetoothStack from ThreadLocal. Used for removing itself from container * threads. Also can be use to initialize different stack in the same thread. */ public static synchronized void releaseThreadBluetoothStack() { if (threadStack == null) { throw new IllegalArgumentException("ThreadLocal configuration is not initialized"); } threadStack.set(null); } /** * Set default Thread BluetoothStack for Threads that do not call * <code>setThreadBluetoothStackID(stackID)</code>. Updating is possible only if * <code>stackID</code> was obtained using the * <code>getThreadBluetoothStackID()</code> method. * * @param stackID * stackID to use or <code>null</code> to remove default */ public static synchronized void setDefaultThreadBluetoothStackID(Object stackID) { if ((stackID != null) && (!(stackID instanceof BluetoothStackHolder))) { throw new IllegalArgumentException("stackID is not valid"); } if (threadStack == null) { throw new IllegalArgumentException("ThreadLocal configuration is not initialized"); } threadStackIDDefault = (BluetoothStackHolder) stackID; } static synchronized void setThreadBluetoothStack(BluetoothStack bluetoothStack) { if (threadStack == null) { return; } BluetoothStackHolder s = ((BluetoothStackHolder) threadStack.get()); if ((s != null) && (s.bluetoothStack == bluetoothStack)) { return; } BluetoothStackHolder sh = (BluetoothStackHolder) stacks.get(bluetoothStack); if (sh == null) { throw new RuntimeException("ThreadLocal not found for BluetoothStack"); } threadStack.set(sh); } /** * Shutdown BluetoothStack assigned for current Thread and clear configuration * properties for this thread */ public static synchronized void shutdownThreadBluetoothStack() { // ThreadLocal configuration is not initialized if (threadStack == null) { return; } BluetoothStackHolder s = ((BluetoothStackHolder) threadStack.get()); if (s == null) { return; } if (threadStackIDDefault == s) { threadStackIDDefault = null; } s.configProperties.clear(); if (s.bluetoothStack != null) { BluetoothConnectionNotifierBase.shutdownConnections(s.bluetoothStack); RemoteDeviceHelper.shutdownConnections(s.bluetoothStack); s.bluetoothStack.destroy(); stacks.remove(s.bluetoothStack); s.bluetoothStack = null; } } /** * Shutdown all BluetoothStacks interfaces initialized by BlueCove */ public static synchronized void shutdown() { for (Enumeration en = stacks.elements(); en.hasMoreElements();) { BluetoothStackHolder s = (BluetoothStackHolder) en.nextElement(); s.configProperties.clear(); if (s.bluetoothStack != null) { BluetoothConnectionNotifierBase.shutdownConnections(s.bluetoothStack); RemoteDeviceHelper.shutdownConnections(s.bluetoothStack); try { s.bluetoothStack.destroy(); } finally { s.bluetoothStack = null; } } } stacks.clear(); singleStack = null; threadStackIDDefault = null; if (shutdownHookRegistered != null) { shutdownHookRegistered.deRegister(); } clearSystemProperties(); } /** * API that can be used to configure BlueCove properties instead of System properties. * Initialization properties should be changed before stack initialized. If * <code>null</code> is passed as the <code>value</code> then the property will be * removed. * * @param name * property name * @param value * property value * * @see com.intel.bluetooth.BlueCoveConfigProperties * @see #setConfigObject(java.lang.String, java.lang.Object) * * @exception IllegalArgumentException * if the stack already initialized and property can't be changed. */ public static void setConfigProperty(String name, String value) { setConfigObject(name, value); } /** * API that can be used to configure BlueCove properties that aren't just strings * Initialization properties should be changed before stack initialized. If * <code>null</code> is passed as the <code>value</code> then the property will be * removed. * * @param name * property name * @param value * property value * * @see com.intel.bluetooth.BlueCoveConfigProperties * * @exception IllegalArgumentException * if the stack already initialized and property can't be changed. */ public static void setConfigObject(String name, Object value) { if (name == null) { throw new NullPointerException("key is null"); } BluetoothStackHolder sh = currentStackHolder(true); if ((sh.bluetoothStack != null) && (initializationProperties.contains(name))) { throw new IllegalArgumentException("BlueCove Stack already initialized"); } if (value == null) { sh.configProperties.remove(name); } else { sh.configProperties.put(name, value); } } public static Object getConfigObject(String key) { if (key == null) { throw new NullPointerException("key is null"); } Object value = null; BluetoothStackHolder sh = currentStackHolder(false); if (sh != null) { value = sh.configProperties.get(key); } if (value == null) { try { value = System.getProperty(key); } catch (SecurityException webstart) { } } if (value == null) { synchronized (resourceConfigProperties) { Object casheValue = resourceConfigProperties.get(key); if (casheValue != null) { if (casheValue instanceof String) { value = (String) casheValue; } } else { value = Utils.getResourceProperty(BlueCoveImpl.class, key); if (value == null) { resourceConfigProperties.put(key, new Object()); } else { resourceConfigProperties.put(key, value); } } } } return value; } public static String getConfigProperty(String key) { return (String) getConfigObject(key); } public static boolean getConfigProperty(String key, boolean defaultValue) { String value = getConfigProperty(key); if (value != null) { return TRUE.equals(value) || "1".equals(value); } else { return defaultValue; } } static int getConfigProperty(String key, int defaultValue) { String value = getConfigProperty(key); if (value != null) { return Integer.parseInt(value); } else { return defaultValue; } } static String[] getSystemPropertiesList() { String[] p = { BluetoothConsts.PROPERTY_BLUETOOTH_MASTER_SWITCH, BluetoothConsts.PROPERTY_BLUETOOTH_SD_ATTR_RETRIEVABLE_MAX, BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_DEVICES_MAX, BluetoothConsts.PROPERTY_BLUETOOTH_L2CAP_RECEIVEMTU_MAX, BluetoothConsts.PROPERTY_BLUETOOTH_SD_TRANS_MAX, BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_INQUIRY_SCAN, BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_PAGE_SCAN, BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_INQUIRY, BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_PAGE }; return p; } static void clearSystemProperties() { UtilsJavaSE.setSystemProperty(BluetoothConsts.PROPERTY_BLUETOOTH_API_VERSION, null); UtilsJavaSE.setSystemProperty(BluetoothConsts.PROPERTY_OBEX_API_VERSION, null); String[] property = getSystemPropertiesList(); for (int i = 0; i < property.length; i++) { UtilsJavaSE.setSystemProperty(property[i], null); } } void copySystemProperties(BluetoothStack bluetoothStack) { UtilsJavaSE.setSystemProperty(BluetoothConsts.PROPERTY_BLUETOOTH_API_VERSION, BlueCoveImpl.BLUETOOTH_API_VERSION); UtilsJavaSE.setSystemProperty(BluetoothConsts.PROPERTY_OBEX_API_VERSION, BlueCoveImpl.OBEX_API_VERSION); if (bluetoothStack != null) { String[] property = getSystemPropertiesList(); for (int i = 0; i < property.length; i++) { UtilsJavaSE.setSystemProperty(property[i], bluetoothStack.getLocalDeviceProperty(property[i])); } } } public String getLocalDeviceFeature(int featureID) throws BluetoothStateException { return ((BluetoothStackHolder.getBluetoothStack().getFeatureSet() & featureID) != 0) ? TRUE : FALSE; } private BluetoothStack createDetectorOnWindows(String stackFirst) throws BluetoothStateException { if (stackFirst != null) { DebugLog.debug("detector stack", stackFirst); BluetoothStack detectorStack; if (STACK_WIDCOMM.equalsIgnoreCase(stackFirst)) { detectorStack = new BluetoothStackWIDCOMM(); if (isNativeLibrariesAvailable(detectorStack)) { return detectorStack; } } else if (STACK_BLUESOLEIL.equalsIgnoreCase(stackFirst)) { detectorStack = new BluetoothStackBlueSoleil(); if (isNativeLibrariesAvailable(detectorStack)) { return detectorStack; } } else if (STACK_WINSOCK.equalsIgnoreCase(stackFirst)) { detectorStack = new BluetoothStackMicrosoft(); if (isNativeLibrariesAvailable(detectorStack)) { return detectorStack; } } else if (STACK_TOSHIBA.equalsIgnoreCase(stackFirst)) { detectorStack = new BluetoothStackToshiba(); if (isNativeLibrariesAvailable(detectorStack)) { return detectorStack; } } else { throw new IllegalArgumentException("Invalid BlueCove detector stack [" + stackFirst + "]"); } } BluetoothStack stack = new BluetoothStackMicrosoft(); if (isNativeLibrariesAvailable(stack)) { return stack; } stack = new BluetoothStackWIDCOMM(); if (isNativeLibrariesAvailable(stack)) { return stack; } throw new BluetoothStateException("BlueCove libraries not available"); } /** * @deprecated use setConfigProperty("bluecove.stack", ...); * * @param stack * @return stack ID * @throws BluetoothStateException */ public String setBluetoothStack(String stack) throws BluetoothStateException { return setBluetoothStack(stack, null).getStackID(); } private synchronized BluetoothStack setBluetoothStack(String stack, BluetoothStack detectorStack) throws BluetoothStateException { if (singleStack != null) { if (singleStack.bluetoothStack != null) { singleStack.bluetoothStack.destroy(); stacks.remove(singleStack.bluetoothStack); singleStack.bluetoothStack = null; } } else if (threadStack != null) { BluetoothStackHolder s = ((BluetoothStackHolder) threadStack.get()); if ((s != null) && (s.bluetoothStack != null)) { s.bluetoothStack.destroy(); stacks.remove(s.bluetoothStack); s.bluetoothStack = null; } } BluetoothStack newStack; if ((detectorStack != null) && (detectorStack.getStackID()).equalsIgnoreCase(stack)) { newStack = detectorStack; } else if (STACK_WIDCOMM.equalsIgnoreCase(stack)) { newStack = new BluetoothStackWIDCOMM(); } else if (STACK_BLUESOLEIL.equalsIgnoreCase(stack)) { newStack = new BluetoothStackBlueSoleil(); } else if (STACK_TOSHIBA.equalsIgnoreCase(stack)) { newStack = new BluetoothStackToshiba(); } else { newStack = new BluetoothStackMicrosoft(); } loadNativeLibraries(newStack); int libraryVersion = newStack.getLibraryVersion(); if (nativeLibraryVersionExpected != libraryVersion) { DebugLog.fatal("BlueCove native library version mismatch " + libraryVersion + " expected " + nativeLibraryVersionExpected); throw new BluetoothStateException("BlueCove native library version mismatch"); } if (DebugLog.isDebugEnabled()) { newStack.enableNativeDebug(DebugLog.class, true); } newStack.initialize(); createShutdownHook(); // Store stack in Thread or static variables BluetoothStackHolder sh = currentStackHolder(true); sh.bluetoothStack = newStack; stacks.put(newStack, sh); if (threadStack != null) { threadStack.set(sh); } return newStack; } public void enableNativeDebug(boolean on) { BluetoothStackHolder s = currentStackHolder(false); if ((s != null) && (s.bluetoothStack != null)) { s.bluetoothStack.enableNativeDebug(DebugLog.class, on); } } private static BluetoothStackHolder currentStackHolder(boolean create) { if (threadStack != null) { BluetoothStackHolder s = ((BluetoothStackHolder) threadStack.get()); if ((s == null) && (threadStackIDDefault != null)) { return threadStackIDDefault; } if ((s == null) && create) { s = new BluetoothStackHolder(); threadStack.set(s); } return s; } else { if ((singleStack == null) && create) { singleStack = new BluetoothStackHolder(); } return singleStack; } } /** * Applications should not used this function. * * @return current BluetoothStack implementation * @throws BluetoothStateException * when BluetoothStack not detected. If one connected the hardware later, * BlueCove would be able to recover and start correctly * @exception Error * if called from outside of BlueCove internal code. */ public synchronized BluetoothStack getBluetoothStack() throws BluetoothStateException { Utils.isLegalAPICall(fqcnSet); BluetoothStackHolder sh = currentStackHolder(false); if ((sh != null) && (sh.bluetoothStack != null)) { return sh.bluetoothStack; } else if ((sh == null) && (threadStack != null)) { throw new BluetoothStateException("No BluetoothStack or Adapter for current thread"); } BluetoothStack stack; if (accessControlContext == null) { stack = detectStack(); } else { stack = detectStackPrivileged(); } return stack; } private BluetoothStack detectStackPrivileged() throws BluetoothStateException { try { return (BluetoothStack) AccessController.doPrivileged(new PrivilegedExceptionAction() { public Object run() throws BluetoothStateException { return detectStack(); } }, (AccessControlContext) accessControlContext); } catch (PrivilegedActionException e) { Throwable cause = UtilsJavaSE.getCause(e); if (cause instanceof BluetoothStateException) { throw (BluetoothStateException) cause; } throw (BluetoothStateException) UtilsJavaSE.initCause(new BluetoothStateException(e.getMessage()), cause); } } }