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