summaryrefslogtreecommitdiff
path: root/cpp/src
diff options
context:
space:
mode:
authorMark Spruiell <mes@zeroc.com>2016-10-20 16:45:10 -0700
committerMark Spruiell <mes@zeroc.com>2016-10-20 16:45:10 -0700
commitedc4c248688d5e4a58be443aa95aab4226991c5d (patch)
tree60f6a8cb5917a7f325a30ea2ff824d5b748f553b /cpp/src
parentFix to allow run es5 test in Windows (diff)
downloadice-edc4c248688d5e4a58be443aa95aab4226991c5d.tar.bz2
ice-edc4c248688d5e4a58be443aa95aab4226991c5d.tar.xz
ice-edc4c248688d5e4a58be443aa95aab4226991c5d.zip
updating IceBT to BlueZ 5
Diffstat (limited to 'cpp/src')
-rwxr-xr-xcpp/src/Ice/Network.cpp25
-rwxr-xr-xcpp/src/Ice/Network.h8
-rw-r--r--cpp/src/Ice/Selector.cpp18
-rw-r--r--cpp/src/IceBT/AcceptorI.cpp153
-rw-r--r--cpp/src/IceBT/AcceptorI.h17
-rw-r--r--cpp/src/IceBT/ConnectorI.cpp28
-rw-r--r--cpp/src/IceBT/ConnectorI.h5
-rw-r--r--cpp/src/IceBT/DBus.h21
-rw-r--r--cpp/src/IceBT/EndpointI.cpp119
-rw-r--r--cpp/src/IceBT/EndpointI.h31
-rw-r--r--cpp/src/IceBT/Engine.cpp1583
-rw-r--r--cpp/src/IceBT/Engine.h60
-rw-r--r--cpp/src/IceBT/EngineF.h4
-rw-r--r--cpp/src/IceBT/InstanceF.h6
-rw-r--r--cpp/src/IceBT/Makefile.mk2
-rw-r--r--cpp/src/IceBT/PluginI.cpp6
-rw-r--r--cpp/src/IceBT/PluginI.h5
-rw-r--r--cpp/src/IceBT/StreamSocket.cpp94
-rw-r--r--cpp/src/IceBT/StreamSocket.h17
-rw-r--r--cpp/src/IceBT/TransceiverI.cpp87
-rw-r--r--cpp/src/IceBT/TransceiverI.h43
-rw-r--r--cpp/src/IceBT/Util.cpp170
-rw-r--r--cpp/src/IceBT/Util.h5
-rw-r--r--cpp/src/IceSSL/OpenSSLTransceiverI.cpp10
24 files changed, 1443 insertions, 1074 deletions
diff --git a/cpp/src/Ice/Network.cpp b/cpp/src/Ice/Network.cpp
index d46e3ab195f..2620a8a34be 100755
--- a/cpp/src/Ice/Network.cpp
+++ b/cpp/src/Ice/Network.cpp
@@ -928,6 +928,27 @@ IceInternal::NativeInfo::checkIfErrorOrCompleted(SocketOperation op, IAsyncInfo^
}
}
+#else
+
+void
+IceInternal::NativeInfo::setNewFd(SOCKET fd)
+{
+ assert(_fd == INVALID_SOCKET); // This can only be called once, when the current socket isn't set yet.
+ _newFd = fd;
+}
+
+bool
+IceInternal::NativeInfo::newFd()
+{
+ if(_newFd == INVALID_SOCKET)
+ {
+ return false;
+ }
+ assert(_fd == INVALID_SOCKET);
+ swap(_fd, _newFd);
+ return true;
+}
+
#endif
bool
@@ -1371,10 +1392,10 @@ IceInternal::closeSocket(SOCKET fd)
WSASetLastError(error);
#else
int error = errno;
-
+
# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
//
- // FreeBSD returns ECONNRESET if the underlying object was
+ // FreeBSD returns ECONNRESET if the underlying object was
// a stream socket that was shut down by the peer before all
// pending data was delivered.
//
diff --git a/cpp/src/Ice/Network.h b/cpp/src/Ice/Network.h
index f313e30a560..8c130b5a4d6 100755
--- a/cpp/src/Ice/Network.h
+++ b/cpp/src/Ice/Network.h
@@ -205,6 +205,9 @@ public:
virtual ~NativeInfo();
NativeInfo(SOCKET socketFd = INVALID_SOCKET) : _fd(socketFd)
+#if !defined(ICE_USE_IOCP) && !defined(ICE_OS_WINRT)
+ , _newFd(INVALID_SOCKET)
+#endif
{
}
@@ -234,6 +237,9 @@ public:
void queueOperation(SocketOperation, Windows::Foundation::IAsyncOperation<unsigned int>^);
void setCompletedHandler(SocketOperationCompletedHandler^);
void completed(SocketOperation);
+#else
+ bool newFd();
+ void setNewFd(SOCKET);
#endif
protected:
@@ -247,6 +253,8 @@ protected:
#elif defined(ICE_OS_WINRT)
bool checkIfErrorOrCompleted(SocketOperation, Windows::Foundation::IAsyncInfo^, bool = false);
SocketOperationCompletedHandler^ _completedHandler;
+#else
+ SOCKET _newFd;
#endif
};
typedef IceUtil::Handle<NativeInfo> NativeInfoPtr;
diff --git a/cpp/src/Ice/Selector.cpp b/cpp/src/Ice/Selector.cpp
index 808b7fe8620..2bd129cc330 100644
--- a/cpp/src/Ice/Selector.cpp
+++ b/cpp/src/Ice/Selector.cpp
@@ -78,7 +78,6 @@ Selector::initialize(EventHandler* handler)
handler->getNativeInfo()->initialize(_handle, reinterpret_cast<ULONG_PTR>(handler));
#else
EventHandlerPtr h = ICE_GET_SHARED_FROM_THIS(handler);
-
handler->getNativeInfo()->setCompletedHandler(
ref new SocketOperationCompletedHandler(
[=](int operation)
@@ -349,7 +348,20 @@ Selector::update(EventHandler* handler, SocketOperation remove, SocketOperation
NativeInfoPtr nativeInfo = handler->getNativeInfo();
if(!nativeInfo || nativeInfo->fd() == INVALID_SOCKET)
{
- return;
+ if(!nativeInfo->newFd()) // If no new FD is set, nothing to do.
+ {
+ return;
+ }
+
+ // If a new FD is set, we update the selector to add operations for the FD, there's
+ // nothing to remove from the selector because the FD wasn't previously set.
+ assert(!handler->_disabled);
+ previous = SocketOperationNone;
+ remove = SocketOperationNone;
+ if(!add)
+ {
+ return;
+ }
}
#if defined(ICE_USE_EPOLL)
@@ -835,13 +847,11 @@ Selector::checkReady(EventHandler* handler)
if(handler->_ready & ~handler->_disabled & handler->_registered)
{
_readyHandlers.insert(make_pair(ICE_GET_SHARED_FROM_THIS(handler), SocketOperationNone));
-
wakeup();
}
else
{
map<EventHandlerPtr, SocketOperation>::iterator p = _readyHandlers.find(ICE_GET_SHARED_FROM_THIS(handler));
-
if(p != _readyHandlers.end())
{
_readyHandlers.erase(p);
diff --git a/cpp/src/IceBT/AcceptorI.cpp b/cpp/src/IceBT/AcceptorI.cpp
index de94b46b7b7..a7df125ffef 100644
--- a/cpp/src/IceBT/AcceptorI.cpp
+++ b/cpp/src/IceBT/AcceptorI.cpp
@@ -8,6 +8,7 @@
// **********************************************************************
#include <IceBT/AcceptorI.h>
+#include <IceBT/Engine.h>
#include <IceBT/EndpointI.h>
#include <IceBT/Instance.h>
#include <IceBT/TransceiverI.h>
@@ -27,6 +28,30 @@ 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()
{
@@ -36,44 +61,44 @@ IceBT::AcceptorI::getNativeInfo()
void
IceBT::AcceptorI::close()
{
- try
+ if(!_path.empty())
{
- _instance->engine()->removeService(_adapter, _serviceHandle);
- }
- catch(const BluetoothException&)
- {
- // Ignore.
- }
-
- if(_fd != INVALID_SOCKET)
- {
- IceInternal::closeSocketNoThrow(_fd);
- _fd = INVALID_SOCKET;
+ try
+ {
+ _instance->engine()->unregisterProfile(_path);
+ }
+ catch(...)
+ {
+ }
}
}
IceInternal::EndpointIPtr
IceBT::AcceptorI::listen()
{
+ assert(!_uuid.empty());
+
+ //
+ // The Bluetooth daemon will select an available channel if _channel == 0.
+ //
+
try
{
- if(!_instance->engine()->adapterExists(_adapter))
- {
- throw SocketException(__FILE__, __LINE__, EADDRNOTAVAIL);
- }
- _addr = doBind(_fd, _addr);
- IceInternal::doListen(_fd, _backlog);
+ ProfileCallbackPtr cb = ICE_MAKE_SHARED(ProfileCallbackI, this);
+ _path = _instance->engine()->registerProfile(_uuid, _name, _channel, cb);
}
- catch(...)
+ catch(const BluetoothException& ex)
{
- _fd = INVALID_SOCKET;
- throw;
+ InitializationException e(__FILE__, __LINE__);
+ e.reason = "unable to register Bluetooth profile";
+ if(!ex.reason.empty())
+ {
+ e.reason += "\n" + ex.reason;
+ }
+ throw e;
}
- assert(!_uuid.empty());
-
- _serviceHandle = _instance->engine()->addService(_adapter, _name, _uuid, _addr.rc_channel);
-
+ _endpoint = _endpoint->endpoint(this);
return _endpoint;
}
@@ -90,9 +115,26 @@ IceBT::AcceptorI::accept()
throw ex;
}
- SOCKET fd = doAccept(_fd);
+ IceInternal::TransceiverPtr t;
+
+ {
+ 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 new TransceiverI(_instance, new StreamSocket(_instance, fd), _uuid);
+ return t;
}
string
@@ -104,7 +146,7 @@ IceBT::AcceptorI::protocol() const
string
IceBT::AcceptorI::toString() const
{
- return addrToString(_addr);
+ return addrToString(_addr, _channel);
}
string
@@ -126,7 +168,27 @@ IceBT::AcceptorI::toDetailedString() const
int
IceBT::AcceptorI::effectiveChannel() const
{
- return _addr.rc_channel;
+ //
+ // 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);
}
IceBT::AcceptorI::AcceptorI(const EndpointIPtr& endpoint, const InstancePtr& instance, const string& adapterName,
@@ -134,40 +196,39 @@ IceBT::AcceptorI::AcceptorI(const EndpointIPtr& endpoint, const InstancePtr& ins
_endpoint(endpoint),
_instance(instance),
_adapterName(adapterName),
+ _addr(addr),
_uuid(uuid),
_name(name),
- _serviceHandle(0)
+ _channel(channel)
{
- // 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())
+ string s = IceUtilInternal::trim(_addr);
+ if(s.empty())
{
//
// If no address was specified, we use the first available BT adapter.
//
- _adapter = _instance->engine()->getDefaultAdapterAddress();
+ s = _instance->engine()->getDefaultAdapterAddress();
}
- _adapter = IceUtilInternal::toUpper(_adapter);
+ s = IceUtilInternal::toUpper(s);
- if(!parseDeviceAddress(_adapter, _addr.rc_bdaddr))
+ DeviceAddress da;
+ if(!parseDeviceAddress(s, da))
+ {
+ EndpointParseException ex(__FILE__, __LINE__);
+ ex.str = "invalid address value `" + s + "' in endpoint " + endpoint->toString();
+ throw ex;
+ }
+ if(!_instance->engine()->adapterExists(s))
{
EndpointParseException ex(__FILE__, __LINE__);
- ex.str = "invalid address value `" + _adapter + "' in endpoint " + endpoint->toString();
+ ex.str = "no device found for `" + s + "' in endpoint " + endpoint->toString();
throw ex;
}
- _fd = createSocket();
- IceInternal::setBlock(_fd, false);
+ const_cast<string&>(_addr) = s;
}
IceBT::AcceptorI::~AcceptorI()
{
- assert(_fd == INVALID_SOCKET);
}
diff --git a/cpp/src/IceBT/AcceptorI.h b/cpp/src/IceBT/AcceptorI.h
index 493fea17249..c0ef8a16c8b 100644
--- a/cpp/src/IceBT/AcceptorI.h
+++ b/cpp/src/IceBT/AcceptorI.h
@@ -12,8 +12,11 @@
#include <Ice/TransceiverF.h>
#include <Ice/Acceptor.h>
+#include <IceBT/Config.h>
+#include <IceBT/EngineF.h>
#include <IceBT/InstanceF.h>
-#include <IceBT/Util.h>
+
+#include <stack>
namespace IceBT
{
@@ -33,6 +36,8 @@ public:
int effectiveChannel() const;
+ void newConnection(int);
+
private:
AcceptorI(const EndpointIPtr&, const InstancePtr&, const std::string&, const std::string&, const std::string&,
@@ -43,12 +48,14 @@ private:
EndpointIPtr _endpoint;
const InstancePtr _instance;
const std::string _adapterName;
+ const std::string _addr;
const std::string _uuid;
const std::string _name;
- std::string _adapter;
- SocketAddress _addr;
- Ice::Int _backlog;
- unsigned int _serviceHandle;
+ const int _channel;
+ std::string _path;
+
+ IceUtil::Monitor<IceUtil::Mutex> _lock;
+ std::stack<IceInternal::TransceiverPtr> _transceivers;
};
}
diff --git a/cpp/src/IceBT/ConnectorI.cpp b/cpp/src/IceBT/ConnectorI.cpp
index ba1ecc2b24a..128e929c140 100644
--- a/cpp/src/IceBT/ConnectorI.cpp
+++ b/cpp/src/IceBT/ConnectorI.cpp
@@ -8,14 +8,10 @@
// **********************************************************************
#include <IceBT/ConnectorI.h>
-#include <IceBT/EndpointI.h>
#include <IceBT/Instance.h>
#include <IceBT/TransceiverI.h>
-#include <IceBT/Util.h>
-#include <Ice/Communicator.h>
#include <Ice/LocalException.h>
-#include <Ice/Network.h>
using namespace std;
using namespace Ice;
@@ -34,7 +30,10 @@ IceBT::ConnectorI::connect()
throw ex;
}
- return new TransceiverI(_instance, new StreamSocket(_instance, _addr), _uuid);
+ //
+ // The transceiver handles all connection activity.
+ //
+ return new TransceiverI(_instance, _addr, _uuid);
}
Short
@@ -46,7 +45,7 @@ IceBT::ConnectorI::type() const
string
IceBT::ConnectorI::toString() const
{
- return addrToString(_addr);
+ return _addr;
}
bool
@@ -58,7 +57,7 @@ IceBT::ConnectorI::operator==(const IceInternal::Connector& r) const
return false;
}
- if(compareAddress(_addr, p->_addr) != 0)
+ if(_addr != p->_addr)
{
return false;
}
@@ -82,6 +81,12 @@ IceBT::ConnectorI::operator==(const IceInternal::Connector& r) const
}
bool
+IceBT::ConnectorI::operator!=(const IceInternal::Connector& r) const
+{
+ return !operator==(r);
+}
+
+bool
IceBT::ConnectorI::operator<(const IceInternal::Connector& r) const
{
const ConnectorI* p = dynamic_cast<const ConnectorI*>(&r);
@@ -90,15 +95,10 @@ IceBT::ConnectorI::operator<(const IceInternal::Connector& r) const
return type() < r.type();
}
- int rc = compareAddress(_addr, p->_addr);
- if(rc < 0)
+ if(_addr < p->_addr)
{
return true;
}
- else if(rc > 0)
- {
- return false;
- }
if(_uuid < p->_uuid)
{
@@ -121,7 +121,7 @@ IceBT::ConnectorI::operator<(const IceInternal::Connector& r) const
return _connectionId < p->_connectionId;
}
-IceBT::ConnectorI::ConnectorI(const InstancePtr& instance, const SocketAddress& addr, const string& uuid, Int timeout,
+IceBT::ConnectorI::ConnectorI(const InstancePtr& instance, const string& addr, const string& uuid, Int timeout,
const string& connectionId) :
_instance(instance),
_addr(addr),
diff --git a/cpp/src/IceBT/ConnectorI.h b/cpp/src/IceBT/ConnectorI.h
index b1ea99c269f..f356300710f 100644
--- a/cpp/src/IceBT/ConnectorI.h
+++ b/cpp/src/IceBT/ConnectorI.h
@@ -30,15 +30,16 @@ public:
virtual std::string toString() const;
virtual bool operator==(const IceInternal::Connector&) const;
+ virtual bool operator!=(const IceInternal::Connector&) const;
virtual bool operator<(const IceInternal::Connector&) const;
private:
- ConnectorI(const InstancePtr&, const SocketAddress&, const std::string&, Ice::Int, const std::string&);
+ ConnectorI(const InstancePtr&, const std::string&, const std::string&, Ice::Int, const std::string&);
friend class EndpointI;
const InstancePtr _instance;
- const SocketAddress _addr;
+ const std::string _addr;
const std::string _uuid;
const Ice::Int _timeout;
const std::string _connectionId;
diff --git a/cpp/src/IceBT/DBus.h b/cpp/src/IceBT/DBus.h
index 0ff914edac4..b42dfedb68e 100644
--- a/cpp/src/IceBT/DBus.h
+++ b/cpp/src/IceBT/DBus.h
@@ -511,13 +511,16 @@ public:
class AsyncResult;
typedef IceUtil::Handle<AsyncResult> AsyncResultPtr;
-class AsyncCallback : public IceUtil::Shared
+class AsyncCallback
+#ifndef ICE_CPP11_MAPPING
+ : public virtual IceUtil::Shared
+#endif
{
public:
virtual void completed(const AsyncResultPtr&) = 0;
};
-typedef IceUtil::Handle<AsyncCallback> AsyncCallbackPtr;
+ICE_DEFINE_PTR(AsyncCallbackPtr, AsyncCallback);
//
// The result of an asynchronous DBus operation.
@@ -542,7 +545,10 @@ typedef IceUtil::Handle<Connection> ConnectionPtr;
//
// Allows a subclass to intercept DBus messages.
//
-class Filter : public IceUtil::Shared
+class Filter
+#ifndef ICE_CPP11_MAPPING
+ : public virtual IceUtil::Shared
+#endif
{
public:
@@ -551,18 +557,21 @@ public:
//
virtual bool handleMessage(const ConnectionPtr&, const MessagePtr&) = 0;
};
-typedef IceUtil::Handle<Filter> FilterPtr;
+ICE_DEFINE_PTR(FilterPtr, Filter);
//
// Allows a subclass to receive DBus method invocations.
//
-class Service : public IceUtil::Shared
+class Service
+#ifndef ICE_CPP11_MAPPING
+ : public virtual IceUtil::Shared
+#endif
{
public:
virtual void handleMethodCall(const ConnectionPtr&, const MessagePtr&) = 0;
};
-typedef IceUtil::Handle<Service> ServicePtr;
+ICE_DEFINE_PTR(ServicePtr, Service);
//
// Encapsulates a DBus connection.
diff --git a/cpp/src/IceBT/EndpointI.cpp b/cpp/src/IceBT/EndpointI.cpp
index 32d0c22aa50..effbf0fc6cc 100644
--- a/cpp/src/IceBT/EndpointI.cpp
+++ b/cpp/src/IceBT/EndpointI.cpp
@@ -14,17 +14,13 @@
#include <IceBT/Instance.h>
#include <IceBT/Util.h>
-#include <Ice/OutputStream.h>
-#include <Ice/InputStream.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/Random.h>
-#include <IceUtil/StringUtil.h>
#include <Ice/UUID.h>
+#include <IceUtil/StringUtil.h>
using namespace std;
using namespace Ice;
@@ -34,19 +30,6 @@ using namespace IceBT;
IceUtil::Shared* IceBT::upCast(EndpointI* p) { return p; }
#endif
-namespace
-{
-
-struct RandomNumberGenerator : public std::unary_function<ptrdiff_t, ptrdiff_t>
-{
- ptrdiff_t operator()(ptrdiff_t d)
- {
- return IceUtilInternal::random(static_cast<int>(d));
- }
-};
-
-}
-
IceBT::EndpointI::EndpointI(const InstancePtr& instance, const string& addr, const string& uuid, const string& name,
Int channel, Int timeout, const string& connectionId, bool compress) :
_instance(instance),
@@ -57,8 +40,7 @@ IceBT::EndpointI::EndpointI(const InstancePtr& instance, const string& addr, con
_timeout(timeout),
_connectionId(connectionId),
_compress(compress),
- _hashValue(0),
- _findPending(false)
+ _hashValue(0)
{
hashInit();
}
@@ -68,8 +50,7 @@ IceBT::EndpointI::EndpointI(const InstancePtr& instance) :
_channel(0),
_timeout(instance->defaultTimeout()),
_compress(false),
- _hashValue(0),
- _findPending(false)
+ _hashValue(0)
{
}
@@ -78,8 +59,7 @@ IceBT::EndpointI::EndpointI(const InstancePtr& instance, InputStream* s) :
_channel(0),
_timeout(-1),
_compress(false),
- _hashValue(0),
- _findPending(false)
+ _hashValue(0)
{
//
// _name and _channel are not marshaled.
@@ -193,22 +173,9 @@ IceBT::EndpointI::transceiver() const
void
IceBT::EndpointI::connectors_async(EndpointSelectionType selType, const IceInternal::EndpointI_connectorsPtr& cb) const
{
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-
- if(!_findPending)
- {
- assert(_callbacks.empty());
- 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(ICE_SHARED_FROM_CONST_THIS(EndpointI), selType));
- }
-
- const_cast<vector<IceInternal::EndpointI_connectorsPtr>&>(_callbacks).push_back(cb);
+ vector<IceInternal::ConnectorPtr> connectors;
+ connectors.push_back(new ConnectorI(_instance, _addr, _uuid, _timeout, _connectionId));
+ cb->connectors(connectors);
}
IceInternal::AcceptorPtr
@@ -479,7 +446,7 @@ IceBT::EndpointI::initWithOptions(vector<string>& args, bool oaEndpoint)
}
else
{
- Ice::EndpointParseException ex(__FILE__, __LINE__);
+ EndpointParseException ex(__FILE__, __LINE__);
ex.str = "a device address must be specified using the -a option or Ice.Default.Host";
throw ex;
}
@@ -497,11 +464,11 @@ IceBT::EndpointI::initWithOptions(vector<string>& args, bool oaEndpoint)
//
// Generate a UUID for object adapters that don't specify one.
//
- const_cast<string&>(_uuid) = Ice::generateUUID();
+ const_cast<string&>(_uuid) = generateUUID();
}
else
{
- Ice::EndpointParseException ex(__FILE__, __LINE__);
+ EndpointParseException ex(__FILE__, __LINE__);
ex.str = "a UUID must be specified using the -u option";
throw ex;
}
@@ -514,7 +481,7 @@ IceBT::EndpointI::initWithOptions(vector<string>& args, bool oaEndpoint)
if(!oaEndpoint && _channel != 0)
{
- Ice::EndpointParseException ex(__FILE__, __LINE__);
+ EndpointParseException ex(__FILE__, __LINE__);
ex.str = "the -c option can only be used for object adapter endpoints";
throw ex;
}
@@ -525,8 +492,8 @@ IceBT::EndpointI::initWithOptions(vector<string>& args, bool oaEndpoint)
IceBT::EndpointIPtr
IceBT::EndpointI::endpoint(const AcceptorIPtr& acceptor) const
{
- return ICE_MAKE_SHARED(EndpointI, _instance, _addr, _uuid, _name, acceptor->effectiveChannel(), _timeout, _connectionId,
- _compress);
+ return ICE_MAKE_SHARED(EndpointI, _instance, _addr, _uuid, _name, acceptor->effectiveChannel(), _timeout,
+ _connectionId, _compress);
}
void
@@ -549,13 +516,13 @@ IceBT::EndpointI::checkOption(const string& option, const string& argument, cons
{
if(arg.empty())
{
- Ice::EndpointParseException ex(__FILE__, __LINE__);
+ EndpointParseException ex(__FILE__, __LINE__);
ex.str = "no argument provided for -a option in endpoint " + endpoint;
throw ex;
}
if(arg != "*" && !isValidDeviceAddress(arg))
{
- Ice::EndpointParseException ex(__FILE__, __LINE__);
+ EndpointParseException ex(__FILE__, __LINE__);
ex.str = "invalid argument provided for -a option in endpoint " + endpoint;
throw ex;
}
@@ -565,7 +532,7 @@ IceBT::EndpointI::checkOption(const string& option, const string& argument, cons
{
if(arg.empty())
{
- Ice::EndpointParseException ex(__FILE__, __LINE__);
+ EndpointParseException ex(__FILE__, __LINE__);
ex.str = "no argument provided for -u option in endpoint " + endpoint;
throw ex;
}
@@ -626,7 +593,7 @@ IceBT::EndpointI::checkOption(const string& option, const string& argument, cons
{
if(arg.empty())
{
- Ice::EndpointParseException ex(__FILE__, __LINE__);
+ EndpointParseException ex(__FILE__, __LINE__);
ex.str = "no argument provided for --name option in endpoint " + endpoint;
throw ex;
}
@@ -639,56 +606,6 @@ IceBT::EndpointI::checkOption(const string& option, const string& argument, cons
return true;
}
-void
-IceBT::EndpointI::findCompleted(const vector<int>& channels, EndpointSelectionType selType)
-{
- assert(!channels.empty());
-
- vector<IceInternal::ConnectorPtr> connectors;
- for(vector<int>::const_iterator p = channels.begin(); p != channels.end(); ++p)
- {
- connectors.push_back(new ConnectorI(_instance, createAddr(_addr, *p), _uuid, _timeout, _connectionId));
- }
-
- if(selType == Ice::Random && connectors.size() > 1)
- {
- RandomNumberGenerator rng;
- random_shuffle(connectors.begin(), connectors.end(), rng);
- }
-
- vector<IceInternal::EndpointI_connectorsPtr> callbacks;
-
- {
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
- assert(!_callbacks.empty());
- callbacks.swap(_callbacks);
- _findPending = false;
- }
-
- for(vector<IceInternal::EndpointI_connectorsPtr>::iterator p = callbacks.begin(); p != callbacks.end(); ++p)
- {
- (*p)->connectors(connectors);
- }
-}
-
-void
-IceBT::EndpointI::findException(const LocalException& ex)
-{
- vector<IceInternal::EndpointI_connectorsPtr> callbacks;
-
- {
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
- assert(!_callbacks.empty());
- callbacks.swap(_callbacks);
- _findPending = false;
- }
-
- for(vector<IceInternal::EndpointI_connectorsPtr>::iterator p = callbacks.begin(); p != callbacks.end(); ++p)
- {
- (*p)->exception(ex);
- }
-}
-
IceBT::EndpointInfoI::EndpointInfoI(const EndpointIPtr& endpoint) : _endpoint(endpoint)
{
}
diff --git a/cpp/src/IceBT/EndpointI.h b/cpp/src/IceBT/EndpointI.h
index ad537fd1461..0aee2e58ec3 100644
--- a/cpp/src/IceBT/EndpointI.h
+++ b/cpp/src/IceBT/EndpointI.h
@@ -73,34 +73,6 @@ private:
void hashInit();
bool checkOption(const std::string&, const std::string&, const std::string&);
- void findCompleted(const std::vector<int>&, Ice::EndpointSelectionType);
- void findException(const Ice::LocalException&);
-
- class FindCallbackI : public FindServiceCallback
- {
- public:
-
- FindCallbackI(const EndpointIPtr& e, Ice::EndpointSelectionType selType) :
- _endpoint(e),
- _selType(selType)
- {
- }
-
- virtual void completed(const std::vector<int>& channels)
- {
- _endpoint->findCompleted(channels, _selType);
- }
-
- virtual void exception(const Ice::LocalException& ex)
- {
- _endpoint->findException(ex);
- }
-
- EndpointIPtr _endpoint;
- Ice::EndpointSelectionType _selType;
- };
- friend class FindCallbackI;
-
const InstancePtr _instance;
const std::string _addr;
const std::string _uuid;
@@ -110,9 +82,6 @@ private:
const std::string _connectionId;
const bool _compress;
const Ice::Int _hashValue;
- IceUtil::Monitor<IceUtil::Mutex> _lock;
- bool _findPending;
- std::vector<IceInternal::EndpointI_connectorsPtr> _callbacks;
};
class EndpointInfoI : public EndpointInfo
diff --git a/cpp/src/IceBT/Engine.cpp b/cpp/src/IceBT/Engine.cpp
index 6d7417c14e0..88ec81f0f3e 100644
--- a/cpp/src/IceBT/Engine.cpp
+++ b/cpp/src/IceBT/Engine.cpp
@@ -13,10 +13,7 @@
#include <Ice/LocalException.h>
#include <IceUtil/StringUtil.h>
#include <IceUtil/Thread.h>
-#include <Ice/UUID.h>
-#include <IceXML/Parser.h>
-
-#include <stack>
+#include <IceUtil/UUID.h>
using namespace std;
using namespace Ice;
@@ -24,85 +21,317 @@ using namespace IceBT;
IceUtil::Shared* IceBT::upCast(IceBT::Engine* p) { return p; }
-typedef map<string, DBus::VariantValuePtr> VariantMap;
-
-namespace
+namespace IceBT
{
-void extractProperties(const DBus::ValuePtr& v, VariantMap& vm)
+class ConnectionI;
+typedef IceUtil::Handle<ConnectionI> ConnectionIPtr;
+
+//
+// ConnectionI implements IceBT::Connection and encapsulates a DBus connection along with
+// some additional state.
+//
+class ConnectionI : public Connection
{
+public:
+
+ ConnectionI(const DBus::ConnectionPtr& conn, const string& devicePath, const string& uuid) :
+ _connection(conn),
+ _devicePath(devicePath),
+ _uuid(uuid)
+ {
+ }
+
+ DBus::ConnectionPtr dbusConnection() const
+ {
+ return _connection;
+ }
+
//
- // The given value is a dictionary structured like this:
- //
- // Key: Property name
- // Value: Property value (variant)
+ // Blocking close.
//
+ 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:
- 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)
+ 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)
{
- 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);
+ 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:
-namespace IceBT
+ Profile() {}
+
+ virtual void newConnection(int) = 0;
+};
+ICE_DEFINE_PTR(ProfilePtr, Profile);
+
+//
+// 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)
+ {
+ //
+ // The callback assumes ownership of the file descriptor and connection.
+ //
+ _callback->completed(fd, _connection);
+ _connection = 0; // Remove circular reference.
+ _callback = 0;
+ }
+
+private:
+
+ ConnectionPtr _connection;
+ ConnectCallbackPtr _callback;
+};
+ICE_DEFINE_PTR(ClientProfilePtr, ClientProfile);
//
-// Engine delegates to BluetoothService.
+// ServerProfile represents an incoming connection profile.
+//
+class ServerProfile : public Profile
+{
+public:
+
+ ServerProfile(const ProfileCallbackPtr& cb) :
+ _callback(cb)
+ {
+ }
+
+protected:
+
+ virtual void newConnection(int fd)
+ {
+ _callback->newConnection(fd);
+ }
+
+private:
+
+ ProfileCallbackPtr _callback;
+};
+ICE_DEFINE_PTR(ServerProfilePtr, ServerProfile);
+
+//
+// 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.
//
class BluetoothService : public DBus::Filter
+#ifdef ICE_CPP11_MAPPING
+ , public std::enable_shared_from_this<BluetoothService>
+#endif
{
public:
+ typedef map<string, DBus::VariantValuePtr> VariantMap;
typedef map<string, VariantMap> InterfacePropertiesMap;
- BluetoothService() :
- _destroyed(false)
+ struct RemoteDevice
{
- DBus::initThreads();
+ RemoteDevice()
+ {
+ }
- try
+ RemoteDevice(const VariantMap& m) :
+ properties(m)
{
- //
- // Block while we establish a DBus connection.
- //
- _dbusConnection = DBus::Connection::getSystemBus();
}
- catch(const DBus::Exception& ex)
+
+ string getAddress() const
{
- throw BluetoothException(__FILE__, __LINE__, ex.reason);
+ 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);
}
- _dbusConnection->addFilter(this);
+ VariantMap properties;
+#ifdef ICE_CPP11_MAPPING
+ vector<function<void(const string&, const PropertyMap&)>> callbacks;
+#else
+ vector<DiscoveryCallbackPtr> callbacks;
+#endif
+ };
+
+ typedef map<string, RemoteDevice> RemoteDeviceMap; // Key is the object path.
+ typedef map<string, Adapter> AdapterMap; // Key is the object path.
+
+ void init()
+ {
+ DBus::initThreads();
try
{
//
- // Use a call DefaultAdapter() to verify that the Bluetooth daemon is present and
- // uses the expected version (BlueZ 4). If the system is running BlueZ 5, this call
- // will return the error org.freedesktop.DBus.Error.UnknownMethod.
+ // Block while we establish a DBus connection and retrieve a snapshot of the managed objects
+ // from the Bluetooth service.
//
- call("/", "org.bluez.Manager", "DefaultAdapter");
+ _dbusConnection = DBus::Connection::getSystemBus();
+ _dbusConnection->addFilter(ICE_SHARED_FROM_THIS);
+ getManagedObjects();
}
catch(const DBus::Exception& ex)
{
- if(ex.reason.find("UnknownMethod") != string::npos)
- {
- throw BluetoothException(__FILE__, __LINE__, "Bluetooth daemon uses an unsupported version");
- }
- else
- {
- throw BluetoothException(__FILE__, __LINE__, ex.reason);
- }
+ throw BluetoothException(__FILE__, __LINE__, ex.reason);
}
}
@@ -119,108 +348,122 @@ public:
string intf = msg->getInterface();
string member = msg->getMember();
- if(intf == "org.bluez.Adapter" && member == "DeviceFound")
+ if(intf == "org.freedesktop.DBus.ObjectManager" && member == "InterfacesAdded")
{
//
- // The DeviceFound signal contains two values:
+ // The InterfacesAdded signal contains two values:
//
- // STRING address
- // DICT<STRING,VARIANT> properties
+ // OBJPATH obj_path
+ // DICT<STRING,DICT<STRING,VARIANT>> interfaces_and_properties
//
+
vector<DBus::ValuePtr> values = msg->readAll();
assert(values.size() == 2);
- DBus::StringValuePtr addr = DBus::StringValuePtr::dynamicCast(values[0]);
- assert(addr);
- VariantMap props;
- extractProperties(values[1], props);
+ DBus::ObjectPathValuePtr path = DBus::ObjectPathValuePtr::dynamicCast(values[0]);
+ assert(path);
-#ifdef ICE_CPP11_MAPPING
- vector<function<void(const string&, const PropertyMap&)>> callbacks;
-#else
- vector<DiscoveryCallbackPtr> callbacks;
-#endif
+ InterfacePropertiesMap interfaceProps;
+ extractInterfaceProperties(values[1], interfaceProps);
+ InterfacePropertiesMap::iterator p = interfaceProps.find("org.bluez.Device1");
+ if(p != interfaceProps.end())
{
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-#ifdef ICE_CPP11_MAPPING
- auto p = _discoveryCallbacks.find(msg->getPath());
-#else
- map<string, vector<DiscoveryCallbackPtr> >::iterator p = _discoveryCallbacks.find(msg->getPath());
-#endif
- if(p != _discoveryCallbacks.end())
- {
- callbacks = p->second;
- }
+ //
+ // A remote device was added.
+ //
+ deviceAdded(path->v, p->second);
}
- if(!callbacks.empty())
+ p = interfaceProps.find("org.bluez.Adapter1");
+ if(p != interfaceProps.end())
{
- PropertyMap pm; // Convert to string-string map.
- for(VariantMap::const_iterator p = props.begin(); p != props.end(); ++p)
- {
- pm[p->first] = p->second->toString();
- }
-#ifdef ICE_CPP11_MAPPING
- for(const auto& discovered : callbacks)
+ //
+ // A local Bluetooth adapter was added.
+ //
+ adapterAdded(path->v, 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)
+ {
+ 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")
{
- try
- {
- discovered(addr->v, pm);
- }
- catch(...)
- {
- }
+ deviceRemoved(path->v);
}
-#else
- for(vector<DiscoveryCallbackPtr>::iterator p = callbacks.begin(); p != callbacks.end(); ++p)
+ else if(ifaceName->v == "org.bluez.Adapter1")
{
- try
- {
- (*p)->discovered(addr->v, pm);
- }
- catch(...)
- {
- }
+ adapterRemoved(path->v);
}
-#endif
}
return true;
}
- else if(intf == "org.bluez.Adapter" && member == "PropertyChanged")
+ else if(intf == "org.freedesktop.DBus.Properties" && member == "PropertiesChanged")
{
//
- // The PropertyChanged signal contains two values:
+ // The PropertiesChanged signal contains three values:
//
- // STRING name
- // VARIANT value
+ // STRING interface_name
+ // DICT<STRING,VARIANT> changed_properties
+ // ARRAY<STRING> invalidated_properties
//
+
vector<DBus::ValuePtr> values = msg->readAll();
- 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);
+ assert(values.size() == 3);
+ DBus::StringValuePtr iface = DBus::StringValuePtr::dynamicCast(values[0]);
+ assert(iface);
-#ifdef ICE_CPP11_MAPPING
- auto p = _discoveryCallbacks.find(msg->getPath());
-#else
- map<string, vector<DiscoveryCallbackPtr> >::iterator p = _discoveryCallbacks.find(msg->getPath());
-#endif
- if(p != _discoveryCallbacks.end())
- {
- _discoveryCallbacks.erase(p);
- }
- }
+ 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);
}
- return false;
+
+ if(iface->v == "org.bluez.Device1")
+ {
+ deviceChanged(msg->getPath(), changed, removedNames);
+ }
+ else
+ {
+ adapterChanged(msg->getPath(), changed, removedNames);
+ }
+
+ return true;
}
return false;
@@ -228,119 +471,119 @@ public:
string getDefaultAdapterAddress() const
{
- string path = getDefaultAdapter();
- VariantMap props = getAdapterProperties(path);
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
- VariantMap::const_iterator i = props.find("Address");
- if(i != props.end())
+ //
+ // 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())
{
- DBus::StringValuePtr str = DBus::StringValuePtr::dynamicCast(i->second->v);
- assert(str);
- return IceUtilInternal::toUpper(str->v);
+ return _adapters.begin()->second.getAddress();
}
- throw BluetoothException(__FILE__, __LINE__, "no default Bluetooth adapter found");
+ throw BluetoothException(__FILE__, __LINE__, "no Bluetooth adapter found");
}
bool adapterExists(const string& addr) const
{
- string path = findAdapter(addr);
- return !path.empty();
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+
+ //
+ // Check if a local adapter exists with the given device address.
+ //
+ for(AdapterMap::const_iterator p = _adapters.begin(); p != _adapters.end(); ++p)
+ {
+ if(addr == p->second.getAddress())
+ {
+ return true;
+ }
+ }
+
+ return false;
}
- unsigned int addService(const string& addr, const string& name, const string& uuid, int channel)
+ bool deviceExists(const string& addr) const
{
- string path = findAdapter(addr);
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
- if(path.empty())
+ //
+ // Check if a remote device exists with the given device address.
+ //
+ for(RemoteDeviceMap::const_iterator p = _remoteDevices.begin(); p != _remoteDevices.end(); ++p)
{
- throw BluetoothException(__FILE__, __LINE__, "no Bluetooth adapter found matching address " + addr);
+ if(p->second.getAddress() == IceUtilInternal::toUpper(addr))
+ {
+ return true;
+ }
}
+ 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)
+ {
//
- // 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>";
+ // As a subclass of DBus::Service, the ServerProfile object will receive DBus method
+ // invocations for a given object path.
+ //
+ ProfilePtr profile = ICE_MAKE_SHARED(ServerProfile, cb);
+
+ string path = generatePath();
try
{
- 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;
+ DBus::AsyncResultPtr ar = registerProfileImpl(_dbusConnection, path, uuid, name, channel, profile);
+ DBus::MessagePtr reply = ar->waitUntilFinished(); // Block until finished.
+ if(reply->isError())
+ {
+ reply->throwException();
+ }
}
catch(const DBus::Exception& ex)
{
throw BluetoothException(__FILE__, __LINE__, ex.reason);
}
+
+ return path;
}
- void findService(const string& addr, const string& uuid, const FindServiceCallbackPtr& cb)
+ void unregisterProfile(const string& path)
{
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-
- if(_destroyed)
+ try
{
- cb->exception(CommunicatorDestroyedException(__FILE__, __LINE__));
+ //
+ // 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();
+ }
}
- else
+ catch(const DBus::Exception& ex)
{
- IceUtil::ThreadPtr t = new FindServiceThread(this, addr, uuid, cb);
- _threads.push_back(t);
- t->start();
+ throw BluetoothException(__FILE__, __LINE__, ex.reason);
}
}
- void removeService(const string& addr, unsigned int handle)
+ void connect(const string& addr, const string& uuid, const ConnectCallbackPtr& cb)
{
- string path = findAdapter(addr);
-
- if(path.empty())
- {
- throw BluetoothException(__FILE__, __LINE__, "no Bluetooth adapter found matching address " + addr);
- }
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
- 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);
- }
- }
+ //
+ // Start a thread to establish the connection.
+ //
+ IceUtil::ThreadPtr t = new ConnectThread(ICE_SHARED_FROM_THIS, addr, uuid, cb);
+ _connectThreads.push_back(t);
+ t->start();
}
#ifdef ICE_CPP11_MAPPING
@@ -350,43 +593,41 @@ public:
#endif
{
string path;
- if(addr.empty())
- {
- path = getDefaultAdapter();
- }
- else
- {
- path = findAdapter(addr);
- }
-
- if(path.empty())
- {
- throw BluetoothException(__FILE__, __LINE__, "no Bluetooth adapter found matching address " + addr);
- }
{
IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-#ifdef ICE_CPP11_MAPPING
- auto p = _discoveryCallbacks.find(path);
- if(p == _discoveryCallbacks.end())
+ for(AdapterMap::iterator p = _adapters.begin(); p != _adapters.end(); ++p)
{
- _discoveryCallbacks[path] = vector<function<void(const string&, const PropertyMap&)>>();
- }
- _discoveryCallbacks[path].push_back(move(cb));
+ if(p->second.getAddress() == IceUtilInternal::toUpper(addr))
+ {
+ path = p->first;
+#ifdef ICE_CPP11_MAPPING
+ p->second.callbacks.push_back(move(cb));
#else
- map<string, vector<DiscoveryCallbackPtr> >::iterator p = _discoveryCallbacks.find(path);
- if(p == _discoveryCallbacks.end())
- {
- _discoveryCallbacks[path] = vector<DiscoveryCallbackPtr>();
- }
- _discoveryCallbacks[path].push_back(cb);
+ p->second.callbacks.push_back(cb);
#endif
+ }
+ }
+ }
+
+ if(path.empty())
+ {
+ throw BluetoothException(__FILE__, __LINE__, "no Bluetooth adapter found matching address " + addr);
}
+ //
+ // Invoke StartDiscovery() on the adapter object.
+ //
try
{
- call(path, "org.bluez.Adapter", "StartDiscovery");
+ 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())
+ {
+ reply->throwException();
+ }
}
catch(const DBus::Exception& ex)
{
@@ -397,13 +638,18 @@ public:
void stopDiscovery(const string& addr)
{
string path;
- if(addr.empty())
- {
- path = getDefaultAdapter();
- }
- else
+
{
- path = findAdapter(addr);
+ 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();
+ }
+ }
}
if(path.empty())
@@ -411,9 +657,18 @@ public:
throw BluetoothException(__FILE__, __LINE__, "no Bluetooth adapter found matching address " + addr);
}
+ //
+ // Invoke StopDiscovery() on the adapter object.
+ //
try
{
- call(path, "org.bluez.Adapter", "StopDiscovery");
+ 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();
+ }
}
catch(const DBus::Exception& ex)
{
@@ -421,18 +676,40 @@ public:
}
}
- void destroy()
+ DeviceMap getDevices() const
{
- vector<IceUtil::ThreadPtr> threads;
+ DeviceMap devices;
{
IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
- _destroyed = true;
- threads.swap(_threads);
+ for(RemoteDeviceMap::const_iterator p = _remoteDevices.begin(); p != _remoteDevices.end(); ++p)
+ {
+ PropertyMap pm; // Convert to string-string map.
+ for(VariantMap::const_iterator q = p->second.properties.begin(); q != p->second.properties.end(); ++q)
+ {
+ pm[q->first] = q->second->toString();
+ }
+ devices[p->second.getAddress()] = pm;
+ }
}
- for(vector<IceUtil::ThreadPtr>::iterator p = threads.begin(); p != threads.end(); ++p)
+ return devices;
+ }
+
+ void destroy()
+ {
+ //
+ // Wait for any active connect threads to finish.
+ //
+ vector<IceUtil::ThreadPtr> v;
+
+ {
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+ v.swap(_connectThreads);
+ }
+
+ for(vector<IceUtil::ThreadPtr>::iterator p = v.begin(); p != v.end(); ++p)
{
(*p)->getThreadControl().join();
}
@@ -449,464 +726,568 @@ public:
}
}
- string getDefaultAdapter() const
+ void getManagedObjects()
{
try
{
//
- // The call to DefaultAdapter returns OBJ_PATH.
+ // Query the Bluetooth service for its managed objects. This is a standard DBus invocation
+ // with the following signature:
//
- 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;
- }
- catch(const DBus::Exception& ex)
- {
- if(ex.reason.find("NoSuchAdapter") == string::npos)
+ // 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())
{
- throw BluetoothException(__FILE__, __LINE__, ex.reason);
+ reply->throwException();
}
- }
- return string();
- }
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
- string findAdapter(const string& addr) const
- {
- try
- {
+ _adapters.clear();
+ _remoteDevices.clear();
+ _defaultAdapterAddress.clear();
+
+ //
+ // The return value of GetManagedObjects is a dictionary structured like this:
//
- // The call to FindAdapter returns OBJ_PATH.
+ // 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::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;
+
+ //
+ // 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)
{
- if(ex.reason.find("NoSuchAdapter") == string::npos)
- {
- throw BluetoothException(__FILE__, __LINE__, ex.reason);
- }
+ throw BluetoothException(__FILE__, __LINE__, ex.reason);
}
-
- return string();
}
- VariantMap getAdapterProperties(const string& path) const
+ DBus::AsyncResultPtr registerProfileImpl(const DBus::ConnectionPtr& conn, const string& path, const string& uuid,
+ const string& name, int channel, const ProfilePtr& profile)
{
+ conn->addService(path, profile);
+
//
- // The call to GetProperties returns
+ // Invoke RegisterProfile on the profile manager object.
+ //
+ 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::AsyncResultPtr unregisterProfileImpl(const DBus::ConnectionPtr& conn, const string& path)
+ {
//
- // DICT<STRING,VARIANT> properties
+ // Invoke UnregisterProfile on the profile manager object.
//
- DBus::MessagePtr reply = call(path, "org.bluez.Adapter", "GetProperties");
- DBus::ValuePtr v = reply->read();
- VariantMap props;
- extractProperties(v, props);
- return props;
+ DBus::MessagePtr msg =
+ DBus::Message::createCall("org.bluez", "/org/bluez", "org.bluez.ProfileManager1", "UnregisterProfile");
+ msg->write(new DBus::ObjectPathValue(path));
+ return conn->callAsync(msg);
}
- IceXML::NodePtr findChild(const IceXML::NodePtr& parent, const string& name) const
+ static string generatePath()
{
- IceXML::NodeList l = parent->getChildren();
- for(IceXML::NodeList::iterator p = l.begin(); p != l.end(); ++p)
+ //
+ // Generate a unique object path. Path elements can only contain "[A-Z][a-z][0-9]_".
+ //
+ string path = "/com/zeroc/P" + IceUtil::generateUUID();
+ for(string::iterator p = path.begin(); p != path.end(); ++p)
{
- if((*p)->getName() == name)
+ if(*p == '-')
{
- return *p;
+ *p = '_';
}
}
-
- return 0;
+ return path;
}
- IceXML::NodePtr findAttribute(const IceXML::NodePtr& record, int id) const
+ void deviceAdded(const string& path, const VariantMap& props)
{
- IceXML::NodeList l = record->getChildren();
- for(IceXML::NodeList::iterator p = l.begin(); p != l.end(); ++p)
+ RemoteDevice dev(props);
+ if(dev.getAddress().empty())
{
- if((*p)->getName() == "attribute")
+ return; // Ignore devices that don't have an Address property.
+ }
+
+#ifdef ICE_CPP11_MAPPING
+ vector<function<void(const string&, const PropertyMap&)>> callbacks;
+#else
+ vector<DiscoveryCallbackPtr> callbacks;
+#endif
+
+ {
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+
+ AdapterMap::iterator p = _adapters.find(dev.getAdapter());
+ if(p != _adapters.end())
{
- string sid = (*p)->getAttribute("id");
- int i;
- if(sscanf(sid.c_str(), "%x", &i) == 1 && i == id)
+ callbacks = p->second.callbacks;
+ }
+ _remoteDevices[path] = dev;
+ }
+
+ if(!callbacks.empty())
+ {
+ PropertyMap pm; // Convert to string-string map.
+ for(VariantMap::const_iterator p = props.begin(); p != props.end(); ++p)
+ {
+ pm[p->first] = p->second->toString();
+ }
+#ifdef ICE_CPP11_MAPPING
+ for(const auto& discovered : callbacks)
+ {
+ try
+ {
+ discovered(dev.getAddress(), pm);
+ }
+ catch(...)
{
- return *p;
}
}
+#else
+ for(vector<DiscoveryCallbackPtr>::iterator p = callbacks.begin(); p != callbacks.end(); ++p)
+ {
+ try
+ {
+ (*p)->discovered(dev.getAddress(), pm);
+ }
+ catch(...)
+ {
+ }
+ }
+#endif
}
-
- return 0;
}
- IceXML::NodePtr findSequence(const IceXML::NodePtr& parent, const string& uuid) const
+ void deviceChanged(const string& path, const VariantMap& changed, const vector<string>& removedProps)
{
- IceXML::NodeList l = parent->getChildren();
- for(IceXML::NodeList::iterator p = l.begin(); p != l.end(); ++p)
+#ifdef ICE_CPP11_MAPPING
+ vector<function<void(const string&, const PropertyMap&)>> callbacks;
+#else
+ vector<DiscoveryCallbackPtr> callbacks;
+#endif
+ string addr;
+ string adapter;
+ VariantMap props;
+
{
- if((*p)->getName() == "sequence")
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+
+ RemoteDeviceMap::iterator p = _remoteDevices.find(path);
+ if(p == _remoteDevices.end())
{
- IceXML::NodePtr u = findChild(*p, "uuid");
- if(u)
+ RemoteDevice dev(changed);
+ addr = dev.getAddress();
+ if(!addr.empty())
{
- string val = u->getAttribute("value");
- if(IceUtilInternal::toUpper(val) == IceUtilInternal::toUpper(uuid))
- {
- return *p;
- }
+ _remoteDevices[path] = dev;
+ props = changed;
+ adapter = dev.getAdapter();
}
+ }
+ else
+ {
+ updateProperties(p->second.properties, changed, removedProps);
- //
- // Recursively search for nested <sequence> elements.
- //
- IceXML::NodePtr n = findSequence(*p, uuid);
- if(n)
+ addr = p->second.getAddress();
+
+ if(addr.empty())
{
- return n;
+ //
+ // Remove the device if we don't know its address.
+ //
+ _remoteDevices.erase(p);
+ }
+ else
+ {
+ props = p->second.properties;
+ adapter = p->second.getAdapter();
}
}
- }
- return 0;
- }
+ AdapterMap::iterator q = _adapters.find(adapter);
+ if(q != _adapters.end())
+ {
+ callbacks = q->second.callbacks;
+ }
+ }
- 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)
+ if(!addr.empty() && !callbacks.empty())
{
- if((*p)->getName() == "sequence")
+ PropertyMap pm; // Convert to string-string map.
+ for(VariantMap::iterator p = props.begin(); p != props.end(); ++p)
{
- IceXML::NodePtr u = findChild(*p, "uuid");
- if(u)
+ pm[p->first] = p->second->toString();
+ }
+#ifdef ICE_CPP11_MAPPING
+ for(const auto& discovered : callbacks)
+ {
+ try
{
- string val = u->getAttribute("value");
- int i;
- if(sscanf(val.c_str(), "%x", &i) == 1 && i == uuid)
- {
- return *p;
- }
+ discovered(addr, pm);
}
-
- //
- // Recursively search for nested <sequence> elements.
- //
- IceXML::NodePtr n = findSequence(*p, uuid);
- if(n)
+ catch(...)
+ {
+ }
+ }
+#else
+ for(vector<DiscoveryCallbackPtr>::iterator p = callbacks.begin(); p != callbacks.end(); ++p)
+ {
+ try
+ {
+ (*p)->discovered(addr, pm);
+ }
+ catch(...)
{
- return n;
}
}
+#endif
}
+ }
- return 0;
+ 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);
}
- DBus::MessagePtr call(const string& path, const string& intf, const string& member,
- const DBus::ValuePtr& arg = 0) const
+ void adapterChanged(const string& path, const VariantMap& changed, const vector<string>& removedProps)
{
- DBus::MessagePtr msg = DBus::Message::createCall("org.bluez", path, intf, member);
- if(arg)
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+
+ AdapterMap::iterator p = _adapters.find(path);
+ if(p == _adapters.end())
{
- msg->write(arg);
+ _adapters[path] = Adapter(changed);
}
- DBus::AsyncResultPtr r = _dbusConnection->callAsync(msg);
- DBus::MessagePtr reply = r->waitUntilFinished();
- if(reply->isError())
+ else
{
- reply->throwException();
+ updateProperties(p->second.properties, changed, removedProps);
}
- return reply;
}
- void runFindService(const IceUtil::ThreadPtr& thread, const string& addr, const string& uuid,
- const FindServiceCallbackPtr& cb)
+ void adapterRemoved(const string& path)
{
- vector<int> channels;
- bool failed = false;
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
- try
+ AdapterMap::iterator p = _adapters.find(path);
+ if(p != _adapters.end())
{
- const string a = IceUtilInternal::toUpper(addr);
- const string adapter = getDefaultAdapter();
+ _adapters.erase(p);
+ }
+ }
- string devicePath;
- bool first = true;
+ 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)
+ //
- while(devicePath.empty())
- {
- try
- {
- 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)
- {
- 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);
- }
- }
- }
+ DBus::ArrayValuePtr ifaces = DBus::ArrayValuePtr::dynamicCast(v);
+ assert(ifaces);
- if(devicePath.empty())
+ 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);
+ }
+ }
+
+ void updateProperties(VariantMap& props, const VariantMap& changed, const vector<string>& removedProps)
+ {
+ //
+ // Remove properties.
+ //
+ for(vector<string>::const_iterator q = removedProps.begin(); q != removedProps.end(); ++q)
+ {
+ VariantMap::iterator r = props.find(*q);
+ if(r != props.end())
{
- //
- // Couldn't get the object path for the device.
- //
- throw BluetoothException(__FILE__, __LINE__, "unable to create device for address " + addr);
+ props.erase(r);
}
+ }
- DBus::ValuePtr services;
+ //
+ // Merge changes.
+ //
+ for(VariantMap::const_iterator q = changed.begin(); q != changed.end(); ++q)
+ {
+ props[q->first] = q->second;
+ }
+ }
- for(int iter = 0; iter < 50; ++iter)
- {
- {
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+ void runConnectThread(const IceUtil::ThreadPtr& thread, const string& addr, const string& uuid,
+ const ConnectCallbackPtr& 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.
+ //
- if(_destroyed)
- {
- throw CommunicatorDestroyedException(__FILE__, __LINE__);
- }
- }
+ ConnectionIPtr conn;
+ bool ok = true;
- 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)
+ try
+ {
+ string devicePath;
+
+ //
+ // Search our list of known devices for one that matches the given address.
+ //
+ {
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+
+ for(RemoteDeviceMap::iterator p = _remoteDevices.begin(); p != _remoteDevices.end(); ++p)
{
- if(ex.reason.find("InProgress") == string::npos)
+ if(p->second.getAddress() == IceUtilInternal::toUpper(addr))
{
- throw BluetoothException(__FILE__, __LINE__, ex.reason);
+ devicePath = p->first;
+ break;
}
-
- //
- // 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)
+ //
+ // If we don't find a match, we're done.
+ //
+ if(devicePath.empty())
{
- throw BluetoothException(__FILE__, __LINE__, "query for services failed for address " + addr);
+ throw BluetoothException(__FILE__, __LINE__, "unknown address `" + addr + "'");
}
//
- // 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 have a matching device, now register a client profile.
//
- 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)
+ DBus::ConnectionPtr dbusConn = DBus::Connection::getSystemBus();
+ conn = new ConnectionI(dbusConn, devicePath, uuid);
+
+ ProfilePtr profile = ICE_MAKE_SHARED(ClientProfile, conn, cb);
+ string path = generatePath();
+
+ //
+ // 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())
{
- 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);
+ reply->throwException();
+ }
+ //
+ // Invoke ConnectProfile to initiate the client-side connection:
+ //
+ // void ConnectProfile(string uuid)
+ //
+ // 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())
+ {
try
{
- //
- // 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");
- int channel;
- if(sscanf(val.c_str(), "%x", &channel) == 1)
- {
- channels.push_back(channel);
- }
- }
- }
- }
- }
+ reply->throwException();
}
- catch(const IceXML::ParserException& ex)
+ catch(const DBus::Exception& ex)
{
- if(doc)
- {
- doc->destroy();
- }
ostringstream ostr;
- ostr << ex.reason() << endl << rec->v;
- throw BluetoothException(__FILE__, __LINE__, ostr.str());
- }
- catch(...)
- {
- if(doc)
+ ostr << "unable to establish connection to " << uuid << " at " << addr;
+ if(!ex.reason.empty())
{
- doc->destroy();
+ ostr << ':' << endl << ex.reason;
}
- throw;
- }
- }
-
- if(doc)
- {
- doc->destroy();
- }
-
- {
- IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
-
- if(_destroyed)
- {
- throw CommunicatorDestroyedException(__FILE__, __LINE__);
+ throw BluetoothException(__FILE__, __LINE__, ostr.str());
}
}
}
catch(const DBus::Exception& ex)
{
- cb->exception(BluetoothException(__FILE__, __LINE__, ex.reason));
- failed = true;
+ ok = false;
+ cb->failed(BluetoothException(__FILE__, __LINE__, ex.reason));
}
- catch(const LocalException& ex)
+ catch(const Ice::LocalException& ex)
{
- cb->exception(ex);
- failed = true;
- }
- catch(const std::exception& ex)
- {
- cb->exception(UnknownException(__FILE__, __LINE__, ex.what()));
- failed = true;
- }
- catch(...)
- {
- cb->exception(UnknownException(__FILE__, __LINE__, "unknown C++ exception"));
- failed = true;
+ ok = false;
+ cb->failed(ex);
}
- if(!failed)
+ //
+ // Clean up.
+ //
+
+ if(!ok && conn)
{
- if(channels.empty())
- {
- //
- // No service found for the UUID at the remote address. We treat this as if the
- // server is not running.
- //
- cb->exception(ConnectFailedException(__FILE__, __LINE__, ECONNREFUSED));
- }
- else
- {
- cb->completed(channels);
- }
+ conn->close();
}
+ //
+ // Remove the thread from the list.
+ //
{
IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
- vector<IceUtil::ThreadPtr>::iterator p = find(_threads.begin(), _threads.end(), thread);
- if(p != _threads.end())
- {
- _threads.erase(p);
- }
+ vector<IceUtil::ThreadPtr>::iterator p = find(_connectThreads.begin(), _connectThreads.end(), thread);
+ assert(p != _connectThreads.end());
+ _connectThreads.erase(p);
}
}
- class FindServiceThread : public IceUtil::Thread
+ class ConnectThread : public IceUtil::Thread
{
public:
- FindServiceThread(const BluetoothServicePtr& service, const string& addr, const string& uuid,
- const FindServiceCallbackPtr& cb) :
- _service(service),
+ ConnectThread(const BluetoothServicePtr& mo, const string& addr, const string& uuid,
+ const ConnectCallbackPtr& cb) :
+ _mo(mo),
_addr(addr),
_uuid(uuid),
_cb(cb)
@@ -915,29 +1296,38 @@ public:
virtual void run()
{
- _service->runFindService(this, _addr, _uuid, _cb);
+ _mo->runConnectThread(this, _addr, _uuid, _cb);
}
- BluetoothServicePtr _service;
+ private:
+
+ BluetoothServicePtr _mo;
string _addr;
string _uuid;
- FindServiceCallbackPtr _cb;
+ ConnectCallbackPtr _cb;
};
IceUtil::Monitor<IceUtil::Mutex> _lock;
- bool _destroyed;
DBus::ConnectionPtr _dbusConnection;
- vector<IceUtil::ThreadPtr> _threads;
+
+ AdapterMap _adapters;
+ RemoteDeviceMap _remoteDevices;
+ string _defaultAdapterAddress;
+ vector<IceUtil::ThreadPtr> _connectThreads;
+
+ bool _discovering;
#ifdef ICE_CPP11_MAPPING
- map<string, vector<function<void(const string&, const PropertyMap&)>>> _discoveryCallbacks;
+ vector<function<void(const string&, const PropertyMap&)>> _discoveryCallbacks;
#else
- map<string, vector<DiscoveryCallbackPtr> > _discoveryCallbacks;
+ vector<DiscoveryCallbackPtr> _discoveryCallbacks;
#endif
};
}
+#ifndef ICE_CPP11_MAPPING
IceUtil::Shared* IceBT::upCast(IceBT::BluetoothService* p) { return p; }
+#endif
IceBT::Engine::Engine(const Ice::CommunicatorPtr& communicator) :
_communicator(communicator),
@@ -954,7 +1344,8 @@ IceBT::Engine::communicator() const
void
IceBT::Engine::initialize()
{
- _service = new BluetoothService;
+ _service = ICE_MAKE_SHARED(BluetoothService);
+ _service->init();
_initialized = true;
}
@@ -976,22 +1367,28 @@ IceBT::Engine::adapterExists(const string& addr) const
return _service->adapterExists(addr);
}
-unsigned int
-IceBT::Engine::addService(const string& address, const string& name, const string& uuid, int channel)
+bool
+IceBT::Engine::deviceExists(const string& addr) const
+{
+ return _service->deviceExists(addr);
+}
+
+string
+IceBT::Engine::registerProfile(const string& uuid, const string& name, int channel, const ProfileCallbackPtr& cb)
{
- return _service->addService(address, name, uuid, channel);
+ return _service->registerProfile(uuid, name, channel, cb);
}
void
-IceBT::Engine::findService(const string& address, const string& uuid, const FindServiceCallbackPtr& cb)
+IceBT::Engine::unregisterProfile(const string& path)
{
- _service->findService(address, uuid, cb);
+ return _service->unregisterProfile(path);
}
void
-IceBT::Engine::removeService(const string& address, unsigned int handle)
+IceBT::Engine::connect(const string& addr, const string& uuid, const ConnectCallbackPtr& cb)
{
- _service->removeService(address, handle);
+ _service->connect(addr, uuid, cb);
}
void
@@ -1010,6 +1407,12 @@ IceBT::Engine::stopDiscovery(const string& address)
_service->stopDiscovery(address);
}
+IceBT::DeviceMap
+IceBT::Engine::getDevices() const
+{
+ return _service->getDevices();
+}
+
void
IceBT::Engine::destroy()
{
diff --git a/cpp/src/IceBT/Engine.h b/cpp/src/IceBT/Engine.h
index 1b674e24aa6..662ae959203 100644
--- a/cpp/src/IceBT/Engine.h
+++ b/cpp/src/IceBT/Engine.h
@@ -19,14 +19,47 @@
namespace IceBT
{
-class FindServiceCallback : public IceUtil::Shared
+//
+// Notifies the transport about a new incoming connection.
+//
+class ProfileCallback
+#ifndef ICE_CPP11_MAPPING
+ : public virtual IceUtil::Shared
+#endif
+{
+public:
+
+ virtual void newConnection(int) = 0;
+};
+ICE_DEFINE_PTR(ProfileCallbackPtr, ProfileCallback);
+
+//
+// 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
{
public:
- virtual void completed(const std::vector<int>&) = 0;
- virtual void exception(const Ice::LocalException&) = 0;
+ virtual void close() = 0;
+
+};
+typedef IceUtil::Handle<Connection> ConnectionPtr;
+
+//
+// Callback API for an outgoing connection attempt.
+//
+class ConnectCallback
+#ifndef ICE_CPP11_MAPPING
+ : public virtual IceUtil::Shared
+#endif
+{
+public:
+
+ virtual void completed(int, const ConnectionPtr&) = 0;
+ virtual void failed(const Ice::LocalException&) = 0;
};
-typedef IceUtil::Handle<FindServiceCallback> FindServiceCallbackPtr;
+ICE_DEFINE_PTR(ConnectCallbackPtr, ConnectCallback);
//
// Engine encapsulates all Bluetooth activities.
@@ -45,21 +78,12 @@ public:
std::string getDefaultAdapterAddress() const;
bool adapterExists(const std::string&) const;
- //
- // 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);
+ bool deviceExists(const std::string&) const;
- //
- // Asynchronously looks for a service at the given target device with the given UUID.
- //
- void findService(const std::string&, const std::string&, const FindServiceCallbackPtr&);
+ std::string registerProfile(const std::string&, const std::string&, int, const ProfileCallbackPtr&);
+ void unregisterProfile(const std::string&);
- //
- // Removes a service added by addService().
- //
- void removeService(const std::string&, unsigned int);
+ void connect(const std::string&, const std::string&, const ConnectCallbackPtr&);
#ifdef ICE_CPP11_MAPPING
void startDiscovery(const std::string&, std::function<void(const std::string&, const PropertyMap&)>);
@@ -68,6 +92,8 @@ public:
#endif
void stopDiscovery(const std::string&);
+ DeviceMap getDevices() const;
+
void destroy();
private:
diff --git a/cpp/src/IceBT/EngineF.h b/cpp/src/IceBT/EngineF.h
index 2999d97e5b8..bbba69ea892 100644
--- a/cpp/src/IceBT/EngineF.h
+++ b/cpp/src/IceBT/EngineF.h
@@ -21,8 +21,12 @@ IceUtil::Shared* upCast(Engine*);
typedef IceInternal::Handle<Engine> EnginePtr;
class BluetoothService;
+#ifdef ICE_CPP11_MAPPING
+using BluetoothServicePtr = ::std::shared_ptr<BluetoothService>;
+#else
IceUtil::Shared* upCast(BluetoothService*);
typedef IceInternal::Handle<BluetoothService> BluetoothServicePtr;
+#endif
}
diff --git a/cpp/src/IceBT/InstanceF.h b/cpp/src/IceBT/InstanceF.h
index b35c8b765f8..01f3249cd24 100644
--- a/cpp/src/IceBT/InstanceF.h
+++ b/cpp/src/IceBT/InstanceF.h
@@ -22,12 +22,16 @@ typedef IceInternal::Handle<Instance> InstancePtr;
class EndpointI;
#ifdef ICE_CPP11_MAPPING
-typedef std::shared_ptr<EndpointI> EndpointIPtr;
+using EndpointIPtr = ::std::shared_ptr<EndpointI>;
#else
IceUtil::Shared* upCast(EndpointI*);
typedef IceInternal::Handle<EndpointI> EndpointIPtr;
#endif
+class TransceiverI;
+IceUtil::Shared* upCast(TransceiverI*);
+typedef IceInternal::Handle<TransceiverI> TransceiverIPtr;
+
class AcceptorI;
IceUtil::Shared* upCast(AcceptorI*);
typedef IceInternal::Handle<AcceptorI> AcceptorIPtr;
diff --git a/cpp/src/IceBT/Makefile.mk b/cpp/src/IceBT/Makefile.mk
index 5041594f8b7..b429d77a2d0 100644
--- a/cpp/src/IceBT/Makefile.mk
+++ b/cpp/src/IceBT/Makefile.mk
@@ -12,7 +12,7 @@ ifneq ($(filter debian ubuntu,$(linux_id)),)
$(project)_libraries := IceBT
IceBT_targetdir := $(libdir)
-IceBT_dependencies := IceXML Ice
+IceBT_dependencies := Ice
IceBT_cppflags := -DICE_BT_API_EXPORTS $(shell pkg-config --cflags dbus-1)
IceBT_system_libs = $(IceSSL_system_libs) $(shell pkg-config --libs dbus-1)
IceBT_sliceflags := --include-dir IceBT --dll-export ICE_BT_API
diff --git a/cpp/src/IceBT/PluginI.cpp b/cpp/src/IceBT/PluginI.cpp
index c8f2882bbfb..b3335e591f9 100644
--- a/cpp/src/IceBT/PluginI.cpp
+++ b/cpp/src/IceBT/PluginI.cpp
@@ -103,3 +103,9 @@ IceBT::PluginI::stopDiscovery(const string& address)
{
_engine->stopDiscovery(address);
}
+
+IceBT::DeviceMap
+IceBT::PluginI::getDevices() const
+{
+ return _engine->getDevices();
+}
diff --git a/cpp/src/IceBT/PluginI.h b/cpp/src/IceBT/PluginI.h
index aa846e01891..a8589e5a38c 100644
--- a/cpp/src/IceBT/PluginI.h
+++ b/cpp/src/IceBT/PluginI.h
@@ -33,12 +33,15 @@ public:
// From IceBT::Plugin.
//
#ifdef ICE_CPP11_MAPPING
- virtual void startDiscovery(const std::string&, std::function<void(const std::string&, const PropertyMap&)>);
+ virtual void startDiscovery(const std::string& address,
+ std::function<void(const std::string& addr, const PropertyMap& props)>);
#else
virtual void startDiscovery(const std::string&, const DiscoveryCallbackPtr&);
#endif
virtual void stopDiscovery(const std::string&);
+ virtual DeviceMap getDevices() const;
+
private:
EnginePtr _engine;
diff --git a/cpp/src/IceBT/StreamSocket.cpp b/cpp/src/IceBT/StreamSocket.cpp
index ba4b27ca57f..fc90473bc35 100644
--- a/cpp/src/IceBT/StreamSocket.cpp
+++ b/cpp/src/IceBT/StreamSocket.cpp
@@ -19,26 +19,14 @@ 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),
- _state(StateConnected)
+ _instance(instance)
{
- init();
+ if(fd != INVALID_SOCKET)
+ {
+ init(fd);
+ }
_desc = fdToString(fd);
}
@@ -47,31 +35,6 @@ 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)
{
@@ -85,9 +48,9 @@ IceBT::StreamSocket::getRecvPacketSize(size_t length)
}
void
-IceBT::StreamSocket::setBufferSize(int rcvSize, int sndSize)
+IceBT::StreamSocket::setBufferSize(SOCKET fd, int rcvSize, int sndSize)
{
- assert(_fd != INVALID_SOCKET);
+ assert(fd != INVALID_SOCKET);
if(rcvSize > 0)
{
@@ -96,8 +59,8 @@ IceBT::StreamSocket::setBufferSize(int rcvSize, int sndSize)
// the size to an acceptable value. Then read the size back to
// get the size that was actually set.
//
- IceInternal::setRecvBufferSize(_fd, rcvSize);
- int size = IceInternal::getRecvBufferSize(_fd);
+ IceInternal::setRecvBufferSize(fd, rcvSize);
+ int size = IceInternal::getRecvBufferSize(fd);
if(size > 0 && size < rcvSize)
{
//
@@ -121,8 +84,8 @@ IceBT::StreamSocket::setBufferSize(int rcvSize, int sndSize)
// the size to an acceptable value. Then read the size back to
// get the size that was actually set.
//
- IceInternal::setSendBufferSize(_fd, sndSize);
- int size = IceInternal::getSendBufferSize(_fd);
+ IceInternal::setSendBufferSize(fd, sndSize);
+ int size = IceInternal::getSendBufferSize(fd);
if(size > 0 && size < sndSize)
{
// Warn if the size that was set is less than the requested size and
@@ -277,16 +240,18 @@ IceBT::StreamSocket::write(const char* buf, size_t length)
void
IceBT::StreamSocket::close()
{
- assert(_fd != INVALID_SOCKET);
- try
- {
- IceInternal::closeSocket(_fd);
- _fd = INVALID_SOCKET;
- }
- catch(const Ice::SocketException&)
+ if(_fd != INVALID_SOCKET)
{
- _fd = INVALID_SOCKET;
- throw;
+ try
+ {
+ IceInternal::closeSocket(_fd);
+ _fd = INVALID_SOCKET;
+ }
+ catch(const Ice::SocketException&)
+ {
+ _fd = INVALID_SOCKET;
+ throw;
+ }
}
}
@@ -297,12 +262,21 @@ IceBT::StreamSocket::toString() const
}
void
-IceBT::StreamSocket::init()
+IceBT::StreamSocket::setFd(SOCKET fd)
+{
+ assert(fd != INVALID_SOCKET);
+ init(fd);
+ setNewFd(fd);
+ _desc = fdToString(fd);
+}
+
+void
+IceBT::StreamSocket::init(SOCKET fd)
{
- IceInternal::setBlock(_fd, false);
+ IceInternal::setBlock(fd, false);
Int rcvSize = _instance->properties()->getPropertyAsInt("IceBT.RcvSize");
Int sndSize = _instance->properties()->getPropertyAsInt("IceBT.SndSize");
- setBufferSize(rcvSize, sndSize);
+ setBufferSize(fd, rcvSize, sndSize);
}
diff --git a/cpp/src/IceBT/StreamSocket.h b/cpp/src/IceBT/StreamSocket.h
index f618d7f1324..53044a9d5b8 100644
--- a/cpp/src/IceBT/StreamSocket.h
+++ b/cpp/src/IceBT/StreamSocket.h
@@ -24,16 +24,13 @@ 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);
- void setBufferSize(int rcvSize, int sndSize);
+ void setBufferSize(SOCKET, int rcvSize, int sndSize);
IceInternal::SocketOperation read(IceInternal::Buffer&);
IceInternal::SocketOperation write(IceInternal::Buffer&);
@@ -44,20 +41,14 @@ public:
void close();
const std::string& toString() const;
-private:
+ void setFd(SOCKET);
- void init();
+private:
- enum State
- {
- StateNeedConnect,
- StateConnectPending,
- StateConnected
- };
+ void init(SOCKET);
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 327c96d9ef3..938c84b2a11 100644
--- a/cpp/src/IceBT/TransceiverI.cpp
+++ b/cpp/src/IceBT/TransceiverI.cpp
@@ -22,6 +22,8 @@ using namespace std;
using namespace Ice;
using namespace IceBT;
+IceUtil::Shared* IceBT::upCast(TransceiverI* p) { return p; }
+
IceInternal::NativeInfoPtr
IceBT::TransceiverI::getNativeInfo()
{
@@ -31,7 +33,26 @@ IceBT::TransceiverI::getNativeInfo()
IceInternal::SocketOperation
IceBT::TransceiverI::initialize(IceInternal::Buffer& readBuffer, IceInternal::Buffer& writeBuffer)
{
- return _stream->connect(readBuffer, writeBuffer);
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+
+ if(_exception)
+ {
+ //
+ // Raise the stored exception from a failed connection attempt.
+ //
+ _exception->ice_throw();
+ }
+ else if(_needConnect)
+ {
+ //
+ // We need to initiate a connection attempt.
+ //
+ _needConnect = false;
+ _instance->engine()->connect(_addr, _uuid, ICE_MAKE_SHARED(ConnectCallbackI, this));
+ return IceInternal::SocketOperationConnect;
+ }
+
+ return IceInternal::SocketOperationNone; // Already connected.
}
IceInternal::SocketOperation
@@ -47,18 +68,38 @@ IceBT::TransceiverI::closing(bool initiator, const Ice::LocalException&)
void
IceBT::TransceiverI::close()
{
+ if(_connection)
+ {
+ _connection->close();
+ }
_stream->close();
}
IceInternal::SocketOperation
IceBT::TransceiverI::write(IceInternal::Buffer& buf)
{
+ if(_stream->fd() == INVALID_SOCKET)
+ {
+ //
+ // This can happen if the connection failed. We return None here and let initialize() handle the rest.
+ //
+ return IceInternal::SocketOperationNone;
+ }
+
return _stream->write(buf);
}
IceInternal::SocketOperation
IceBT::TransceiverI::read(IceInternal::Buffer& buf)
{
+ if(_stream->fd() == INVALID_SOCKET)
+ {
+ //
+ // This can happen if we connected successfully but the selector has not updated our FD yet.
+ //
+ return IceInternal::SocketOperationRead;
+ }
+
return _stream->read(buf);
}
@@ -103,16 +144,54 @@ IceBT::TransceiverI::checkSendSize(const IceInternal::Buffer&)
void
IceBT::TransceiverI::setBufferSize(int rcvSize, int sndSize)
{
- _stream->setBufferSize(rcvSize, sndSize);
+ _stream->setBufferSize(_stream->fd(), rcvSize, sndSize);
}
-IceBT::TransceiverI::TransceiverI(const InstancePtr& instance, const StreamSocketPtr& stream, const string& uuid) :
+IceBT::TransceiverI::TransceiverI(const InstancePtr& instance, const StreamSocketPtr& stream, const ConnectionPtr& conn,
+ const string& uuid) :
_instance(instance),
_stream(stream),
- _uuid(uuid)
+ _connection(conn),
+ _uuid(uuid),
+ _needConnect(false)
+{
+}
+
+IceBT::TransceiverI::TransceiverI(const InstancePtr& instance, const string& addr, const string& uuid) :
+ _instance(instance),
+ _stream(new StreamSocket(instance, INVALID_SOCKET)),
+ _addr(addr),
+ _uuid(uuid),
+ _needConnect(true)
{
}
IceBT::TransceiverI::~TransceiverI()
{
}
+
+void
+IceBT::TransceiverI::connectCompleted(int fd, const ConnectionPtr& conn)
+{
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+ _connection = conn;
+ _stream->setFd(fd);
+ //
+ // Triggers a call to write() from a different thread.
+ //
+ _stream->ready(IceInternal::SocketOperationConnect, true);
+}
+
+void
+IceBT::TransceiverI::connectFailed(const Ice::LocalException& ex)
+{
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+ //
+ // Save the exception - it will be raised in initialize().
+ //
+ ICE_SET_EXCEPTION_FROM_CLONE(_exception, ex.ice_clone());
+ //
+ // Triggers a call to write() from a different thread.
+ //
+ _stream->ready(IceInternal::SocketOperationConnect, true);
+}
diff --git a/cpp/src/IceBT/TransceiverI.h b/cpp/src/IceBT/TransceiverI.h
index 04170a52327..705d22865a4 100644
--- a/cpp/src/IceBT/TransceiverI.h
+++ b/cpp/src/IceBT/TransceiverI.h
@@ -11,9 +11,11 @@
#define ICE_BT_TRANSCEIVER_H
#include <IceBT/InstanceF.h>
+#include <IceBT/Engine.h>
#include <IceBT/StreamSocket.h>
#include <Ice/Transceiver.h>
+#include <IceUtil/UniquePtr.h>
namespace IceBT
{
@@ -28,7 +30,6 @@ public:
virtual IceInternal::NativeInfoPtr getNativeInfo();
virtual IceInternal::SocketOperation initialize(IceInternal::Buffer&, IceInternal::Buffer&);
-
virtual IceInternal::SocketOperation closing(bool, const Ice::LocalException&);
virtual void close();
virtual IceInternal::SocketOperation write(IceInternal::Buffer&);
@@ -42,15 +43,49 @@ public:
private:
- TransceiverI(const InstancePtr&, const StreamSocketPtr&, const std::string&);
+ TransceiverI(const InstancePtr&, const StreamSocketPtr&, const ConnectionPtr&, const std::string&);
+ TransceiverI(const InstancePtr&, const std::string&, const std::string&);
virtual ~TransceiverI();
friend class ConnectorI;
friend class AcceptorI;
const InstancePtr _instance;
- const StreamSocketPtr _stream;
- const std::string _uuid;
+ StreamSocketPtr _stream;
+ ConnectionPtr _connection;
+ std::string _addr;
+ std::string _uuid;
+ bool _needConnect;
+ IceUtil::UniquePtr<Ice::Exception> _exception;
+ IceUtil::Monitor<IceUtil::Mutex> _lock;
+
+ void connectCompleted(int, const ConnectionPtr&);
+ void connectFailed(const Ice::LocalException&);
+
+ class ConnectCallbackI : public ConnectCallback
+ {
+ public:
+
+ ConnectCallbackI(const TransceiverIPtr& transceiver) :
+ _transceiver(transceiver)
+ {
+ }
+
+ virtual void completed(int fd, const ConnectionPtr& conn)
+ {
+ _transceiver->connectCompleted(fd, conn);
+ }
+
+ virtual void failed(const Ice::LocalException& ex)
+ {
+ _transceiver->connectFailed(ex);
+ }
+
+ private:
+
+ TransceiverIPtr _transceiver;
+ };
+ friend class ConnectCallbackI;
};
}
diff --git a/cpp/src/IceBT/Util.cpp b/cpp/src/IceBT/Util.cpp
index 3953de2bd58..c5021cea415 100644
--- a/cpp/src/IceBT/Util.cpp
+++ b/cpp/src/IceBT/Util.cpp
@@ -87,182 +87,20 @@ 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, reinterpret_cast<struct sockaddr*>(&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(sockaddr_rc));
+ socklen_t len = static_cast<socklen_t>(sizeof(SocketAddress));
if(::getsockname(fd, reinterpret_cast<struct sockaddr*>(&addr), &len) == SOCKET_ERROR)
{
IceInternal::closeSocketNoThrow(fd);
@@ -275,7 +113,7 @@ fdToLocalAddress(SOCKET fd, SocketAddress& addr)
bool
fdToRemoteAddress(SOCKET fd, SocketAddress& addr)
{
- socklen_t len = static_cast<socklen_t>(sizeof(sockaddr_rc));
+ socklen_t len = static_cast<socklen_t>(sizeof(SocketAddress));
if(::getpeername(fd, reinterpret_cast<struct sockaddr*>(&addr), &len) == SOCKET_ERROR)
{
if(IceInternal::notConnected())
@@ -386,5 +224,5 @@ IceBT::compareAddress(const SocketAddress& addr1, const SocketAddress& addr2)
return 1;
}
- return ::memcmp(&addr1.rc_bdaddr, &addr2.rc_bdaddr, sizeof(bdaddr_t));
+ return ::memcmp(&addr1.rc_bdaddr, &addr2.rc_bdaddr, sizeof(DeviceAddress));
}
diff --git a/cpp/src/IceBT/Util.h b/cpp/src/IceBT/Util.h
index 29cd8ff9518..5f35afb56d9 100644
--- a/cpp/src/IceBT/Util.h
+++ b/cpp/src/IceBT/Util.h
@@ -26,11 +26,6 @@ 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&);
diff --git a/cpp/src/IceSSL/OpenSSLTransceiverI.cpp b/cpp/src/IceSSL/OpenSSLTransceiverI.cpp
index 75bc146e14f..d3195e2b141 100644
--- a/cpp/src/IceSSL/OpenSSLTransceiverI.cpp
+++ b/cpp/src/IceSSL/OpenSSLTransceiverI.cpp
@@ -101,7 +101,15 @@ IceSSL::TransceiverI::initialize(IceInternal::Buffer& readBuffer, IceInternal::B
// This static_cast is necessary due to 64bit windows. There SOCKET is a non-int type.
//
SOCKET fd = _delegate->getNativeInfo()->fd();
- assert(fd != INVALID_SOCKET); // Underlying transport must be SOCKET based.
+ if(fd == INVALID_SOCKET)
+ {
+ //
+ // The delegate has finished its initialization but may not have a file descriptor yet (e.g., Bluetooth).
+ // The underlying transport must (eventually) be socket-based.
+ //
+ return IceInternal::SocketOperationRead;
+ }
+
BIO* bio = BIO_new_socket(static_cast<int>(fd), 0);
if(!bio)
{