/** * BlueCove BlueZ module - Java library for Bluetooth on Linux * Copyright (C) 2008 Mark Swanson * Copyright (C) 2008-2009 Vlad Skarzhevskyy * * 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. * * * @version $Id$ */ package com.intel.bluetooth; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.TreeMap; import java.util.Vector; import javax.bluetooth.BluetoothStateException; import javax.bluetooth.DataElement; import javax.bluetooth.DeviceClass; import javax.bluetooth.DiscoveryAgent; import javax.bluetooth.DiscoveryListener; import javax.bluetooth.RemoteDevice; import javax.bluetooth.ServiceRecord; import javax.bluetooth.ServiceRegistrationException; import javax.bluetooth.UUID; import org.bluez.BlueZAPI; import org.bluez.BlueZAPIFactory; import org.freedesktop.dbus.DBusConnection; import org.freedesktop.dbus.Path; import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.DBusExecutionException; /** * A Java/DBUS implementation. Property "bluecove.deviceID" or "bluecove.deviceAddress" * can be used to select Local Bluetooth device. * * bluecove.deviceID: String HCI ID. ID e.g. hci0, hci1, hci2, etc. bluecove.deviceID: * String Device number. e.g. 0, 1, 2, etc. bluecove.deviceAddress: String in JSR-82 * format. * * Please help with these questions: * * 0. I note that Adapter.java has a bunch of methods commented out. Do you feel these * aren't needed to get a bare bones implementation working? I notice that * getLocalDeviceDiscoverable() could use adapter.getMode() "discoverable" though I have * no idea how to convert that to an int return value... 1. * * A: The idea was that I copied all the method descriptors from bluez-d-bus * documentation. Some I tested and this is uncommented. Some I'm not sure are implemented * as described so I commented out. */ class BluetoothStackBlueZDBus implements BluetoothStack, DeviceInquiryRunnable, SearchServicesRunnable { // This native lib contains the rfcomm and l2cap linux-specific // implementation for this bluez d-bus implementation. public static final String NATIVE_BLUECOVE_LIB_BLUEZ = "bluecovez"; private final static String BLUEZ_DEVICEID_PREFIX = "hci"; private final static int LISTEN_BACKLOG_RFCOMM = 4; private final static int LISTEN_BACKLOG_L2CAP = 4; private final int l2cap_receiveMTU_max = 65535; private final static Vector<String> devicesUsed = new Vector<String>(); // Our reusable DBUS connection. private DBusConnection dbusConn = null; private String deviceID; private BlueZAPI blueZ; static final int BLUECOVE_DBUS_VERSION = BlueCoveImpl.nativeLibraryVersionExpected; /** * The parsed long value of the adapter's BT 00:00:... address. */ private long localDeviceBTAddress = -1; private Map<String, String> propertiesMap; private DiscoveryListener discoveryListener; private boolean deviceInquiryCanceled = false; private class DiscoveryData { public int deviceClass; public String name; boolean paired; } BluetoothStackBlueZDBus() { } public String getStackID() { return BlueCoveImpl.STACK_BLUEZ_DBUS; } public String toString() { if (deviceID != null) { return getStackID() + ":" + deviceID; } else { return getStackID(); } } // --- Library initialization /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#isNativeCodeLoaded() */ public native boolean isNativeCodeLoaded(); /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#requireNativeLibraries() */ public LibraryInformation[] requireNativeLibraries() { try { LibraryInformation unixSocketLib = new LibraryInformation("unix-java", false); unixSocketLib.stackClass = cx.ath.matthew.unix.UnixSocket.class; return new LibraryInformation[] { new LibraryInformation(NATIVE_BLUECOVE_LIB_BLUEZ), unixSocketLib }; } catch (NoClassDefFoundError e) { // dubs may use different UnixSocket implementation return new LibraryInformation[] { new LibraryInformation(NATIVE_BLUECOVE_LIB_BLUEZ) }; } } private native int getLibraryVersionNative(); public int getLibraryVersion() throws BluetoothStateException { int version = getLibraryVersionNative(); if (version != BLUECOVE_DBUS_VERSION) { DebugLog.fatal("BlueCove native library version mismatch " + version + " expected " + BLUECOVE_DBUS_VERSION); throw new BluetoothStateException("BlueCove native library version mismatch"); } return version; } public int detectBluetoothStack() { return BlueCoveImpl.BLUECOVE_STACK_DETECT_BLUEZ; } /** * Returns a colon formatted BT address required by BlueZ. e.g. 00:01:C2:51:D1:31 * * @param l * The long address to be converted to a string. * @return Note: can be optimized - was playing around with the formats required by * BlueZ. */ private String toHexString(long l) { StringBuffer buf = new StringBuffer(); String lo = Integer.toHexString((int) l); if (l > 0xffffffffl) { String hi = Integer.toHexString((int) (l >> 32)); buf.append(hi); } buf.append(lo); StringBuffer result = new StringBuffer(); int prependZeros = 12 - buf.length(); for (int i = 0; i < prependZeros; ++i) { result.append("0"); } result.append(buf.toString()); StringBuffer hex = new StringBuffer(); for (int i = 0; i < 12; i += 2) { hex.append(result.substring(i, i + 2)); if (i < 10) { hex.append(":"); } } return hex.toString().toUpperCase(Locale.ENGLISH); } private long convertBTAddress(String anAddress) { long btAddress = Long.parseLong(anAddress.replaceAll(":", ""), 16); return btAddress; } public void initialize() throws BluetoothStateException { boolean intialized = false; try { try { dbusConn = DBusConnection.getConnection(DBusConnection.SYSTEM); } catch (DBusException e) { DebugLog.error("Failed to get the dbus connection", e); throw new BluetoothStateException(e.getMessage()); } try { blueZ = BlueZAPIFactory.getBlueZAPI(dbusConn); } catch (DBusException e) { DebugLog.error("Failed to get bluez dbus manager", e); throw (BluetoothStateException) UtilsJavaSE.initCause(new BluetoothStateException("Can't access BlueZ D-Bus"), e); } Path adapterPath; // If the user specifies a specific deviceID then we try to find it. String findID = BlueCoveImpl.getConfigProperty(BlueCoveConfigProperties.PROPERTY_LOCAL_DEVICE_ID); String deviceAddressStr = BlueCoveImpl.getConfigProperty(BlueCoveConfigProperties.PROPERTY_LOCAL_DEVICE_ADDRESS); if (findID != null) { if (findID.startsWith(BLUEZ_DEVICEID_PREFIX)) { adapterPath = blueZ.findAdapter(findID); if (adapterPath == null) { throw new BluetoothStateException("Can't find '" + findID + "' adapter"); } } else { int findNumber = Integer.parseInt(findID); adapterPath = blueZ.getAdapter(findNumber); if (adapterPath == null) { throw new BluetoothStateException("Can't find adapter #" + findID); } } } else if (deviceAddressStr != null) { String pattern = toHexString(Long.parseLong(deviceAddressStr, 0x10)); adapterPath = blueZ.findAdapter(pattern); if (adapterPath == null) { throw new BluetoothStateException("Can't find adapter with address '" + deviceAddressStr + "'"); } } else { adapterPath = blueZ.defaultAdapter(); if (adapterPath == null) { throw new BluetoothStateException("Can't find default adapter"); } } try { blueZ.selectAdapter(adapterPath); } catch (DBusException e) { throw new BluetoothStateException(adapterPath + " " + e.getMessage()); } localDeviceBTAddress = convertBTAddress(blueZ.getAdapterAddress()); deviceID = blueZ.getAdapterID(); if (devicesUsed.contains(deviceID)) { throw new BluetoothStateException("LocalDevice " + deviceID + " alredy in use"); } propertiesMap = new TreeMap<String, String>(); propertiesMap.put(BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_DEVICES_MAX, "7"); propertiesMap.put(BluetoothConsts.PROPERTY_BLUETOOTH_SD_TRANS_MAX, "7"); propertiesMap.put(BlueCoveLocalDeviceProperties.LOCAL_DEVICE_PROPERTY_DEVICE_ID, deviceID); final String TRUE = "true"; final String FALSE = "false"; propertiesMap.put(BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_INQUIRY_SCAN, TRUE); propertiesMap.put(BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_PAGE_SCAN, TRUE); propertiesMap.put(BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_INQUIRY, TRUE); propertiesMap.put(BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_PAGE, TRUE); propertiesMap.put(BluetoothConsts.PROPERTY_BLUETOOTH_SD_ATTR_RETRIEVABLE_MAX, String.valueOf(256)); propertiesMap.put(BluetoothConsts.PROPERTY_BLUETOOTH_MASTER_SWITCH, FALSE); propertiesMap.put(BluetoothConsts.PROPERTY_BLUETOOTH_L2CAP_RECEIVEMTU_MAX, String.valueOf(l2cap_receiveMTU_max)); intialized = true; } finally { if (!intialized) { if (dbusConn != null) { dbusConn.disconnect(); } dbusConn = null; } } } public void destroy() { DebugLog.debug("destroy()"); if (deviceID != null) { devicesUsed.removeElement(deviceID); deviceID = null; } if (dbusConn != null) { dbusConn.disconnect(); dbusConn = null; } } @SuppressWarnings("unchecked") public native void enableNativeDebug(Class nativeDebugCallback, boolean on); /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#isCurrentThreadInterruptedCallback() */ public boolean isCurrentThreadInterruptedCallback() { // DebugLog.debug("isCurrentThreadInterruptedCallback()"); return Thread.interrupted(); } /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#getFeatureSet() */ public int getFeatureSet() { return FEATURE_SERVICE_ATTRIBUTES | FEATURE_L2CAP | FEATURE_ASSIGN_SERVER_PSM; } // --- LocalDevice public String getLocalDeviceBluetoothAddress() throws BluetoothStateException { return RemoteDeviceHelper.getBluetoothAddress(localDeviceBTAddress); } public DeviceClass getLocalDeviceClass() { try { int record = blueZ.getAdapterDeviceClass(); if (DiscoveryAgent.LIAC == getLocalDeviceDiscoverable()) { record |= BluetoothConsts.DeviceClassConsts.LIMITED_DISCOVERY_SERVICE; } return new DeviceClass(record); } catch (DBusExecutionException e) { DebugLog.error("getLocalDeviceClass", e); return null; } } /** * Retrieves the name of the local device. * * @see javax.bluetooth.LocalDevice#getFriendlyName() */ public String getLocalDeviceName() { return blueZ.getAdapterName(); } public boolean isLocalDevicePowerOn() { return blueZ.isAdapterPowerOn(); } public String getLocalDeviceProperty(String property) { if (BlueCoveLocalDeviceProperties.LOCAL_DEVICE_DEVICES_LIST.equals(property)) { StringBuffer b = new StringBuffer(); for (String adapterId : blueZ.listAdapters()) { if (b.length() > 0) { b.append(','); } b.append(adapterId); } return b.toString(); } else if (BlueCoveLocalDeviceProperties.LOCAL_DEVICE_RADIO_VERSION.equals(property)) { return blueZ.getAdapterVersion() + "; HCI " + blueZ.getAdapterRevision(); } else if (BlueCoveLocalDeviceProperties.LOCAL_DEVICE_RADIO_MANUFACTURER.equals(property)) { return blueZ.getAdapterManufacturer(); } else { return propertiesMap.get(property); } } public int getLocalDeviceDiscoverable() { if (blueZ.isAdapterDiscoverable()) { int timeout = blueZ.getAdapterDiscoverableTimeout(); if (timeout == 0) { return DiscoveryAgent.GIAC; } else { return DiscoveryAgent.LIAC; } } else { return DiscoveryAgent.NOT_DISCOVERABLE; } } public boolean setLocalDeviceDiscoverable(int mode) throws BluetoothStateException { if (getLocalDeviceDiscoverable() == mode) { return true; } try { return blueZ.setAdapterDiscoverable(mode); } catch (DBusException e) { throw (BluetoothStateException) UtilsJavaSE.initCause(new BluetoothStateException(e.getMessage()), e); } catch (DBusExecutionException e) { throw (BluetoothStateException) UtilsJavaSE.initCause(new BluetoothStateException(e.getMessage()), e); } } /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#setLocalDeviceServiceClasses(int) */ public void setLocalDeviceServiceClasses(int classOfDevice) { DebugLog.debug("setLocalDeviceServiceClasses()"); throw new NotSupportedRuntimeException(getStackID()); } public boolean authenticateRemoteDevice(long address) throws IOException { try { blueZ.authenticateRemoteDevice(toHexString(address)); return true; } catch (Throwable e) { DebugLog.error("Error creating bonding", e); return false; } } public boolean authenticateRemoteDevice(long address, String passkey) throws IOException { try { return blueZ.authenticateRemoteDevice(toHexString(address), passkey); } catch (Throwable e) { throw (IOException) UtilsJavaSE.initCause(new IOException(e.getMessage()), e); } } /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#removeAuthenticationWithRemoteDevice (long) */ public void removeAuthenticationWithRemoteDevice(long address) throws IOException { try { blueZ.removeAuthenticationWithRemoteDevice(toHexString(address)); } catch (Throwable e) { throw (IOException) UtilsJavaSE.initCause(new IOException(e.getMessage()), e); } } // --- Device Inquiry public boolean startInquiry(int accessCode, DiscoveryListener listener) throws BluetoothStateException { DebugLog.debug("startInquiry()"); if (discoveryListener != null) { throw new BluetoothStateException("Another inquiry already running"); } discoveryListener = listener; deviceInquiryCanceled = false; return DeviceInquiryThread.startInquiry(this, this, accessCode, listener); } public int runDeviceInquiry(final DeviceInquiryThread startedNotify, int accessCode, DiscoveryListener listener) throws BluetoothStateException { DebugLog.debug("runDeviceInquiry()"); try { // Different signal handlers get different device attributes // so we cache the data until device discovery is finished // and then create the RemoteDevice objects. final Map<Long, DiscoveryData> address2DiscoveryData = new HashMap<Long, DiscoveryData>(); BlueZAPI.DeviceInquiryListener bluezDiscoveryListener = new BlueZAPI.DeviceInquiryListener() { public void deviceInquiryStarted() { startedNotify.deviceInquiryStartedCallback(); } public void deviceDiscovered(String deviceAddr, String deviceName, int deviceClass, boolean paired) { long longAddress = convertBTAddress(deviceAddr); DiscoveryData discoveryData = address2DiscoveryData.get(longAddress); if (discoveryData == null) { discoveryData = new DiscoveryData(); address2DiscoveryData.put(longAddress, discoveryData); } if (deviceName != null) { discoveryData.name = deviceName; } if (deviceClass >= 0) { discoveryData.deviceClass = deviceClass; } } }; try { blueZ.deviceInquiry(bluezDiscoveryListener); } catch (Throwable e) { if (deviceInquiryCanceled) { return DiscoveryListener.INQUIRY_TERMINATED; } else { DebugLog.error("deviceInquiry error", e); throw (BluetoothStateException) UtilsJavaSE.initCause(new BluetoothStateException(e.getMessage()), e); } } for (Long address : address2DiscoveryData.keySet()) { DiscoveryData discoveryData = address2DiscoveryData.get(address); if (discoveryData.name == null) { try { discoveryData.name = blueZ.getRemoteDeviceFriendlyName(toHexString(address)); } catch (Throwable e) { DebugLog.error("can't get device name", e); } if (discoveryData.name == null) { discoveryData.name = ""; } } RemoteDevice remoteDevice = RemoteDeviceHelper.createRemoteDevice(BluetoothStackBlueZDBus.this, address, discoveryData.name, discoveryData.paired); listener.deviceDiscovered(remoteDevice, new DeviceClass(discoveryData.deviceClass)); if (deviceInquiryCanceled) { break; } } if (deviceInquiryCanceled) { return DiscoveryListener.INQUIRY_TERMINATED; } else { return DiscoveryListener.INQUIRY_COMPLETED; } } finally { discoveryListener = null; } } public void deviceDiscoveredCallback(DiscoveryListener listener, long deviceAddr, int deviceClass, String deviceName, boolean paired) { // Not used here since there are no native callbacks } public boolean cancelInquiry(DiscoveryListener listener) { DebugLog.debug("cancelInquiry()"); if (discoveryListener != null && discoveryListener == listener) { deviceInquiryCanceled = true; try { blueZ.deviceInquiryCancel(); return true; } catch (Throwable e) { return false; } } else { return false; } } /** * Contact the remote device */ public String getRemoteDeviceFriendlyName(final long deviceAddress) throws IOException { // return adapter.GetRemoteName(toHexString(anAddress)); // For JSR-82 GetRemoteName can't be since it use cash. if (discoveryListener != null) { throw new IOException("DeviceInquiry alredy running"); } try { return blueZ.getRemoteDeviceFriendlyName(toHexString(deviceAddress)); } catch (DBusExecutionException e) { throw (BluetoothStateException) UtilsJavaSE.initCause(new BluetoothStateException(e.getMessage()), e); } catch (DBusException e) { throw (BluetoothStateException) UtilsJavaSE.initCause(new BluetoothStateException(e.getMessage()), e); } } public RemoteDevice[] retrieveDevices(int option) { List<String> preKnownDevices = blueZ.retrieveDevices((DiscoveryAgent.PREKNOWN == option)); if (preKnownDevices == null) { return null; } final Vector<RemoteDevice> devices = new Vector<RemoteDevice>(); for (String addres : preKnownDevices) { devices.add(RemoteDeviceHelper.createRemoteDevice(this, convertBTAddress(addres), null, true)); } return RemoteDeviceHelper.remoteDeviceListToArray(devices); } public Boolean isRemoteDeviceTrusted(long address) { try { return blueZ.isRemoteDeviceTrusted(toHexString(address)); } catch (DBusExecutionException e) { DebugLog.error("isRemoteDeviceTrusted", e); return Boolean.FALSE; } catch (DBusException e) { DebugLog.error("isRemoteDeviceTrusted", e); return Boolean.FALSE; } } public Boolean isRemoteDeviceAuthenticated(long address) { try { return Boolean.valueOf(blueZ.isRemoteDeviceConnected(toHexString(address)) && blueZ.isRemoteDeviceTrusted(toHexString(address))); } catch (DBusExecutionException e) { DebugLog.error("isRemoteDeviceAuthenticated", e); return Boolean.FALSE; } catch (DBusException e) { DebugLog.error("isRemoteDeviceAuthenticated", e); return false; } } // --- Service search /** * Starts searching for services. * * @return transId */ public int searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice device, DiscoveryListener listener) throws BluetoothStateException { try { DebugLog.debug("searchServices() device", device.getBluetoothAddress()); return SearchServicesThread.startSearchServices(this, this, attrSet, uuidSet, device, listener); } catch (Exception ex) { DebugLog.debug("searchServices() failed", ex); throw new BluetoothStateException("searchServices() failed: " + ex.getMessage()); } } private int getRemoteServices(SearchServicesThread sst, UUID[] uuidSet, RemoteDevice remoteDevice) { Map<Integer, String> xmlRecords; try { xmlRecords = blueZ.getRemoteDeviceServices(toHexString(RemoteDeviceHelper.getAddress(remoteDevice))); } catch (DBusException e) { DebugLog.error("get Service records failed", e); return DiscoveryListener.SERVICE_SEARCH_ERROR; } catch (DBusExecutionException e) { DebugLog.error("get Service records failed", e); return DiscoveryListener.SERVICE_SEARCH_ERROR; } if (xmlRecords == null) { return DiscoveryListener.SERVICE_SEARCH_DEVICE_NOT_REACHABLE; } nextRecord: for (Map.Entry<Integer, String> record : xmlRecords.entrySet()) { DebugLog.debug("pars service record", record.getValue()); ServiceRecordImpl sr = new ServiceRecordImpl(this, remoteDevice, record.getKey().intValue()); Map<Integer, DataElement> elements; try { elements = BlueZServiceRecordXML.parsXMLRecord(record.getValue()); } catch (IOException e) { DebugLog.error("Error parsing service record", e); continue nextRecord; } for (Map.Entry<Integer, DataElement> element : elements.entrySet()) { sr.populateAttributeValue(element.getKey().intValue(), element.getValue()); } for (int u = 0; u < uuidSet.length; u++) { if (!((sr.hasServiceClassUUID(uuidSet[u])) || (sr.hasProtocolClassUUID(uuidSet[u])))) { DebugLog.debug("ignoring service", sr); continue nextRecord; } } DebugLog.debug("found service"); sst.addServicesRecords(sr); } return DiscoveryListener.SERVICE_SEARCH_COMPLETED; } @SuppressWarnings("unchecked") public int runSearchServices(SearchServicesThread sst, int[] attrSet, UUID[] uuidSet, RemoteDevice device, DiscoveryListener listener) throws BluetoothStateException { DebugLog.debug("runSearchServices()"); sst.searchServicesStartedCallback(); int respCode = getRemoteServices(sst, uuidSet, device); DebugLog.debug("SearchServices finished", sst.getTransID()); Vector<ServiceRecord> records = sst.getServicesRecords(); if (records.size() != 0) { ServiceRecord[] servRecordArray = (ServiceRecord[]) Utils.vector2toArray(records, new ServiceRecord[records.size()]); listener.servicesDiscovered(sst.getTransID(), servRecordArray); } if ((respCode != DiscoveryListener.SERVICE_SEARCH_ERROR) && (sst.isTerminated())) { return DiscoveryListener.SERVICE_SEARCH_TERMINATED; } else if (respCode == DiscoveryListener.SERVICE_SEARCH_COMPLETED) { if (records.size() != 0) { return DiscoveryListener.SERVICE_SEARCH_COMPLETED; } else { return DiscoveryListener.SERVICE_SEARCH_NO_RECORDS; } } else { return respCode; } } public boolean cancelServiceSearch(int transID) { DebugLog.debug("cancelServiceSearch()"); SearchServicesThread sst = SearchServicesThread.getServiceSearchThread(transID); if (sst != null) { return sst.setTerminated(); } else { return false; } } public boolean populateServicesRecordAttributeValues(ServiceRecordImpl serviceRecord, int[] attrIDs) throws IOException { DebugLog.debug("populateServicesRecordAttributeValues()"); long remoteDeviceAddress = RemoteDeviceHelper.getAddress(serviceRecord.getHostDevice()); throw new UnsupportedOperationException("populateServicesRecordAttributeValues Not supported yet."); } // --- SDP Server private synchronized void registerSDPRecord(ServiceRecordImpl serviceRecord) throws ServiceRegistrationException { long handle; try { handle = blueZ.registerSDPRecord(BlueZServiceRecordXML.exportXMLRecord(serviceRecord)); } catch (Throwable e) { throw (ServiceRegistrationException) UtilsJavaSE.initCause(new ServiceRegistrationException(e.getMessage()), e); } serviceRecord.setHandle(handle); serviceRecord.populateAttributeValue(BluetoothConsts.ServiceRecordHandle, new DataElement(DataElement.U_INT_4, handle)); } private synchronized void updateSDPRecord(ServiceRecordImpl serviceRecord) throws ServiceRegistrationException { try { blueZ.updateSDPRecord(serviceRecord.getHandle(), BlueZServiceRecordXML.exportXMLRecord(serviceRecord)); } catch (Throwable e) { throw (ServiceRegistrationException) UtilsJavaSE.initCause(new ServiceRegistrationException(e.getMessage()), e); } } private synchronized void unregisterSDPRecord(ServiceRecordImpl serviceRecord) throws ServiceRegistrationException { try { blueZ.unregisterSDPRecord(serviceRecord.getHandle()); } catch (Throwable e) { throw (ServiceRegistrationException) UtilsJavaSE.initCause(new ServiceRegistrationException(e.getMessage()), e); } } // --- Client RFCOMM connections private native long connectionRfOpenClientConnectionImpl(long localDeviceBTAddress, long address, int channel, boolean authenticate, boolean encrypt, int timeout) throws IOException; public long connectionRfOpenClientConnection(BluetoothConnectionParams params) throws IOException { return connectionRfOpenClientConnectionImpl(this.localDeviceBTAddress, params.address, params.channel, params.authenticate, params.encrypt, params.timeout); } public native void connectionRfCloseClientConnection(long handle) throws IOException; public native int rfGetSecurityOptImpl(long handle) throws IOException; public int rfGetSecurityOpt(long handle, int expected) throws IOException { return rfGetSecurityOptImpl(handle); } /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2Encrypt(long,long,boolean) */ public boolean rfEncrypt(long address, long handle, boolean on) throws IOException { // TODO return false; } private native long rfServerOpenImpl(long localDeviceBTAddress, boolean authorize, boolean authenticate, boolean encrypt, boolean master, boolean timeouts, int backlog) throws IOException; private native int rfServerGetChannelIDImpl(long handle) throws IOException; public long rfServerOpen(BluetoothConnectionNotifierParams params, ServiceRecordImpl serviceRecord) throws IOException { long socket = rfServerOpenImpl(this.localDeviceBTAddress, params.authorize, params.authenticate, params.encrypt, params.master, params.timeouts, LISTEN_BACKLOG_RFCOMM); boolean success = false; try { int channel = rfServerGetChannelIDImpl(socket); serviceRecord.populateRFCOMMAttributes(0, channel, params.uuid, params.name, params.obex); registerSDPRecord(serviceRecord); success = true; return socket; } finally { if (!success) { rfServerCloseImpl(socket, true); } } } private native void rfServerCloseImpl(long handle, boolean quietly) throws IOException; public void rfServerClose(long handle, ServiceRecordImpl serviceRecord) throws IOException { try { unregisterSDPRecord(serviceRecord); } finally { rfServerCloseImpl(handle, false); } } public void rfServerUpdateServiceRecord(long handle, ServiceRecordImpl serviceRecord, boolean acceptAndOpen) throws ServiceRegistrationException { updateSDPRecord(serviceRecord); } public native long rfServerAcceptAndOpenRfServerConnection(long handle) throws IOException; public void connectionRfCloseServerConnection(long clientHandle) throws IOException { connectionRfCloseClientConnection(clientHandle); } // --- Shared Client and Server RFCOMM connections public int connectionRfRead(long handle) throws IOException { byte[] data = new byte[1]; int size = connectionRfRead(handle, data, 0, 1); if (size == -1) { return -1; } return 0xFF & data[0]; } public native int connectionRfRead(long handle, byte[] b, int off, int len) throws IOException; public native int connectionRfReadAvailable(long handle) throws IOException; public native void connectionRfWrite(long handle, int b) throws IOException; public native void connectionRfWrite(long handle, byte[] b, int off, int len) throws IOException; public native void connectionRfFlush(long handle) throws IOException; public native long getConnectionRfRemoteAddress(long handle) throws IOException; // --- Client and Server L2CAP connections private void validateMTU(int receiveMTU, int transmitMTU) { if (receiveMTU > l2cap_receiveMTU_max) { throw new IllegalArgumentException("invalid ReceiveMTU value " + receiveMTU); } } private native long l2OpenClientConnectionImpl(long localDeviceBTAddress, long address, int channel, boolean authenticate, boolean encrypt, int receiveMTU, int transmitMTU, int timeout) throws IOException; /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2OpenClientConnection(com.intel.bluetooth * .BluetoothConnectionParams, int, int) */ public long l2OpenClientConnection(BluetoothConnectionParams params, int receiveMTU, int transmitMTU) throws IOException { validateMTU(receiveMTU, transmitMTU); return l2OpenClientConnectionImpl(this.localDeviceBTAddress, params.address, params.channel, params.authenticate, params.encrypt, receiveMTU, transmitMTU, params.timeout); } /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2CloseClientConnection(long) */ public native void l2CloseClientConnection(long handle) throws IOException; private native long l2ServerOpenImpl(long localDeviceBTAddress, boolean authorize, boolean authenticate, boolean encrypt, boolean master, boolean timeouts, int backlog, int receiveMTU, int transmitMTU, int assignPsm) throws IOException; public native int l2ServerGetPSMImpl(long handle) throws IOException; /* * (non-Javadoc) * * @seecom.intel.bluetooth.BluetoothStack#l2ServerOpen(com.intel.bluetooth. * BluetoothConnectionNotifierParams, int, int, com.intel.bluetooth.ServiceRecordImpl) */ public long l2ServerOpen(BluetoothConnectionNotifierParams params, int receiveMTU, int transmitMTU, ServiceRecordImpl serviceRecord) throws IOException { validateMTU(receiveMTU, transmitMTU); long socket = l2ServerOpenImpl(this.localDeviceBTAddress, params.authorize, params.authenticate, params.encrypt, params.master, params.timeouts, LISTEN_BACKLOG_L2CAP, receiveMTU, transmitMTU, params.bluecove_ext_psm); boolean success = false; try { int channel = l2ServerGetPSMImpl(socket); serviceRecord.populateL2CAPAttributes(0, channel, params.uuid, params.name); registerSDPRecord(serviceRecord); success = true; return socket; } finally { if (!success) { l2ServerCloseImpl(socket, true); } } } /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2ServerUpdateServiceRecord(long, * com.intel.bluetooth.ServiceRecordImpl, boolean) */ public void l2ServerUpdateServiceRecord(long handle, ServiceRecordImpl serviceRecord, boolean acceptAndOpen) throws ServiceRegistrationException { updateSDPRecord(serviceRecord); } /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2ServerAcceptAndOpenServerConnection * (long) */ public native long l2ServerAcceptAndOpenServerConnection(long handle) throws IOException; /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2CloseServerConnection(long) */ public void l2CloseServerConnection(long handle) throws IOException { l2CloseClientConnection(handle); } private native void l2ServerCloseImpl(long handle, boolean quietly) throws IOException; /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2ServerClose(long, * com.intel.bluetooth.ServiceRecordImpl) */ public void l2ServerClose(long handle, ServiceRecordImpl serviceRecord) throws IOException { try { unregisterSDPRecord(serviceRecord); } finally { l2ServerCloseImpl(handle, false); } } /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2Ready(long) */ public native boolean l2Ready(long handle) throws IOException; /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2receive(long, byte[]) */ public native int l2Receive(long handle, byte[] inBuf) throws IOException; /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2send(long, byte[]) */ public native void l2Send(long handle, byte[] data, int transmitMTU) throws IOException; /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2GetReceiveMTU(long) */ public native int l2GetReceiveMTU(long handle) throws IOException; /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2GetTransmitMTU(long) */ public native int l2GetTransmitMTU(long handle) throws IOException; /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2RemoteAddress(long) */ public native long l2RemoteAddress(long handle) throws IOException; /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2GetSecurityOpt(long, int) */ public native int l2GetSecurityOpt(long handle, int expected) throws IOException; /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2Encrypt(long,long,boolean) */ public boolean l2Encrypt(long address, long handle, boolean on) throws IOException { // TODO return false; } }