/**
* 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;
}
}