/**
* BlueCove - Java library for Bluetooth
* Copyright (C) 2008-2009 Michael Lifshits
* 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.
*
* @author vlads
* @version $Id$
*/
package com.intel.bluetooth;
import java.io.IOException;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRegistrationException;
import javax.bluetooth.UUID;
class BluetoothEmulator implements BluetoothStack {
static final int NATIVE_LIBRARY_VERSION = BlueCoveImpl.nativeLibraryVersionExpected;
private EmulatorLocalDevice localDevice;
private EmulatorDeviceInquiry deviceInquiry;
BluetoothEmulator() {
}
// --- Library initialization
public String getStackID() {
return BlueCoveImpl.STACK_EMULATOR;
}
public String toString() {
if (localDevice != null) {
return getStackID() + ":" + RemoteDeviceHelper.getBluetoothAddress(localDevice.getAddress());
} else {
return getStackID();
}
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#isNativeCodeLoaded()
*/
public boolean isNativeCodeLoaded() {
return true;
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#requireNativeLibraries()
*/
public LibraryInformation[] requireNativeLibraries() {
return null;
}
public int getLibraryVersion() throws BluetoothStateException {
return NATIVE_LIBRARY_VERSION;
}
public int detectBluetoothStack() {
return BlueCoveImpl.BLUECOVE_STACK_DETECT_EMULATOR;
}
public void initialize() throws BluetoothStateException {
localDevice = EmulatorHelper.createNewLocalDevice();
}
public void destroy() {
EmulatorHelper.releaseDevice(localDevice);
localDevice = null;
}
public void enableNativeDebug(Class nativeDebugCallback, boolean on) {
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#isCurrentThreadInterruptedCallback()
*/
public boolean isCurrentThreadInterruptedCallback() {
return Thread.interrupted();
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#getFeatureSet()
*/
public int getFeatureSet() {
return FEATURE_SET_DEVICE_SERVICE_CLASSES | FEATURE_SERVICE_ATTRIBUTES | FEATURE_L2CAP | FEATURE_ASSIGN_SERVER_PSM;
}
// --- LocalDevice
private void assertClosed() {
if (localDevice == null) {
throw new RuntimeException("Device Closed");
}
}
public String getLocalDeviceBluetoothAddress() throws BluetoothStateException {
assertClosed();
return RemoteDeviceHelper.getBluetoothAddress(localDevice.getAddress());
}
public DeviceClass getLocalDeviceClass() {
assertClosed();
return new DeviceClass(localDevice.getDeviceClass());
}
public String getLocalDeviceName() {
assertClosed();
return localDevice.getName();
}
public boolean isLocalDevicePowerOn() {
assertClosed();
return localDevice.isLocalDevicePowerOn();
}
public String getLocalDeviceProperty(String property) {
assertClosed();
return localDevice.getLocalDeviceProperty(property);
}
public int getLocalDeviceDiscoverable() {
assertClosed();
return localDevice.getLocalDeviceDiscoverable();
}
public boolean setLocalDeviceDiscoverable(int mode) throws BluetoothStateException {
assertClosed();
return localDevice.setLocalDeviceDiscoverable(mode);
}
private EmulatorLocalDevice activeLocalDevice() throws BluetoothStateException {
assertClosed();
if (!localDevice.isActive()) {
throw new BluetoothStateException("Bluetooth system is off");
}
return localDevice;
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#setLocalDeviceServiceClasses(int)
*/
public void setLocalDeviceServiceClasses(int classOfDevice) {
assertClosed();
localDevice.setLocalDeviceServiceClasses(classOfDevice);
}
// --- 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;
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#removeAuthenticationWithRemoteDevice (long)
*/
public void removeAuthenticationWithRemoteDevice(long address) throws IOException {
throw new NotSupportedIOException(getStackID());
}
// --- Device Inquiry
public boolean startInquiry(int accessCode, DiscoveryListener listener) throws BluetoothStateException {
if (deviceInquiry != null) {
throw new BluetoothStateException("Another inquiry already running");
}
deviceInquiry = new EmulatorDeviceInquiry(activeLocalDevice(), this, listener);
return DeviceInquiryThread.startInquiry(this, deviceInquiry, accessCode, listener);
}
public boolean cancelInquiry(DiscoveryListener listener) {
assertClosed();
if (deviceInquiry == null) {
return false;
}
if (deviceInquiry.cancelInquiry(listener)) {
deviceInquiry = null;
return true;
} else {
return false;
}
}
public String getRemoteDeviceFriendlyName(long address) throws IOException {
return activeLocalDevice().getDeviceManagerService().getRemoteDeviceFriendlyName(address);
}
public RemoteDevice[] retrieveDevices(int option) {
return null;
}
public Boolean isRemoteDeviceTrusted(long address) {
return null;
}
public Boolean isRemoteDeviceAuthenticated(long address) {
return null;
}
// --- Service search
public int searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice device, DiscoveryListener listener)
throws BluetoothStateException {
return SearchServicesThread.startSearchServices(this, new EmulatorSearchServices(activeLocalDevice(), this),
attrSet, uuidSet, device, listener);
}
public boolean cancelServiceSearch(int transID) {
assertClosed();
SearchServicesThread sst = SearchServicesThread.getServiceSearchThread(transID);
if (sst != null) {
synchronized (sst) {
if (!sst.isTerminated()) {
sst.setTerminated();
return true;
}
}
}
return false;
}
public boolean populateServicesRecordAttributeValues(ServiceRecordImpl serviceRecord, int[] attrIDs)
throws IOException {
if (attrIDs.length > localDevice.getBluetooth_sd_attr_retrievable_max()) {
throw new IllegalArgumentException();
}
return EmulatorSearchServices.populateServicesRecordAttributeValues(activeLocalDevice(), serviceRecord,
attrIDs, RemoteDeviceHelper.getAddress(serviceRecord.getHostDevice()), serviceRecord.getHandle());
}
// --- Client RFCOMM connections
public long connectionRfOpenClientConnection(BluetoothConnectionParams params) throws IOException {
EmulatorRFCOMMClient c = activeLocalDevice().createRFCOMMClient(params.address);
boolean success = false;
try {
c.connect(params);
success = true;
} finally {
if (!success) {
localDevice.removeConnection(c);
}
}
return c.getHandle();
}
public void connectionRfCloseClientConnection(long handle) throws IOException {
assertClosed();
if (localDevice == null) {
return;
}
EmulatorRFCOMMClient c = ((EmulatorRFCOMMClient) localDevice.getConnection(handle));
try {
c.close();
} finally {
if (localDevice != null) {
localDevice.removeConnection(c);
}
}
}
public int rfGetSecurityOpt(long handle, int expected) throws IOException {
assertClosed();
return ((EmulatorLinkedConnection) localDevice.getConnection(handle)).getSecurityOpt(expected);
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#l2Encrypt(long,long,boolean)
*/
public boolean rfEncrypt(long address, long handle, boolean on) throws IOException {
assertClosed();
return ((EmulatorLinkedConnection) localDevice.getConnection(handle)).encrypt(address, on);
}
// --- Server RFCOMM connections
public long rfServerOpen(BluetoothConnectionNotifierParams params, ServiceRecordImpl serviceRecord)
throws IOException {
EmulatorRFCOMMService s = activeLocalDevice().createRFCOMMService();
boolean success = false;
try {
s.open(params);
serviceRecord.setHandle(s.getHandle());
serviceRecord
.populateRFCOMMAttributes(s.getHandle(), s.getChannel(), params.uuid, params.name, params.obex);
s.updateServiceRecord(serviceRecord);
success = true;
} finally {
if (!success) {
localDevice.removeConnection(s);
}
}
return s.getHandle();
}
public void rfServerClose(long handle, ServiceRecordImpl serviceRecord) throws IOException {
assertClosed();
if (localDevice == null) {
return;
}
EmulatorRFCOMMService s = ((EmulatorRFCOMMService) localDevice.getConnection(handle));
try {
s.close(serviceRecord);
} finally {
if (localDevice != null) {
localDevice.removeConnection(s);
}
}
}
private void serverUpdateServiceRecord(long handle, ServiceRecordImpl serviceRecord, boolean acceptAndOpen)
throws ServiceRegistrationException {
EmulatorServiceConnection s;
try {
s = ((EmulatorServiceConnection) activeLocalDevice().getConnection(handle));
} catch (IOException e) {
throw new ServiceRegistrationException(e.getMessage());
}
s.updateServiceRecord(serviceRecord);
}
public void rfServerUpdateServiceRecord(long handle, ServiceRecordImpl serviceRecord, boolean acceptAndOpen)
throws ServiceRegistrationException {
serverUpdateServiceRecord(handle, serviceRecord, acceptAndOpen);
}
public long rfServerAcceptAndOpenRfServerConnection(long handle) throws IOException {
EmulatorRFCOMMService s = ((EmulatorRFCOMMService) activeLocalDevice().getConnection(handle));
if (!localDevice.isConnectable()) {
throw new BluetoothStateException("Local device is not connectable");
}
long clientHandle = 0;
boolean success = false;
while (!success) {
long connectionHandle = s.accept();
try {
long remoteAddress = localDevice.getDeviceManagerService().getRemoteAddress(localDevice.getAddress(),
connectionHandle);
EmulatorRFCOMMClient c = localDevice.createRFCOMMClient(remoteAddress);
c.connect(remoteAddress, connectionHandle);
localDevice.getDeviceManagerService().connectionAccepted(localDevice.getAddress(), connectionHandle);
success = true;
clientHandle = c.getHandle();
} catch (IOException e) {
DebugLog.debug("fail to accept connection", e);
continue;
} finally {
if (!success) {
localDevice.getDeviceManagerService().closeConnection(localDevice.getAddress(), connectionHandle);
}
}
}
return clientHandle;
}
public void connectionRfCloseServerConnection(long handle) throws IOException {
connectionRfCloseClientConnection(handle);
}
// --- Shared Client and Server RFCOMM connections
public int connectionRfRead(long handle) throws IOException {
return ((EmulatorRFCOMMClient) activeLocalDevice().getConnection(handle)).read();
}
public int connectionRfRead(long handle, byte[] b, int off, int len) throws IOException {
return ((EmulatorRFCOMMClient) activeLocalDevice().getConnection(handle)).read(b, off, len);
}
public int connectionRfReadAvailable(long handle) throws IOException {
return ((EmulatorRFCOMMClient) activeLocalDevice().getConnection(handle)).available();
}
public void connectionRfWrite(long handle, int b) throws IOException {
((EmulatorRFCOMMClient) activeLocalDevice().getConnection(handle)).write(b);
}
public void connectionRfWrite(long handle, byte[] b, int off, int len) throws IOException {
((EmulatorRFCOMMClient) activeLocalDevice().getConnection(handle)).write(b, off, len);
}
public void connectionRfFlush(long handle) throws IOException {
((EmulatorRFCOMMClient) activeLocalDevice().getConnection(handle)).flush();
}
public long getConnectionRfRemoteAddress(long handle) throws IOException {
return ((EmulatorRFCOMMClient) activeLocalDevice().getConnection(handle)).getRemoteAddress();
}
// --- Client and Server L2CAP connections
private void validateMTU(int receiveMTU, int transmitMTU) {
if (receiveMTU > localDevice.getBluetooth_l2cap_receiveMTU_max()) {
throw new IllegalArgumentException("invalid ReceiveMTU value " + receiveMTU);
}
}
public long l2OpenClientConnection(BluetoothConnectionParams params, int receiveMTU, int transmitMTU)
throws IOException {
validateMTU(receiveMTU, transmitMTU);
EmulatorL2CAPClient c = activeLocalDevice().createL2CAPClient(params.address);
boolean success = false;
try {
c.connect(params, receiveMTU, transmitMTU);
success = true;
} finally {
if (!success) {
localDevice.removeConnection(c);
}
}
return c.getHandle();
}
public void l2CloseClientConnection(long handle) throws IOException {
assertClosed();
EmulatorL2CAPClient c = ((EmulatorL2CAPClient) localDevice.getConnection(handle));
try {
c.close();
} finally {
localDevice.removeConnection(c);
}
}
public long l2ServerOpen(BluetoothConnectionNotifierParams params, int receiveMTU, int transmitMTU,
ServiceRecordImpl serviceRecord) throws IOException {
validateMTU(receiveMTU, transmitMTU);
EmulatorL2CAPService s = activeLocalDevice().createL2CAPService(params.bluecove_ext_psm);
boolean success = false;
try {
s.open(params, receiveMTU, transmitMTU);
serviceRecord.setHandle(s.getHandle());
serviceRecord.populateL2CAPAttributes((int) s.getHandle(), s.getPcm(), params.uuid, params.name);
s.updateServiceRecord(serviceRecord);
success = true;
} finally {
if (!success) {
localDevice.removeConnection(s);
}
}
return s.getHandle();
}
public void l2ServerUpdateServiceRecord(long handle, ServiceRecordImpl serviceRecord, boolean acceptAndOpen)
throws ServiceRegistrationException {
serverUpdateServiceRecord(handle, serviceRecord, acceptAndOpen);
}
public long l2ServerAcceptAndOpenServerConnection(long handle) throws IOException {
EmulatorL2CAPService s = ((EmulatorL2CAPService) activeLocalDevice().getConnection(handle));
if (!localDevice.isConnectable()) {
throw new BluetoothStateException("Local device is not connectable");
}
long clientHandle = 0;
boolean success = false;
while (!success) {
long connectionHandle = s.accept();
try {
long remoteAddress = localDevice.getDeviceManagerService().getRemoteAddress(localDevice.getAddress(),
connectionHandle);
EmulatorL2CAPClient c = localDevice.createL2CAPClient(remoteAddress);
c.connect(remoteAddress, connectionHandle, s.getReceiveMTU(), s.getTransmitMTU());
localDevice.getDeviceManagerService().connectionAccepted(localDevice.getAddress(), connectionHandle);
success = true;
clientHandle = c.getHandle();
} catch (IOException e) {
DebugLog.debug("fail to accept connection", e);
continue;
} finally {
if (!success) {
localDevice.getDeviceManagerService().closeConnection(localDevice.getAddress(), connectionHandle);
}
}
}
return clientHandle;
}
public void l2CloseServerConnection(long handle) throws IOException {
l2CloseClientConnection(handle);
}
public void l2ServerClose(long handle, ServiceRecordImpl serviceRecord) throws IOException {
assertClosed();
EmulatorL2CAPService s = ((EmulatorL2CAPService) localDevice.getConnection(handle));
try {
s.close(serviceRecord);
} finally {
localDevice.removeConnection(s);
}
}
public int l2GetSecurityOpt(long handle, int expected) throws IOException {
assertClosed();
return ((EmulatorLinkedConnection) localDevice.getConnection(handle)).getSecurityOpt(expected);
}
public boolean l2Ready(long handle) throws IOException {
return ((EmulatorL2CAPClient) activeLocalDevice().getConnection(handle)).ready();
}
public int l2Receive(long handle, byte[] inBuf) throws IOException {
return ((EmulatorL2CAPClient) activeLocalDevice().getConnection(handle)).receive(inBuf);
}
public void l2Send(long handle, byte[] data, int transmitMTU) throws IOException {
((EmulatorL2CAPClient) activeLocalDevice().getConnection(handle)).send(data);
}
public int l2GetReceiveMTU(long handle) throws IOException {
return ((EmulatorL2CAPClient) activeLocalDevice().getConnection(handle)).getReceiveMTU();
}
public int l2GetTransmitMTU(long handle) throws IOException {
return ((EmulatorL2CAPClient) activeLocalDevice().getConnection(handle)).getTransmitMTU();
}
public long l2RemoteAddress(long handle) throws IOException {
return ((EmulatorL2CAPClient) activeLocalDevice().getConnection(handle)).getRemoteAddress();
}
/*
* (non-Javadoc)
*
* @see com.intel.bluetooth.BluetoothStack#l2Encrypt(long,long,boolean)
*/
public boolean l2Encrypt(long address, long handle, boolean on) throws IOException {
assertClosed();
return ((EmulatorLinkedConnection) localDevice.getConnection(handle)).encrypt(address, on);
}
}