/**
* BlueCove - Java library for Bluetooth
* Copyright (C) 2007-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 BluetoothStackToshiba implements BluetoothStack, DeviceInquiryRunnable, SearchServicesRunnable {
private boolean initialized = false;
private Vector deviceDiscoveryListeners = new Vector/* <DiscoveryListener> */();
private Hashtable deviceDiscoveryListenerFoundDevices = new Hashtable();
private Hashtable deviceDiscoveryListenerReportedDevices = new Hashtable();
private final static int ATTR_RETRIEVABLE_MAX = 0xFFFF;
private final static int RECEIVE_MTU_MAX = 1024;
// FIXME
private String getBTWVersionInfo()
{
return "";
}
// FIXME
private int getDeviceVersion()
{
return 0;
}
// FIXME
private int getDeviceManufacturer()
{
return 0;
}
BluetoothStackToshiba() {
}
// ---------------------- Library initialization
public String getStackID() {
return BlueCoveImpl.STACK_TOSHIBA;
}
public String toString() {
return getStackID();
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#getFeatureSet()
*/
public int getFeatureSet() {
return 0;
}
/*
* (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_TOSHIBA);
}
public native int getLibraryVersion();
public native int detectBluetoothStack();
public native void enableNativeDebug(Class nativeDebugCallback, boolean on);
private native boolean initializeImpl() throws BluetoothStateException;
private native void destroyImpl();
public void initialize() throws BluetoothStateException {
if (!initializeImpl()) {
throw new BluetoothStateException("TOSHIBA BluetoothStack not found");
}
initialized = true;
}
public void destroy() {
if (initialized) {
destroyImpl();
initialized = false;
DebugLog.debug("TOSHIBA destroyed");
}
}
/*
* (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.BluetoothStack#removeAuthenticationWithRemoteDevice (long)
*/
public void removeAuthenticationWithRemoteDevice(long address) throws IOException {
}
// ---------------------- LocalDevice
public native String getLocalDeviceBluetoothAddress() throws BluetoothStateException;
public DeviceClass getLocalDeviceClass() {
// TODO Auto-generated method stub
return null;
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#setLocalDeviceServiceClasses(int)
*/
public void setLocalDeviceServiceClasses(int classOfDevice) {
// TODO Auto-generated method stub
}
public String getLocalDeviceName() {
// TODO Auto-generated method stub
return null;
}
public boolean isLocalDevicePowerOn() {
// TODO Auto-generated method stub
return true;
}
public String getLocalDeviceProperty(String property) {
// Copied directly from WIDCOMM: probably needs to be changed
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 ("bluecove.radio.version".equals(property)) {
return String.valueOf(getDeviceVersion());
}
if ("bluecove.radio.manufacturer".equals(property)) {
return String.valueOf(getDeviceManufacturer());
}
if ("bluecove.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;
}
public int getLocalDeviceDiscoverable() {
// TODO Auto-generated method stub
return 0;
}
public boolean setLocalDeviceDiscoverable(int mode) throws BluetoothStateException {
// TODO Auto-generated method stub
return false;
}
// ---------------------- Remote Device authentication
public boolean authenticateRemoteDevice(long address) throws IOException {
return false;
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#authenticateRemoteDevice(long,
* java.lang.String)
*/
public boolean authenticateRemoteDevice(long address, String passkey) throws IOException {
return false;
}
// --- Some testing functions accessible by LocalDevice.getProperty
// FIXME ALL
public boolean isRemoteDeviceConnected(long address)
{
return true;
}
public String getRemoteDeviceLinkMode(long address)
{
return "";
}
public String getRemoteDeviceVersionInfo(long address)
{
return "";
}
public boolean setSniffMode(long address)
{
return false;
}
public boolean cancelSniffMode(long address)
{
return false;
}
public int getRemoteDeviceRSSI(long address)
{
return 0;
}
// ---------------------- Device Inquiry
private native int runDeviceInquiryImpl(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());
return DeviceInquiryThread.startInquiry(this, this, accessCode, listener);
}
public int runDeviceInquiry(DeviceInquiryThread startedNotify, int accessCode, DiscoveryListener listener)
throws BluetoothStateException {
try {
int discType = runDeviceInquiryImpl(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);
}
}
public void deviceDiscoveredCallback(DiscoveryListener listener, long deviceAddr, int deviceClass,
String deviceName, boolean paired) {
// Copied directly from WIDCOMM driver
DebugLog.debug("deviceDiscoveredCallback deviceName", deviceName);
if (!deviceDiscoveryListeners.contains(listener)) {
return;
}
// Update name if name retrieved
RemoteDevice remoteDevice = RemoteDeviceHelper.createRemoteDevice(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);
}
}
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();
}
/**
* get device name while discovery running. Device may not report its name first time while discovering.
*
* @param address
* @return name
*/
private native String getRemoteDeviceFriendlyNameImpl(long address);
public String getRemoteDeviceFriendlyName(long address) throws IOException {
return getRemoteDeviceFriendlyNameImpl(address);
}
// ---------------------- Service search
private native short connectSDPImpl(long address);
private native void disconnectSDPImpl(short cid);
private native long[] searchServicesImpl(SearchServicesThread startedNotify, short cid, byte[][] uuidSet);
private native byte[] populateWorkerImpl(short cid, long handle, int[] attrSet);
private boolean setAttributes(ServiceRecordImpl serviceRecord, int[] attrIDs, byte[] bytes) {
boolean anyRetrived = false;
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
// Need to rename this class
BluetoothStackWIDCOMMSDPInputStream btis = null;
try {
btis = new BluetoothStackWIDCOMMSDPInputStream(bais);
}
catch (Exception e) {
}
for (int i = 0; i < attrIDs.length; i++) {
int id = attrIDs[i];
try {
if (BluetoothStackWIDCOMMSDPInputStream.debug) {
DebugLog.debug("decode attribute " + id + " Ox" + Integer.toHexString(id));
}
DataElement element = btis.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;
} catch (Throwable e) {
if (BluetoothStackWIDCOMMSDPInputStream.debug) {
DebugLog.error("error populate attribute " + id + " Ox" + Integer.toHexString(id), e);
}
}
}
return anyRetrived;
}
public int runSearchServices(SearchServicesThread startedNotify, int[] attrSet, UUID[] uuidSet,
RemoteDevice device, DiscoveryListener listener) throws BluetoothStateException {
short cid;
try {
cid = connectSDPImpl(RemoteDeviceHelper.getAddress(device.getBluetoothAddress()));
}
catch (Exception e) {
return DiscoveryListener.SERVICE_SEARCH_DEVICE_NOT_REACHABLE;
}
byte[][] uuidBytes = new byte[uuidSet.length][16];
for (int i = 0; i < uuidSet.length; i++) {
uuidBytes[i] = new byte[16];
String full = uuidSet[i].toString();
for (int j = 0; j < 16; j++) {
String sub = full.substring(j*2, j*2+2).toUpperCase();
uuidBytes[i][j] = (byte)Integer.parseInt(sub, 16);
}
}
long[] handles;
try {
handles = searchServicesImpl(startedNotify, cid, uuidBytes);
}
catch (Exception e) {
disconnectSDPImpl(cid);
return DiscoveryListener.SERVICE_SEARCH_ERROR;
}
if (handles.length <= 0) {
disconnectSDPImpl(cid);
return DiscoveryListener.SERVICE_SEARCH_NO_RECORDS;
}
ServiceRecordImpl[] records = new ServiceRecordImpl[handles.length];
for (int i = 0; i < handles.length; i++) {
records[i] = new ServiceRecordImpl(this, device, handles[i]);
byte[] bytes;
try {
bytes = populateWorkerImpl(cid, handles[i], attrSet);
}
catch (Exception e) {
disconnectSDPImpl(cid);
return DiscoveryListener.SERVICE_SEARCH_ERROR;
}
if (bytes != null) {
setAttributes(records[i], attrSet, bytes);
}
}
listener.servicesDiscovered(startedNotify.getTransID(), records);
disconnectSDPImpl(cid);
return DiscoveryListener.SERVICE_SEARCH_COMPLETED;
}
public int searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice device, DiscoveryListener listener)
throws BluetoothStateException {
return SearchServicesThread.startSearchServices(this, this, attrSet, uuidSet, device, listener);
}
public boolean cancelServiceSearch(int transID) {
// No service search cancel on Toshiba
return false;
}
public boolean populateServicesRecordAttributeValues(ServiceRecordImpl serviceRecord, int[] attrIDs)
throws IOException {
if (attrIDs.length > 0xFFFF) {
throw new IllegalArgumentException();
}
short cid;
try {
cid = connectSDPImpl(RemoteDeviceHelper.getAddress(serviceRecord.getHostDevice().getBluetoothAddress()));
}
catch (Exception e) {
return false;
}
byte[] bytes;
try {
bytes = populateWorkerImpl(cid, serviceRecord.getHandle(), attrIDs);
}
catch (Exception e) {
disconnectSDPImpl(cid);
return false;
}
if (bytes == null) {
return false;
}
boolean ret;
ret = setAttributes(serviceRecord, attrIDs, bytes);
disconnectSDPImpl(cid);
return ret;
}
// ---------------------- Client RFCOMM connections
public long connectionRfOpenClientConnection(BluetoothConnectionParams params) throws IOException {
// TODO Auto-generated method stub
return 0;
}
public void connectionRfCloseClientConnection(long handle) throws IOException {
// TODO Auto-generated method stub
}
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;
}
// ---------------------- Server RFCOMM connections
public long rfServerOpen(BluetoothConnectionNotifierParams params, ServiceRecordImpl serviceRecord)
throws IOException {
// TODO Auto-generated method stub
return 0;
}
public void rfServerClose(long handle, ServiceRecordImpl serviceRecord) throws IOException {
// TODO Auto-generated method stub
}
public void rfServerUpdateServiceRecord(long handle, ServiceRecordImpl serviceRecord, boolean acceptAndOpen)
throws ServiceRegistrationException {
// TODO Auto-generated method stub
}
public long rfServerAcceptAndOpenRfServerConnection(long handle) throws IOException {
// TODO Auto-generated method stub
return 0;
}
public void connectionRfCloseServerConnection(long handle) throws IOException {
// TODO Auto-generated method stub
}
// ---------------------- Shared Client and Server RFCOMM connections
public void connectionRfFlush(long handle) throws IOException {
// TODO Auto-generated method stub
}
public int connectionRfRead(long handle) throws IOException {
// TODO Auto-generated method stub
return 0;
}
public int connectionRfRead(long handle, byte[] b, int off, int len) throws IOException {
// TODO Auto-generated method stub
return 0;
}
public int connectionRfReadAvailable(long handle) throws IOException {
// TODO Auto-generated method stub
return 0;
}
public void connectionRfWrite(long handle, int b) throws IOException {
// TODO Auto-generated method stub
}
public void connectionRfWrite(long handle, byte[] b, int off, int len) throws IOException {
// TODO Auto-generated method stub
}
public long getConnectionRfRemoteAddress(long handle) throws IOException {
// TODO Auto-generated method stub
return 0;
}
// ---------------------- Client and Server L2CAP connections
/*
* (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 {
// TODO Auto-generated method stub
return 0;
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#l2CloseClientConnection(long)
*/
public void l2CloseClientConnection(long handle) throws IOException {
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
*
* @see com.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 {
// TODO Auto-generated method stub
return 0;
}
/*
* (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 {
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#l2ServerAcceptAndOpenServerConnection(long)
*/
public long l2ServerAcceptAndOpenServerConnection(long handle) throws IOException {
// TODO Auto-generated method stub
return 0;
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#l2CloseServerConnection(long)
*/
public void l2CloseServerConnection(long handle) throws IOException {
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#l2ServerClose(long,
* com.intel.bluetooth.ServiceRecordImpl)
*/
public void l2ServerClose(long handle, ServiceRecordImpl serviceRecord) throws IOException {
// TODO Auto-generated method stub
}
public int l2GetSecurityOpt(long handle, int expected) throws IOException {
return expected;
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#l2Ready(long)
*/
public boolean l2Ready(long handle) throws IOException {
// TODO Auto-generated method stub
return false;
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#l2receive(long, byte[])
*/
public int l2Receive(long handle, byte[] inBuf) throws IOException {
// TODO Auto-generated method stub
return 0;
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#l2send(long, byte[])
*/
public void l2Send(long handle, byte[] data, int transmitMTU) throws IOException {
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#l2GetReceiveMTU(long)
*/
public int l2GetReceiveMTU(long handle) throws IOException {
// TODO Auto-generated method stub
return 0;
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#l2GetTransmitMTU(long)
*/
public int l2GetTransmitMTU(long handle) throws IOException {
// TODO Auto-generated method stub
return 0;
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#l2RemoteAddress(long)
*/
public long l2RemoteAddress(long handle) throws IOException {
// TODO Auto-generated method stub
return 0;
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#l2Encrypt(long,long,boolean)
*/
public boolean l2Encrypt(long address, long handle, boolean on) throws IOException {
// TODO Auto-generated method stub
return false;
}
}