summaryrefslogtreecommitdiff
path: root/cpp/src
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src')
-rw-r--r--cpp/src/IceBT/AcceptorI.cpp146
-rw-r--r--cpp/src/IceBT/AcceptorI.h17
-rw-r--r--cpp/src/IceBT/ConnectorI.cpp40
-rw-r--r--cpp/src/IceBT/ConnectorI.h8
-rw-r--r--cpp/src/IceBT/EndpointI.cpp67
-rw-r--r--cpp/src/IceBT/EndpointI.h24
-rw-r--r--cpp/src/IceBT/Engine.cpp1409
-rw-r--r--cpp/src/IceBT/Engine.h54
-rw-r--r--cpp/src/IceBT/Makefile2
-rw-r--r--cpp/src/IceBT/StreamSocket.cpp42
-rw-r--r--cpp/src/IceBT/StreamSocket.h11
-rw-r--r--cpp/src/IceBT/TransceiverI.cpp17
-rw-r--r--cpp/src/IceBT/TransceiverI.h4
-rw-r--r--cpp/src/IceBT/Util.cpp170
-rw-r--r--cpp/src/IceBT/Util.h5
15 files changed, 853 insertions, 1163 deletions
diff --git a/cpp/src/IceBT/AcceptorI.cpp b/cpp/src/IceBT/AcceptorI.cpp
index 0d077b94265..89532aee6f1 100644
--- a/cpp/src/IceBT/AcceptorI.cpp
+++ b/cpp/src/IceBT/AcceptorI.cpp
@@ -8,7 +8,6 @@
// **********************************************************************
#include <IceBT/AcceptorI.h>
-#include <IceBT/Engine.h>
#include <IceBT/EndpointI.h>
#include <IceBT/Instance.h>
#include <IceBT/TransceiverI.h>
@@ -28,30 +27,6 @@ using namespace IceBT;
IceUtil::Shared* IceBT::upCast(AcceptorI* p) { return p; }
-namespace
-{
-
-class ProfileCallbackI : public ProfileCallback
-{
-public:
-
- ProfileCallbackI(const AcceptorIPtr& acceptor) :
- _acceptor(acceptor)
- {
- }
-
- virtual void newConnection(int fd)
- {
- _acceptor->newConnection(fd);
- }
-
-private:
-
- AcceptorIPtr _acceptor;
-};
-
-}
-
IceInternal::NativeInfoPtr
IceBT::AcceptorI::getNativeInfo()
{
@@ -61,43 +36,40 @@ IceBT::AcceptorI::getNativeInfo()
void
IceBT::AcceptorI::close()
{
- if(!_path.empty())
+ try
+ {
+ _instance->engine()->removeService(_adapter, _serviceHandle);
+ }
+ catch(const BluetoothException&)
+ {
+ // Ignore.
+ }
+
+ if(_fd != INVALID_SOCKET)
{
- try
- {
- _instance->engine()->unregisterProfile(_path);
- }
- catch(...)
- {
- }
+ IceInternal::closeSocketNoThrow(_fd);
+ _fd = INVALID_SOCKET;
}
}
IceInternal::EndpointIPtr
IceBT::AcceptorI::listen()
{
- assert(!_uuid.empty());
-
- //
- // The Bluetooth daemon will select an available channel if _channel == 0.
- //
-
try
{
- _path = _instance->engine()->registerProfile(_uuid, _name, _channel, new ProfileCallbackI(this));
+ _addr = doBind(_fd, _addr);
+ IceInternal::doListen(_fd, _backlog);
}
- catch(const BluetoothException& ex)
+ catch(...)
{
- InitializationException e(__FILE__, __LINE__);
- e.reason = "unable to register SDP service";
- if(!ex.reason.empty())
- {
- e.reason += "\n" + ex.reason;
- }
- throw e;
+ _fd = INVALID_SOCKET;
+ throw;
}
- _endpoint = _endpoint->endpoint(this);
+ assert(!_uuid.empty());
+
+ _serviceHandle = _instance->engine()->addService(_adapter, _name, _uuid, _addr.rc_channel);
+
return _endpoint;
}
@@ -114,26 +86,9 @@ IceBT::AcceptorI::accept()
throw ex;
}
- IceInternal::TransceiverPtr t;
+ SOCKET fd = doAccept(_fd);
- {
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-
- //
- // The thread pool should only call accept() when we've notified it that we have a
- // new transceiver ready to be accepted.
- //
- assert(!_transceivers.empty());
- t = _transceivers.top();
- _transceivers.pop();
-
- //
- // Update our status with the thread pool.
- //
- ready(IceInternal::SocketOperationRead, !_transceivers.empty());
- }
-
- return t;
+ return new TransceiverI(_instance, new StreamSocket(_instance, fd), _uuid);
}
string
@@ -145,7 +100,7 @@ IceBT::AcceptorI::protocol() const
string
IceBT::AcceptorI::toString() const
{
- return addrToString(_addr, _channel);
+ return addrToString(_addr);
}
string
@@ -167,27 +122,7 @@ IceBT::AcceptorI::toDetailedString() const
int
IceBT::AcceptorI::effectiveChannel() const
{
- //
- // If no channel was specified in the endpoint (_channel == 0), the Bluetooth daemon will select
- // an available channel for us. Unfortunately, there's no way to discover what that channel is
- // (aside from waiting for the first incoming connection and inspecting the socket endpoint).
- //
-
- return _channel;
-}
-
-void
-IceBT::AcceptorI::newConnection(int fd)
-{
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-
- _transceivers.push(new TransceiverI(_instance, new StreamSocket(_instance, fd), 0, _uuid));
-
- //
- // Notify the thread pool that we are ready to "read". The thread pool will invoke accept()
- // and we can return the new transceiver.
- //
- ready(IceInternal::SocketOperationRead, true);
+ return _addr.rc_channel;
}
IceBT::AcceptorI::AcceptorI(const EndpointIPtr& endpoint, const InstancePtr& instance, const string& adapterName,
@@ -195,39 +130,46 @@ IceBT::AcceptorI::AcceptorI(const EndpointIPtr& endpoint, const InstancePtr& ins
_endpoint(endpoint),
_instance(instance),
_adapterName(adapterName),
- _addr(addr),
_uuid(uuid),
_name(name),
- _channel(channel)
+ _serviceHandle(0)
{
- string s = IceUtilInternal::trim(_addr);
- if(s.empty())
+ // TBD - Necessary?
+ //_backlog = instance->properties()->getPropertyAsIntWithDefault("IceBT.Backlog", 1);
+ _backlog = 1;
+
+ _addr.rc_family = AF_BLUETOOTH;
+ _addr.rc_channel = channel;
+
+ _adapter = IceUtilInternal::trim(addr);
+ if(_adapter.empty())
{
//
// If no address was specified, we use the first available BT adapter.
//
- s = _instance->engine()->getDefaultAdapterAddress();
+ _adapter = _instance->engine()->getDefaultAdapterAddress();
}
- s = IceUtilInternal::toUpper(s);
+ _adapter = IceUtilInternal::toUpper(_adapter);
- DeviceAddress da;
- if(!parseDeviceAddress(s, da))
+ if(!parseDeviceAddress(_adapter, _addr.rc_bdaddr))
{
EndpointParseException ex(__FILE__, __LINE__);
- ex.str = "invalid address value `" + s + "' in endpoint " + endpoint->toString();
+ ex.str = "invalid address value `" + _adapter + "' in endpoint " + endpoint->toString();
throw ex;
}
- if(!_instance->engine()->adapterExists(s))
+ if(!_instance->engine()->adapterExists(_adapter))
{
EndpointParseException ex(__FILE__, __LINE__);
- ex.str = "no device found for `" + s + "' in endpoint " + endpoint->toString();
+ ex.str = "no device found for `" + _adapter + "' in endpoint " + endpoint->toString();
throw ex;
}
- const_cast<string&>(_addr) = s;
+ _fd = createSocket();
+ IceInternal::setBlock(_fd, false);
}
IceBT::AcceptorI::~AcceptorI()
{
+ assert(_fd == INVALID_SOCKET);
}
diff --git a/cpp/src/IceBT/AcceptorI.h b/cpp/src/IceBT/AcceptorI.h
index 496956992d0..37f3e0e593e 100644
--- a/cpp/src/IceBT/AcceptorI.h
+++ b/cpp/src/IceBT/AcceptorI.h
@@ -12,11 +12,8 @@
#include <Ice/TransceiverF.h>
#include <Ice/Acceptor.h>
-#include <IceBT/Config.h>
-#include <IceBT/EngineF.h>
#include <IceBT/InstanceF.h>
-
-#include <stack>
+#include <IceBT/Util.h>
namespace IceBT
{
@@ -36,8 +33,6 @@ public:
int effectiveChannel() const;
- void newConnection(int);
-
private:
AcceptorI(const EndpointIPtr&, const InstancePtr&, const std::string&, const std::string&, const std::string&,
@@ -48,14 +43,12 @@ private:
EndpointIPtr _endpoint;
const InstancePtr _instance;
const std::string _adapterName;
- const std::string _addr;
const std::string _uuid;
const std::string _name;
- const int _channel;
- std::string _path;
-
- IceUtil::Monitor<IceUtil::Mutex> _lock;
- std::stack<IceInternal::TransceiverPtr> _transceivers;
+ std::string _adapter;
+ SocketAddress _addr;
+ Ice::Int _backlog;
+ unsigned int _serviceHandle;
};
}
diff --git a/cpp/src/IceBT/ConnectorI.cpp b/cpp/src/IceBT/ConnectorI.cpp
index 7937ec772e0..3a2620d32d5 100644
--- a/cpp/src/IceBT/ConnectorI.cpp
+++ b/cpp/src/IceBT/ConnectorI.cpp
@@ -34,16 +34,7 @@ IceBT::ConnectorI::connect()
throw ex;
}
- assert(_fd != -1);
-
- //
- // Transceiver takes ownership of the file descriptor and connection.
- //
- IceInternal::TransceiverPtr t = new TransceiverI(_instance, new StreamSocket(_instance, _fd), _connection, _uuid);
- _fd = -1;
- _connection = 0;
-
- return t;
+ return new TransceiverI(_instance, new StreamSocket(_instance, _addr), _uuid);
}
Short
@@ -55,7 +46,7 @@ IceBT::ConnectorI::type() const
string
IceBT::ConnectorI::toString() const
{
- return _addr;
+ return addrToString(_addr);
}
bool
@@ -67,7 +58,7 @@ IceBT::ConnectorI::operator==(const IceInternal::Connector& r) const
return false;
}
- if(_addr != p->_addr)
+ if(compareAddress(_addr, p->_addr) != 0)
{
return false;
}
@@ -105,10 +96,15 @@ IceBT::ConnectorI::operator<(const IceInternal::Connector& r) const
return type() < r.type();
}
- if(_addr < p->_addr)
+ int rc = compareAddress(_addr, p->_addr);
+ if(rc < 0)
{
return true;
}
+ else if(rc > 0)
+ {
+ return false;
+ }
if(_uuid < p->_uuid)
{
@@ -131,26 +127,12 @@ IceBT::ConnectorI::operator<(const IceInternal::Connector& r) const
return _connectionId < p->_connectionId;
}
-IceBT::ConnectorI::ConnectorI(const InstancePtr& instance, SOCKET fd, const ConnectionPtr& conn, const string& addr,
- const string& uuid, Int timeout, const string& connectionId) :
+IceBT::ConnectorI::ConnectorI(const InstancePtr& instance, const SocketAddress& addr, const string& uuid, Int timeout,
+ const string& connectionId) :
_instance(instance),
- _fd(fd),
- _connection(conn),
_addr(addr),
_uuid(uuid),
_timeout(timeout),
_connectionId(connectionId)
{
}
-
-IceBT::ConnectorI::~ConnectorI()
-{
- //
- // We must close the connection and socket if we haven't passed them to a transceiver yet.
- //
- if(_fd != -1)
- {
- _connection->close();
- IceInternal::closeSocketNoThrow(_fd);
- }
-}
diff --git a/cpp/src/IceBT/ConnectorI.h b/cpp/src/IceBT/ConnectorI.h
index 3cdbd73023d..58776bd3c58 100644
--- a/cpp/src/IceBT/ConnectorI.h
+++ b/cpp/src/IceBT/ConnectorI.h
@@ -35,15 +35,11 @@ public:
private:
- ConnectorI(const InstancePtr&, SOCKET, const ConnectionPtr&, const std::string&, const std::string&, Ice::Int,
- const std::string&);
- virtual ~ConnectorI();
+ ConnectorI(const InstancePtr&, const SocketAddress&, const std::string&, Ice::Int, const std::string&);
friend class EndpointI;
const InstancePtr _instance;
- SOCKET _fd;
- ConnectionPtr _connection;
- const std::string _addr;
+ const SocketAddress _addr;
const std::string _uuid;
const Ice::Int _timeout;
const std::string _connectionId;
diff --git a/cpp/src/IceBT/EndpointI.cpp b/cpp/src/IceBT/EndpointI.cpp
index 1be9e999db5..41fa5e579bd 100644
--- a/cpp/src/IceBT/EndpointI.cpp
+++ b/cpp/src/IceBT/EndpointI.cpp
@@ -15,9 +15,10 @@
#include <IceBT/Util.h>
#include <Ice/BasicStream.h>
-#include <Ice/LocalException.h>
#include <Ice/DefaultsAndOverrides.h>
#include <Ice/HashUtil.h>
+#include <Ice/LocalException.h>
+#include <Ice/Logger.h>
#include <Ice/Object.h>
#include <Ice/Properties.h>
#include <IceUtil/StringUtil.h>
@@ -39,7 +40,7 @@ IceBT::EndpointI::EndpointI(const InstancePtr& instance, const string& addr, con
_connectionId(connectionId),
_compress(compress),
_hashValue(0),
- _connectPending(false)
+ _findPending(false)
{
hashInit();
}
@@ -50,7 +51,7 @@ IceBT::EndpointI::EndpointI(const InstancePtr& instance) :
_timeout(instance->defaultTimeout()),
_compress(false),
_hashValue(0),
- _connectPending(false)
+ _findPending(false)
{
}
@@ -60,7 +61,7 @@ IceBT::EndpointI::EndpointI(const InstancePtr& instance, IceInternal::BasicStrea
_timeout(-1),
_compress(false),
_hashValue(0),
- _connectPending(false)
+ _findPending(false)
{
//
// _name and _channel are not marshaled.
@@ -174,15 +175,21 @@ IceBT::EndpointI::transceiver() const
}
void
-IceBT::EndpointI::connectors_async(EndpointSelectionType selType, const IceInternal::EndpointI_connectorsPtr& cb) const
+IceBT::EndpointI::connectors_async(EndpointSelectionType, const IceInternal::EndpointI_connectorsPtr& cb) const
{
IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
- if(!_connectPending)
+ if(!_findPending)
{
assert(_callbacks.empty());
- _instance->engine()->connect(_addr, _uuid, new ConnectCallbackI(const_cast<EndpointI*>(this)));
- const_cast<bool&>(_connectPending) = true;
+ const_cast<bool&>(_findPending) = true;
+ if(_instance->traceLevel() > 1)
+ {
+ ostringstream ostr;
+ ostr << "searching for service " << _uuid << " at " << _addr;
+ _instance->logger()->trace(_instance->traceCategory(), ostr.str());
+ }
+ _instance->engine()->findService(_addr, _uuid, new FindCallbackI(const_cast<EndpointI*>(this)));
}
const_cast<vector<IceInternal::EndpointI_connectorsPtr>&>(_callbacks).push_back(cb);
@@ -596,25 +603,19 @@ IceBT::EndpointI::checkOption(const string& option, const string& argument, cons
}
void
-IceBT::EndpointI::connectCompleted(int fd, const ConnectionPtr& conn)
+IceBT::EndpointI::findCompleted(int channel)
{
- //
- // We are responsible for closing the given file descriptor and connection.
- //
-
vector<IceInternal::ConnectorPtr> connectors;
-
- if(fd != -1)
- {
- connectors.push_back(new ConnectorI(_instance, fd, conn, _addr, _uuid, _timeout, _connectionId));
- }
+ assert(channel >= 0);
+ connectors.push_back(new ConnectorI(_instance, createAddr(_addr, channel), _uuid, _timeout, _connectionId));
vector<IceInternal::EndpointI_connectorsPtr> callbacks;
+
{
IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
assert(!_callbacks.empty());
callbacks.swap(_callbacks);
- _connectPending = false;
+ _findPending = false;
}
for(vector<IceInternal::EndpointI_connectorsPtr>::iterator p = callbacks.begin(); p != callbacks.end(); ++p)
@@ -624,38 +625,20 @@ IceBT::EndpointI::connectCompleted(int fd, const ConnectionPtr& conn)
}
void
-IceBT::EndpointI::connectFailed(const Ice::LocalException& ex)
+IceBT::EndpointI::findException(const LocalException& ex)
{
vector<IceInternal::EndpointI_connectorsPtr> callbacks;
+
{
IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
assert(!_callbacks.empty());
callbacks.swap(_callbacks);
- _connectPending = false;
+ _findPending = false;
}
- //
- // NoEndpointException means the device address was unknown, so we just return an empty list of
- // connectors (similar to a DNS lookup failure).
- //
- try
- {
- ex.ice_throw();
- }
- catch(const Ice::NoEndpointException&)
- {
- vector<IceInternal::ConnectorPtr> connectors;
- for(vector<IceInternal::EndpointI_connectorsPtr>::iterator p = callbacks.begin(); p != callbacks.end(); ++p)
- {
- (*p)->connectors(connectors);
- }
- }
- catch(...)
+ for(vector<IceInternal::EndpointI_connectorsPtr>::iterator p = callbacks.begin(); p != callbacks.end(); ++p)
{
- for(vector<IceInternal::EndpointI_connectorsPtr>::iterator p = callbacks.begin(); p != callbacks.end(); ++p)
- {
- (*p)->exception(ex);
- }
+ (*p)->exception(ex);
}
}
diff --git a/cpp/src/IceBT/EndpointI.h b/cpp/src/IceBT/EndpointI.h
index db2e0492567..7d895812df3 100644
--- a/cpp/src/IceBT/EndpointI.h
+++ b/cpp/src/IceBT/EndpointI.h
@@ -65,33 +65,31 @@ private:
void hashInit();
bool checkOption(const std::string&, const std::string&, const std::string&);
- void connectCompleted(int, const ConnectionPtr&);
- void connectFailed(const Ice::LocalException&);
+ void findCompleted(int);
+ void findException(const Ice::LocalException&);
- class ConnectCallbackI : public ConnectCallback
+ class FindCallbackI : public FindServiceCallback
{
public:
- ConnectCallbackI(const EndpointIPtr& endpoint) :
- _endpoint(endpoint)
+ FindCallbackI(const EndpointIPtr& e) :
+ _endpoint(e)
{
}
- virtual void completed(int fd, const ConnectionPtr& conn)
+ virtual void completed(int channel)
{
- _endpoint->connectCompleted(fd, conn);
+ _endpoint->findCompleted(channel);
}
- virtual void failed(const Ice::LocalException& ex)
+ virtual void exception(const Ice::LocalException& ex)
{
- _endpoint->connectFailed(ex);
+ _endpoint->findException(ex);
}
- private:
-
EndpointIPtr _endpoint;
};
- friend class ConnectCallbackI;
+ friend class FindCallbackI;
const InstancePtr _instance;
const std::string _addr;
@@ -103,7 +101,7 @@ private:
const bool _compress;
const Ice::Int _hashValue;
IceUtil::Monitor<IceUtil::Mutex> _lock;
- bool _connectPending;
+ bool _findPending;
std::vector<IceInternal::EndpointI_connectorsPtr> _callbacks;
};
diff --git a/cpp/src/IceBT/Engine.cpp b/cpp/src/IceBT/Engine.cpp
index 14dc424b198..878deec1f73 100644
--- a/cpp/src/IceBT/Engine.cpp
+++ b/cpp/src/IceBT/Engine.cpp
@@ -14,6 +14,7 @@
#include <IceUtil/StringUtil.h>
#include <IceUtil/Thread.h>
#include <IceUtil/UUID.h>
+#include <IceXML/Parser.h>
#include <stack>
@@ -23,306 +24,59 @@ using namespace IceBT;
IceUtil::Shared* IceBT::upCast(IceBT::Engine* p) { return p; }
-namespace IceBT
-{
-
-class ConnectionI;
-typedef IceUtil::Handle<ConnectionI> ConnectionIPtr;
+typedef map<string, DBus::VariantValuePtr> VariantMap;
-//
-// ConnectionI implements IceBT::Connection and encapsulates a DBus connection along with
-// some additional state.
-//
-class ConnectionI : public Connection
+namespace
{
-public:
-
- ConnectionI(const DBus::ConnectionPtr& conn, const string& devicePath, const string& uuid) :
- _connection(conn),
- _devicePath(devicePath),
- _uuid(uuid)
- {
- }
-
- DBus::ConnectionPtr dbusConnection() const
- {
- return _connection;
- }
+void extractProperties(const DBus::ValuePtr& v, VariantMap& vm)
+{
//
- // Blocking close.
+ // The given value is a dictionary structured like this:
+ //
+ // Key: Property name
+ // Value: Property value (variant)
//
- virtual void close()
- {
- try
- {
- //
- // Invoke DisconnectProfile to terminate the client-side connection.
- //
- DBus::MessagePtr msg =
- DBus::Message::createCall("org.bluez", _devicePath, "org.bluez.Device1", "DisconnectProfile");
- msg->write(new DBus::StringValue(_uuid));
- DBus::AsyncResultPtr r = _connection->callAsync(msg);
- r->waitUntilFinished(); // Block until the call completes.
- }
- catch(const DBus::Exception&)
- {
- // Ignore.
- }
-
- try
- {
- _connection->close();
- }
- catch(const DBus::Exception&)
- {
- // Ignore.
- }
- }
-
-private:
-
- DBus::ConnectionPtr _connection;
- string _devicePath;
- string _uuid;
-};
-
-//
-// Profile is an abstract base class representing a Bluetooth "profile". We have to register a DBus
-// profile object for a UUID in order to receive connection notifications. This is necessary for both
-// outgoing and incoming connections.
-//
-class Profile : public DBus::Service
-{
-public:
-
- virtual void handleMethodCall(const DBus::ConnectionPtr& conn, const DBus::MessagePtr& m)
- {
- string member = m->getMember();
- if(member == "Release")
- {
- //
- // Ignore - no reply necessary.
- //
- }
- else if(member == "NewConnection")
- {
- vector<DBus::ValuePtr> values = m->readAll();
- assert(values.size() == 3);
-
- //
- // This argument is the Unix file descriptor for the new connection.
- //
- DBus::UnixFDValuePtr fd = DBus::UnixFDValuePtr::dynamicCast(values[1]);
- assert(fd);
-
- try
- {
- //
- // Send an empty reply.
- //
- DBus::MessagePtr ret = DBus::Message::createReturn(m);
- conn->sendAsync(ret);
- }
- catch(const DBus::Exception&)
- {
- // Ignore.
- }
-
- try
- {
- newConnection(fd->v);
- }
- catch(...)
- {
- // Ignore.
- }
- }
- else if(member == "RequestDisconnection")
- {
- try
- {
- //
- // Send an empty reply.
- //
- DBus::MessagePtr ret = DBus::Message::createReturn(m);
- conn->sendAsync(ret);
- }
- catch(const DBus::Exception&)
- {
- // Ignore.
- }
-
- //
- // Ignore disconnect requests.
- //
- }
- }
-
-protected:
-
- Profile() {}
-
- virtual void newConnection(int) = 0;
-};
-typedef IceUtil::Handle<Profile> ProfilePtr;
-
-//
-// ClientProfile represents an outgoing connection profile.
-//
-class ClientProfile : public Profile
-{
-public:
-
- ClientProfile(const ConnectionPtr& conn, const ConnectCallbackPtr& cb) :
- _connection(conn),
- _callback(cb)
- {
- }
-
- ~ClientProfile()
- {
- }
-
-protected:
- virtual void newConnection(int fd)
+ assert(v->getType()->getKind() == DBus::Type::KindArray);
+ DBus::ArrayValuePtr props = DBus::ArrayValuePtr::dynamicCast(v);
+ for(vector<DBus::ValuePtr>::const_iterator s = props->elements.begin(); s != props->elements.end(); ++s)
{
- //
- // The callback assumes ownership of the file descriptor and connection.
- //
- _callback->completed(fd, _connection);
- _connection = 0; // Remove circular reference.
- _callback = 0;
+ assert((*s)->getType()->getKind() == DBus::Type::KindDictEntry);
+ DBus::DictEntryValuePtr pe = DBus::DictEntryValuePtr::dynamicCast(*s);
+ assert(pe->key->getType()->getKind() == DBus::Type::KindString);
+ DBus::StringValuePtr propName = DBus::StringValuePtr::dynamicCast(pe->key);
+ assert(pe->value->getType()->getKind() == DBus::Type::KindVariant);
+ vm[propName->v] = DBus::VariantValuePtr::dynamicCast(pe->value);
}
+}
-private:
-
- ConnectionPtr _connection;
- ConnectCallbackPtr _callback;
-};
-typedef IceUtil::Handle<ClientProfile> ClientProfilePtr;
+}
-//
-// ServerProfile represents an incoming connection profile.
-//
-class ServerProfile : public Profile
+namespace IceBT
{
-public:
-
- ServerProfile(const ProfileCallbackPtr& cb) :
- _callback(cb)
- {
- }
-
-protected:
-
- virtual void newConnection(int fd)
- {
- _callback->newConnection(fd);
- }
-
-private:
-
- ProfileCallbackPtr _callback;
-};
-typedef IceUtil::Handle<ServerProfile> ServerProfilePtr;
//
-// Engine delegates to BluetoothService. It encapsulates a snapshot of the "objects" managed by the
-// DBus Bluetooth daemon. These objects include local Bluetooth adapters, paired devices, etc.
+// Engine delegates to BluetoothService.
//
class BluetoothService : public DBus::Filter
{
public:
- typedef map<string, DBus::VariantValuePtr> VariantMap;
typedef map<string, VariantMap> InterfacePropertiesMap;
- struct RemoteDevice
- {
- RemoteDevice()
- {
- }
-
- RemoteDevice(const VariantMap& m) :
- properties(m)
- {
- }
-
- string getAddress() const
- {
- string addr;
- VariantMap::const_iterator i = properties.find("Address");
- if(i != properties.end())
- {
- DBus::StringValuePtr str = DBus::StringValuePtr::dynamicCast(i->second->v);
- assert(str);
- addr = str->v;
- }
- return IceUtilInternal::toUpper(addr);
- }
-
- string getAdapter() const
- {
- string adapter;
- VariantMap::const_iterator i = properties.find("Adapter");
- if(i != properties.end())
- {
- DBus::ObjectPathValuePtr path = DBus::ObjectPathValuePtr::dynamicCast(i->second->v);
- assert(path);
- adapter = path->v;
- }
- return adapter;
- }
-
- VariantMap properties;
- };
-
- struct Adapter
- {
- Adapter()
- {
- }
-
- Adapter(const VariantMap& p) :
- properties(p)
- {
- }
-
- string getAddress() const
- {
- string addr;
- VariantMap::const_iterator i = properties.find("Address");
- if(i != properties.end())
- {
- DBus::StringValuePtr str = DBus::StringValuePtr::dynamicCast(i->second->v);
- assert(str);
- addr = str->v;
- }
- return IceUtilInternal::toUpper(addr);
- }
-
- VariantMap properties;
- vector<DiscoveryCallbackPtr> callbacks;
- };
-
- typedef map<string, RemoteDevice> RemoteDeviceMap; // Key is the object path.
- typedef map<string, Adapter> AdapterMap; // Key is the object path.
-
- BluetoothService()
+ BluetoothService() :
+ _destroyed(false)
{
DBus::initThreads();
try
{
//
- // Block while we establish a DBus connection and retrieve a snapshot of the managed objects
- // from the Bluetooth service.
+ // Block while we establish a DBus connection.
//
_dbusConnection = DBus::Connection::getSystemBus();
_dbusConnection->addFilter(this);
- getManagedObjects();
}
catch(const DBus::Exception& ex)
{
@@ -343,122 +97,84 @@ public:
string intf = msg->getInterface();
string member = msg->getMember();
- if(intf == "org.freedesktop.DBus.ObjectManager" && member == "InterfacesAdded")
+ if(intf == "org.bluez.Adapter" && member == "DeviceFound")
{
//
- // The InterfacesAdded signal contains two values:
+ // The DeviceFound signal contains two values:
//
- // OBJPATH obj_path
- // DICT<STRING,DICT<STRING,VARIANT>> interfaces_and_properties
+ // STRING address
+ // DICT<STRING,VARIANT> properties
//
-
vector<DBus::ValuePtr> values = msg->readAll();
assert(values.size() == 2);
- DBus::ObjectPathValuePtr path = DBus::ObjectPathValuePtr::dynamicCast(values[0]);
- assert(path);
+ DBus::StringValuePtr addr = DBus::StringValuePtr::dynamicCast(values[0]);
+ assert(addr);
+ VariantMap props;
+ extractProperties(values[1], props);
- InterfacePropertiesMap interfaceProps;
- extractInterfaceProperties(values[1], interfaceProps);
+ vector<DiscoveryCallbackPtr> callbacks;
- InterfacePropertiesMap::iterator p = interfaceProps.find("org.bluez.Device1");
- if(p != interfaceProps.end())
{
- //
- // A remote device was added.
- //
- deviceAdded(path->v, p->second);
- }
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
- p = interfaceProps.find("org.bluez.Adapter1");
- if(p != interfaceProps.end())
- {
- //
- // A local Bluetooth adapter was added.
- //
- adapterAdded(path->v, p->second);
+ map<string, vector<DiscoveryCallbackPtr> >::iterator p = _discoveryCallbacks.find(msg->getPath());
+ if(p != _discoveryCallbacks.end())
+ {
+ callbacks = p->second;
+ }
}
- return true;
- }
- else if(intf == "org.freedesktop.DBus.ObjectManager" && member == "InterfacesRemoved")
- {
- //
- // The InterfacesRemoved signal contains two values:
- //
- // OBJPATH obj_path
- // ARRAY<STRING> interfaces
- //
-
- vector<DBus::ValuePtr> values = msg->readAll();
- assert(values.size() == 2);
- DBus::ObjectPathValuePtr path = DBus::ObjectPathValuePtr::dynamicCast(values[0]);
- assert(path);
- DBus::ArrayValuePtr ifaces = DBus::ArrayValuePtr::dynamicCast(values[1]);
- assert(ifaces);
-
- for(vector<DBus::ValuePtr>::const_iterator q = ifaces->elements.begin(); q != ifaces->elements.end(); ++q)
+ if(!callbacks.empty())
{
- assert((*q)->getType()->getKind() == DBus::Type::KindString);
- DBus::StringValuePtr ifaceName = DBus::StringValuePtr::dynamicCast(*q);
-
- //
- // A remote device was removed.
- //
- if(ifaceName->v == "org.bluez.Device1")
+ PropertyMap pm; // Convert to string-string map.
+ for(VariantMap::const_iterator p = props.begin(); p != props.end(); ++p)
{
- deviceRemoved(path->v);
+ pm[p->first] = p->second->toString();
}
- else if(ifaceName->v == "org.bluez.Adapter1")
+ for(vector<DiscoveryCallbackPtr>::iterator p = callbacks.begin(); p != callbacks.end(); ++p)
{
- adapterRemoved(path->v);
+ try
+ {
+ (*p)->discovered(addr->v, pm);
+ }
+ catch(...)
+ {
+ }
}
}
return true;
}
- else if(intf == "org.freedesktop.DBus.Properties" && member == "PropertiesChanged")
+ else if(intf == "org.bluez.Adapter" && member == "PropertyChanged")
{
//
- // The PropertiesChanged signal contains three values:
+ // The PropertyChanged signal contains two values:
//
- // STRING interface_name
- // DICT<STRING,VARIANT> changed_properties
- // ARRAY<STRING> invalidated_properties
+ // STRING name
+ // VARIANT value
//
-
vector<DBus::ValuePtr> values = msg->readAll();
- assert(values.size() == 3);
- DBus::StringValuePtr iface = DBus::StringValuePtr::dynamicCast(values[0]);
- assert(iface);
-
- if(iface->v != "org.bluez.Device1" && iface->v != "org.bluez.Adapter1")
- {
- return false;
- }
-
- VariantMap changed;
- extractProperties(values[1], changed);
-
- DBus::ArrayValuePtr a = DBus::ArrayValuePtr::dynamicCast(values[2]);
- assert(a);
- vector<string> removedNames;
- for(vector<DBus::ValuePtr>::const_iterator p = a->elements.begin(); p != a->elements.end(); ++p)
- {
- DBus::StringValuePtr sv = DBus::StringValuePtr::dynamicCast(*p);
- assert(sv);
- removedNames.push_back(sv->v);
- }
+ assert(values.size() == 2);
+ DBus::StringValuePtr name = DBus::StringValuePtr::dynamicCast(values[0]);
+ assert(name);
+ if(name->v == "Discovering")
+ {
+ DBus::VariantValuePtr v = DBus::VariantValuePtr::dynamicCast(values[1]);
+ assert(v);
+ DBus::BooleanValuePtr b = DBus::BooleanValuePtr::dynamicCast(v->v);
+ assert(b);
+ if(!b->v)
+ {
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
- if(iface->v == "org.bluez.Device1")
- {
- deviceChanged(msg->getPath(), changed, removedNames);
- }
- else
- {
- adapterChanged(msg->getPath(), changed, removedNames);
+ map<string, vector<DiscoveryCallbackPtr> >::iterator p = _discoveryCallbacks.find(msg->getPath());
+ if(p != _discoveryCallbacks.end())
+ {
+ _discoveryCallbacks.erase(p);
+ }
+ }
}
-
- return true;
+ return false;
}
return false;
@@ -466,118 +182,131 @@ public:
string getDefaultAdapterAddress() const
{
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+ string path = getDefaultAdapter();
+ VariantMap props = getAdapterProperties(path);
- //
- // Return the device address of the default local adapter.
- //
- // TBD: Be smarter about this? E.g., consider the state of the Powered property?
- //
- if(!_adapters.empty())
+ VariantMap::const_iterator i = props.find("Address");
+ if(i != props.end())
{
- return _adapters.begin()->second.getAddress();
+ DBus::StringValuePtr str = DBus::StringValuePtr::dynamicCast(i->second->v);
+ assert(str);
+ return IceUtilInternal::toUpper(str->v);
}
- throw BluetoothException(__FILE__, __LINE__, "no Bluetooth adapter found");
+ throw BluetoothException(__FILE__, __LINE__, "no default Bluetooth adapter found");
}
bool adapterExists(const string& addr) const
{
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+ string path = findAdapter(addr);
+ return !path.empty();
+ }
- //
- // Check if a local adapter exists with the given device address.
- //
- for(AdapterMap::const_iterator p = _adapters.begin(); p != _adapters.end(); ++p)
+ unsigned int addService(const string& addr, const string& name, const string& uuid, int channel)
+ {
+ string path = findAdapter(addr);
+
+ if(path.empty())
{
- if(addr == p->second.getAddress())
- {
- return true;
- }
+ throw BluetoothException(__FILE__, __LINE__, "no Bluetooth adapter found matching address " + addr);
}
- return false;
- }
-
- //
- // Calling registerProfile will advertise a service (SDP) profile with the Bluetooth daemon.
- //
- string registerProfile(const string& uuid, const string& name, int channel, const ProfileCallbackPtr& cb)
- {
//
- // As a subclass of DBus::Service, the ServerProfile object will receive DBus method
- // invocations for a given object path.
- //
- ProfilePtr profile = new ServerProfile(cb);
-
- string path = generatePath();
+ // Compose an XML record.
+ //
+ ostringstream ostr;
+ ostr << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" << endl
+ << "<record>" << endl
+ << " <attribute id=\"0x0001\">" << endl // UUID
+ << " <sequence>" << endl
+ << " <uuid value=\"" << uuid << "\" />" << endl
+ << " </sequence>" << endl
+ << " </attribute>" << endl
+ << " <attribute id=\"0x0004\">" << endl
+ << " <sequence>" << endl
+ << " <sequence>" << endl
+ << " <uuid value=\"0x0100\" />" << endl // L2CAP
+ << " </sequence>" << endl
+ << " <sequence>" << endl
+ << " <uuid value=\"0x0003\" />" << endl // RFCOMM channel
+ << " <uint8 value=\"0x" << hex << channel << dec << "\" />" << endl
+ << " </sequence>" << endl
+ << " </sequence>" << endl
+ << " </attribute>" << endl
+ << " <attribute id=\"0x0005\">" << endl
+ << " <sequence>" << endl
+ << " <uuid value=\"0x1002\" />" << endl
+ << " </sequence>" << endl
+ << " </attribute>" << endl;
+ if(!name.empty())
+ {
+ ostr << " <attribute id=\"0x0100\">" << endl
+ << " <text value=\"" << name << "\" />" << endl
+ << " </attribute>" << endl;
+ }
+ ostr << "</record>";
try
{
- DBus::AsyncResultPtr ar = registerProfileImpl(_dbusConnection, path, uuid, name, channel, profile);
- DBus::MessagePtr reply = ar->waitUntilFinished(); // Block until finished.
- if(reply->isError())
- {
- reply->throwException();
- }
+ DBus::MessagePtr reply = call(path, "org.bluez.Service", "AddRecord", new DBus::StringValue(ostr.str()));
+ DBus::ValuePtr v = reply->read();
+ DBus::Uint32ValuePtr handle = DBus::Uint32ValuePtr::dynamicCast(v);
+ return handle->v;
}
catch(const DBus::Exception& ex)
{
throw BluetoothException(__FILE__, __LINE__, ex.reason);
}
-
- return path;
}
- void unregisterProfile(const string& path)
+ void findService(const string& addr, const string& uuid, const FindServiceCallbackPtr& cb)
{
- try
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+
+ if(_destroyed)
{
- //
- // Block while we unregister the profile.
- //
- DBus::AsyncResultPtr ar = unregisterProfileImpl(_dbusConnection, path);
- ar->waitUntilFinished();
- DBus::MessagePtr reply = ar->getReply();
- _dbusConnection->removeService(path);
- if(reply->isError())
- {
- reply->throwException();
- }
+ cb->exception(CommunicatorDestroyedException(__FILE__, __LINE__));
}
- catch(const DBus::Exception& ex)
+ else
{
- throw BluetoothException(__FILE__, __LINE__, ex.reason);
+ IceUtil::ThreadPtr t = new FindServiceThread(this, addr, uuid, cb);
+ _threads.push_back(t);
+ t->start();
}
}
- void connect(const string& addr, const string& uuid, const ConnectCallbackPtr& cb)
+ void removeService(const string& addr, unsigned int handle)
{
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+ string path = findAdapter(addr);
- //
- // Start a thread to establish the connection.
- //
- IceUtil::ThreadPtr t = new ConnectThread(this, addr, uuid, cb);
- _connectThreads.push_back(t);
- t->start();
+ if(path.empty())
+ {
+ throw BluetoothException(__FILE__, __LINE__, "no Bluetooth adapter found matching address " + addr);
+ }
+
+ try
+ {
+ call(path, "org.bluez.Service", "RemoveRecord", new DBus::Uint32Value(handle));
+ }
+ catch(const DBus::Exception& ex)
+ {
+ if(ex.reason.find("DoesNotExist") != string::npos)
+ {
+ throw BluetoothException(__FILE__, __LINE__, ex.reason);
+ }
+ }
}
void startDiscovery(const string& addr, const DiscoveryCallbackPtr& cb)
{
string path;
-
+ if(addr.empty())
{
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-
- for(AdapterMap::iterator p = _adapters.begin(); p != _adapters.end(); ++p)
- {
- if(p->second.getAddress() == IceUtilInternal::toUpper(addr))
- {
- path = p->first;
- p->second.callbacks.push_back(cb);
- }
- }
+ path = getDefaultAdapter();
+ }
+ else
+ {
+ path = findAdapter(addr);
}
if(path.empty())
@@ -585,18 +314,20 @@ public:
throw BluetoothException(__FILE__, __LINE__, "no Bluetooth adapter found matching address " + addr);
}
- //
- // Invoke StartDiscovery() on the adapter object.
- //
- try
{
- DBus::MessagePtr msg = DBus::Message::createCall("org.bluez", path, "org.bluez.Adapter1", "StartDiscovery");
- DBus::AsyncResultPtr r = _dbusConnection->callAsync(msg);
- DBus::MessagePtr reply = r->waitUntilFinished();
- if(reply->isError())
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+
+ map<string, vector<DiscoveryCallbackPtr> >::iterator p = _discoveryCallbacks.find(path);
+ if(p == _discoveryCallbacks.end())
{
- reply->throwException();
+ _discoveryCallbacks[path] = vector<DiscoveryCallbackPtr>();
}
+ _discoveryCallbacks[path].push_back(cb);
+ }
+
+ try
+ {
+ call(path, "org.bluez.Adapter", "StartDiscovery");
}
catch(const DBus::Exception& ex)
{
@@ -607,18 +338,13 @@ public:
void stopDiscovery(const string& addr)
{
string path;
-
+ if(addr.empty())
{
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-
- for(AdapterMap::iterator p = _adapters.begin(); p != _adapters.end(); ++p)
- {
- if(p->second.getAddress() == IceUtilInternal::toUpper(addr))
- {
- path = p->first;
- p->second.callbacks.clear();
- }
- }
+ path = getDefaultAdapter();
+ }
+ else
+ {
+ path = findAdapter(addr);
}
if(path.empty())
@@ -626,18 +352,9 @@ public:
throw BluetoothException(__FILE__, __LINE__, "no Bluetooth adapter found matching address " + addr);
}
- //
- // Invoke StopDiscovery() on the adapter object.
- //
try
{
- DBus::MessagePtr msg = DBus::Message::createCall("org.bluez", path, "org.bluez.Adapter1", "StopDiscovery");
- DBus::AsyncResultPtr r = _dbusConnection->callAsync(msg);
- DBus::MessagePtr reply = r->waitUntilFinished();
- if(reply->isError())
- {
- reply->throwException();
- }
+ call(path, "org.bluez.Adapter", "StopDiscovery");
}
catch(const DBus::Exception& ex)
{
@@ -647,17 +364,16 @@ public:
void destroy()
{
- //
- // Wait for any active connect threads to finish.
- //
- vector<IceUtil::ThreadPtr> v;
+ vector<IceUtil::ThreadPtr> threads;
{
IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
- v.swap(_connectThreads);
+
+ _destroyed = true;
+ threads.swap(_threads);
}
- for(vector<IceUtil::ThreadPtr>::iterator p = v.begin(); p != v.end(); ++p)
+ for(vector<IceUtil::ThreadPtr>::iterator p = threads.begin(); p != threads.end(); ++p)
{
(*p)->getThreadControl().join();
}
@@ -674,537 +390,423 @@ public:
}
}
- void getManagedObjects()
- {
- try
- {
- //
- // Query the Bluetooth service for its managed objects. This is a standard DBus invocation
- // with the following signature:
- //
- // org.freedesktop.DBus.ObjectManager.GetManagedObjects (
- // out DICT<OBJPATH,DICT<STRING,DICT<STRING,VARIANT>>> objpath_interfaces_and_properties);
- //
- DBus::MessagePtr msg =
- DBus::Message::createCall("org.bluez", "/", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
- DBus::AsyncResultPtr r = _dbusConnection->callAsync(msg);
- DBus::MessagePtr reply = r->waitUntilFinished();
- if(reply->isError())
- {
- reply->throwException();
- }
-
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-
- _adapters.clear();
- _remoteDevices.clear();
- _defaultAdapterAddress.clear();
-
- //
- // The return value of GetManagedObjects is a dictionary structured like this:
- //
- // Key: Object path (e.g., "/org/bluez")
- // Value: Dictionary of interfaces
- // Key: Interface name (e.g., "org.bluez.Adapter1")
- // Value: Dictionary of properties
- // Key: Property name
- // Value: Property value (variant)
- //
-
- //
- // Extract the dictionary from the reply message.
- //
- DBus::ValuePtr v = reply->read();
-
- //
- // Iterate through the dictionary and collect the objects that we need.
- //
- assert(v->getType()->getKind() == DBus::Type::KindArray);
- DBus::ArrayValuePtr a = DBus::ArrayValuePtr::dynamicCast(v);
- for(vector<DBus::ValuePtr>::const_iterator p = a->elements.begin(); p != a->elements.end(); ++p)
- {
- assert((*p)->getType()->getKind() == DBus::Type::KindDictEntry);
- DBus::DictEntryValuePtr e = DBus::DictEntryValuePtr::dynamicCast(*p);
- assert(e->key->getType()->getKind() == DBus::Type::KindObjectPath);
- DBus::ObjectPathValuePtr path = DBus::ObjectPathValuePtr::dynamicCast(e->key);
-
- assert(e->value->getType()->getKind() == DBus::Type::KindArray);
- InterfacePropertiesMap ipmap;
- extractInterfaceProperties(e->value, ipmap);
-
- InterfacePropertiesMap::iterator q;
-
- q = ipmap.find("org.bluez.Adapter1");
- if(q != ipmap.end())
- {
- //
- // org.bluez.Adapter1 is the interface for local Bluetooth adapters.
- //
- _adapters[path->v] = Adapter(q->second);
- }
-
- q = ipmap.find("org.bluez.Device1");
- if(q != ipmap.end())
- {
- //
- // org.bluez.Device1 is the interface for paired remote devices.
- //
- RemoteDevice d(q->second);
- if(!d.getAddress().empty())
- {
- _remoteDevices[path->v] = d;
- }
- }
- }
- }
- catch(const DBus::Exception& ex)
- {
- throw BluetoothException(__FILE__, __LINE__, ex.reason);
- }
- }
-
- DBus::AsyncResultPtr registerProfileImpl(const DBus::ConnectionPtr& conn, const string& path, const string& uuid,
- const string& name, int channel, const ProfilePtr& profile)
+ string getDefaultAdapter() const
{
- conn->addService(path, profile);
-
//
- // Invoke RegisterProfile on the profile manager object.
+ // The call to DefaultAdapter returns OBJ_PATH.
//
- DBus::MessagePtr msg =
- DBus::Message::createCall("org.bluez", "/org/bluez", "org.bluez.ProfileManager1", "RegisterProfile");
- vector<DBus::ValuePtr> args;
- args.push_back(new DBus::ObjectPathValue(path));
- args.push_back(new DBus::StringValue(uuid));
- DBus::DictEntryTypePtr dt =
- new DBus::DictEntryType(DBus::Type::getPrimitive(DBus::Type::KindString), new DBus::VariantType);
- DBus::TypePtr t = new DBus::ArrayType(dt);
- DBus::ArrayValuePtr options = new DBus::ArrayValue(t);
- if(!name.empty())
- {
- options->elements.push_back(
- new DBus::DictEntryValue(dt, new DBus::StringValue("Name"),
- new DBus::VariantValue(new DBus::StringValue(name))));
- }
- if(channel != -1)
- {
- options->elements.push_back(
- new DBus::DictEntryValue(dt, new DBus::StringValue("Channel"),
- new DBus::VariantValue(new DBus::Uint16Value(channel))));
- options->elements.push_back(
- new DBus::DictEntryValue(dt, new DBus::StringValue("Role"),
- new DBus::VariantValue(new DBus::StringValue("server"))));
- }
- else
- {
- options->elements.push_back(
- new DBus::DictEntryValue(dt, new DBus::StringValue("Role"),
- new DBus::VariantValue(new DBus::StringValue("client"))));
- }
- args.push_back(options);
- msg->write(args);
- return conn->callAsync(msg);
+ DBus::MessagePtr reply = call("/", "org.bluez.Manager", "DefaultAdapter");
+ DBus::ValuePtr v = reply->read();
+ DBus::ObjectPathValuePtr path = DBus::ObjectPathValuePtr::dynamicCast(v);
+ assert(path);
+ return path->v;
}
- DBus::AsyncResultPtr unregisterProfileImpl(const DBus::ConnectionPtr& conn, const string& path)
+ string findAdapter(const string& addr) const
{
//
- // Invoke UnregisterProfile on the profile manager object.
+ // The call to FindAdapter returns OBJ_PATH.
//
- DBus::MessagePtr msg =
- DBus::Message::createCall("org.bluez", "/org/bluez", "org.bluez.ProfileManager1", "UnregisterProfile");
- msg->write(new DBus::ObjectPathValue(path));
- return conn->callAsync(msg);
+ DBus::MessagePtr reply = call("/", "org.bluez.Manager", "FindAdapter", new DBus::StringValue(addr));
+ DBus::ValuePtr v = reply->read();
+ DBus::ObjectPathValuePtr path = DBus::ObjectPathValuePtr::dynamicCast(v);
+ assert(path);
+ return path->v;
}
- static string generatePath()
+ VariantMap getAdapterProperties(const string& path) const
{
//
- // Generate a unique object path. Path elements can only contain "[A-Z][a-z][0-9]_".
+ // The call to GetProperties returns
//
- string path = "/com/zeroc/P" + IceUtil::generateUUID();
- for(string::iterator p = path.begin(); p != path.end(); ++p)
- {
- if(*p == '-')
- {
- *p = '_';
- }
- }
- return path;
+ // DICT<STRING,VARIANT> properties
+ //
+ DBus::MessagePtr reply = call(path, "org.bluez.Adapter", "GetProperties");
+ DBus::ValuePtr v = reply->read();
+ VariantMap props;
+ extractProperties(v, props);
+ return props;
}
- void deviceAdded(const string& path, const VariantMap& props)
+ IceXML::NodePtr findChild(const IceXML::NodePtr& parent, const string& name) const
{
- RemoteDevice dev(props);
- if(dev.getAddress().empty())
- {
- return; // Ignore devices that don't have an Address property.
- }
-
- vector<DiscoveryCallbackPtr> callbacks;
-
+ IceXML::NodeList l = parent->getChildren();
+ for(IceXML::NodeList::iterator p = l.begin(); p != l.end(); ++p)
{
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-
- AdapterMap::iterator p = _adapters.find(dev.getAdapter());
- if(p != _adapters.end())
+ if((*p)->getName() == name)
{
- callbacks = p->second.callbacks;
+ return *p;
}
- _remoteDevices[path] = dev;
}
- if(!callbacks.empty())
+ return 0;
+ }
+
+ IceXML::NodePtr findAttribute(const IceXML::NodePtr& record, int id) const
+ {
+ IceXML::NodeList l = record->getChildren();
+ for(IceXML::NodeList::iterator p = l.begin(); p != l.end(); ++p)
{
- PropertyMap pm; // Convert to string-string map.
- for(VariantMap::const_iterator p = props.begin(); p != props.end(); ++p)
- {
- pm[p->first] = p->second->toString();
- }
- for(vector<DiscoveryCallbackPtr>::iterator p = callbacks.begin(); p != callbacks.end(); ++p)
+ if((*p)->getName() == "attribute")
{
- try
- {
- (*p)->discovered(dev.getAddress(), pm);
- }
- catch(...)
+ string sid = (*p)->getAttribute("id");
+ int i;
+ if(sscanf(sid.c_str(), "%x", &i) == 1 && i == id)
{
+ return *p;
}
}
}
+
+ return 0;
}
- void deviceChanged(const string& path, const VariantMap& changed, const vector<string>& removedProps)
+ IceXML::NodePtr findSequence(const IceXML::NodePtr& parent, const string& uuid) const
{
- vector<DiscoveryCallbackPtr> callbacks;
- string addr;
- string adapter;
- VariantMap props;
-
+ IceXML::NodeList l = parent->getChildren();
+ for(IceXML::NodeList::iterator p = l.begin(); p != l.end(); ++p)
{
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-
- RemoteDeviceMap::iterator p = _remoteDevices.find(path);
- if(p == _remoteDevices.end())
+ if((*p)->getName() == "sequence")
{
- RemoteDevice dev(changed);
- addr = dev.getAddress();
- if(!addr.empty())
+ IceXML::NodePtr u = findChild(*p, "uuid");
+ if(u)
{
- _remoteDevices[path] = dev;
- props = changed;
- adapter = dev.getAdapter();
+ string val = u->getAttribute("value");
+ if(IceUtilInternal::toUpper(val) == IceUtilInternal::toUpper(uuid))
+ {
+ return *p;
+ }
}
- }
- else
- {
- updateProperties(p->second.properties, changed, removedProps);
-
- addr = p->second.getAddress();
- if(addr.empty())
- {
- //
- // Remove the device if we don't know its address.
- //
- _remoteDevices.erase(p);
- }
- else
+ //
+ // Recursively search for nested <sequence> elements.
+ //
+ IceXML::NodePtr n = findSequence(*p, uuid);
+ if(n)
{
- props = p->second.properties;
- adapter = p->second.getAdapter();
+ return n;
}
}
-
- AdapterMap::iterator q = _adapters.find(adapter);
- if(q != _adapters.end())
- {
- callbacks = q->second.callbacks;
- }
}
- if(!addr.empty() && !callbacks.empty())
+ return 0;
+ }
+
+ IceXML::NodePtr findSequence(const IceXML::NodePtr& parent, int uuid) const
+ {
+ IceXML::NodeList l = parent->getChildren();
+ for(IceXML::NodeList::iterator p = l.begin(); p != l.end(); ++p)
{
- PropertyMap pm; // Convert to string-string map.
- for(VariantMap::iterator p = props.begin(); p != props.end(); ++p)
+ if((*p)->getName() == "sequence")
{
- pm[p->first] = p->second->toString();
- }
- for(vector<DiscoveryCallbackPtr>::iterator p = callbacks.begin(); p != callbacks.end(); ++p)
- {
- try
+ IceXML::NodePtr u = findChild(*p, "uuid");
+ if(u)
{
- (*p)->discovered(addr, pm);
+ string val = u->getAttribute("value");
+ int i;
+ if(sscanf(val.c_str(), "%x", &i) == 1 && i == uuid)
+ {
+ return *p;
+ }
}
- catch(...)
+
+ //
+ // Recursively search for nested <sequence> elements.
+ //
+ IceXML::NodePtr n = findSequence(*p, uuid);
+ if(n)
{
+ return n;
}
}
}
- }
-
- void deviceRemoved(const string& path)
- {
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-
- RemoteDeviceMap::iterator p = _remoteDevices.find(path);
- if(p != _remoteDevices.end())
- {
- _remoteDevices.erase(p);
- }
- }
-
- void adapterAdded(const string& path, const VariantMap& props)
- {
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-
- _adapters[path] = Adapter(props);
- }
-
- void adapterChanged(const string& path, const VariantMap& changed, const vector<string>& removedProps)
- {
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-
- AdapterMap::iterator p = _adapters.find(path);
- if(p == _adapters.end())
- {
- _adapters[path] = Adapter(changed);
- }
- else
- {
- updateProperties(p->second.properties, changed, removedProps);
- }
- }
-
- void adapterRemoved(const string& path)
- {
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-
- AdapterMap::iterator p = _adapters.find(path);
- if(p != _adapters.end())
- {
- _adapters.erase(p);
- }
- }
-
- void extractInterfaceProperties(const DBus::ValuePtr& v, InterfacePropertiesMap& interfaceProps)
- {
- //
- // The given value is a dictionary structured like this:
- //
- // Key: Interface name (e.g., "org.bluez.Adapter1")
- // Value: Dictionary of properties
- // Key: Property name
- // Value: Property value (variant)
- //
-
- DBus::ArrayValuePtr ifaces = DBus::ArrayValuePtr::dynamicCast(v);
- assert(ifaces);
-
- for(vector<DBus::ValuePtr>::const_iterator q = ifaces->elements.begin(); q != ifaces->elements.end(); ++q)
- {
- assert((*q)->getType()->getKind() == DBus::Type::KindDictEntry);
- DBus::DictEntryValuePtr ie = DBus::DictEntryValuePtr::dynamicCast(*q);
- assert(ie->key->getType()->getKind() == DBus::Type::KindString);
- DBus::StringValuePtr ifaceName = DBus::StringValuePtr::dynamicCast(ie->key);
-
- VariantMap pm;
- extractProperties(ie->value, pm);
-
- interfaceProps[ifaceName->v] = pm;
- }
- }
- void extractProperties(const DBus::ValuePtr& v, VariantMap& vm)
- {
- //
- // The given value is a dictionary structured like this:
- //
- // Key: Property name
- // Value: Property value (variant)
- //
-
- assert(v->getType()->getKind() == DBus::Type::KindArray);
- DBus::ArrayValuePtr props = DBus::ArrayValuePtr::dynamicCast(v);
- for(vector<DBus::ValuePtr>::const_iterator s = props->elements.begin(); s != props->elements.end(); ++s)
- {
- assert((*s)->getType()->getKind() == DBus::Type::KindDictEntry);
- DBus::DictEntryValuePtr pe = DBus::DictEntryValuePtr::dynamicCast(*s);
- assert(pe->key->getType()->getKind() == DBus::Type::KindString);
- DBus::StringValuePtr propName = DBus::StringValuePtr::dynamicCast(pe->key);
- assert(pe->value->getType()->getKind() == DBus::Type::KindVariant);
- vm[propName->v] = DBus::VariantValuePtr::dynamicCast(pe->value);
- }
+ return 0;
}
- void updateProperties(VariantMap& props, const VariantMap& changed, const vector<string>& removedProps)
+ DBus::MessagePtr call(const string& path, const string& intf, const string& member,
+ const DBus::ValuePtr& arg = 0) const
{
- //
- // Remove properties.
- //
- for(vector<string>::const_iterator q = removedProps.begin(); q != removedProps.end(); ++q)
+ DBus::MessagePtr msg = DBus::Message::createCall("org.bluez", path, intf, member);
+ if(arg)
{
- VariantMap::iterator r = props.find(*q);
- if(r != props.end())
- {
- props.erase(r);
- }
+ msg->write(arg);
}
-
- //
- // Merge changes.
- //
- for(VariantMap::const_iterator q = changed.begin(); q != changed.end(); ++q)
+ DBus::AsyncResultPtr r = _dbusConnection->callAsync(msg);
+ DBus::MessagePtr reply = r->waitUntilFinished();
+ if(reply->isError())
{
- props[q->first] = q->second;
+ reply->throwException();
}
+ return reply;
}
- void runConnectThread(const IceUtil::ThreadPtr& thread, const string& addr, const string& uuid,
- const ConnectCallbackPtr& cb)
+ void runFindService(const IceUtil::ThreadPtr& thread, const string& addr, const string& uuid,
+ const FindServiceCallbackPtr& cb)
{
- //
- // Establishing a connection is a complicated process.
- //
- // 1) Determine whether our local Bluetooth service knows about the target
- // remote device denoted by the 'addr' argument. The known remote devices
- // are included in the managed objects returned by the GetManagedObjects
- // invocation on the Bluetooth service and updated dynamically during
- // discovery.
- //
- // 2) After we find the remote device, we have to register a client profile
- // for the given UUID.
- //
- // 3) After registering the profile, we have to invoke ConnectDevice on the
- // local device object corresponding to the target address. The Bluetooth
- // service will attempt to establish a connection to the remote device.
- // If the connection succeeds, our profile object will receive a
- // NewConnection invocation that supplies the file descriptor.
- //
-
- ConnectionIPtr conn;
- bool ok = true;
+ int channel = -1;
try
{
+ const string a = IceUtilInternal::toUpper(addr);
+ const string adapter = getDefaultAdapter();
+
string devicePath;
+ bool first = true;
- //
- // Search our list of known devices for one that matches the given address.
- //
+ while(devicePath.empty())
{
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-
- for(RemoteDeviceMap::iterator p = _remoteDevices.begin(); p != _remoteDevices.end(); ++p)
+ try
{
- if(p->second.getAddress() == IceUtilInternal::toUpper(addr))
+ DBus::MessagePtr reply = call(adapter, "org.bluez.Adapter", "FindDevice", new DBus::StringValue(a));
+ DBus::ValuePtr v = reply->read();
+ DBus::ObjectPathValuePtr path = DBus::ObjectPathValuePtr::dynamicCast(v);
+ assert(path);
+ devicePath = path->v;
+ }
+ catch(const DBus::Exception& ex)
+ {
+ if(ex.reason.find("DoesNotExist") != string::npos)
{
- devicePath = p->first;
- break;
+ if(first)
+ {
+ first = false;
+ try
+ {
+ DBus::MessagePtr reply = call(adapter, "org.bluez.Adapter", "CreateDevice",
+ new DBus::StringValue(a));
+ DBus::ValuePtr v = reply->read();
+ DBus::ObjectPathValuePtr path = DBus::ObjectPathValuePtr::dynamicCast(v);
+ assert(path);
+ devicePath = path->v;
+ }
+ catch(const DBus::Exception&)
+ {
+ // Try FindDevice one more time.
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ else
+ {
+ throw BluetoothException(__FILE__, __LINE__, ex.reason);
}
}
}
- //
- // If we don't find a match, we're done.
- //
if(devicePath.empty())
{
//
- // ConnectorI handles this situation specially.
+ // Couldn't get the object path for the device.
//
- throw Ice::NoEndpointException(__FILE__, __LINE__, addr);
+ throw BluetoothException(__FILE__, __LINE__, "unable to create device for address " + addr);
}
- //
- // We have a matching device, now register a client profile.
- //
- DBus::ConnectionPtr dbusConn = DBus::Connection::getSystemBus();
- conn = new ConnectionI(dbusConn, devicePath, uuid);
+ DBus::ValuePtr services;
- ProfilePtr profile = new ClientProfile(conn, cb);
- string path = generatePath();
+ for(int iter = 0; iter < 50; ++iter)
+ {
+ {
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
- //
- // Register a client profile. Client profiles are not advertised in SDP.
- //
- DBus::AsyncResultPtr r = registerProfileImpl(dbusConn, path, uuid, string(), -1, profile);
- DBus::MessagePtr reply = r->waitUntilFinished();
- if(reply->isError())
+ if(_destroyed)
+ {
+ throw CommunicatorDestroyedException(__FILE__, __LINE__);
+ }
+ }
+
+ try
+ {
+ //
+ // Try to call DiscoverServices on the device to get the XML service records.
+ // The return value is a DICT<UINT32, STRING>.
+ //
+ DBus::MessagePtr reply = call(devicePath, "org.bluez.Device", "DiscoverServices",
+ new DBus::StringValue(""));
+ services = reply->read();
+ break;
+ }
+ catch(const DBus::Exception& ex)
+ {
+ if(ex.reason.find("InProgress") == string::npos)
+ {
+ throw BluetoothException(__FILE__, __LINE__, ex.reason);
+ }
+
+ //
+ // Another call to DiscoverServices is already in progress for the target device.
+ // Sleep a little and try again.
+ //
+ IceUtil::ThreadControl::sleep(IceUtil::Time::milliSeconds(100));
+ }
+ }
+
+ if(!services)
{
- reply->throwException();
+ throw BluetoothException(__FILE__, __LINE__, "query for services failed for address " + addr);
}
//
- // Invoke ConnectProfile to initiate the client-side connection:
- //
- // void ConnectProfile(string uuid)
+ // The returned dictionary contains an XML record for each service advertised by the
+ // remote device. We have to parse the XML and look for certain elements. Specifically,
+ // we're looking for a UUID that matches the one supplied by the caller. If we find
+ // that, we look in the same record for an RFCOMM channel.
//
- // We only care about errors from this invocation. If the connection succeeds, our
- // client profile will receive a separate NewConnection invocation.
- //
- DBus::MessagePtr msg =
- DBus::Message::createCall("org.bluez", devicePath, "org.bluez.Device1", "ConnectProfile");
- msg->write(new DBus::StringValue(uuid));
- r = dbusConn->callAsync(msg);
- reply = r->waitUntilFinished();
- if(reply->isError())
+ IceXML::DocumentPtr doc;
+ assert(services->getType()->getKind() == DBus::Type::KindArray);
+ DBus::ArrayValuePtr d = DBus::ArrayValuePtr::dynamicCast(services);
+ for(vector<DBus::ValuePtr>::const_iterator p = d->elements.begin(); p != d->elements.end(); ++p)
{
+ assert((*p)->getType()->getKind() == DBus::Type::KindDictEntry);
+ DBus::DictEntryValuePtr pe = DBus::DictEntryValuePtr::dynamicCast(*p);
+ assert(pe->value->getType()->getKind() == DBus::Type::KindString);
+ DBus::StringValuePtr rec = DBus::StringValuePtr::dynamicCast(pe->value);
+
try
{
- reply->throwException();
+ //
+ // This is what we're processing:
+ //
+ // <record>
+ // <attribute id="0x0001">
+ // <sequence>
+ // <uuid value="f6d289b4-1596-4294-ac34-f08e8adbfe5b" />
+ // </sequence>
+ // </attribute>
+ // <attribute id="0x0004">
+ // <sequence>
+ // <sequence>
+ // <uuid value="0x0100" />
+ // </sequence>
+ // <sequence>
+ // <uuid value="0x0003" />
+ // <uint8 value="0x01" />
+ // </sequence>
+ // </sequence>
+ // </attribute>
+ // ...
+ // </record>
+ //
+
+ istringstream istr(rec->v);
+ doc = IceXML::Parser::parse(istr);
+ IceXML::NodePtr record = findChild(doc, "record");
+ if(!record)
+ {
+ throw BluetoothException(__FILE__, __LINE__, "malformed XML - can't find <record>");
+ }
+
+ //
+ // Attribute 0x0001 holds the UUID. See if it matches the target UUID.
+ //
+ IceXML::NodePtr attrib = findAttribute(record, 1);
+ IceXML::NodePtr seq;
+ if(attrib)
+ {
+ seq = findSequence(attrib, uuid);
+ }
+ if(seq)
+ {
+ //
+ // We found a matching service UUID. Now look for the RFCOMM channel in the
+ // protocol descriptor list.
+ //
+ attrib = findAttribute(record, 4);
+ if(attrib)
+ {
+ seq = findSequence(attrib, 3);
+ if(seq)
+ {
+ IceXML::NodePtr ch = findChild(seq, "uint8");
+ if(ch)
+ {
+ string val = ch->getAttribute("value");
+ if(sscanf(val.c_str(), "%x", &channel) == 1)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
}
- catch(const DBus::Exception& ex)
+ catch(const IceXML::ParserException& ex)
{
- ostringstream ostr;
- ostr << "unable to establish connection to " << uuid << " at " << addr;
- if(!ex.reason.empty())
+ if(doc)
{
- ostr << ':' << endl << ex.reason;
+ doc->destroy();
}
+ ostringstream ostr;
+ ostr << ex.reason() << endl << rec->v;
throw BluetoothException(__FILE__, __LINE__, ostr.str());
}
+ catch(...)
+ {
+ if(doc)
+ {
+ doc->destroy();
+ }
+ throw;
+ }
+ }
+
+ if(doc)
+ {
+ doc->destroy();
+ }
+
+ {
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+
+ if(_destroyed)
+ {
+ throw CommunicatorDestroyedException(__FILE__, __LINE__);
+ }
}
}
catch(const DBus::Exception& ex)
{
- ok = false;
- cb->failed(BluetoothException(__FILE__, __LINE__, ex.reason));
+ cb->exception(BluetoothException(__FILE__, __LINE__, ex.reason));
}
- catch(const Ice::LocalException& ex)
+ catch(const LocalException& ex)
{
- ok = false;
- cb->failed(ex);
+ cb->exception(ex);
+ }
+ catch(const std::exception& ex)
+ {
+ cb->exception(UnknownException(__FILE__, __LINE__, ex.what()));
+ }
+ catch(...)
+ {
+ cb->exception(UnknownException(__FILE__, __LINE__, "unknown C++ exception"));
}
- //
- // Clean up.
- //
-
- if(!ok && conn)
+ if(channel != -1)
{
- conn->close();
+ cb->completed(channel);
}
- //
- // Remove the thread from the list.
- //
{
IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
- vector<IceUtil::ThreadPtr>::iterator p = find(_connectThreads.begin(), _connectThreads.end(), thread);
- assert(p != _connectThreads.end());
- _connectThreads.erase(p);
+ vector<IceUtil::ThreadPtr>::iterator p = find(_threads.begin(), _threads.end(), thread);
+ if(p != _threads.end())
+ {
+ _threads.erase(p);
+ }
}
}
- class ConnectThread : public IceUtil::Thread
+ class FindServiceThread : public IceUtil::Thread
{
public:
- ConnectThread(const BluetoothServicePtr& mo, const string& addr, const string& uuid,
- const ConnectCallbackPtr& cb) :
- _mo(mo),
+ FindServiceThread(const BluetoothServicePtr& service, const string& addr, const string& uuid,
+ const FindServiceCallbackPtr& cb) :
+ _service(service),
_addr(addr),
_uuid(uuid),
_cb(cb)
@@ -1213,27 +815,20 @@ public:
virtual void run()
{
- _mo->runConnectThread(this, _addr, _uuid, _cb);
+ _service->runFindService(this, _addr, _uuid, _cb);
}
- private:
-
- BluetoothServicePtr _mo;
+ BluetoothServicePtr _service;
string _addr;
string _uuid;
- ConnectCallbackPtr _cb;
+ FindServiceCallbackPtr _cb;
};
IceUtil::Monitor<IceUtil::Mutex> _lock;
+ bool _destroyed;
DBus::ConnectionPtr _dbusConnection;
-
- AdapterMap _adapters;
- RemoteDeviceMap _remoteDevices;
- string _defaultAdapterAddress;
- vector<IceUtil::ThreadPtr> _connectThreads;
-
- bool _discovering;
- vector<DiscoveryCallbackPtr> _discoveryCallbacks;
+ vector<IceUtil::ThreadPtr> _threads;
+ map<string, vector<DiscoveryCallbackPtr> > _discoveryCallbacks;
};
}
@@ -1277,22 +872,22 @@ IceBT::Engine::adapterExists(const string& addr) const
return _service->adapterExists(addr);
}
-string
-IceBT::Engine::registerProfile(const string& uuid, const string& name, int channel, const ProfileCallbackPtr& cb)
+unsigned int
+IceBT::Engine::addService(const string& address, const string& name, const string& uuid, int channel)
{
- return _service->registerProfile(uuid, name, channel, cb);
+ return _service->addService(address, name, uuid, channel);
}
void
-IceBT::Engine::unregisterProfile(const string& path)
+IceBT::Engine::findService(const string& address, const string& uuid, const FindServiceCallbackPtr& cb)
{
- return _service->unregisterProfile(path);
+ _service->findService(address, uuid, cb);
}
void
-IceBT::Engine::connect(const string& addr, const string& uuid, const ConnectCallbackPtr& cb)
+IceBT::Engine::removeService(const string& address, unsigned int handle)
{
- _service->connect(addr, uuid, cb);
+ _service->removeService(address, handle);
}
void
diff --git a/cpp/src/IceBT/Engine.h b/cpp/src/IceBT/Engine.h
index ac25cf9e158..fe2aba60a7d 100644
--- a/cpp/src/IceBT/Engine.h
+++ b/cpp/src/IceBT/Engine.h
@@ -19,41 +19,14 @@
namespace IceBT
{
-//
-// Notifies the transport about a new incoming connection.
-//
-class ProfileCallback : public IceUtil::Shared
-{
-public:
-
- virtual void newConnection(int) = 0;
-};
-typedef IceUtil::Handle<ProfileCallback> ProfileCallbackPtr;
-
-//
-// Represents an outgoing (client) connection. The transport must keep a reference to this object
-// and call close() when no longer needed.
-//
-class Connection : public IceUtil::Shared
+class FindServiceCallback : public IceUtil::Shared
{
public:
- virtual void close() = 0;
-
+ virtual void completed(int) = 0;
+ virtual void exception(const Ice::LocalException&) = 0;
};
-typedef IceUtil::Handle<Connection> ConnectionPtr;
-
-//
-// Callback API for an outgoing connection attempt.
-//
-class ConnectCallback : public IceUtil::Shared
-{
-public:
-
- virtual void completed(int, const ConnectionPtr&) = 0;
- virtual void failed(const Ice::LocalException&) = 0;
-};
-typedef IceUtil::Handle<ConnectCallback> ConnectCallbackPtr;
+typedef IceUtil::Handle<FindServiceCallback> FindServiceCallbackPtr;
//
// Engine encapsulates all Bluetooth activities.
@@ -72,10 +45,21 @@ public:
std::string getDefaultAdapterAddress() const;
bool adapterExists(const std::string&) const;
- std::string registerProfile(const std::string&, const std::string&, int, const ProfileCallbackPtr&);
- void unregisterProfile(const std::string&);
-
- void connect(const std::string&, const std::string&, const ConnectCallbackPtr&);
+ //
+ // Blocks while we register a service with the Bluetooth daemon. Returns a
+ // handle to the service that can be passed to removeService().
+ //
+ unsigned int addService(const std::string&, const std::string&, const std::string&, int);
+
+ //
+ // Asynchronously looks for a service at the given target device with the given UUID.
+ //
+ void findService(const std::string&, const std::string&, const FindServiceCallbackPtr&);
+
+ //
+ // Removes a service added by addService().
+ //
+ void removeService(const std::string&, unsigned int);
void startDiscovery(const std::string&, const DiscoveryCallbackPtr&);
void stopDiscovery(const std::string&);
diff --git a/cpp/src/IceBT/Makefile b/cpp/src/IceBT/Makefile
index feaa2c74114..a10e437a6dd 100644
--- a/cpp/src/IceBT/Makefile
+++ b/cpp/src/IceBT/Makefile
@@ -40,7 +40,7 @@ include $(top_srcdir)/config/Make.rules
CPPFLAGS := -I.. $(CPPFLAGS) -DICE_BT_API_EXPORTS `pkg-config --cflags dbus-1`
SLICE2CPPFLAGS := --ice --include-dir IceBT --dll-export ICE_BT_API $(SLICE2CPPFLAGS)
-LINKWITH := $(BZIP2_RPATH_LINK) -lIce -lIceUtil `pkg-config --libs dbus-1` $(CXXLIBS)
+LINKWITH := $(BZIP2_RPATH_LINK) -lIce -lIceXML -lIceUtil `pkg-config --libs dbus-1` $(CXXLIBS)
ifeq ($(STATICLIBS),yes)
$(libdir)/$(LIBNAME): $(OBJS)
diff --git a/cpp/src/IceBT/StreamSocket.cpp b/cpp/src/IceBT/StreamSocket.cpp
index 9f8e621bead..3d241f7eb7d 100644
--- a/cpp/src/IceBT/StreamSocket.cpp
+++ b/cpp/src/IceBT/StreamSocket.cpp
@@ -19,9 +19,24 @@ using namespace std;
using namespace Ice;
using namespace IceBT;
+IceBT::StreamSocket::StreamSocket(const InstancePtr& instance, const SocketAddress& addr) :
+ IceInternal::NativeInfo(createSocket()),
+ _instance(instance),
+ _addr(addr),
+ _state(StateNeedConnect)
+{
+ init();
+ if(doConnect(_fd, _addr))
+ {
+ _state = StateConnected;
+ }
+ _desc = fdToString(_fd);
+}
+
IceBT::StreamSocket::StreamSocket(const InstancePtr& instance, SOCKET fd) :
IceInternal::NativeInfo(fd),
- _instance(instance)
+ _instance(instance),
+ _state(StateConnected)
{
init();
_desc = fdToString(fd);
@@ -32,6 +47,31 @@ IceBT::StreamSocket::~StreamSocket()
assert(_fd == INVALID_SOCKET);
}
+IceInternal::SocketOperation
+IceBT::StreamSocket::connect(IceInternal::Buffer& readBuffer, IceInternal::Buffer& writeBuffer)
+{
+ if(_state == StateNeedConnect)
+ {
+ _state = StateConnectPending;
+ return IceInternal::SocketOperationConnect;
+ }
+ else if(_state <= StateConnectPending)
+ {
+ doFinishConnect(_fd);
+ _desc = fdToString(_fd);
+ _state = StateConnected;
+ }
+
+ assert(_state == StateConnected);
+ return IceInternal::SocketOperationNone;
+}
+
+bool
+IceBT::StreamSocket::isConnected()
+{
+ return _state == StateConnected;
+}
+
size_t
IceBT::StreamSocket::getSendPacketSize(size_t length)
{
diff --git a/cpp/src/IceBT/StreamSocket.h b/cpp/src/IceBT/StreamSocket.h
index a11f1cd149d..829e53a3532 100644
--- a/cpp/src/IceBT/StreamSocket.h
+++ b/cpp/src/IceBT/StreamSocket.h
@@ -24,9 +24,12 @@ class StreamSocket : public IceInternal::NativeInfo
{
public:
+ StreamSocket(const InstancePtr&, const SocketAddress&);
StreamSocket(const InstancePtr&, SOCKET);
virtual ~StreamSocket();
+ IceInternal::SocketOperation connect(IceInternal::Buffer&, IceInternal::Buffer&);
+ bool isConnected();
size_t getSendPacketSize(size_t);
size_t getRecvPacketSize(size_t);
@@ -45,8 +48,16 @@ private:
void init();
+ enum State
+ {
+ StateNeedConnect,
+ StateConnectPending,
+ StateConnected
+ };
+
const InstancePtr _instance;
SocketAddress _addr;
+ State _state;
std::string _desc;
};
typedef IceUtil::Handle<StreamSocket> StreamSocketPtr;
diff --git a/cpp/src/IceBT/TransceiverI.cpp b/cpp/src/IceBT/TransceiverI.cpp
index 8ce1241870a..c81791c3799 100644
--- a/cpp/src/IceBT/TransceiverI.cpp
+++ b/cpp/src/IceBT/TransceiverI.cpp
@@ -31,24 +31,27 @@ IceBT::TransceiverI::getNativeInfo()
IceInternal::SocketOperation
IceBT::TransceiverI::initialize(IceInternal::Buffer& readBuffer, IceInternal::Buffer& writeBuffer)
{
- return IceInternal::SocketOperationNone; // Already connected.
+ return _stream->connect(readBuffer, writeBuffer);
}
IceInternal::SocketOperation
IceBT::TransceiverI::closing(bool initiator, const Ice::LocalException&)
{
+ //
+ // Bluetooth sockets seem to need this in order to shut down in a timely fashion.
+ //
+ ::shutdown(_stream->fd(), SHUT_RDWR);
+
+ //
// If we are initiating the connection closure, wait for the peer
// to close the TCP/IP connection. Otherwise, close immediately.
+ //
return initiator ? IceInternal::SocketOperationRead : IceInternal::SocketOperationNone;
}
void
IceBT::TransceiverI::close()
{
- if(_connection)
- {
- _connection->close();
- }
_stream->close();
}
@@ -103,11 +106,9 @@ IceBT::TransceiverI::setBufferSize(int rcvSize, int sndSize)
_stream->setBufferSize(rcvSize, sndSize);
}
-IceBT::TransceiverI::TransceiverI(const InstancePtr& instance, const StreamSocketPtr& stream, const ConnectionPtr& conn,
- const string& uuid) :
+IceBT::TransceiverI::TransceiverI(const InstancePtr& instance, const StreamSocketPtr& stream, const string& uuid) :
_instance(instance),
_stream(stream),
- _connection(conn),
_uuid(uuid)
{
}
diff --git a/cpp/src/IceBT/TransceiverI.h b/cpp/src/IceBT/TransceiverI.h
index db7460e8a2b..0f8df49e9cf 100644
--- a/cpp/src/IceBT/TransceiverI.h
+++ b/cpp/src/IceBT/TransceiverI.h
@@ -11,7 +11,6 @@
#define ICE_BT_TRANSCEIVER_H
#include <IceBT/InstanceF.h>
-#include <IceBT/Engine.h>
#include <IceBT/StreamSocket.h>
#include <Ice/Transceiver.h>
@@ -42,7 +41,7 @@ public:
private:
- TransceiverI(const InstancePtr&, const StreamSocketPtr&, const ConnectionPtr&, const std::string&);
+ TransceiverI(const InstancePtr&, const StreamSocketPtr&, const std::string&);
virtual ~TransceiverI();
friend class ConnectorI;
@@ -50,7 +49,6 @@ private:
const InstancePtr _instance;
const StreamSocketPtr _stream;
- const ConnectionPtr _connection;
const std::string _uuid;
};
diff --git a/cpp/src/IceBT/Util.cpp b/cpp/src/IceBT/Util.cpp
index baf3edff738..8d5001d84fd 100644
--- a/cpp/src/IceBT/Util.cpp
+++ b/cpp/src/IceBT/Util.cpp
@@ -87,20 +87,182 @@ SocketAddress
IceBT::createAddr(const string& addr, Ice::Int channel)
{
SocketAddress ret;
- memset(&ret, 0, sizeof(ret));
ret.rc_family = AF_BLUETOOTH;
ret.rc_channel = static_cast<uint8_t>(channel);
parseDeviceAddress(addr, ret.rc_bdaddr);
return ret;
}
+SOCKET
+IceBT::createSocket()
+{
+ SOCKET fd = ::socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if(fd == INVALID_SOCKET)
+ {
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = errno;
+ throw ex;
+ }
+ return fd;
+}
+
+bool
+IceBT::doConnect(SOCKET fd, const SocketAddress& addr)
+{
+ int size = sizeof(SocketAddress);
+ assert(size != 0);
+
+repeatConnect:
+ if(::connect(fd, reinterpret_cast<const struct sockaddr*>(&addr), size) == SOCKET_ERROR)
+ {
+ if(IceInternal::interrupted())
+ {
+ goto repeatConnect;
+ }
+
+ if(IceInternal::connectInProgress())
+ {
+ return false;
+ }
+
+ IceInternal::closeSocketNoThrow(fd);
+ if(IceInternal::connectionRefused())
+ {
+ ConnectionRefusedException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ else if(IceInternal::connectFailed())
+ {
+ ConnectFailedException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ else
+ {
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ }
+ return true;
+}
+
+void
+IceBT::doFinishConnect(SOCKET fd)
+{
+ //
+ // Note: we don't close the socket if there's an exception. It's the responsibility
+ // of the caller to do so.
+ //
+
+ int val;
+ socklen_t len = static_cast<socklen_t>(sizeof(int));
+ if(::getsockopt(fd, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&val), &len) == SOCKET_ERROR)
+ {
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+
+ if(val > 0)
+ {
+ errno = val;
+ if(IceInternal::connectionRefused())
+ {
+ ConnectionRefusedException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ else if(IceInternal::connectFailed())
+ {
+ ConnectFailedException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ else
+ {
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ }
+}
+
+SocketAddress
+IceBT::doBind(SOCKET fd, const SocketAddress& addr)
+{
+ if(addr.rc_channel <= 0)
+ {
+ //
+ // Find an available by channel by looping over the valid channel numbers (1-30) until bind succeeds.
+ //
+ SocketAddress tmp = addr;
+ for(tmp.rc_channel = 1; tmp.rc_channel <= 30; ++tmp.rc_channel)
+ {
+ if(::bind(fd, reinterpret_cast<const struct sockaddr*>(&tmp), sizeof(SocketAddress)) == 0)
+ {
+ break;
+ }
+ }
+ if(tmp.rc_channel > 30)
+ {
+ IceInternal::closeSocketNoThrow(fd);
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = errno;
+ throw ex;
+ }
+ }
+ else
+ {
+ if(::bind(fd, reinterpret_cast<const struct sockaddr*>(&addr), sizeof(SocketAddress)) == SOCKET_ERROR)
+ {
+ IceInternal::closeSocketNoThrow(fd);
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = errno;
+ throw ex;
+ }
+ }
+
+ SocketAddress local;
+ socklen_t len = static_cast<socklen_t>(sizeof(SocketAddress));
+# ifdef NDEBUG
+ getsockname(fd, &local, &len);
+# else
+ int ret = getsockname(fd, reinterpret_cast<struct sockaddr*>(&local), &len);
+ assert(ret != SOCKET_ERROR);
+# endif
+ return local;
+}
+
+SOCKET
+IceBT::doAccept(SOCKET fd)
+{
+ int ret;
+
+repeatAccept:
+ if((ret = ::accept(fd, 0, 0)) == INVALID_SOCKET)
+ {
+ if(IceInternal::acceptInterrupted())
+ {
+ goto repeatAccept;
+ }
+
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = errno;
+ throw ex;
+ }
+
+ return ret;
+}
+
namespace
{
void
fdToLocalAddress(SOCKET fd, SocketAddress& addr)
{
- socklen_t len = static_cast<socklen_t>(sizeof(SocketAddress));
+ socklen_t len = static_cast<socklen_t>(sizeof(sockaddr_rc));
if(::getsockname(fd, reinterpret_cast<struct sockaddr*>(&addr), &len) == SOCKET_ERROR)
{
IceInternal::closeSocketNoThrow(fd);
@@ -113,7 +275,7 @@ fdToLocalAddress(SOCKET fd, SocketAddress& addr)
bool
fdToRemoteAddress(SOCKET fd, SocketAddress& addr)
{
- socklen_t len = static_cast<socklen_t>(sizeof(SocketAddress));
+ socklen_t len = static_cast<socklen_t>(sizeof(sockaddr_rc));
if(::getpeername(fd, reinterpret_cast<struct sockaddr*>(&addr), &len) == SOCKET_ERROR)
{
if(IceInternal::notConnected())
@@ -224,5 +386,5 @@ IceBT::compareAddress(const SocketAddress& addr1, const SocketAddress& addr2)
return 1;
}
- return ::memcmp(&addr1.rc_bdaddr, &addr2.rc_bdaddr, sizeof(DeviceAddress));
+ return ::memcmp(&addr1.rc_bdaddr, &addr2.rc_bdaddr, sizeof(bdaddr_t));
}
diff --git a/cpp/src/IceBT/Util.h b/cpp/src/IceBT/Util.h
index 1d11baaa8b7..7b0bcaee4de 100644
--- a/cpp/src/IceBT/Util.h
+++ b/cpp/src/IceBT/Util.h
@@ -26,6 +26,11 @@ std::string addrToString(const std::string&, Ice::Int);
std::string addrToString(const SocketAddress&);
SocketAddress createAddr(const std::string&, Ice::Int);
+SOCKET createSocket();
+bool doConnect(SOCKET, const SocketAddress&);
+void doFinishConnect(SOCKET);
+SocketAddress doBind(SOCKET, const SocketAddress&);
+SOCKET doAccept(SOCKET);
std::string fdToString(SOCKET);
void fdToAddressAndChannel(SOCKET, std::string&, int&, std::string&, int&);
int compareAddress(const SocketAddress&, const SocketAddress&);