diff options
author | Mark Spruiell <mes@zeroc.com> | 2016-10-20 16:45:10 -0700 |
---|---|---|
committer | Mark Spruiell <mes@zeroc.com> | 2016-10-20 16:45:10 -0700 |
commit | edc4c248688d5e4a58be443aa95aab4226991c5d (patch) | |
tree | 60f6a8cb5917a7f325a30ea2ff824d5b748f553b /cpp/src | |
parent | Fix to allow run es5 test in Windows (diff) | |
download | ice-edc4c248688d5e4a58be443aa95aab4226991c5d.tar.bz2 ice-edc4c248688d5e4a58be443aa95aab4226991c5d.tar.xz ice-edc4c248688d5e4a58be443aa95aab4226991c5d.zip |
updating IceBT to BlueZ 5
Diffstat (limited to 'cpp/src')
-rwxr-xr-x | cpp/src/Ice/Network.cpp | 25 | ||||
-rwxr-xr-x | cpp/src/Ice/Network.h | 8 | ||||
-rw-r--r-- | cpp/src/Ice/Selector.cpp | 18 | ||||
-rw-r--r-- | cpp/src/IceBT/AcceptorI.cpp | 153 | ||||
-rw-r--r-- | cpp/src/IceBT/AcceptorI.h | 17 | ||||
-rw-r--r-- | cpp/src/IceBT/ConnectorI.cpp | 28 | ||||
-rw-r--r-- | cpp/src/IceBT/ConnectorI.h | 5 | ||||
-rw-r--r-- | cpp/src/IceBT/DBus.h | 21 | ||||
-rw-r--r-- | cpp/src/IceBT/EndpointI.cpp | 119 | ||||
-rw-r--r-- | cpp/src/IceBT/EndpointI.h | 31 | ||||
-rw-r--r-- | cpp/src/IceBT/Engine.cpp | 1583 | ||||
-rw-r--r-- | cpp/src/IceBT/Engine.h | 60 | ||||
-rw-r--r-- | cpp/src/IceBT/EngineF.h | 4 | ||||
-rw-r--r-- | cpp/src/IceBT/InstanceF.h | 6 | ||||
-rw-r--r-- | cpp/src/IceBT/Makefile.mk | 2 | ||||
-rw-r--r-- | cpp/src/IceBT/PluginI.cpp | 6 | ||||
-rw-r--r-- | cpp/src/IceBT/PluginI.h | 5 | ||||
-rw-r--r-- | cpp/src/IceBT/StreamSocket.cpp | 94 | ||||
-rw-r--r-- | cpp/src/IceBT/StreamSocket.h | 17 | ||||
-rw-r--r-- | cpp/src/IceBT/TransceiverI.cpp | 87 | ||||
-rw-r--r-- | cpp/src/IceBT/TransceiverI.h | 43 | ||||
-rw-r--r-- | cpp/src/IceBT/Util.cpp | 170 | ||||
-rw-r--r-- | cpp/src/IceBT/Util.h | 5 | ||||
-rw-r--r-- | cpp/src/IceSSL/OpenSSLTransceiverI.cpp | 10 |
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) { |