/** * BlueCove - Java library for Bluetooth * Copyright (C) 2006-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.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Enumeration; import java.util.Hashtable; 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; class BluetoothStackWIDCOMM implements BluetoothStack, BluetoothStackExtension { private static BluetoothStackWIDCOMM singleInstance = null; private boolean initialized = false; private Vector deviceDiscoveryListeners = new Vector/* <DiscoveryListener> */(); private Hashtable deviceDiscoveryListenerFoundDevices = new Hashtable(); private Hashtable deviceDiscoveryListenerReportedDevices = new Hashtable(); // TODO what is the real number for Attributes retrievable ? private final static int ATTR_RETRIEVABLE_MAX = 256; private final static int RECEIVE_MTU_MAX = 1024; // from WIDCOMM BtIfDefinitions.h final static short NULL_DESC_TYPE = 0; final static short UINT_DESC_TYPE = 1; final static short TWO_COMP_INT_DESC_TYPE = 2; final static short UUID_DESC_TYPE = 3; final static short TEXT_STR_DESC_TYPE = 4; final static short BOOLEAN_DESC_TYPE = 5; final static short DATA_ELE_SEQ_DESC_TYPE = 6; final static short DATA_ELE_ALT_DESC_TYPE = 7; final static short URL_DESC_TYPE = 8; BluetoothStackWIDCOMM() { } public String getStackID() { return BlueCoveImpl.STACK_WIDCOMM; } public String toString() { return getStackID(); } private native int nativeBuildFeatures(); /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#getFeatureSet() */ public int getFeatureSet() { int nativeBuildFeaturs = nativeBuildFeatures(); return FEATURE_SERVICE_ATTRIBUTES | FEATURE_L2CAP | FEATURE_ASSIGN_SERVER_PSM | ((nativeBuildFeaturs>0)?FEATURE_RSSI:0); } // ---------------------- 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() { return LibraryInformation.library(BlueCoveImpl.NATIVE_LIB_WIDCOMM); } public native int getLibraryVersion(); public native int detectBluetoothStack(); public native void enableNativeDebug(Class nativeDebugCallback, boolean on); public void initialize() throws BluetoothStateException { if (singleInstance != null) { throw new BluetoothStateException("Only one instance of " + getStackID() + " stack supported"); } if (!initializeImpl()) { throw new RuntimeException("WIDCOMM BluetoothStack not found"); } initialized = true; singleInstance = this; } public native boolean initializeImpl(); private native void uninitialize(); public void destroy() { if (singleInstance != this) { throw new RuntimeException("Destroy invalid instance"); } if (initialized) { uninitialize(); initialized = false; DebugLog.debug("WIDCOMM destroyed"); } singleInstance = null; } protected void finalize() { destroy(); } public native String getLocalDeviceBluetoothAddress() throws BluetoothStateException; public native String getLocalDeviceName(); private native int getDeviceClassImpl(); /** * There are no functions to set WIDCOMM stack */ public DeviceClass getLocalDeviceClass() { return new DeviceClass(getDeviceClassImpl()); } /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#setLocalDeviceServiceClasses(int) */ public void setLocalDeviceServiceClasses(int classOfDevice) { throw new NotSupportedRuntimeException(getStackID()); } /** * There are no functions to set WIDCOMM stack discoverable status. * * @return <code>true</code> if the request succeeded, otherwise <code>false</code> if the request failed because * the BCC denied the request; <code>false</code> if the Bluetooth system does not support the access mode * specified in <code>mode</code> */ public boolean setLocalDeviceDiscoverable(int mode) throws BluetoothStateException { int curentMode = getLocalDeviceDiscoverable(); if (curentMode == mode) { return true; } else { return false; } } private native boolean isStackServerUp(); private synchronized void verifyDeviceReady() throws BluetoothStateException { if (!isLocalDevicePowerOn()) { throw new BluetoothStateException("Bluetooth Device is not ready"); } } public native boolean isLocalDeviceDiscoverable(); public int getLocalDeviceDiscoverable() { if (isStackServerUp() && isLocalDeviceDiscoverable()) { return DiscoveryAgent.GIAC; } else { return DiscoveryAgent.NOT_DISCOVERABLE; } } public native boolean isLocalDevicePowerOn(); private native String getBTWVersionInfo(); private native int getDeviceVersion(); private native int getDeviceManufacturer(); public String getLocalDeviceProperty(String property) { if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_DEVICES_MAX.equals(property)) { return "7"; } if (BluetoothConsts.PROPERTY_BLUETOOTH_SD_TRANS_MAX.equals(property)) { return "1"; } if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_INQUIRY_SCAN.equals(property)) { return BlueCoveImpl.TRUE; } if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_PAGE_SCAN.equals(property)) { return BlueCoveImpl.TRUE; } if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_INQUIRY.equals(property)) { return BlueCoveImpl.TRUE; } if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_PAGE.equals(property)) { return BlueCoveImpl.TRUE; } if (BluetoothConsts.PROPERTY_BLUETOOTH_SD_ATTR_RETRIEVABLE_MAX.equals(property)) { return String.valueOf(ATTR_RETRIEVABLE_MAX); } if (BluetoothConsts.PROPERTY_BLUETOOTH_MASTER_SWITCH.equals(property)) { return BlueCoveImpl.FALSE; } if (BluetoothConsts.PROPERTY_BLUETOOTH_L2CAP_RECEIVEMTU_MAX.equals(property)) { return String.valueOf(RECEIVE_MTU_MAX); } if (BlueCoveLocalDeviceProperties.LOCAL_DEVICE_RADIO_VERSION.equals(property)) { return String.valueOf(getDeviceVersion()); } if (BlueCoveLocalDeviceProperties.LOCAL_DEVICE_RADIO_MANUFACTURER.equals(property)) { return String.valueOf(getDeviceManufacturer()); } if (BlueCoveLocalDeviceProperties.LOCAL_DEVICE_PROPERTY_STACK_VERSION.equals(property)) { return getBTWVersionInfo(); } // Some Hack and testing functions, not documented if (property.startsWith("bluecove.nativeFunction:")) { String functionDescr = property.substring(property.indexOf(':') + 1, property.length()); int paramIdx = functionDescr.indexOf(':'); if (paramIdx == -1) { throw new RuntimeException("Invalid native function " + functionDescr + "; arguments expected"); } String function = functionDescr.substring(0, paramIdx); long address = RemoteDeviceHelper.getAddress(functionDescr.substring(function.length() + 1, functionDescr .length())); if ("getRemoteDeviceVersionInfo".equals(function)) { return getRemoteDeviceVersionInfo(address); } else if ("cancelSniffMode".equals(function)) { return String.valueOf(cancelSniffMode(address)); } else if ("setSniffMode".equals(function)) { return String.valueOf(setSniffMode(address)); } else if ("getRemoteDeviceRSSI".equals(function)) { return String.valueOf(getRemoteDeviceRSSI(address)); } else if ("getRemoteDeviceLinkMode".equals(function)) { if (isRemoteDeviceConnected(address)) { return getRemoteDeviceLinkMode(address); } else { return "disconnected"; } } } return null; } /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#isCurrentThreadInterruptedCallback() */ public boolean isCurrentThreadInterruptedCallback() { return UtilsJavaSE.isCurrentThreadInterrupted(); } public RemoteDevice[] retrieveDevices(int option) { return null; } public Boolean isRemoteDeviceTrusted(long address) { return null; } public Boolean isRemoteDeviceAuthenticated(long address) { return null; } /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStackExtension#readRemoteDeviceRSSI(long) */ public int readRemoteDeviceRSSI(long address) throws IOException { return getRemoteDeviceRSSI(address); } // ---------------------- Remote Device authentication public boolean authenticateRemoteDevice(long address) throws IOException { return false; } private native boolean authenticateRemoteDeviceImpl(long address, String passkey) throws IOException; /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#authenticateRemoteDevice(long, java.lang.String) */ public boolean authenticateRemoteDevice(long address, String passkey) throws IOException { return authenticateRemoteDeviceImpl(address, passkey); } private native void removeAuthenticationWithRemoteDeviceImpl(long address) throws IOException; /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#removeAuthenticationWithRemoteDevice (long) */ public void removeAuthenticationWithRemoteDevice(long address) throws IOException { removeAuthenticationWithRemoteDeviceImpl(address); } // --- Some testing functions accessible by LocalDevice.getProperty private native boolean isRemoteDeviceConnected(long address); private native String getRemoteDeviceLinkMode(long address); private native String getRemoteDeviceVersionInfo(long address); private native boolean setSniffMode(long address); private native boolean cancelSniffMode(long address); private native int getRemoteDeviceRSSI(long address); // --- Device Inquiry private native int runDeviceInquiryImpl(DeviceInquiryRunnable inquiryRunnable, DeviceInquiryThread startedNotify, int accessCode, DiscoveryListener listener) throws BluetoothStateException; public boolean startInquiry(int accessCode, DiscoveryListener listener) throws BluetoothStateException { deviceDiscoveryListeners.addElement(listener); if (BlueCoveImpl.getConfigProperty(BlueCoveConfigProperties.PROPERTY_INQUIRY_REPORT_ASAP, false)) { deviceDiscoveryListenerFoundDevices.put(listener, new Hashtable()); } deviceDiscoveryListenerReportedDevices.put(listener, new Vector()); DeviceInquiryRunnable inquiryRunnable = new DeviceInquiryRunnable() { public int runDeviceInquiry(DeviceInquiryThread startedNotify, int accessCode, DiscoveryListener listener) throws BluetoothStateException { try { int discType = runDeviceInquiryImpl(this, startedNotify, accessCode, listener); if (discType == DiscoveryListener.INQUIRY_COMPLETED) { // Report found devices if any not reported Hashtable previouslyFound = (Hashtable) deviceDiscoveryListenerFoundDevices.get(listener); if (previouslyFound != null) { Vector reported = (Vector) deviceDiscoveryListenerReportedDevices.get(listener); for (Enumeration en = previouslyFound.keys(); en.hasMoreElements();) { RemoteDevice remoteDevice = (RemoteDevice) en.nextElement(); if (reported.contains(remoteDevice)) { continue; } reported.addElement(remoteDevice); Integer deviceClassInt = (Integer) previouslyFound.get(remoteDevice); DeviceClass deviceClass = new DeviceClass(deviceClassInt.intValue()); listener.deviceDiscovered(remoteDevice, deviceClass); // If cancelInquiry has been called if (!deviceDiscoveryListeners.contains(listener)) { return DiscoveryListener.INQUIRY_TERMINATED; } } } } return discType; } finally { deviceDiscoveryListeners.removeElement(listener); deviceDiscoveryListenerFoundDevices.remove(listener); deviceDiscoveryListenerReportedDevices.remove(listener); } } /* * This function may trigger multiple times per inquiry - even multiple times per device - once for the * address alone, and once for the address and the user-friendly name. */ public void deviceDiscoveredCallback(DiscoveryListener listener, long deviceAddr, int deviceClass, String deviceName, boolean paired) { DebugLog.debug("deviceDiscoveredCallback deviceName", deviceName); if (!deviceDiscoveryListeners.contains(listener)) { return; } // Update name if name retrieved RemoteDevice remoteDevice = RemoteDeviceHelper.createRemoteDevice(BluetoothStackWIDCOMM.this, deviceAddr, deviceName, paired); Vector reported = (Vector) deviceDiscoveryListenerReportedDevices.get(listener); if (reported == null || (reported.contains(remoteDevice))) { return; } // See -Dbluecove.inquiry.report_asap=false Hashtable previouslyFound = (Hashtable) deviceDiscoveryListenerFoundDevices.get(listener); if (previouslyFound != null) { Integer deviceClassInt = (Integer) previouslyFound.get(remoteDevice); if (deviceClassInt == null) { previouslyFound.put(remoteDevice, new Integer(deviceClass)); } else if (deviceClass != 0) { previouslyFound.put(remoteDevice, new Integer(deviceClass)); } } else { DeviceClass cod = new DeviceClass(deviceClass); reported.addElement(remoteDevice); DebugLog.debug("deviceDiscoveredCallback address", remoteDevice.getBluetoothAddress()); DebugLog.debug("deviceDiscoveredCallback deviceClass", cod); listener.deviceDiscovered(remoteDevice, cod); } } }; return DeviceInquiryThread.startInquiry(this, inquiryRunnable, accessCode, listener); } private native boolean deviceInquiryCancelImpl(); public boolean cancelInquiry(DiscoveryListener listener) { // no further deviceDiscovered() events will occur for this inquiry if (!deviceDiscoveryListeners.removeElement(listener)) { return false; } return deviceInquiryCancelImpl(); } native String getRemoteDeviceFriendlyName(long address, int majorDeviceClass, int minorDeviceClass) throws IOException; /** * get device name while discovery running. Device may not report its name first time while discovering. * * @param address * @return name */ native String peekRemoteDeviceFriendlyName(long address); public String getRemoteDeviceFriendlyName(long address) throws IOException { if (deviceDiscoveryListeners.size() != 0) { // discovery running return peekRemoteDeviceFriendlyName(address); } else { // Another way to get name is to run deviceInquiry DiscoveryListener listener = new DiscoveryListenerAdapter(); if (startInquiry(DiscoveryAgent.GIAC, listener)) { String name = peekRemoteDeviceFriendlyName(address); cancelInquiry(listener); return name; } } return null; } // --- Service search private native long[] runSearchServicesImpl(SearchServicesThread startedNotify, byte[] uuidValue, long address) throws BluetoothStateException, SearchServicesTerminatedException; public int searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice device, DiscoveryListener listener) throws BluetoothStateException { SearchServicesRunnable searchRunnable = new SearchServicesRunnable() { public int runSearchServices(SearchServicesThread sst, int[] attrSet, UUID[] uuidSet, RemoteDevice device, DiscoveryListener listener) throws BluetoothStateException { // Retrieve all Records, Filter here in Java synchronized (BluetoothStackWIDCOMM.class) { byte[] uuidValue = Utils.UUIDToByteArray(BluetoothConsts.L2CAP_PROTOCOL_UUID); for (int u = 0; u < uuidSet.length; u++) { if (uuidSet[u].equals(BluetoothConsts.L2CAP_PROTOCOL_UUID)) { continue; } else if (uuidSet[u].equals(BluetoothConsts.RFCOMM_PROTOCOL_UUID)) { uuidValue = Utils.UUIDToByteArray(uuidSet[u]); continue; } else { // Look for the most specific UUID uuidValue = Utils.UUIDToByteArray(uuidSet[u]); break; } } long[] handles; try { handles = runSearchServicesImpl(sst, uuidValue, RemoteDeviceHelper.getAddress(device)); } catch (SearchServicesTerminatedException e) { DebugLog.debug("SERVICE_SEARCH_TERMINATED"); return DiscoveryListener.SERVICE_SEARCH_TERMINATED; } if (handles == null) { DebugLog.debug("SERVICE_SEARCH_ERROR"); return DiscoveryListener.SERVICE_SEARCH_ERROR; } else if (handles.length > 0) { Vector records = new Vector(); int[] uuidFilerAttrIDs = new int[] { BluetoothConsts.ServiceClassIDList, BluetoothConsts.ProtocolDescriptorList }; int[] requiredAttrIDs = new int[] { BluetoothConsts.ServiceRecordHandle, BluetoothConsts.ServiceRecordState, BluetoothConsts.ServiceID }; nextRecord: for (int i = 0; i < handles.length; i++) { ServiceRecordImpl sr = new ServiceRecordImpl(BluetoothStackWIDCOMM.this, device, handles[i]); try { sr.populateRecord(uuidFilerAttrIDs); // Apply JSR-82 filter, all UUID should be present for (int u = 0; u < uuidSet.length; u++) { if (!((sr.hasServiceClassUUID(uuidSet[u]) || sr.hasProtocolClassUUID(uuidSet[u])))) { if (BluetoothStackWIDCOMMSDPInputStream.debug) { DebugLog.debug("filtered ServiceRecord (" + i + ")", sr); } continue nextRecord; } } if (BluetoothStackWIDCOMMSDPInputStream.debug) { DebugLog.debug("accepted ServiceRecord (" + i + ")", sr); } if (!isServiceRecordDiscoverable(RemoteDeviceHelper.getAddress(device), sr.getHandle())) { continue; } records.addElement(sr); sr.populateRecord(requiredAttrIDs); if (attrSet != null) { sr.populateRecord(attrSet); } DebugLog.debug("ServiceRecord (" + i + ") sr.handle", handles[i]); DebugLog.debug("ServiceRecord (" + i + ")", sr); } catch (Exception e) { DebugLog.debug("populateRecord error", e); } if (sst.isTerminated()) { DebugLog.debug("SERVICE_SEARCH_TERMINATED"); return DiscoveryListener.SERVICE_SEARCH_TERMINATED; } } if (records.size() != 0) { DebugLog.debug("SERVICE_SEARCH_COMPLETED"); ServiceRecord[] fileteredRecords = (ServiceRecord[]) Utils.vector2toArray(records, new ServiceRecord[records.size()]); listener.servicesDiscovered(sst.getTransID(), fileteredRecords); return DiscoveryListener.SERVICE_SEARCH_COMPLETED; } } DebugLog.debug("SERVICE_SEARCH_NO_RECORDS"); return DiscoveryListener.SERVICE_SEARCH_NO_RECORDS; } } }; return SearchServicesThread.startSearchServices(this, searchRunnable, attrSet, uuidSet, device, listener); } /** * Only one concurrent ServiceSearch supported */ private native void cancelServiceSearchImpl(); public boolean cancelServiceSearch(int transID) { SearchServicesThread sst = SearchServicesThread.getServiceSearchThread(transID); if (sst != null) { synchronized (this) { if (!sst.isTerminated()) { sst.setTerminated(); cancelServiceSearchImpl(); return true; } } } return false; } private native byte[] getServiceAttribute(int attrID, long handle) throws IOException; private native boolean isServiceRecordDiscoverable(long address, long handle) throws IOException; public boolean populateServicesRecordAttributeValues(ServiceRecordImpl serviceRecord, int[] attrIDs) throws IOException { if (attrIDs.length > ATTR_RETRIEVABLE_MAX) { throw new IllegalArgumentException(); } boolean anyRetrived = false; for (int i = 0; i < attrIDs.length; i++) { int id = attrIDs[i]; try { byte[] sdpStruct = getServiceAttribute(id, serviceRecord.getHandle()); if (sdpStruct != null) { if (BluetoothStackWIDCOMMSDPInputStream.debug) { DebugLog.debug("decode attribute " + id + " Ox" + Integer.toHexString(id)); } DataElement element = (new BluetoothStackWIDCOMMSDPInputStream(new ByteArrayInputStream(sdpStruct))) .readElement(); // Do special case conversion for only one element in the // list. if (id == BluetoothConsts.ProtocolDescriptorList) { Enumeration protocolsSeqEnum = (Enumeration) element.getValue(); if (protocolsSeqEnum.hasMoreElements()) { DataElement protocolElement = (DataElement) protocolsSeqEnum.nextElement(); if (protocolElement.getDataType() != DataElement.DATSEQ) { DataElement newMainSeq = new DataElement(DataElement.DATSEQ); newMainSeq.addElement(element); element = newMainSeq; } } } serviceRecord.populateAttributeValue(id, element); anyRetrived = true; } else { if (BluetoothStackWIDCOMMSDPInputStream.debug) { DebugLog.debug("no data for attribute " + id + " Ox" + Integer.toHexString(id)); } } } catch (Throwable e) { if (BluetoothStackWIDCOMMSDPInputStream.debug) { DebugLog.error("error populate attribute " + id + " Ox" + Integer.toHexString(id), e); } } } return anyRetrived; } // --- Client RFCOMM connections private native long connectionRfOpenClientConnectionImpl(long address, int channel, boolean authenticate, boolean encrypt, int timeout) throws IOException; public long connectionRfOpenClientConnection(BluetoothConnectionParams params) throws IOException { verifyDeviceReady(); return connectionRfOpenClientConnectionImpl(params.address, params.channel, params.authenticate, params.encrypt, params.timeout); } private native void closeRfCommPortImpl(long handle) throws IOException; public void connectionRfCloseClientConnection(long handle) throws IOException { closeRfCommPortImpl(handle); } public native long getConnectionRfRemoteAddress(long handle) throws IOException; public native int connectionRfRead(long handle) throws IOException; public native int connectionRfRead(long handle, byte[] b, int off, int len) throws IOException; public native int connectionRfReadAvailable(long handle) throws IOException; private native void connectionRfWriteImpl(long handle, byte[] b, int off, int len) throws IOException; public void connectionRfWrite(long handle, int b) throws IOException { byte buf[] = new byte[1]; buf[0] = (byte) (b & 0xFF); connectionRfWriteImpl(handle, buf, 0, 1); } public void connectionRfWrite(long handle, byte[] b, int off, int len) throws IOException { // Max in WIDCOMM is 64K that will cause ACCESS_VIOLATION. final int maxNativeBuffer = 0x10000 - 1; if (len < maxNativeBuffer) { connectionRfWriteImpl(handle, b, off, len); } else { int done = 0; while (done < len) { int l = len - done; if (l > maxNativeBuffer) { l = maxNativeBuffer; } connectionRfWriteImpl(handle, b, off + done, l); done += maxNativeBuffer; } } } public void connectionRfFlush(long handle) throws IOException { // TODO are there any flush } public int rfGetSecurityOpt(long handle, int expected) throws IOException { return expected; } /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2Encrypt(long,long,boolean) */ public boolean rfEncrypt(long address, long handle, boolean on) throws IOException { return false; } private native synchronized long rfServerOpenImpl(byte[] uuidValue, byte[] uuidValue2, boolean obexSrv, String name, boolean authenticate, boolean encrypt) throws IOException; private native int rfServerSCN(long handle) throws IOException; public long rfServerOpen(BluetoothConnectionNotifierParams params, ServiceRecordImpl serviceRecord) throws IOException { verifyDeviceReady(); byte[] uuidValue = Utils.UUIDToByteArray(params.uuid); byte[] uuidValue2 = params.obex ? null : Utils.UUIDToByteArray(BluetoothConsts.SERIAL_PORT_UUID); long handle = rfServerOpenImpl(uuidValue, uuidValue2, params.obex, params.name, params.authenticate, params.encrypt); int channel = rfServerSCN(handle); DebugLog.debug("serverSCN", channel); long serviceRecordHandle = handle; serviceRecord.populateRFCOMMAttributes(serviceRecordHandle, channel, params.uuid, params.name, params.obex); return handle; } private native void sdpServiceAddAttribute(long handle, char handleType, int attrID, short attrType, byte[] value) throws ServiceRegistrationException; private byte[] long2byte(long value, int len) { byte[] cvalue = new byte[len]; long l = value; for (int i = len - 1; i >= 0; i--) { cvalue[i] = (byte) (l & 0xFF); l >>= 8; } return cvalue; } public void rfServerUpdateServiceRecord(long handle, ServiceRecordImpl serviceRecord, boolean acceptAndOpen) throws ServiceRegistrationException { sdpServiceUpdateServiceRecord(handle, 'r', serviceRecord); } private byte[] sdpServiceSequenceAttribute(Enumeration en) throws ServiceRegistrationException { ByteArrayOutputStream out = new ByteArrayOutputStream(); SDPOutputStream sdpOut = new SDPOutputStream(out); try { while (en.hasMoreElements()) { sdpOut.writeElement((DataElement) en.nextElement()); } } catch (IOException e) { throw new ServiceRegistrationException(e.getMessage()); } return out.toByteArray(); } private native void sdpServiceAddServiceClassIdList(long handle, char handleType, byte[][] uuidValues) throws ServiceRegistrationException; private void sdpServiceUpdateServiceRecord(long handle, char handleType, ServiceRecordImpl serviceRecord) throws ServiceRegistrationException { int[] ids = serviceRecord.getAttributeIDs(); if ((ids == null) || (ids.length == 0)) { return; } // Update the records that can be update only once DataElement serviceClassIDList = serviceRecord.getAttributeValue(BluetoothConsts.ServiceClassIDList); if (serviceClassIDList.getDataType() != DataElement.DATSEQ) { throw new ServiceRegistrationException("Invalid serviceClassIDList"); } Enumeration en = (Enumeration) serviceClassIDList.getValue(); Vector uuids = new Vector(); while (en.hasMoreElements()) { DataElement u = (DataElement) en.nextElement(); if (u.getDataType() != DataElement.UUID) { throw new ServiceRegistrationException("Invalid serviceClassIDList element " + u); } uuids.add(u.getValue()); } if (uuids.size() > 0) { byte[][] uuidValues = new byte[uuids.size()][]; for (int u = 0; u < uuidValues.length; u++) { uuidValues[u] = Utils.UUIDToByteArray((UUID) uuids.elementAt(u)); } sdpServiceAddServiceClassIdList(handle, handleType, uuidValues); } // Update all other records for (int i = 0; i < ids.length; i++) { int id = ids[i]; switch (id) { case BluetoothConsts.ServiceRecordHandle: case BluetoothConsts.ServiceClassIDList: case BluetoothConsts.ProtocolDescriptorList: case BluetoothConsts.AttributeIDServiceName: continue; } DataElement d = serviceRecord.getAttributeValue(id); switch (d.getDataType()) { case DataElement.U_INT_1: sdpServiceAddAttribute(handle, handleType, id, UINT_DESC_TYPE, long2byte(d.getLong(), 1)); break; case DataElement.U_INT_2: sdpServiceAddAttribute(handle, handleType, id, UINT_DESC_TYPE, long2byte(d.getLong(), 2)); break; case DataElement.U_INT_4: sdpServiceAddAttribute(handle, handleType, id, UINT_DESC_TYPE, long2byte(d.getLong(), 4)); break; case DataElement.U_INT_8: case DataElement.U_INT_16: sdpServiceAddAttribute(handle, handleType, id, UINT_DESC_TYPE, (byte[]) d.getValue()); break; case DataElement.INT_1: sdpServiceAddAttribute(handle, handleType, id, TWO_COMP_INT_DESC_TYPE, long2byte(d.getLong(), 1)); break; case DataElement.INT_2: sdpServiceAddAttribute(handle, handleType, id, TWO_COMP_INT_DESC_TYPE, long2byte(d.getLong(), 2)); break; case DataElement.INT_4: sdpServiceAddAttribute(handle, handleType, id, TWO_COMP_INT_DESC_TYPE, long2byte(d.getLong(), 4)); break; case DataElement.INT_8: sdpServiceAddAttribute(handle, handleType, id, TWO_COMP_INT_DESC_TYPE, long2byte(d.getLong(), 8)); break; case DataElement.INT_16: sdpServiceAddAttribute(handle, handleType, id, TWO_COMP_INT_DESC_TYPE, (byte[]) d.getValue()); break; case DataElement.URL: sdpServiceAddAttribute(handle, handleType, id, URL_DESC_TYPE, Utils.getASCIIBytes(d.getValue() .toString())); break; case DataElement.STRING: sdpServiceAddAttribute(handle, handleType, id, TEXT_STR_DESC_TYPE, Utils.getUTF8Bytes(d.getValue() .toString())); break; case DataElement.NULL: sdpServiceAddAttribute(handle, handleType, id, NULL_DESC_TYPE, null); break; case DataElement.BOOL: sdpServiceAddAttribute(handle, handleType, id, BOOLEAN_DESC_TYPE, new byte[] { (byte) (d.getBoolean() ? 1 : 0) }); break; case DataElement.UUID: sdpServiceAddAttribute(handle, handleType, id, UUID_DESC_TYPE, BluetoothStackWIDCOMMSDPInputStream .getUUIDHexBytes((UUID) d.getValue())); break; case DataElement.DATSEQ: sdpServiceAddAttribute(handle, handleType, id, DATA_ELE_SEQ_DESC_TYPE, sdpServiceSequenceAttribute((Enumeration) d.getValue())); break; case DataElement.DATALT: sdpServiceAddAttribute(handle, handleType, id, DATA_ELE_ALT_DESC_TYPE, sdpServiceSequenceAttribute((Enumeration) d.getValue())); break; default: throw new ServiceRegistrationException("Invalid " + d.getDataType()); } } } public native long rfServerAcceptAndOpenRfServerConnection(long handle) throws IOException; public native void connectionRfCloseServerConnection(long handle) throws IOException; private native void rfServerCloseImpl(long handle) throws IOException; public void rfServerClose(long handle, ServiceRecordImpl serviceRecord) throws IOException { rfServerCloseImpl(handle); } // ---------------------- Client and Server L2CAP connections private void validateMTU(int receiveMTU, int transmitMTU) { if (receiveMTU > RECEIVE_MTU_MAX) { throw new IllegalArgumentException("invalid ReceiveMTU value " + receiveMTU); } // if (transmitMTU > RECEIVE_MTU_MAX) { // throw new IllegalArgumentException("invalid TransmitMTU value " + // transmitMTU); // } // int min = L2CAPConnection.DEFAULT_MTU; // if ((receiveMTU > L2CAPConnection.MINIMUM_MTU) && (receiveMTU < min)) // { // min = receiveMTU; // } // if ((transmitMTU > L2CAPConnection.MINIMUM_MTU) && (transmitMTU < // min)) { // min = transmitMTU; // } // return min; } private native long l2OpenClientConnectionImpl(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 { verifyDeviceReady(); validateMTU(receiveMTU, transmitMTU); return l2OpenClientConnectionImpl(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 synchronized long l2ServerOpenImpl(byte[] uuidValue, boolean authenticate, boolean encrypt, String name, int receiveMTU, int transmitMTU, int assignPsm) throws IOException; public native int l2ServerPSM(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 { verifyDeviceReady(); validateMTU(receiveMTU, transmitMTU); byte[] uuidValue = Utils.UUIDToByteArray(params.uuid); long handle = l2ServerOpenImpl(uuidValue, params.authenticate, params.encrypt, params.name, receiveMTU, transmitMTU, params.bluecove_ext_psm); int channel = l2ServerPSM(handle); int serviceRecordHandle = (int) handle; serviceRecord.populateL2CAPAttributes(serviceRecordHandle, channel, params.uuid, params.name); return handle; } /* * (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 { sdpServiceUpdateServiceRecord(handle, 'l', 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 native void l2CloseServerConnection(long handle) throws IOException; private native void l2ServerCloseImpl(long handle) throws IOException; /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2ServerClose(long, com.intel.bluetooth.ServiceRecordImpl) */ public void l2ServerClose(long handle, ServiceRecordImpl serviceRecord) throws IOException { l2ServerCloseImpl(handle); } /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2GetSecurityOpt(long, int) */ public int l2GetSecurityOpt(long handle, int expected) throws IOException { return expected; } /* * (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#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#l2RemoteAddress(long) */ public native long l2RemoteAddress(long handle) throws IOException; /* * (non-Javadoc) * * @see com.intel.bluetooth.BluetoothStack#l2Encrypt(long,long,boolean) */ public boolean l2Encrypt(long address, long handle, boolean on) throws IOException { return false; } }