diff options
author | Mark Spruiell <mes@zeroc.com> | 2015-11-06 11:24:22 -0800 |
---|---|---|
committer | Mark Spruiell <mes@zeroc.com> | 2015-11-06 11:24:22 -0800 |
commit | 61f9f44b8eda9c9cdb9139436d4ad87e6e7d5839 (patch) | |
tree | 286025a254ce57d02068551f11cf69b89fa66a72 /cpp/src | |
parent | A few changes to support move of Freeze (diff) | |
download | ice-61f9f44b8eda9c9cdb9139436d4ad87e6e7d5839.tar.bz2 ice-61f9f44b8eda9c9cdb9139436d4ad87e6e7d5839.tar.xz ice-61f9f44b8eda9c9cdb9139436d4ad87e6e7d5839.zip |
initial commit of C++ Bluetooth transport
Diffstat (limited to 'cpp/src')
29 files changed, 5342 insertions, 21 deletions
diff --git a/cpp/src/Ice/PropertyNames.cpp b/cpp/src/Ice/PropertyNames.cpp index f4d74f3bc50..e6278635e3c 100644 --- a/cpp/src/Ice/PropertyNames.cpp +++ b/cpp/src/Ice/PropertyNames.cpp @@ -6,7 +6,7 @@ // ICE_LICENSE file included in this distribution. // // ********************************************************************** -// Generated by makeprops.py from file ../config/PropertyNames.xml, Thu Nov 5 12:25:50 2015 +// Generated by makeprops.py from file ../config/PropertyNames.xml, Fri Nov 6 09:55:41 2015 // IMPORTANT: Do not edit this file -- any edits made here will be lost! @@ -1053,6 +1053,17 @@ const IceInternal::PropertyArray IceInternal::PropertyNames::IceStormAdminProps(IceStormAdminPropsData, sizeof(IceStormAdminPropsData)/sizeof(IceStormAdminPropsData[0])); +const IceInternal::Property IceBTPropsData[] = +{ + IceInternal::Property("IceBT.DefaultAddress", false, 0), + IceInternal::Property("IceBT.RcvSize", false, 0), + IceInternal::Property("IceBT.SndSize", false, 0), +}; + +const IceInternal::PropertyArray + IceInternal::PropertyNames::IceBTProps(IceBTPropsData, + sizeof(IceBTPropsData)/sizeof(IceBTPropsData[0])); + const IceInternal::Property Glacier2PropsData[] = { IceInternal::Property("Glacier2.AddConnectionContext", false, 0), @@ -1262,6 +1273,7 @@ const IceInternal::PropertyArray IceInternal::PropertyNames::validProps[] = IcePatch2ClientProps, IceSSLProps, IceStormAdminProps, + IceBTProps, Glacier2Props, Glacier2CryptPermissionsVerifierProps, FreezeProps, @@ -1283,6 +1295,7 @@ const char* IceInternal::PropertyNames::clPropNames[] = "IcePatch2Client", "IceSSL", "IceStormAdmin", + "IceBT", "Glacier2", "Glacier2CryptPermissionsVerifier", "Freeze", diff --git a/cpp/src/Ice/PropertyNames.h b/cpp/src/Ice/PropertyNames.h index a3fb5aec422..823aa1174f1 100644 --- a/cpp/src/Ice/PropertyNames.h +++ b/cpp/src/Ice/PropertyNames.h @@ -6,7 +6,7 @@ // ICE_LICENSE file included in this distribution. // // ********************************************************************** -// Generated by makeprops.py from file ../config/PropertyNames.xml, Thu Nov 5 12:25:50 2015 +// Generated by makeprops.py from file ../config/PropertyNames.xml, Fri Nov 6 09:55:41 2015 // IMPORTANT: Do not edit this file -- any edits made here will be lost! @@ -68,6 +68,7 @@ public: static const PropertyArray IcePatch2ClientProps; static const PropertyArray IceSSLProps; static const PropertyArray IceStormAdminProps; + static const PropertyArray IceBTProps; static const PropertyArray Glacier2Props; static const PropertyArray Glacier2CryptPermissionsVerifierProps; static const PropertyArray FreezeProps; diff --git a/cpp/src/Ice/ProtocolInstance.cpp b/cpp/src/Ice/ProtocolInstance.cpp index c9b26c56f7e..c365abad9c6 100644 --- a/cpp/src/Ice/ProtocolInstance.cpp +++ b/cpp/src/Ice/ProtocolInstance.cpp @@ -44,6 +44,30 @@ IceInternal::ProtocolInstance::ProtocolInstance(const InstancePtr& instance, Sho { } +const LoggerPtr& +IceInternal::ProtocolInstance::logger() const +{ + return _instance->initializationData().logger; +} + +BufSizeWarnInfo +IceInternal::ProtocolInstance::getBufSizeWarn(Short type) +{ + return _instance->getBufSizeWarn(type); +} + +void +IceInternal::ProtocolInstance::setSndBufSizeWarn(Short type, int size) +{ + _instance->setSndBufSizeWarn(type, size); +} + +void +IceInternal::ProtocolInstance::setRcvBufSizeWarn(Short type, int size) +{ + _instance->setRcvBufSizeWarn(type, size); +} + bool IceInternal::ProtocolInstance::preferIPv6() const { diff --git a/cpp/src/Ice/ProtocolInstance.h b/cpp/src/Ice/ProtocolInstance.h index fc757d788e2..c67e491a935 100644 --- a/cpp/src/Ice/ProtocolInstance.h +++ b/cpp/src/Ice/ProtocolInstance.h @@ -39,10 +39,7 @@ public: return _traceCategory; } - const Ice::LoggerPtr& logger() const - { - return _instance->initializationData().logger; - } + const Ice::LoggerPtr& logger() const; const std::string& protocol() const { @@ -64,21 +61,9 @@ public: return _secure; } - BufSizeWarnInfo getBufSizeWarn(Ice::Short type) - { - return _instance->getBufSizeWarn(type); - } - - void setSndBufSizeWarn(Ice::Short type, int size) - { - _instance->setSndBufSizeWarn(type, size); - } - - void setRcvBufSizeWarn(Ice::Short type, int size) - { - _instance->setRcvBufSizeWarn(type, size); - } - + BufSizeWarnInfo getBufSizeWarn(Ice::Short type); + void setSndBufSizeWarn(Ice::Short type, int size); + void setRcvBufSizeWarn(Ice::Short type, int size); bool preferIPv6() const; ProtocolSupport protocolSupport() const; const std::string& defaultHost() const; diff --git a/cpp/src/IceBT/.gitignore b/cpp/src/IceBT/.gitignore new file mode 100644 index 00000000000..72e3ccb71f5 --- /dev/null +++ b/cpp/src/IceBT/.gitignore @@ -0,0 +1,6 @@ +// Generated by makegitignore.py + +// IMPORTANT: Do not edit this file -- any edits made here will be lost! +EndpointInfo.cpp +ConnectionInfo.cpp +Types.cpp diff --git a/cpp/src/IceBT/AcceptorI.cpp b/cpp/src/IceBT/AcceptorI.cpp new file mode 100644 index 00000000000..b561e61197b --- /dev/null +++ b/cpp/src/IceBT/AcceptorI.cpp @@ -0,0 +1,233 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <IceBT/AcceptorI.h> +#include <IceBT/Engine.h> +#include <IceBT/EndpointI.h> +#include <IceBT/Instance.h> +#include <IceBT/TransceiverI.h> +#include <IceBT/Util.h> + +#include <Ice/Communicator.h> +#include <Ice/Exception.h> +#include <Ice/LocalException.h> +#include <Ice/Network.h> +#include <Ice/Properties.h> +#include <Ice/StreamSocket.h> +#include <IceUtil/StringUtil.h> + +using namespace std; +using namespace Ice; +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() +{ + return this; +} + +void +IceBT::AcceptorI::close() +{ + if(!_path.empty()) + { + 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 + { + _path = _instance->engine()->registerProfile(_uuid, _name, _channel, new ProfileCallbackI(this)); + } + catch(const BluetoothException& ex) + { + InitializationException e(__FILE__, __LINE__); + e.reason = "unable to register SDP service"; + if(!ex.reason.empty()) + { + e.reason += "\n" + ex.reason; + } + throw e; + } + + _endpoint = _endpoint->endpoint(this); + return _endpoint; +} + +IceInternal::TransceiverPtr +IceBT::AcceptorI::accept() +{ + // + // The plug-in may not be initialized. + // + if(!_instance->initialized()) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceBT: plug-in is not initialized"; + throw ex; + } + + 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 t; +} + +string +IceBT::AcceptorI::protocol() const +{ + return _instance->protocol(); +} + +string +IceBT::AcceptorI::toString() const +{ + return addrToString(_addr, _channel); +} + +string +IceBT::AcceptorI::toDetailedString() const +{ + ostringstream os; + os << "local address = " << toString(); + if(!_name.empty()) + { + os << "\nservice name = '" << _name << "'"; + } + if(!_uuid.empty()) + { + os << "\nservice uuid = " << _uuid; + } + return os.str(); +} + +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); +} + +IceBT::AcceptorI::AcceptorI(const EndpointIPtr& endpoint, const InstancePtr& instance, const string& adapterName, + const string& addr, const string& uuid, const string& name, int channel) : + _endpoint(endpoint), + _instance(instance), + _adapterName(adapterName), + _addr(addr), + _uuid(uuid), + _name(name), + _channel(channel) +{ + string s = IceUtilInternal::trim(_addr); + if(s.empty()) + { + // + // If no address was specified, we use the first available BT adapter. + // + s = _instance->engine()->getDefaultDeviceAddress(); + } + + s = IceUtilInternal::toUpper(s); + + 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()->deviceExists(s)) + { + EndpointParseException ex(__FILE__, __LINE__); + ex.str = "no device found for `" + s + "' in endpoint " + endpoint->toString(); + throw ex; + } + + const_cast<string&>(_addr) = s; +} + +IceBT::AcceptorI::~AcceptorI() +{ +} diff --git a/cpp/src/IceBT/AcceptorI.h b/cpp/src/IceBT/AcceptorI.h new file mode 100644 index 00000000000..496956992d0 --- /dev/null +++ b/cpp/src/IceBT/AcceptorI.h @@ -0,0 +1,63 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICE_BT_ACCEPTOR_I_H +#define ICE_BT_ACCEPTOR_I_H + +#include <Ice/TransceiverF.h> +#include <Ice/Acceptor.h> +#include <IceBT/Config.h> +#include <IceBT/EngineF.h> +#include <IceBT/InstanceF.h> + +#include <stack> + +namespace IceBT +{ + +class AcceptorI : public IceInternal::Acceptor, public IceInternal::NativeInfo +{ +public: + + virtual IceInternal::NativeInfoPtr getNativeInfo(); + + virtual void close(); + virtual IceInternal::EndpointIPtr listen(); + virtual IceInternal::TransceiverPtr accept(); + virtual std::string protocol() const; + virtual std::string toString() const; + virtual std::string toDetailedString() const; + + int effectiveChannel() const; + + void newConnection(int); + +private: + + AcceptorI(const EndpointIPtr&, const InstancePtr&, const std::string&, const std::string&, const std::string&, + const std::string&, int); + virtual ~AcceptorI(); + friend class EndpointI; + + 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; +}; + +} + +#endif diff --git a/cpp/src/IceBT/Config.h b/cpp/src/IceBT/Config.h new file mode 100644 index 00000000000..26e0691ffed --- /dev/null +++ b/cpp/src/IceBT/Config.h @@ -0,0 +1,28 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICE_BT_CONFIG_H +#define ICE_BT_CONFIG_H + +#include <Ice/Config.h> +#include <Ice/Network.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> + +namespace IceBT +{ + +typedef bdaddr_t DeviceAddress; + +typedef sockaddr_rc SocketAddress; + +} + +#endif diff --git a/cpp/src/IceBT/ConnectorI.cpp b/cpp/src/IceBT/ConnectorI.cpp new file mode 100644 index 00000000000..7937ec772e0 --- /dev/null +++ b/cpp/src/IceBT/ConnectorI.cpp @@ -0,0 +1,156 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#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; +using namespace IceBT; + +IceInternal::TransceiverPtr +IceBT::ConnectorI::connect() +{ + // + // The plug-in may not be initialized. + // + if(!_instance->initialized()) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceBT: plug-in is not initialized"; + 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; +} + +Short +IceBT::ConnectorI::type() const +{ + return _instance->type(); +} + +string +IceBT::ConnectorI::toString() const +{ + return _addr; +} + +bool +IceBT::ConnectorI::operator==(const IceInternal::Connector& r) const +{ + const ConnectorI* p = dynamic_cast<const ConnectorI*>(&r); + if(!p) + { + return false; + } + + if(_addr != p->_addr) + { + return false; + } + + if(_uuid != p->_uuid) + { + return false; + } + + if(_timeout != p->_timeout) + { + return false; + } + + if(_connectionId != p->_connectionId) + { + return false; + } + + return true; +} + +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); + if(!p) + { + return type() < r.type(); + } + + if(_addr < p->_addr) + { + return true; + } + + if(_uuid < p->_uuid) + { + return true; + } + else if(p->_uuid < _uuid) + { + return false; + } + + if(_timeout < p->_timeout) + { + return true; + } + else if(p->_timeout < _timeout) + { + return false; + } + + 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) : + _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 new file mode 100644 index 00000000000..3cdbd73023d --- /dev/null +++ b/cpp/src/IceBT/ConnectorI.h @@ -0,0 +1,54 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICE_BT_CONNECTOR_I_H +#define ICE_BT_CONNECTOR_I_H + +#include <IceBT/Config.h> +#include <IceBT/Engine.h> +#include <IceBT/InstanceF.h> + +#include <Ice/TransceiverF.h> +#include <Ice/Connector.h> + +namespace IceBT +{ + +class ConnectorI : public IceInternal::Connector +{ +public: + + virtual IceInternal::TransceiverPtr connect(); + + virtual Ice::Short type() const; + 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&, SOCKET, const ConnectionPtr&, const std::string&, const std::string&, Ice::Int, + const std::string&); + virtual ~ConnectorI(); + friend class EndpointI; + + const InstancePtr _instance; + SOCKET _fd; + ConnectionPtr _connection; + const std::string _addr; + const std::string _uuid; + const Ice::Int _timeout; + const std::string _connectionId; +}; + +} + +#endif diff --git a/cpp/src/IceBT/DBus.cpp b/cpp/src/IceBT/DBus.cpp new file mode 100644 index 00000000000..53b631a69d0 --- /dev/null +++ b/cpp/src/IceBT/DBus.cpp @@ -0,0 +1,1320 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <IceBT/DBus.h> +#include <IceUtil/Thread.h> +#include <IceUtil/Mutex.h> +#include <IceUtil/Monitor.h> + +#include <dbus/dbus.h> +#include <stack> + +using namespace std; +using namespace IceBT::DBus; + +namespace +{ + +class ErrorWrapper +{ +public: + + ErrorWrapper() + { + ::dbus_error_init(&err); + } + + ~ErrorWrapper() + { + ::dbus_error_free(&err); + } + + bool isSet() const + { + return ::dbus_error_is_set(&err); + } + + DBusError err; +}; + +class ExceptionI : public IceBT::DBus::Exception +{ +public: + + ExceptionI(const ErrorWrapper& w) + { + init(w.err); + } + + ExceptionI(const DBusError& err) + { + init(err); + } + + ExceptionI(const string& s) : + Exception(s) + { + } + + string reason; + +private: + + void init(const DBusError& err) + { + assert(::dbus_error_is_set(&err)); + reason = err.message; + } +}; + +class PrimitiveType : public Type +{ +public: + + PrimitiveType(Kind k) : + _kind(k) + { + } + + virtual Kind getKind() const + { + return _kind; + } + + virtual std::string getSignature() const + { + switch(_kind) + { + case KindBoolean: + return DBUS_TYPE_BOOLEAN_AS_STRING; + case KindByte: + return DBUS_TYPE_BYTE_AS_STRING; + case KindUint16: + return DBUS_TYPE_UINT16_AS_STRING; + case KindInt16: + return DBUS_TYPE_INT16_AS_STRING; + case KindUint32: + return DBUS_TYPE_UINT32_AS_STRING; + case KindInt32: + return DBUS_TYPE_INT32_AS_STRING; + case KindUint64: + return DBUS_TYPE_UINT64_AS_STRING; + case KindInt64: + return DBUS_TYPE_INT64_AS_STRING; + case KindDouble: + return DBUS_TYPE_DOUBLE_AS_STRING; + case KindString: + return DBUS_TYPE_STRING_AS_STRING; + case KindObjectPath: + return DBUS_TYPE_OBJECT_PATH_AS_STRING; + case KindSignature: + return DBUS_TYPE_SIGNATURE_AS_STRING; + case KindUnixFD: + return DBUS_TYPE_UNIX_FD_AS_STRING; + case KindInvalid: + case KindArray: + case KindVariant: + case KindStruct: + case KindDictEntry: + default: + assert(false); + return ""; + } + } + +private: + + Kind _kind; +}; + +class MessageI : public Message +{ +public: + + static MessagePtr wrap(DBusMessage* m) + { + return new MessageI(m, false); + } + + static MessagePtr adopt(DBusMessage* m) + { + return new MessageI(m, true); + } + + virtual ~MessageI() + { + ::dbus_message_unref(_message); + } + + virtual bool isError() const + { + const int t = ::dbus_message_get_type(const_cast<DBusMessage*>(_message)); + return t == DBUS_MESSAGE_TYPE_ERROR; + } + + virtual string getErrorName() const + { + const char* name = ::dbus_message_get_error_name(const_cast<DBusMessage*>(_message)); + return name ? name : string(); + } + + virtual void throwException() + { + assert(isError()); + + // + // Format the error name and any arguments into a string. + // + ostringstream ostr; + ostr << getErrorName(); + ValuePtr v = read(); + if(v) + { + ostr << ":" << endl << v; + } + throw ExceptionI(ostr.str()); + } + + virtual bool isSignal() const + { + const int t = ::dbus_message_get_type(const_cast<DBusMessage*>(_message)); + return t == DBUS_MESSAGE_TYPE_SIGNAL; + } + + virtual bool isMethodCall() const + { + const int t = ::dbus_message_get_type(const_cast<DBusMessage*>(_message)); + return t == DBUS_MESSAGE_TYPE_METHOD_CALL; + } + + virtual bool isMethodReturn() const + { + const int t = ::dbus_message_get_type(const_cast<DBusMessage*>(_message)); + return t == DBUS_MESSAGE_TYPE_METHOD_RETURN; + } + + virtual string getPath() const + { + const char* s = ::dbus_message_get_path(const_cast<DBusMessage*>(_message)); + return s ? string(s) : string(); + } + + virtual string getInterface() const + { + const char* s = ::dbus_message_get_interface(const_cast<DBusMessage*>(_message)); + return s ? string(s) : string(); + } + + virtual string getMember() const + { + const char* s = ::dbus_message_get_member(const_cast<DBusMessage*>(_message)); + return s ? string(s) : string(); + } + + virtual string getDestination() const + { + const char* s = ::dbus_message_get_destination(const_cast<DBusMessage*>(_message)); + return s ? string(s) : string(); + } + + virtual void write(const ValuePtr& v) + { + DBusMessageIter iter; + ::dbus_message_iter_init_append(_message, &iter); + writeValue(v, &iter); + } + + virtual void write(const vector<ValuePtr>& v) + { + DBusMessageIter iter; + ::dbus_message_iter_init_append(_message, &iter); + for(vector<ValuePtr>::const_iterator p = v.begin(); p != v.end(); ++p) + { + writeValue(*p, &iter); + } + } + + virtual bool checkTypes(const vector<TypePtr>& types) const + { + string msgSig = ::dbus_message_get_signature(_message); + string sig; + for(vector<TypePtr>::const_iterator p = types.begin(); p != types.end(); ++p) + { + sig += (*p)->getSignature(); + } + return sig == msgSig; + } + + virtual ValuePtr read() + { + // + // Read a single value. + // + + TypePtr type = buildType(); // Build a type from the message's signature. + if(!type) + { + return 0; + } + assert(_iterators.empty()); + _iterators.push(DBusMessageIter()); + _iter = &_iterators.top(); + ::dbus_message_iter_init(_message, _iter); + ValuePtr v = readValue(type); + assert(_iterators.size() == 1); + _iterators.pop(); + return v; + } + + virtual vector<ValuePtr> readAll() + { + vector<TypePtr> types = buildTypes(); // Build types from the message's signature. + + assert(_iterators.empty()); + _iterators.push(DBusMessageIter()); + _iter = &_iterators.top(); + ::dbus_message_iter_init(_message, _iter); + + vector<ValuePtr> values; + for(vector<TypePtr>::iterator p = types.begin(); p != types.end(); ++p) + { + values.push_back(readValue(*p)); + next(); + } + + assert(_iterators.size() == 1); + _iterators.pop(); + + return values; + } + + DBusMessage* message() + { + return _message; + } + +private: + + MessageI(DBusMessage* m, bool adopt) : + _message(m), + _iter(0) + { + assert(_message); + if(!adopt) + { + ::dbus_message_ref(m); // Bump the reference count. + } + } + + vector<TypePtr> buildTypes() + { + vector<TypePtr> types; + + string sig = ::dbus_message_get_signature(_message); + + string::iterator p = sig.begin(); + while(p != sig.end()) + { + types.push_back(buildType(p)); + } + + return types; + } + + TypePtr buildType() + { + string sig = ::dbus_message_get_signature(_message); + if(sig.empty()) + { + return 0; + } + string::iterator p = sig.begin(); + return buildType(p); + } + + TypePtr buildType(string::iterator& iter) + { + string::value_type ch = *iter++; + switch(ch) + { + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_BYTE: + case DBUS_TYPE_UINT16: + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT32: + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_INT64: + case DBUS_TYPE_DOUBLE: + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + case DBUS_TYPE_UNIX_FD: + return Type::getPrimitive(convertKind(ch)); + case DBUS_TYPE_ARRAY: + { + TypePtr elem = buildType(iter); + return new ArrayType(elem); + } + case DBUS_TYPE_VARIANT: + { + return new VariantType; + } + case '(': // Struct + { + vector<TypePtr> types; + while(*iter != ')') + { + types.push_back(buildType(iter)); + } + assert(*iter == ')'); + ++iter; + return new StructType(types); + } + case '{': // Dict entry + { + TypePtr key, value; + key = buildType(iter); + value = buildType(iter); + assert(*iter == '}'); + ++iter; + return new DictEntryType(key, value); + } + case DBUS_TYPE_INVALID: + assert(false); + break; + } + + return 0; + } + + void writeValue(const ValuePtr& p, DBusMessageIter* iter) + { + switch(p->getType()->getKind()) + { + case Type::KindBoolean: + { + BooleanValuePtr v = BooleanValuePtr::dynamicCast(p); + assert(v); + const dbus_bool_t b = v->v ? TRUE : FALSE; + ::dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &b); + break; + } + case Type::KindByte: + { + ByteValuePtr v = ByteValuePtr::dynamicCast(p); + assert(v); + ::dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &v->v); + break; + } + case Type::KindUint16: + { + Uint16ValuePtr v = Uint16ValuePtr::dynamicCast(p); + assert(v); + ::dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &v->v); + break; + } + case Type::KindInt16: + { + Int16ValuePtr v = Int16ValuePtr::dynamicCast(p); + assert(v); + ::dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, &v->v); + break; + } + case Type::KindUint32: + { + Uint32ValuePtr v = Uint32ValuePtr::dynamicCast(p); + assert(v); + ::dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &v->v); + break; + } + case Type::KindInt32: + { + Int32ValuePtr v = Int32ValuePtr::dynamicCast(p); + assert(v); + ::dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &v->v); + break; + } + case Type::KindUint64: + { + Uint64ValuePtr v = Uint64ValuePtr::dynamicCast(p); + assert(v); + ::dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &v->v); + break; + } + case Type::KindInt64: + { + Int64ValuePtr v = Int64ValuePtr::dynamicCast(p); + assert(v); + ::dbus_message_iter_append_basic(iter, DBUS_TYPE_INT64, &v->v); + break; + } + case Type::KindDouble: + { + DoubleValuePtr v = DoubleValuePtr::dynamicCast(p); + assert(v); + ::dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &v->v); + break; + } + case Type::KindString: + { + StringValuePtr v = StringValuePtr::dynamicCast(p); + assert(v); + const char* s = v->v.c_str(); + ::dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &s); + break; + } + case Type::KindObjectPath: + { + ObjectPathValuePtr v = ObjectPathValuePtr::dynamicCast(p); + assert(v); + const char* s = v->v.c_str(); + ::dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &s); + break; + } + case Type::KindSignature: + { + SignatureValuePtr v = SignatureValuePtr::dynamicCast(p); + assert(v); + const char* s = v->v.c_str(); + ::dbus_message_iter_append_basic(iter, DBUS_TYPE_SIGNATURE, &s); + break; + } + case Type::KindUnixFD: + { + UnixFDValuePtr v = UnixFDValuePtr::dynamicCast(p); + assert(v); + ::dbus_message_iter_append_basic(iter, DBUS_TYPE_UNIX_FD, &v->v); + break; + } + case Type::KindArray: + { + ArrayTypePtr t = ArrayTypePtr::dynamicCast(p->getType()); + assert(t); + ArrayValuePtr arr = ArrayValuePtr::dynamicCast(p); + assert(arr); + + string sig = t->elementType->getSignature(); + + DBusMessageIter sub; + if(!::dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, sig.c_str(), &sub)) + { + throw ExceptionI("out of memory while calling dbus_message_iter_open_container"); + } + for(vector<ValuePtr>::iterator q = arr->elements.begin(); q != arr->elements.end(); ++q) + { + writeValue(*q, &sub); + } + if(!::dbus_message_iter_close_container(iter, &sub)) + { + throw ExceptionI("out of memory while calling dbus_message_iter_close_container"); + } + break; + } + case Type::KindVariant: + { + VariantValuePtr v = VariantValuePtr::dynamicCast(p); + assert(v); + + string sig = v->v->getType()->getSignature(); + + DBusMessageIter sub; + if(!::dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig.c_str(), &sub)) + { + throw ExceptionI("out of memory while calling dbus_message_iter_open_container"); + } + writeValue(v->v, &sub); + if(!::dbus_message_iter_close_container(iter, &sub)) + { + throw ExceptionI("out of memory while calling dbus_message_iter_close_container"); + } + break; + } + case Type::KindStruct: + { + StructValuePtr v = StructValuePtr::dynamicCast(p); + assert(v); + + DBusMessageIter sub; + if(!::dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, 0, &sub)) + { + throw ExceptionI("out of memory while calling dbus_message_iter_open_container"); + } + for(vector<ValuePtr>::iterator q = v->members.begin(); q != v->members.end(); ++q) + { + writeValue(*q, &sub); + } + if(!::dbus_message_iter_close_container(iter, &sub)) + { + throw ExceptionI("out of memory while calling dbus_message_iter_close_container"); + } + break; + } + case Type::KindDictEntry: + { + DictEntryValuePtr v = DictEntryValuePtr::dynamicCast(p); + assert(v); + + DBusMessageIter sub; + if(!::dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, 0, &sub)) + { + throw ExceptionI("out of memory while calling dbus_message_iter_open_container"); + } + writeValue(v->key, &sub); + writeValue(v->value, &sub); + if(!::dbus_message_iter_close_container(iter, &sub)) + { + throw ExceptionI("out of memory while calling dbus_message_iter_close_container"); + } + break; + } + case Type::KindInvalid: + default: + assert(false); + break; + } + } + + void next() + { + ::dbus_message_iter_next(_iter); + } + + static Type::Kind convertKind(int t) + { + switch(t) + { + case DBUS_TYPE_INVALID: + return Type::KindInvalid; + case DBUS_TYPE_BOOLEAN: + return Type::KindBoolean; + case DBUS_TYPE_BYTE: + return Type::KindByte; + case DBUS_TYPE_UINT16: + return Type::KindUint16; + case DBUS_TYPE_INT16: + return Type::KindInt16; + case DBUS_TYPE_UINT32: + return Type::KindUint32; + case DBUS_TYPE_INT32: + return Type::KindInt32; + case DBUS_TYPE_UINT64: + return Type::KindUint64; + case DBUS_TYPE_INT64: + return Type::KindInt64; + case DBUS_TYPE_DOUBLE: + return Type::KindDouble; + case DBUS_TYPE_STRING: + return Type::KindString; + case DBUS_TYPE_OBJECT_PATH: + return Type::KindObjectPath; + case DBUS_TYPE_SIGNATURE: + return Type::KindSignature; + case DBUS_TYPE_UNIX_FD: + return Type::KindUnixFD; + case DBUS_TYPE_ARRAY: + return Type::KindArray; + case DBUS_TYPE_VARIANT: + return Type::KindVariant; + case DBUS_TYPE_STRUCT: + return Type::KindStruct; + case DBUS_TYPE_DICT_ENTRY: + return Type::KindDictEntry; + default: + throw ExceptionI("unknown arg type"); + return Type::KindInvalid; + } + } + + Type::Kind currentKind() const + { + int t = ::dbus_message_iter_get_arg_type(const_cast<DBusMessageIter*>(_iter)); + return convertKind(t); + } + + ValuePtr readValue(const TypePtr& t) + { + switch(t->getKind()) + { + case Type::KindInvalid: + assert(false); + return 0; + case Type::KindBoolean: + { + bool v; + ::dbus_message_iter_get_basic(_iter, &v); + return new BooleanValue(v); + } + case Type::KindByte: + { + unsigned char v; + ::dbus_message_iter_get_basic(_iter, &v); + return new ByteValue(v); + } + case Type::KindUint16: + { + unsigned short v; + ::dbus_message_iter_get_basic(_iter, &v); + return new Uint16Value(v); + } + case Type::KindInt16: + { + short v; + ::dbus_message_iter_get_basic(_iter, &v); + return new Int16Value(v); + } + case Type::KindUint32: + { + unsigned int v; + ::dbus_message_iter_get_basic(_iter, &v); + return new Uint32Value(v); + } + case Type::KindInt32: + { + int v; + ::dbus_message_iter_get_basic(_iter, &v); + return new Int32Value(v); + } + case Type::KindUint64: + { + unsigned long long v; + ::dbus_message_iter_get_basic(_iter, &v); + return new Uint64Value(v); + } + case Type::KindInt64: + { + long long v; + ::dbus_message_iter_get_basic(_iter, &v); + return new Int64Value(v); + } + case Type::KindDouble: + { + double v; + ::dbus_message_iter_get_basic(_iter, &v); + return new DoubleValue(v); + } + case Type::KindString: + { + char* str; + ::dbus_message_iter_get_basic(_iter, &str); + return new StringValue(str); + } + case Type::KindObjectPath: + { + char* str; + ::dbus_message_iter_get_basic(_iter, &str); + return new ObjectPathValue(str); + } + case Type::KindSignature: + { + char* str; + ::dbus_message_iter_get_basic(_iter, &str); + return new SignatureValue(str); + } + case Type::KindUnixFD: + { + unsigned int v; + ::dbus_message_iter_get_basic(_iter, &v); + return new UnixFDValue(v); + } + case Type::KindArray: + { + ArrayTypePtr arr = ArrayTypePtr::dynamicCast(t); + assert(arr); + pushIter(); + ArrayValuePtr v = new ArrayValue(arr); + while(true) + { + Type::Kind k = currentKind(); + if(k == Type::KindInvalid) + { + break; + } + assert(k == arr->elementType->getKind()); + v->elements.push_back(readValue(arr->elementType)); + next(); + } + popIter(); + return v; + } + case Type::KindVariant: + { + pushIter(); + // + // Get the type signature of this variant's value. + // + string sig = ::dbus_message_iter_get_signature(_iter); + string::iterator p = sig.begin(); + TypePtr t = buildType(p); + VariantValuePtr v = new VariantValue; + v->v = readValue(t); + popIter(); + return v; + } + case Type::KindStruct: + { + StructTypePtr st = StructTypePtr::dynamicCast(t); + assert(st); + pushIter(); + StructValuePtr v = new StructValue(st); + for(vector<TypePtr>::iterator p = st->memberTypes.begin(); p != st->memberTypes.end(); ++p) + { + v->members.push_back(readValue(*p)); + next(); + } + popIter(); + return v; + } + case Type::KindDictEntry: + { + DictEntryTypePtr dt = DictEntryTypePtr::dynamicCast(t); + assert(dt); + pushIter(); + DictEntryValuePtr v = new DictEntryValue(dt); + v->key = readValue(dt->keyType); + next(); + v->value = readValue(dt->valueType); + popIter(); + return v; + } + default: + assert(false); + return 0; + } + } + + void pushIter() + { + DBusMessageIter* parent = _iter; + _iterators.push(DBusMessageIter()); + _iter = &_iterators.top(); + ::dbus_message_iter_recurse(parent, _iter); + } + + void popIter() + { + assert(_iterators.size() > 1); + _iterators.pop(); + _iter = &_iterators.top(); + } + + DBusMessage* _message; + stack<DBusMessageIter> _iterators; + DBusMessageIter* _iter; +}; +typedef IceUtil::Handle<MessageI> MessageIPtr; + +static void pendingCallback(DBusPendingCall*, void*); + +class AsyncResultI : public AsyncResult +{ +public: + + AsyncResultI(DBusPendingCall* call, const AsyncCallbackPtr& cb) : + _call(call), + _callback(cb), + _status(StatusPending) + { + if(!::dbus_pending_call_set_notify(_call, pendingCallback, this, 0)) + { + throw ExceptionI("dbus_pending_call_set_notify failed"); + } + // + // Bump our refcount to ensure this object lives until the reply is received. + // + __incRef(); + } + + ~AsyncResultI() + { + ::dbus_pending_call_unref(_call); + } + + virtual bool isPending() const + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + return _status == StatusPending; + } + + virtual bool isComplete() const + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + return _status == StatusComplete; + } + + virtual MessagePtr waitUntilFinished() const + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + while(_status == StatusPending) + { + _lock.wait(); + } + return _reply; + } + + virtual MessagePtr getReply() const + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + return _reply; + } + + virtual void setCallback(const AsyncCallbackPtr& cb) + { + bool call = false; + + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + _callback = cb; + if(_status == StatusComplete) + { + call = true; + } + } + + if(call) + { + try + { + cb->completed(this); + } + catch(...) + { + } + } + } + + void replyReceived() + { + assert(::dbus_pending_call_get_completed(_call)); + DBusMessage* m = ::dbus_pending_call_steal_reply(_call); + assert(m); + + AsyncCallbackPtr cb; + + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + _reply = MessageI::adopt(m); + _status = StatusComplete; + cb = _callback; + _lock.notifyAll(); + } + + if(cb) + { + try + { + cb->completed(this); + } + catch(...) + { + } + } + + // + // Decrement our refcount (see constructor). This object may be destroyed immediately. + // + __decRef(); + } + +private: + + IceUtil::Monitor<IceUtil::Mutex> _lock; + DBusPendingCall* _call; + AsyncCallbackPtr _callback; + + enum Status { StatusPending, StatusComplete }; + Status _status; + + MessagePtr _reply; +}; + +static void +pendingCallback(DBusPendingCall*, void* userData) +{ + AsyncResultI* r = static_cast<AsyncResultI*>(userData); + assert(r); + r->replyReceived(); +} + +static DBusHandlerResult filterCallback(DBusConnection*, DBusMessage*, void*); +static void freeConnection(void*); + +class ConnectionI; +typedef IceUtil::Handle<ConnectionI> ConnectionIPtr; + +class ConnectionI : public Connection +{ +public: + + ConnectionI() : + _connection(0), + _closed(false) + { + } + + virtual ~ConnectionI() + { + if(_connection) + { + ::dbus_connection_unref(_connection); + } + } + + virtual void addFilter(const FilterPtr& f) + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + + _filters.push_back(f); + } + + virtual void removeFilter(const FilterPtr& f) + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + + for(vector<FilterPtr>::iterator p = _filters.begin(); p != _filters.end(); ++p) + { + if(p->get() == f.get()) + { + _filters.erase(p); + break; + } + } + } + + virtual void addService(const string& path, const ServicePtr& s) + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + + map<string, ServicePtr>::iterator p = _services.find(path); + if(p != _services.end()) + { + throw ExceptionI("service with path `" + path + "' already registered"); + } + _services[path] = s; + } + + virtual void removeService(const string& path) + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + + map<string, ServicePtr>::iterator p = _services.find(path); + if(p != _services.end()) + { + _services.erase(p); + } + } + + virtual AsyncResultPtr callAsync(const MessagePtr& m, const AsyncCallbackPtr& cb) + { + MessageIPtr mi = MessageIPtr::dynamicCast(m); + assert(mi); + + DBusPendingCall* call; + if(!::dbus_connection_send_with_reply(_connection, mi->message(), &call, -1)) + { + throw ExceptionI("dbus_connection_send_with_reply failed"); + } + if(!call) + { + throw ExceptionI("dbus_connection_send_with_reply failed - disconnected?"); + } + return new AsyncResultI(call, cb); + } + + virtual void sendAsync(const MessagePtr& m) + { + MessageIPtr mi = MessageIPtr::dynamicCast(m); + assert(mi); + + // + // D-Bus queues the message without blocking. + // + dbus_uint32_t serial; + if(!::dbus_connection_send(_connection, mi->message(), &serial)) + { + throw ExceptionI("dbus_connection_send failed"); + } + } + + virtual void close() + { + ::dbus_connection_close(_connection); + + // + // Send the "close" message. + // + while(::dbus_connection_dispatch(_connection) == DBUS_DISPATCH_DATA_REMAINS) + ; + + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + _closed = true; + _services.clear(); + } + + _thread->getThreadControl().join(); + } + + void connect(bool system) + { + ErrorWrapper err; + + _connection = ::dbus_bus_get_private(system ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &err.err); + if(err.isSet()) + { + throw ExceptionI(err); + } + assert(_connection); + + ::dbus_connection_set_exit_on_disconnect(_connection, FALSE); + + if(!::dbus_connection_add_filter(_connection, filterCallback, this, freeConnection)) + { + throw ExceptionI("out of memory calling dbus_connection_add_filter"); + } + + // + // The filter function will only see the message types that we add below. + // + ::dbus_bus_add_match(_connection, "type='signal'", 0); + //::dbus_bus_add_match(_connection, "type='method_call'", 0); + + __incRef(); // __decRef called in freeConnection. + + _thread = new HelperThread(this); + _thread->start(); + } + + DBusConnection* connection() + { + return _connection; + } + + DBusHandlerResult handleMessage(DBusMessage* m) + { + vector<FilterPtr> filters; + map<string, ServicePtr> services; + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + filters = _filters; + services = _services; + } + + MessagePtr msg = MessageI::wrap(m); + for(vector<FilterPtr>::iterator p = filters.begin(); p != filters.end(); ++p) + { + try + { + if((*p)->handleMessage(this, msg)) + { + return DBUS_HANDLER_RESULT_HANDLED; + } + } + catch(...) + { + // Ignore. + } + } + + if(msg->isMethodCall()) + { + map<string, ServicePtr>::iterator p = services.find(msg->getPath()); + if(p != _services.end()) + { + try + { + p->second->handleMethodCall(this, msg); + } + catch(...) + { + // Ignore. + } + return DBUS_HANDLER_RESULT_HANDLED; + } + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + +private: + + void run() + { + while(::dbus_connection_read_write_dispatch(_connection, 200)) + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + if(_closed) + { + break; + } + } + } + + class HelperThread : public IceUtil::Thread + { + public: + + HelperThread(ConnectionI* con) : + _con(con) + { + } + + void run() + { + _con->run(); + } + + private: + + ConnectionI* _con; + }; + + friend class HelperThread; + + DBusConnection* _connection; + IceUtil::ThreadPtr _thread; + IceUtil::Monitor<IceUtil::Mutex> _lock; + bool _closed; + vector<FilterPtr> _filters; + map<string, ServicePtr> _services; +}; + +static DBusHandlerResult +filterCallback(DBusConnection*, DBusMessage* message, void* userData) +{ + ConnectionI* c = static_cast<ConnectionI*>(userData); + assert(c); + return c->handleMessage(message); +} + +static void freeConnection(void* p) +{ + ConnectionI* c = static_cast<ConnectionI*>(p); + assert(c); + c->__decRef(); +} + +} + +TypePtr +IceBT::DBus::Type::getPrimitive(Kind k) +{ + switch(k) + { + case KindBoolean: + case KindByte: + case KindUint16: + case KindInt16: + case KindUint32: + case KindInt32: + case KindUint64: + case KindInt64: + case KindDouble: + case KindString: + case KindObjectPath: + case KindSignature: + case KindUnixFD: + return new PrimitiveType(k); + case KindInvalid: + case KindArray: + case KindVariant: + case KindStruct: + case KindDictEntry: + default: + assert(false); + return 0; + } +} + +string +IceBT::DBus::ArrayType::getSignature() const +{ + string r = DBUS_TYPE_ARRAY_AS_STRING; + r += elementType->getSignature(); + return r; +} + +string +IceBT::DBus::VariantType::getSignature() const +{ + return DBUS_TYPE_VARIANT_AS_STRING; +} + +string +IceBT::DBus::StructType::getSignature() const +{ + string r = "("; + for(vector<TypePtr>::const_iterator p = memberTypes.begin(); p != memberTypes.end(); ++p) + { + r += (*p)->getSignature(); + } + r += ")"; + return r; +} + +string +IceBT::DBus::DictEntryType::getSignature() const +{ + string r = "{"; + r += keyType->getSignature(); + r += valueType->getSignature(); + r += "}"; + return r; +} + +MessagePtr +IceBT::DBus::Message::createCall(const string& dest, const string& path, const string& iface, const string& method) +{ + assert(!path.empty() && !method.empty()); + const char* sdest = dest.empty() ? 0 : dest.c_str(); + const char* spath = path.c_str(); + const char* siface = iface.empty() ? 0 : iface.c_str(); + const char* smethod = method.c_str(); + DBusMessage* m = ::dbus_message_new_method_call(sdest, spath, siface, smethod); + if(!m) + { + throw ExceptionI("failure creating DBus method call"); + } + return MessageI::adopt(m); +} + +MessagePtr +IceBT::DBus::Message::createReturn(const MessagePtr& call) +{ + MessageIPtr c = MessageIPtr::dynamicCast(call); + assert(c); + DBusMessage* r = ::dbus_message_new_method_return(c->message()); + if(!r) + { + throw ExceptionI("failure creating DBus method return"); + } + return MessageI::adopt(r); +} + +ConnectionPtr +IceBT::DBus::Connection::getSystemBus() +{ + ConnectionI* conn = new ConnectionI; + conn->connect(true); + return conn; +} + +ConnectionPtr +IceBT::DBus::Connection::getSessionBus() +{ + ConnectionI* conn = new ConnectionI; + conn->connect(false); + return conn; +} + +void +IceBT::DBus::initThreads() +{ + ::dbus_threads_init_default(); +} diff --git a/cpp/src/IceBT/DBus.h b/cpp/src/IceBT/DBus.h new file mode 100644 index 00000000000..2d2a3559790 --- /dev/null +++ b/cpp/src/IceBT/DBus.h @@ -0,0 +1,558 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICE_BT_DBUS_H +#define ICE_BT_DBUS_H + +#include <IceUtil/Shared.h> +#include <Ice/Handle.h> + +using namespace std; + +namespace IceBT +{ +namespace DBus +{ + +class Exception +{ +public: + + std::string reason; + +protected: + + Exception() {} + Exception(const std::string& s) : reason(s) {} +}; + +// +// Type is the base class for a hierarchy representing DBus data types. +// +class Type; +typedef IceUtil::Handle<Type> TypePtr; + +class Type : public IceUtil::SimpleShared +{ +public: + + enum Kind + { + KindInvalid, + KindBoolean, + KindByte, + KindUint16, + KindInt16, + KindUint32, + KindInt32, + KindUint64, + KindInt64, + KindDouble, + KindString, + KindObjectPath, + KindSignature, + KindUnixFD, + KindArray, + KindVariant, + KindStruct, + KindDictEntry + }; + + static TypePtr getPrimitive(Kind); + + virtual Kind getKind() const = 0; + virtual std::string getSignature() const = 0; + +protected: + + Type() {} +}; + +class ArrayType : public Type +{ +public: + + ArrayType(const TypePtr& t) : + elementType(t) + { + } + + virtual Kind getKind() const + { + return KindArray; + } + + virtual std::string getSignature() const; + + TypePtr elementType; +}; +typedef IceUtil::Handle<ArrayType> ArrayTypePtr; + +class VariantType : public Type +{ +public: + + VariantType() {} + + virtual Kind getKind() const + { + return KindVariant; + } + + virtual std::string getSignature() const; +}; +typedef IceUtil::Handle<VariantType> VariantTypePtr; + +class StructType : public Type +{ +public: + + StructType(const std::vector<TypePtr>& types) : + memberTypes(types) + { + } + + virtual Kind getKind() const + { + return KindStruct; + } + + virtual std::string getSignature() const; + + std::vector<TypePtr> memberTypes; +}; +typedef IceUtil::Handle<StructType> StructTypePtr; + +class DictEntryType : public Type +{ +public: + + DictEntryType(const TypePtr& k, const TypePtr& v) : + keyType(k), + valueType(v) + { + } + + virtual Kind getKind() const + { + return KindDictEntry; + } + + virtual std::string getSignature() const; + + TypePtr keyType; + TypePtr valueType; +}; +typedef IceUtil::Handle<DictEntryType> DictEntryTypePtr; + +// +// Value is the base class of a hierarchy representing DBus data values. +// +class Value; +typedef IceUtil::Handle<Value> ValuePtr; + +class Value : public IceUtil::SimpleShared +{ +public: + + virtual TypePtr getType() const = 0; + + virtual ValuePtr clone() const = 0; + +protected: + + virtual void print(std::ostream&) = 0; + + friend std::ostream& operator<<(std::ostream&, const ValuePtr&); +}; + +inline std::ostream& +operator<<(std::ostream& ostr, const ValuePtr& v) +{ + if(v) + { + v->print(ostr); + } + else + { + ostr << "nil"; + } + return ostr; +} + +template<typename E, Type::Kind K> +class PrimitiveValue : public Value +{ +public: + + PrimitiveValue() : v(E()), kind(K) {} + PrimitiveValue(const E& val) : v(val), kind(K) {} + + virtual TypePtr getType() const + { + return Type::getPrimitive(kind); + } + + virtual ValuePtr clone() const + { + return new PrimitiveValue(v); + } + + E v; + Type::Kind kind; + +protected: + + virtual void print(std::ostream& ostr) + { + ostr << v; + } +}; + +typedef PrimitiveValue<bool, Type::KindBoolean> BooleanValue; +typedef IceUtil::Handle<BooleanValue> BooleanValuePtr; +typedef PrimitiveValue<unsigned char, Type::KindByte> ByteValue; +typedef IceUtil::Handle<ByteValue> ByteValuePtr; +typedef PrimitiveValue<unsigned short, Type::KindUint16> Uint16Value; +typedef IceUtil::Handle<Uint16Value> Uint16ValuePtr; +typedef PrimitiveValue<short, Type::KindInt16> Int16Value; +typedef IceUtil::Handle<Int16Value> Int16ValuePtr; +typedef PrimitiveValue<unsigned int, Type::KindUint32> Uint32Value; +typedef IceUtil::Handle<Uint32Value> Uint32ValuePtr; +typedef PrimitiveValue<int, Type::KindInt32> Int32Value; +typedef IceUtil::Handle<Int32Value> Int32ValuePtr; +typedef PrimitiveValue<unsigned long long, Type::KindUint64> Uint64Value; +typedef IceUtil::Handle<Uint64Value> Uint64ValuePtr; +typedef PrimitiveValue<long long, Type::KindInt64> Int64Value; +typedef IceUtil::Handle<Int64Value> Int64ValuePtr; +typedef PrimitiveValue<double, Type::KindDouble> DoubleValue; +typedef IceUtil::Handle<DoubleValue> DoubleValuePtr; +typedef PrimitiveValue<string, Type::KindString> StringValue; +typedef IceUtil::Handle<StringValue> StringValuePtr; +typedef PrimitiveValue<string, Type::KindObjectPath> ObjectPathValue; +typedef IceUtil::Handle<ObjectPathValue> ObjectPathValuePtr; +typedef PrimitiveValue<string, Type::KindSignature> SignatureValue; +typedef IceUtil::Handle<SignatureValue> SignatureValuePtr; +typedef PrimitiveValue<unsigned int, Type::KindUnixFD> UnixFDValue; +typedef IceUtil::Handle<UnixFDValue> UnixFDValuePtr; + +class VariantValue; +typedef IceUtil::Handle<VariantValue> VariantValuePtr; + +class VariantValue : public Value +{ +public: + + VariantValue() : _type(new VariantType) {} + + VariantValue(const ValuePtr& val) : + v(val), + _type(new VariantType) + { + } + + virtual TypePtr getType() const + { + return _type; + } + + virtual ValuePtr clone() const + { + return const_cast<VariantValue*>(this); + } + + ValuePtr v; + +protected: + + virtual void print(std::ostream& ostr) + { + ostr << v; + } + +private: + + TypePtr _type; +}; + +class DictEntryValue; +typedef IceUtil::Handle<DictEntryValue> DictEntryValuePtr; + +class DictEntryValue : public Value +{ +public: + + DictEntryValue(const DictEntryTypePtr& t) : _type(t) {} + + DictEntryValue(const DictEntryTypePtr& t, const ValuePtr& k, const ValuePtr& v) : + key(k), + value(v), + _type(t) + { + } + + virtual TypePtr getType() const + { + return _type; + } + + virtual ValuePtr clone() const + { + DictEntryValuePtr r = new DictEntryValue(_type); + r->key = key->clone(); + r->value = value->clone(); + return r; + } + + ValuePtr key; + ValuePtr value; + +protected: + + virtual void print(std::ostream& ostr) + { + ostr << '{' << key << ": " << value << '}' << endl; + } + +private: + + DictEntryTypePtr _type; +}; + +class ArrayValue; +typedef IceUtil::Handle<ArrayValue> ArrayValuePtr; + +class ArrayValue : public Value +{ +public: + + ArrayValue(const TypePtr& t) : _type(t) {} + + virtual TypePtr getType() const + { + return _type; + } + + virtual ValuePtr clone() const + { + ArrayValuePtr r = new ArrayValue(_type); + for(std::vector<ValuePtr>::const_iterator p = elements.begin(); p != elements.end(); ++p) + { + r->elements.push_back((*p)->clone()); + } + return r; + } + + void toStringMap(std::map<std::string, ValuePtr>& m) + { + for(std::vector<ValuePtr>::const_iterator p = elements.begin(); p != elements.end(); ++p) + { + DictEntryValuePtr de = DictEntryValuePtr::dynamicCast(*p); + assert(de); + StringValuePtr s = StringValuePtr::dynamicCast(de->key); + assert(s); + m[s->v] = de->value; + } + } + + std::vector<ValuePtr> elements; + +protected: + + virtual void print(std::ostream& ostr) + { + for(std::vector<ValuePtr>::const_iterator p = elements.begin(); p != elements.end(); ++p) + { + ostr << *p << endl; + } + } + +private: + + TypePtr _type; +}; + +class StructValue; +typedef IceUtil::Handle<StructValue> StructValuePtr; + +class StructValue : public Value +{ +public: + + StructValue(const StructTypePtr& t) : _type(t) {} + + virtual TypePtr getType() const + { + return _type; + } + + virtual ValuePtr clone() const + { + StructValuePtr r = new StructValue(_type); + for(std::vector<ValuePtr>::const_iterator p = members.begin(); p != members.end(); ++p) + { + r->members.push_back((*p)->clone()); + } + return r; + } + + std::vector<ValuePtr> members; + +protected: + + virtual void print(std::ostream& ostr) + { + for(std::vector<ValuePtr>::const_iterator p = members.begin(); p != members.end(); ++p) + { + ostr << *p << endl; + } + } + +private: + + StructTypePtr _type; +}; + +// +// Message encapsulates a DBus message. It only provides the functionality required by the IceBT transport. +// +class Message; +typedef IceUtil::Handle<Message> MessagePtr; + +class Message : public IceUtil::Shared +{ +public: + + virtual bool isError() const = 0; + virtual std::string getErrorName() const = 0; + virtual void throwException() = 0; + + virtual bool isSignal() const = 0; + virtual bool isMethodCall() const = 0; + virtual bool isMethodReturn() const = 0; + + virtual std::string getPath() const = 0; + virtual std::string getInterface() const = 0; + virtual std::string getMember() const = 0; + virtual std::string getDestination() const = 0; + + // + // Writing arguments. + // + virtual void write(const ValuePtr&) = 0; + virtual void write(const std::vector<ValuePtr>&) = 0; + + // + // Reading arguments. + // + virtual bool checkTypes(const std::vector<TypePtr>&) const = 0; + virtual ValuePtr read() = 0; + virtual std::vector<ValuePtr> readAll() = 0; + + static MessagePtr createCall(const string& dest, const string& path, const string& iface, const string& method); + static MessagePtr createReturn(const MessagePtr& call); +}; + +class AsyncResult; +typedef IceUtil::Handle<AsyncResult> AsyncResultPtr; + +class AsyncCallback : public IceUtil::Shared +{ +public: + + virtual void completed(const AsyncResultPtr&) = 0; +}; +typedef IceUtil::Handle<AsyncCallback> AsyncCallbackPtr; + +// +// The result of an asynchronous DBus operation. +// +class AsyncResult : public IceUtil::Shared +{ +public: + + virtual bool isPending() const = 0; + virtual bool isComplete() const = 0; + + virtual MessagePtr waitUntilFinished() const = 0; + + virtual MessagePtr getReply() const = 0; + + virtual void setCallback(const AsyncCallbackPtr&) = 0; +}; + +class Connection; +typedef IceUtil::Handle<Connection> ConnectionPtr; + +// +// Allows a subclass to intercept DBus messages. +// +class Filter : public IceUtil::Shared +{ +public: + + // + // Return true if message is handled or false otherwise. + // + virtual bool handleMessage(const ConnectionPtr&, const MessagePtr&) = 0; +}; +typedef IceUtil::Handle<Filter> FilterPtr; + +// +// Allows a subclass to receive DBus method invocations. +// +class Service : public IceUtil::Shared +{ +public: + + virtual void handleMethodCall(const ConnectionPtr&, const MessagePtr&) = 0; +}; +typedef IceUtil::Handle<Service> ServicePtr; + +// +// Encapsulates a DBus connection. +// +class Connection : public IceUtil::Shared +{ +public: + + static ConnectionPtr getSystemBus(); + static ConnectionPtr getSessionBus(); + + virtual void addFilter(const FilterPtr&) = 0; + virtual void removeFilter(const FilterPtr&) = 0; + + virtual void addService(const std::string&, const ServicePtr&) = 0; + virtual void removeService(const std::string&) = 0; + + // + // Asynchronously invokes a method call. The returned AsyncResult can be used + // to determine completion status and obtain the reply, or supply a callback + // to be notified when the call completes. + // + virtual AsyncResultPtr callAsync(const MessagePtr&, const AsyncCallbackPtr& = 0) = 0; + + // + // Sends a message without blocking. Use this to send signals and replies. + // + virtual void sendAsync(const MessagePtr&) = 0; + + virtual void close() = 0; + +protected: + + Connection() {} +}; + +void initThreads(); + +} +} + +#endif diff --git a/cpp/src/IceBT/EndpointI.cpp b/cpp/src/IceBT/EndpointI.cpp new file mode 100644 index 00000000000..9efdd46656b --- /dev/null +++ b/cpp/src/IceBT/EndpointI.cpp @@ -0,0 +1,732 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <IceBT/EndpointI.h> +#include <IceBT/AcceptorI.h> +#include <IceBT/ConnectorI.h> +#include <IceBT/Engine.h> +#include <IceBT/Instance.h> +#include <IceBT/Util.h> + +#include <Ice/BasicStream.h> +#include <Ice/LocalException.h> +#include <Ice/DefaultsAndOverrides.h> +#include <Ice/HashUtil.h> +#include <Ice/Object.h> +#include <Ice/Properties.h> +#include <IceUtil/StringUtil.h> + +using namespace std; +using namespace Ice; +using namespace IceBT; + +IceUtil::Shared* IceBT::upCast(EndpointI* p) { return p; } + +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), + _addr(addr), + _uuid(uuid), + _name(name), + _channel(channel), + _timeout(timeout), + _connectionId(connectionId), + _compress(compress), + _hashValue(0), + _connectPending(false) +{ + hashInit(); +} + +IceBT::EndpointI::EndpointI(const InstancePtr& instance) : + _instance(instance), + _channel(0), + _timeout(instance->defaultTimeout()), + _compress(false), + _hashValue(0), + _connectPending(false) +{ +} + +IceBT::EndpointI::EndpointI(const InstancePtr& instance, IceInternal::BasicStream* s) : + _instance(instance), + _channel(0), + _timeout(-1), + _compress(false), + _hashValue(0), + _connectPending(false) +{ + // + // _name and _channel are not marshaled. + // + s->read(const_cast<string&>(_addr), false); + s->read(const_cast<string&>(_uuid), false); + s->read(const_cast<Int&>(_timeout)); + s->read(const_cast<bool&>(_compress)); + hashInit(); +} + +void +IceBT::EndpointI::streamWrite(IceInternal::BasicStream* s) const +{ + // + // _name and _channel are not marshaled. + // + s->startWriteEncaps(); + s->write(_addr, false); + s->write(_uuid, false); + s->write(_timeout); + s->write(_compress); + s->endWriteEncaps(); +} + +Ice::Short +IceBT::EndpointI::type() const +{ + return _instance->type(); +} + +const string& +IceBT::EndpointI::protocol() const +{ + return _instance->protocol(); +} + +Int +IceBT::EndpointI::timeout() const +{ + return _timeout; +} + +IceInternal::EndpointIPtr +IceBT::EndpointI::timeout(Int timeout) const +{ + if(timeout == _timeout) + { + return const_cast<EndpointI*>(this); + } + else + { + return new EndpointI(_instance, _addr, _uuid, _name, _channel, timeout, _connectionId, _compress); + } +} + +const string& +IceBT::EndpointI::connectionId() const +{ + return _connectionId; +} + +IceInternal::EndpointIPtr +IceBT::EndpointI::connectionId(const string& connectionId) const +{ + if(connectionId == _connectionId) + { + return const_cast<EndpointI*>(this); + } + else + { + return new EndpointI(_instance, _addr, _uuid, _name, _channel, _timeout, connectionId, _compress); + } +} + +bool +IceBT::EndpointI::compress() const +{ + return _compress; +} + +IceInternal::EndpointIPtr +IceBT::EndpointI::compress(bool compress) const +{ + if(compress == _compress) + { + return const_cast<EndpointI*>(this); + } + else + { + return new EndpointI(_instance, _addr, _uuid, _name, _channel, _timeout, _connectionId, compress); + } +} + +bool +IceBT::EndpointI::datagram() const +{ + return false; +} + +bool +IceBT::EndpointI::secure() const +{ + return _instance->secure(); +} + +IceInternal::TransceiverPtr +IceBT::EndpointI::transceiver() const +{ + return 0; +} + +void +IceBT::EndpointI::connectors_async(EndpointSelectionType selType, const IceInternal::EndpointI_connectorsPtr& cb) const +{ + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + + if(!_connectPending) + { + assert(_callbacks.empty()); + _instance->engine()->connect(_addr, _uuid, new ConnectCallbackI(const_cast<EndpointI*>(this))); + const_cast<bool&>(_connectPending) = true; + } + + const_cast<vector<IceInternal::EndpointI_connectorsPtr>&>(_callbacks).push_back(cb); +} + +IceInternal::AcceptorPtr +IceBT::EndpointI::acceptor(const string& adapterName) const +{ + return new AcceptorI(const_cast<EndpointI*>(this), _instance, adapterName, _addr, _uuid, _name, _channel); +} + +vector<IceInternal::EndpointIPtr> +IceBT::EndpointI::expand() const +{ + // + // Nothing to do here. + // + vector<IceInternal::EndpointIPtr> endps; + endps.push_back(const_cast<EndpointI*>(this)); + return endps; +} + +bool +IceBT::EndpointI::equivalent(const IceInternal::EndpointIPtr& endpoint) const +{ + const EndpointI* btEndpointI = dynamic_cast<const EndpointI*>(endpoint.get()); + if(!btEndpointI) + { + return false; + } + return btEndpointI->type() == type() && btEndpointI->_addr == _addr && btEndpointI->_uuid == _uuid; +} + +bool +IceBT::EndpointI::operator==(const Ice::LocalObject& r) const +{ + const EndpointI* p = dynamic_cast<const EndpointI*>(&r); + if(!p) + { + return false; + } + + if(this == p) + { + return true; + } + + if(_addr != p->_addr) + { + return false; + } + + if(_uuid != p->_uuid) + { + return false; + } + + if(_connectionId != p->_connectionId) + { + return false; + } + + if(_channel != p->_channel) + { + return false; + } + + if(_timeout != p->_timeout) + { + return false; + } + + if(_compress != p->_compress) + { + return false; + } + + return true; +} + +bool +IceBT::EndpointI::operator<(const Ice::LocalObject& r) const +{ + const EndpointI* p = dynamic_cast<const EndpointI*>(&r); + if(!p) + { + const IceInternal::EndpointI* e = dynamic_cast<const IceInternal::EndpointI*>(&r); + if(!e) + { + return false; + } + return type() < e->type(); + } + + if(this == p) + { + return false; + } + + if(type() < p->type()) + { + return true; + } + else if(p->type() < type()) + { + return false; + } + + if(_addr < p->_addr) + { + return true; + } + else if(p->_addr < _addr) + { + return false; + } + + if(_uuid < p->_uuid) + { + return true; + } + else if(p->_uuid < _uuid) + { + return false; + } + + if(_connectionId < p->_connectionId) + { + return true; + } + else if(p->_connectionId < _connectionId) + { + return false; + } + + if(_channel < p->_channel) + { + return true; + } + else if(p->_channel < _channel) + { + return false; + } + + if(_timeout < p->_timeout) + { + return true; + } + else if(p->_timeout < _timeout) + { + return false; + } + + if(!_compress && p->_compress) + { + return true; + } + else if(p->_compress < _compress) + { + return false; + } + + return false; +} + +Ice::Int +IceBT::EndpointI::hash() const +{ + return _hashValue; +} + +string +IceBT::EndpointI::options() const +{ + // + // WARNING: Certain features, such as proxy validation in Glacier2, + // depend on the format of proxy strings. Changes to toString() and + // methods called to generate parts of the reference string could break + // these features. Please review for all features that depend on the + // format of proxyToString() before changing this and related code. + // + ostringstream s; + + if(!_addr.empty()) + { + s << " -a "; + bool addQuote = _addr.find(':') != string::npos; + if(addQuote) + { + s << "\""; + } + s << _addr; + if(addQuote) + { + s << "\""; + } + } + + if(!_uuid.empty()) + { + s << " -u "; + bool addQuote = _uuid.find(':') != string::npos; + if(addQuote) + { + s << "\""; + } + s << _uuid; + if(addQuote) + { + s << "\""; + } + } + + if(_channel != 0) + { + s << " -c " << _channel; + } + + if(_timeout == -1) + { + s << " -t infinite"; + } + else + { + s << " -t " << _timeout; + } + + if(_compress) + { + s << " -z"; + } + + return s.str(); +} + +Ice::EndpointInfoPtr +IceBT::EndpointI::getInfo() const +{ + EndpointInfoPtr info = new EndpointInfoI(const_cast<EndpointI*>(this)); + info->addr = _addr; + info->uuid = _uuid; + return info; +} + +void +IceBT::EndpointI::initWithOptions(vector<string>& args, bool oaEndpoint) +{ + IceInternal::EndpointI::initWithOptions(args); + + if(_addr.empty()) + { + const_cast<string&>(_addr) = _instance->properties()->getProperty("IceBT.DefaultAddress"); + } + + if(oaEndpoint && _addr.empty()) + { + // + // getDefaultDeviceAddress can throw BluetoothException. + // + const_cast<string&>(_addr) = _instance->engine()->getDefaultDeviceAddress(); + } + + if(!oaEndpoint && _addr.empty()) + { + Ice::EndpointParseException ex(__FILE__, __LINE__); + ex.str = "a device address must be specified using the -a option or IceBT.DefaultAddress"; + throw ex; + } + + if(_name.empty()) + { + const_cast<string&>(_name) = "Ice Service"; + } + + if(_uuid.empty()) + { + Ice::EndpointParseException ex(__FILE__, __LINE__); + ex.str = "a UUID must be specified using the -u option"; + throw ex; + } + + if(_channel < 0) + { + const_cast<Int&>(_channel) = 0; + } + + if(!oaEndpoint && _channel != 0) + { + Ice::EndpointParseException ex(__FILE__, __LINE__); + ex.str = "the -c option can only be used for object adapter endpoints"; + throw ex; + } + + hashInit(); +} + +IceBT::EndpointIPtr +IceBT::EndpointI::endpoint(const AcceptorIPtr& acceptor) const +{ + return new EndpointI(_instance, _addr, _uuid, _name, acceptor->effectiveChannel(), _timeout, _connectionId, + _compress); +} + +void +IceBT::EndpointI::hashInit() +{ + Int h = 5381; + IceInternal::hashAdd(h, type()); + IceInternal::hashAdd(h, _addr); + IceInternal::hashAdd(h, _uuid); + IceInternal::hashAdd(h, _connectionId); + const_cast<Int&>(_hashValue) = h; +} + +bool +IceBT::EndpointI::checkOption(const string& option, const string& argument, const string& endpoint) +{ + string arg = IceUtilInternal::trim(argument); + if(option == "-a") + { + if(arg.empty()) + { + Ice::EndpointParseException ex(__FILE__, __LINE__); + ex.str = "no argument provided for -a option in endpoint " + endpoint; + throw ex; + } + if(!isValidDeviceAddress(arg)) + { + Ice::EndpointParseException ex(__FILE__, __LINE__); + ex.str = "invalid argument provided for -a option in endpoint " + endpoint; + throw ex; + } + const_cast<string&>(_addr) = arg; + } + else if(option == "-u") + { + if(arg.empty()) + { + Ice::EndpointParseException ex(__FILE__, __LINE__); + ex.str = "no argument provided for -u option in endpoint " + endpoint; + throw ex; + } + const_cast<string&>(_uuid) = arg; + } + else if(option == "-c") + { + if(arg.empty()) + { + EndpointParseException ex(__FILE__, __LINE__); + ex.str = "no argument provided for -c option in endpoint " + endpoint; + throw ex; + } + + istringstream t(argument); + if(!(t >> const_cast<Int&>(_channel)) || !t.eof() || _channel > 30) + { + EndpointParseException ex(__FILE__, __LINE__); + ex.str = "invalid channel value `" + arg + "' in endpoint " + endpoint; + throw ex; + } + } + else if(option == "-t") + { + if(arg.empty()) + { + EndpointParseException ex(__FILE__, __LINE__); + ex.str = "no argument provided for -t option in endpoint " + endpoint; + throw ex; + } + + if(arg == "infinite") + { + const_cast<Int&>(_timeout) = -1; + } + else + { + istringstream t(argument); + if(!(t >> const_cast<Int&>(_timeout)) || !t.eof() || _timeout < 1) + { + EndpointParseException ex(__FILE__, __LINE__); + ex.str = "invalid timeout value `" + arg + "' in endpoint " + endpoint; + throw ex; + } + } + } + else if(option == "-z") + { + if(!arg.empty()) + { + EndpointParseException ex(__FILE__, __LINE__); + ex.str = "unexpected argument `" + arg + "' provided for -z option in " + endpoint; + throw ex; + } + const_cast<bool&>(_compress) = true; + } + else if(option == "--name") + { + if(arg.empty()) + { + Ice::EndpointParseException ex(__FILE__, __LINE__); + ex.str = "no argument provided for --name option in endpoint " + endpoint; + throw ex; + } + const_cast<string&>(_name) = arg; + } + else + { + return false; + } + return true; +} + +void +IceBT::EndpointI::connectCompleted(int fd, const ConnectionPtr& conn) +{ + // + // 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)); + } + + vector<IceInternal::EndpointI_connectorsPtr> callbacks; + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + assert(!_callbacks.empty()); + callbacks.swap(_callbacks); + _connectPending = false; + } + + for(vector<IceInternal::EndpointI_connectorsPtr>::iterator p = callbacks.begin(); p != callbacks.end(); ++p) + { + (*p)->connectors(connectors); + } +} + +void +IceBT::EndpointI::connectFailed(const Ice::LocalException& ex) +{ + vector<IceInternal::EndpointI_connectorsPtr> callbacks; + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + assert(!_callbacks.empty()); + callbacks.swap(_callbacks); + _connectPending = 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) + { + (*p)->exception(ex); + } + } +} + +IceBT::EndpointInfoI::EndpointInfoI(const EndpointIPtr& endpoint) : _endpoint(endpoint) +{ +} + +IceBT::EndpointInfoI::~EndpointInfoI() +{ +} + +Ice::Short +IceBT::EndpointInfoI::type() const +{ + return _endpoint->type(); +} + +bool +IceBT::EndpointInfoI::datagram() const +{ + return _endpoint->datagram(); +} + +bool +IceBT::EndpointInfoI::secure() const +{ + return _endpoint->secure(); +} + +IceBT::EndpointFactoryI::EndpointFactoryI(const InstancePtr& instance) : _instance(instance) +{ +} + +IceBT::EndpointFactoryI::~EndpointFactoryI() +{ +} + +Short +IceBT::EndpointFactoryI::type() const +{ + return _instance->type(); +} + +string +IceBT::EndpointFactoryI::protocol() const +{ + return _instance->protocol(); +} + +IceInternal::EndpointIPtr +IceBT::EndpointFactoryI::create(vector<string>& args, bool oaEndpoint) const +{ + EndpointIPtr endpt = new EndpointI(_instance); + endpt->initWithOptions(args, oaEndpoint); + return endpt; +} + +IceInternal::EndpointIPtr +IceBT::EndpointFactoryI::read(IceInternal::BasicStream* s) const +{ + return new EndpointI(_instance, s); +} + +void +IceBT::EndpointFactoryI::destroy() +{ + _instance = 0; +} + +IceInternal::EndpointFactoryPtr +IceBT::EndpointFactoryI::clone(const IceInternal::ProtocolInstancePtr& instance) const +{ + return new EndpointFactoryI(new Instance(_instance->engine(), instance->type(), instance->protocol())); +} diff --git a/cpp/src/IceBT/EndpointI.h b/cpp/src/IceBT/EndpointI.h new file mode 100644 index 00000000000..db2e0492567 --- /dev/null +++ b/cpp/src/IceBT/EndpointI.h @@ -0,0 +1,150 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICE_BT_ENDPOINT_I_H +#define ICE_BT_ENDPOINT_I_H + +#include <Ice/EndpointI.h> +#include <Ice/EndpointFactory.h> +#include <IceBT/Config.h> +#include <IceBT/EndpointInfo.h> +#include <IceBT/Engine.h> +#include <IceBT/InstanceF.h> +#include <IceUtil/Thread.h> + +namespace IceBT +{ + +class EndpointI : public IceInternal::EndpointI +{ +public: + + EndpointI(const InstancePtr&, const std::string&, const std::string&, const std::string&, Ice::Int, + Ice::Int, const std::string&, bool); + EndpointI(const InstancePtr&); + EndpointI(const InstancePtr&, IceInternal::BasicStream*); + + virtual void streamWrite(IceInternal::BasicStream*) const; + virtual Ice::Short type() const; + virtual const std::string& protocol() const; + virtual Ice::Int timeout() const; + virtual IceInternal::EndpointIPtr timeout(Ice::Int) const; + virtual const std::string& connectionId() const; + virtual IceInternal::EndpointIPtr connectionId(const std::string&) const; + virtual bool compress() const; + virtual IceInternal::EndpointIPtr compress(bool) const; + virtual bool datagram() const; + virtual bool secure() const; + virtual IceInternal::TransceiverPtr transceiver() const; + virtual void connectors_async(Ice::EndpointSelectionType, const IceInternal::EndpointI_connectorsPtr&) const; + virtual IceInternal::AcceptorPtr acceptor(const std::string&) const; + virtual std::vector<IceInternal::EndpointIPtr> expand() const; + virtual bool equivalent(const IceInternal::EndpointIPtr&) const; + + virtual bool operator==(const Ice::LocalObject&) const; + virtual bool operator<(const Ice::LocalObject&) const; + + virtual Ice::Int hash() const; + + virtual std::string options() const; + + Ice::EndpointInfoPtr getInfo() const; + + void initWithOptions(std::vector<std::string>&, bool); + + EndpointIPtr endpoint(const AcceptorIPtr&) const; + +private: + + void hashInit(); + bool checkOption(const std::string&, const std::string&, const std::string&); + + void connectCompleted(int, const ConnectionPtr&); + void connectFailed(const Ice::LocalException&); + + class ConnectCallbackI : public ConnectCallback + { + public: + + ConnectCallbackI(const EndpointIPtr& endpoint) : + _endpoint(endpoint) + { + } + + virtual void completed(int fd, const ConnectionPtr& conn) + { + _endpoint->connectCompleted(fd, conn); + } + + virtual void failed(const Ice::LocalException& ex) + { + _endpoint->connectFailed(ex); + } + + private: + + EndpointIPtr _endpoint; + }; + friend class ConnectCallbackI; + + const InstancePtr _instance; + const std::string _addr; + const std::string _uuid; + const std::string _name; + const Ice::Int _channel; + const Ice::Int _timeout; + const std::string _connectionId; + const bool _compress; + const Ice::Int _hashValue; + IceUtil::Monitor<IceUtil::Mutex> _lock; + bool _connectPending; + std::vector<IceInternal::EndpointI_connectorsPtr> _callbacks; +}; + +class EndpointInfoI : public EndpointInfo +{ +public: + + EndpointInfoI(const EndpointIPtr&); + virtual ~EndpointInfoI(); + + virtual Ice::Short type() const; + virtual bool datagram() const; + virtual bool secure() const; + +private: + + const EndpointIPtr _endpoint; +}; + +class EndpointFactoryI : public IceInternal::EndpointFactory +{ +public: + + virtual ~EndpointFactoryI(); + + virtual Ice::Short type() const; + virtual std::string protocol() const; + virtual IceInternal::EndpointIPtr create(std::vector<std::string>&, bool) const; + virtual IceInternal::EndpointIPtr read(IceInternal::BasicStream*) const; + virtual void destroy(); + + virtual IceInternal::EndpointFactoryPtr clone(const IceInternal::ProtocolInstancePtr&) const; + +private: + + EndpointFactoryI(const InstancePtr&); + friend class PluginI; + + InstancePtr _instance; +}; + +} + +#endif diff --git a/cpp/src/IceBT/Engine.cpp b/cpp/src/IceBT/Engine.cpp new file mode 100644 index 00000000000..293056c120d --- /dev/null +++ b/cpp/src/IceBT/Engine.cpp @@ -0,0 +1,831 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <IceBT/Engine.h> +#include <IceBT/DBus.h> +#include <IceBT/Util.h> +#include <Ice/LocalException.h> +#include <IceUtil/StringUtil.h> +#include <IceUtil/Thread.h> +#include <IceUtil/UUID.h> + +#include <stack> + +using namespace std; +using namespace Ice; +using namespace IceBT; + +IceUtil::Shared* IceBT::upCast(IceBT::Engine* p) { return p; } + +namespace IceBT +{ + +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; + } + + // + // 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: + + 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) + { + // + // 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; +}; +typedef IceUtil::Handle<ClientProfile> ClientProfilePtr; + +// +// 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; +}; +typedef IceUtil::Handle<ServerProfile> ServerProfilePtr; + +// +// Engine delegates to ManagedObjects. It encapsulates a snapshot of the "objects" managed by the +// DBus Bluetooth daemon. These objects include local Bluetooth adapters, paired devices, etc. +// +class ManagedObjects : public IceUtil::Shared +{ +public: + + ManagedObjects() + { + DBus::initThreads(); + + try + { + // + // Block while we establish a DBus connection and retrieve a snapshot of the managed objects + // from the Bluetooth service. + // + _dbusConnection = DBus::Connection::getSystemBus(); + getManagedObjects(); + } + catch(const DBus::Exception& ex) + { + throw BluetoothException(__FILE__, __LINE__, ex.reason); + } + } + + string getDefaultDeviceAddress() const + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + + // + // Return the device address of the default local adapter. + // + + if(_defaultAdapterAddress.empty()) + { + throw BluetoothException(__FILE__, __LINE__, "no Bluetooth adapter found"); + } + + return _defaultAdapterAddress; + } + + bool deviceExists(const string& addr) const + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + + // + // Check if a local adapter exists with the given device address. + // + for(ObjectMap::const_iterator p = _adapters.begin(); p != _adapters.end(); ++p) + { + PropertyMap::const_iterator q = p->second.find("Address"); + if(q != p->second.end()) + { + DBus::StringValuePtr str = DBus::StringValuePtr::dynamicCast(q->second->v); + assert(str); + if(addr == str->v) + { + 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) + { + // + // 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(); + + try + { + 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 unregisterProfile(const string& path) + { + try + { + // + // 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(); + } + } + catch(const DBus::Exception& ex) + { + throw BluetoothException(__FILE__, __LINE__, ex.reason); + } + } + + void connect(const string& addr, const string& uuid, const ConnectCallbackPtr& cb) + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + + // + // Start a thread to establish the connection. + // + IceUtil::ThreadPtr t = new ConnectThread(this, addr, uuid, cb); + _connectThreads.push_back(t); + t->start(); + } + + 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(); + } + + if(_dbusConnection) + { + try + { + _dbusConnection->close(); + } + catch(const DBus::Exception& ex) + { + } + } + } + + 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); + DBus::ArrayValuePtr ifaces = DBus::ArrayValuePtr::dynamicCast(e->value); + 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); + + PropertyMap propertyMap; + assert(ie->value->getType()->getKind() == DBus::Type::KindArray); + DBus::ArrayValuePtr props = DBus::ArrayValuePtr::dynamicCast(ie->value); + 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); + propertyMap[propName->v] = DBus::VariantValuePtr::dynamicCast(pe->value); + } + + if(ifaceName->v == "org.bluez.Adapter1") + { + // + // org.bluez.Adapter1 is the interface for local Bluetooth adapters. + // + + _adapters[path->v] = propertyMap; + + // + // Save the address of the first adapter we encounter as our "default" adapter. + // + if(_defaultAdapterAddress.empty()) + { + PropertyMap::iterator t = propertyMap.find("Address"); + if(t != propertyMap.end()) + { + DBus::StringValuePtr str = DBus::StringValuePtr::dynamicCast(t->second->v); + assert(str); + if(!str->v.empty()) + { + _defaultAdapterAddress = str->v; + } + } + } + } + else if(ifaceName->v == "org.bluez.Device1") + { + // + // org.bluez.Device1 is the interface for paired devices. + // + + PropertyMap::iterator t = propertyMap.find("Address"); + if(t != propertyMap.end()) + { + DBus::StringValuePtr str = DBus::StringValuePtr::dynamicCast(t->second->v); + assert(str); + if(!str->v.empty()) + { + _remoteDevices[IceUtilInternal::toUpper(str->v)] = path->v; + } + } + } + } + } + } + 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) + { + conn->addService(path, profile); + + // + // 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) + { + // + // Invoke UnregisterProfile on the profile manager object. + // + DBus::MessagePtr msg = + DBus::Message::createCall("org.bluez", "/org/bluez", "org.bluez.ProfileManager1", "UnregisterProfile"); + msg->write(new DBus::ObjectPathValue(path)); + return conn->callAsync(msg); + } + + static string generatePath() + { + // + // 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 == '-') + { + *p = '_'; + } + } + return path; + } + + 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. + // + // 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; + + try + { + // + // Block while we refresh the list of known devices. + // + getManagedObjects(); + + string devicePath; + + // + // Search our list of known devices for one that matches the given address. + // + { + IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock); + + DeviceMap::iterator p = _remoteDevices.find(IceUtilInternal::toUpper(addr)); + if(p != _remoteDevices.end()) + { + devicePath = p->second; + } + } + + // + // If we don't find a match, we're done. + // + if(devicePath.empty()) + { + // + // ConnectorI handles this situation specially. + // + throw Ice::NoEndpointException(__FILE__, __LINE__, addr); + } + + // + // We have a matching device, now register a client profile. + // + DBus::ConnectionPtr dbusConn = DBus::Connection::getSystemBus(); + conn = new ConnectionI(dbusConn, devicePath, uuid); + + ProfilePtr profile = new 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()) + { + 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 + { + reply->throwException(); + } + catch(const DBus::Exception& ex) + { + ostringstream ostr; + ostr << "unable to establish connection to " << uuid << " at " << addr; + if(!ex.reason.empty()) + { + ostr << ':' << endl << ex.reason; + } + throw BluetoothException(__FILE__, __LINE__, ostr.str()); + } + } + } + catch(const DBus::Exception& ex) + { + ok = false; + cb->failed(BluetoothException(__FILE__, __LINE__, ex.reason)); + } + catch(const Ice::LocalException& ex) + { + ok = false; + cb->failed(ex); + } + + // + // Clean up. + // + + if(!ok && conn) + { + conn->close(); + } + + // + // 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); + } + } + + class ConnectThread : public IceUtil::Thread + { + public: + + ConnectThread(const ManagedObjectsPtr& mo, const string& addr, const string& uuid, + const ConnectCallbackPtr& cb) : + _mo(mo), + _addr(addr), + _uuid(uuid), + _cb(cb) + { + } + + virtual void run() + { + _mo->runConnectThread(this, _addr, _uuid, _cb); + } + + private: + + ManagedObjectsPtr _mo; + string _addr; + string _uuid; + ConnectCallbackPtr _cb; + }; + + IceUtil::Monitor<IceUtil::Mutex> _lock; + DBus::ConnectionPtr _dbusConnection; + + typedef map<string, DBus::VariantValuePtr> PropertyMap; + typedef map<string, PropertyMap> ObjectMap; + typedef map<string, string> DeviceMap; // Maps device address to object path. + + ObjectMap _adapters; + DeviceMap _remoteDevices; + string _defaultAdapterAddress; + vector<IceUtil::ThreadPtr> _connectThreads; +}; + +} + +IceUtil::Shared* IceBT::upCast(IceBT::ManagedObjects* p) { return p; } + +IceBT::Engine::Engine(const Ice::CommunicatorPtr& communicator) : + _communicator(communicator), + _initialized(false) +{ +} + +Ice::CommunicatorPtr +IceBT::Engine::communicator() const +{ + return _communicator; +} + +void +IceBT::Engine::initialize() +{ + _managedObjects = new ManagedObjects; + _initialized = true; +} + +bool +IceBT::Engine::initialized() const +{ + return _initialized; +} + +string +IceBT::Engine::getDefaultDeviceAddress() const +{ + return _managedObjects->getDefaultDeviceAddress(); +} + +bool +IceBT::Engine::deviceExists(const string& addr) const +{ + return _managedObjects->deviceExists(addr); +} + +string +IceBT::Engine::registerProfile(const string& uuid, const string& name, int channel, const ProfileCallbackPtr& cb) +{ + return _managedObjects->registerProfile(uuid, name, channel, cb); +} + +void +IceBT::Engine::unregisterProfile(const string& path) +{ + return _managedObjects->unregisterProfile(path); +} + +void +IceBT::Engine::connect(const string& addr, const string& uuid, const ConnectCallbackPtr& cb) +{ + _managedObjects->connect(addr, uuid, cb); +} + +void +IceBT::Engine::destroy() +{ + _managedObjects->destroy(); +} diff --git a/cpp/src/IceBT/Engine.h b/cpp/src/IceBT/Engine.h new file mode 100644 index 00000000000..b9884d4de41 --- /dev/null +++ b/cpp/src/IceBT/Engine.h @@ -0,0 +1,91 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICE_BT_ENGINE_H +#define ICE_BT_ENGINE_H + +#include <IceBT/EngineF.h> +#include <IceBT/Config.h> +#include <IceBT/Types.h> +#include <Ice/CommunicatorF.h> + +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 +{ +public: + + virtual void close() = 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; + +// +// Engine encapsulates all Bluetooth activities. +// +class Engine : public IceUtil::Shared +{ +public: + + Engine(const Ice::CommunicatorPtr&); + + Ice::CommunicatorPtr communicator() const; + + void initialize(); + bool initialized() const; + + std::string getDefaultDeviceAddress() const; + bool deviceExists(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&); + + void destroy(); + +private: + + const Ice::CommunicatorPtr _communicator; + bool _initialized; + IceUtil::Monitor<IceUtil::Mutex> _lock; + ManagedObjectsPtr _managedObjects; +}; + +} + +#endif diff --git a/cpp/src/IceBT/EngineF.h b/cpp/src/IceBT/EngineF.h new file mode 100644 index 00000000000..c369e17f0dd --- /dev/null +++ b/cpp/src/IceBT/EngineF.h @@ -0,0 +1,29 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICE_BT_ENGINE_F_H +#define ICE_BT_ENGINE_F_H + +#include <IceUtil/Shared.h> +#include <Ice/Handle.h> + +namespace IceBT +{ + +class Engine; +IceUtil::Shared* upCast(Engine*); +typedef IceInternal::Handle<Engine> EnginePtr; + +class ManagedObjects; +IceUtil::Shared* upCast(ManagedObjects*); +typedef IceInternal::Handle<ManagedObjects> ManagedObjectsPtr; + +} + +#endif diff --git a/cpp/src/IceBT/Instance.cpp b/cpp/src/IceBT/Instance.cpp new file mode 100644 index 00000000000..5381ad5ce94 --- /dev/null +++ b/cpp/src/IceBT/Instance.cpp @@ -0,0 +1,33 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <IceBT/Instance.h> +#include <IceBT/Engine.h> + +using namespace std; +using namespace Ice; +using namespace IceBT; + +IceUtil::Shared* IceBT::upCast(IceBT::Instance* p) { return p; } + +IceBT::Instance::Instance(const EnginePtr& engine, Short type, const string& protocol) : + ProtocolInstance(engine->communicator(), type, protocol, true), + _engine(engine) +{ +} + +IceBT::Instance::~Instance() +{ +} + +bool +IceBT::Instance::initialized() const +{ + return _engine->initialized(); +} diff --git a/cpp/src/IceBT/Instance.h b/cpp/src/IceBT/Instance.h new file mode 100644 index 00000000000..dd385ac7561 --- /dev/null +++ b/cpp/src/IceBT/Instance.h @@ -0,0 +1,41 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICE_BT_INSTANCE_H +#define ICE_BT_INSTANCE_H + +#include <IceBT/InstanceF.h> +#include <IceBT/EngineF.h> +#include <Ice/ProtocolInstance.h> + +namespace IceBT +{ + +class Instance : public IceInternal::ProtocolInstance +{ +public: + + Instance(const EnginePtr&, Ice::Short, const std::string&); + virtual ~Instance(); + + EnginePtr engine() const + { + return _engine; + } + + bool initialized() const; + +private: + + const EnginePtr _engine; +}; + +} + +#endif diff --git a/cpp/src/IceBT/InstanceF.h b/cpp/src/IceBT/InstanceF.h new file mode 100644 index 00000000000..a5ae9844e69 --- /dev/null +++ b/cpp/src/IceBT/InstanceF.h @@ -0,0 +1,33 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICE_BT_INSTANCE_F_H +#define ICE_BT_INSTANCE_F_H + +#include <IceUtil/Shared.h> +#include <Ice/Handle.h> + +namespace IceBT +{ + +class Instance; +IceUtil::Shared* upCast(Instance*); +typedef IceInternal::Handle<Instance> InstancePtr; + +class EndpointI; +IceUtil::Shared* upCast(EndpointI*); +typedef IceInternal::Handle<EndpointI> EndpointIPtr; + +class AcceptorI; +IceUtil::Shared* upCast(AcceptorI*); +typedef IceInternal::Handle<AcceptorI> AcceptorIPtr; + +} + +#endif diff --git a/cpp/src/IceBT/Makefile b/cpp/src/IceBT/Makefile new file mode 100644 index 00000000000..feaa2c74114 --- /dev/null +++ b/cpp/src/IceBT/Makefile @@ -0,0 +1,67 @@ + +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../.. + +LIBFILENAME = $(call mklibfilename,IceBT,$(VERSION)) +SONAME = $(call mksoname,IceBT,$(SOVERSION)) +LIBNAME = $(call mklibname,IceBT) + +TARGETS = $(call mklibtargets,$(libdir)/$(LIBFILENAME),$(libdir)/$(SONAME),$(libdir)$(cpp11libdirsuffix)/$(LIBNAME)) + +SLICE_OBJS = ConnectionInfo.o \ + EndpointInfo.o \ + Types.o + +OBJS = AcceptorI.o \ + ConnectorI.o \ + DBus.o \ + EndpointI.o \ + Engine.o \ + Instance.o \ + PluginI.o \ + StreamSocket.o \ + TransceiverI.o \ + Util.o \ + $(SLICE_OBJS) + +HDIR = $(headerdir)/IceBT +SDIR = $(slicedir)/IceBT + +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) + +ifeq ($(STATICLIBS),yes) +$(libdir)/$(LIBNAME): $(OBJS) + @mkdir -p $(dir $@) + rm -f $@ + $(call mklib,$@,$(OBJS)) +else +$(libdir)/$(LIBFILENAME): $(OBJS) + @mkdir -p $(dir $@) + rm -f $@ + $(call mkshlib,$@,$(SONAME),$(OBJS),$(LINKWITH)) + +$(libdir)/$(SONAME): $(libdir)/$(LIBFILENAME) + rm -f $@ + ln -s $(LIBFILENAME) $@ + +$(libdir)$(cpp11libdirsuffix)/$(LIBNAME): $(libdir)/$(SONAME) + @mkdir -p $(libdir)$(cpp11libdirsuffix) + rm -f $@ + ln -s $(cpp11sonamedir)$(SONAME) $@ +endif + +install:: all + $(call installlib,$(DESTDIR)$(install_libdir),$(libdir),$(LIBFILENAME),$(SONAME),$(LIBNAME)) diff --git a/cpp/src/IceBT/PluginI.cpp b/cpp/src/IceBT/PluginI.cpp new file mode 100644 index 00000000000..c95ce5db9e0 --- /dev/null +++ b/cpp/src/IceBT/PluginI.cpp @@ -0,0 +1,70 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <IceBT/PluginI.h> +#include <IceBT/EndpointI.h> +#include <IceBT/Engine.h> +#include <IceBT/Instance.h> +#include <IceBT/Util.h> + +#include <Ice/LocalException.h> +#include <Ice/ProtocolPluginFacade.h> +#include <Ice/ProtocolInstance.h> + +using namespace std; +using namespace Ice; +using namespace IceBT; + +void +IceBT::BluetoothException::ice_print(ostream& out) const +{ + Exception::ice_print(out); + out << ":\nbluetooth exception: `" << reason << "'"; +} + +// +// Plug-in factory function. +// +extern "C" +{ + +ICE_BT_API Ice::Plugin* +createIceBT(const CommunicatorPtr& communicator, const string& /*name*/, const StringSeq& /*args*/) +{ + return new PluginI(communicator); +} + +} + +// +// Plugin implementation. +// +IceBT::PluginI::PluginI(const Ice::CommunicatorPtr& com) : + _engine(new Engine(com)) +{ + // + // Register the endpoint factory. We have to do this now, rather + // than in initialize, because the communicator may need to + // interpret proxies before the plug-in is fully initialized. + // + IceInternal::EndpointFactoryPtr btFactory = new EndpointFactoryI(new Instance(_engine, EndpointType, "bt")); + IceInternal::getProtocolPluginFacade(com)->addEndpointFactory(btFactory); +} + +void +IceBT::PluginI::initialize() +{ + _engine->initialize(); +} + +void +IceBT::PluginI::destroy() +{ + _engine->destroy(); +} diff --git a/cpp/src/IceBT/PluginI.h b/cpp/src/IceBT/PluginI.h new file mode 100644 index 00000000000..5c78665011b --- /dev/null +++ b/cpp/src/IceBT/PluginI.h @@ -0,0 +1,39 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICE_BT_PLUGIN_I_H +#define ICE_BT_PLUGIN_I_H + +#include <Ice/CommunicatorF.h> +#include <Ice/Plugin.h> +#include <IceBT/EngineF.h> + +namespace IceBT +{ + +class PluginI : public Ice::Plugin +{ +public: + + PluginI(const Ice::CommunicatorPtr&); + + // + // From Ice::Plugin. + // + virtual void initialize(); + virtual void destroy(); + +private: + + EnginePtr _engine; +}; + +} + +#endif diff --git a/cpp/src/IceBT/StreamSocket.cpp b/cpp/src/IceBT/StreamSocket.cpp new file mode 100644 index 00000000000..9f8e621bead --- /dev/null +++ b/cpp/src/IceBT/StreamSocket.cpp @@ -0,0 +1,268 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <IceBT/StreamSocket.h> +#include <IceBT/EndpointInfo.h> +#include <IceBT/Instance.h> +#include <IceBT/Util.h> + +#include <Ice/LoggerUtil.h> +#include <Ice/Properties.h> + +using namespace std; +using namespace Ice; +using namespace IceBT; + +IceBT::StreamSocket::StreamSocket(const InstancePtr& instance, SOCKET fd) : + IceInternal::NativeInfo(fd), + _instance(instance) +{ + init(); + _desc = fdToString(fd); +} + +IceBT::StreamSocket::~StreamSocket() +{ + assert(_fd == INVALID_SOCKET); +} + +size_t +IceBT::StreamSocket::getSendPacketSize(size_t length) +{ + return length; +} + +size_t +IceBT::StreamSocket::getRecvPacketSize(size_t length) +{ + return length; +} + +void +IceBT::StreamSocket::setBufferSize(int rcvSize, int sndSize) +{ + assert(_fd != INVALID_SOCKET); + + if(rcvSize > 0) + { + // + // Try to set the buffer size. The kernel will silently adjust + // 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); + if(size > 0 && size < rcvSize) + { + // + // Warn if the size that was set is less than the requested size and + // we have not already warned. + // + IceInternal::BufSizeWarnInfo winfo = _instance->getBufSizeWarn(EndpointType); + if(!winfo.rcvWarn || rcvSize != winfo.rcvSize) + { + Ice::Warning out(_instance->logger()); + out << "BT receive buffer size: requested size of " << rcvSize << " adjusted to " << size; + _instance->setRcvBufSizeWarn(EndpointType, rcvSize); + } + } + } + + if(sndSize > 0) + { + // + // Try to set the buffer size. The kernel will silently adjust + // 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); + if(size > 0 && size < sndSize) + { + // Warn if the size that was set is less than the requested size and + // we have not already warned. + IceInternal::BufSizeWarnInfo winfo = _instance->getBufSizeWarn(EndpointType); + if(!winfo.sndWarn || sndSize != winfo.sndSize) + { + Ice::Warning out(_instance->logger()); + out << "BT send buffer size: requested size of " << sndSize << " adjusted to " << size; + _instance->setSndBufSizeWarn(EndpointType, sndSize); + } + } + } +} + +IceInternal::SocketOperation +IceBT::StreamSocket::read(IceInternal::Buffer& buf) +{ + buf.i += read(reinterpret_cast<char*>(&*buf.i), buf.b.end() - buf.i); + return buf.i != buf.b.end() ? IceInternal::SocketOperationRead : IceInternal::SocketOperationNone; +} + +IceInternal::SocketOperation +IceBT::StreamSocket::write(IceInternal::Buffer& buf) +{ + buf.i += write(reinterpret_cast<const char*>(&*buf.i), buf.b.end() - buf.i); + return buf.i != buf.b.end() ? IceInternal::SocketOperationWrite : IceInternal::SocketOperationNone; +} + +ssize_t +IceBT::StreamSocket::read(char* buf, size_t length) +{ + assert(_fd != INVALID_SOCKET); + + size_t packetSize = length; + ssize_t read = 0; + + while(length > 0) + { + ssize_t ret = ::recv(_fd, buf, packetSize, 0); + if(ret == 0) + { + Ice::ConnectionLostException ex(__FILE__, __LINE__); + ex.error = 0; + throw ex; + } + else if(ret == SOCKET_ERROR) + { + if(IceInternal::interrupted()) + { + continue; + } + + if(IceInternal::noBuffers() && packetSize > 1024) + { + packetSize /= 2; + continue; + } + + if(IceInternal::wouldBlock()) + { + return read; + } + + if(IceInternal::connectionLost()) + { + Ice::ConnectionLostException ex(__FILE__, __LINE__); + ex.error = IceInternal::getSocketErrno(); + throw ex; + } + else + { + Ice::SocketException ex(__FILE__, __LINE__); + ex.error = IceInternal::getSocketErrno(); + throw ex; + } + } + + buf += ret; + read += ret; + length -= ret; + + if(packetSize > length) + { + packetSize = length; + } + } + return read; +} + +ssize_t +IceBT::StreamSocket::write(const char* buf, size_t length) +{ + assert(_fd != INVALID_SOCKET); + + size_t packetSize = length; + + ssize_t sent = 0; + while(length > 0) + { + ssize_t ret = ::send(_fd, buf, packetSize, 0); + if(ret == 0) + { + Ice::ConnectionLostException ex(__FILE__, __LINE__); + ex.error = 0; + throw ex; + } + else if(ret == SOCKET_ERROR) + { + if(IceInternal::interrupted()) + { + continue; + } + + if(IceInternal::noBuffers() && packetSize > 1024) + { + packetSize /= 2; + continue; + } + + if(IceInternal::wouldBlock()) + { + return sent; + } + + if(IceInternal::connectionLost()) + { + Ice::ConnectionLostException ex(__FILE__, __LINE__); + ex.error = IceInternal::getSocketErrno(); + throw ex; + } + else + { + Ice::SocketException ex(__FILE__, __LINE__); + ex.error = IceInternal::getSocketErrno(); + throw ex; + } + } + + buf += ret; + sent += ret; + length -= ret; + + if(packetSize > length) + { + packetSize = length; + } + } + return sent; +} + +void +IceBT::StreamSocket::close() +{ + assert(_fd != INVALID_SOCKET); + try + { + IceInternal::closeSocket(_fd); + _fd = INVALID_SOCKET; + } + catch(const Ice::SocketException&) + { + _fd = INVALID_SOCKET; + throw; + } +} + +const string& +IceBT::StreamSocket::toString() const +{ + return _desc; +} + +void +IceBT::StreamSocket::init() +{ + IceInternal::setBlock(_fd, false); + + Int rcvSize = _instance->properties()->getPropertyAsInt("IceBT.RcvSize"); + Int sndSize = _instance->properties()->getPropertyAsInt("IceBT.SndSize"); + + setBufferSize(rcvSize, sndSize); +} diff --git a/cpp/src/IceBT/StreamSocket.h b/cpp/src/IceBT/StreamSocket.h new file mode 100644 index 00000000000..a11f1cd149d --- /dev/null +++ b/cpp/src/IceBT/StreamSocket.h @@ -0,0 +1,57 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICE_BT_STREAM_SOCKET_H +#define ICE_BT_STREAM_SOCKET_H + +#include <IceBT/Config.h> +#include <IceBT/InstanceF.h> + +#include <IceUtil/Shared.h> +#include <Ice/Network.h> +#include <Ice/Buffer.h> + +namespace IceBT +{ + +class StreamSocket : public IceInternal::NativeInfo +{ +public: + + StreamSocket(const InstancePtr&, SOCKET); + virtual ~StreamSocket(); + + size_t getSendPacketSize(size_t); + size_t getRecvPacketSize(size_t); + + void setBufferSize(int rcvSize, int sndSize); + + IceInternal::SocketOperation read(IceInternal::Buffer&); + IceInternal::SocketOperation write(IceInternal::Buffer&); + + ssize_t read(char*, size_t); + ssize_t write(const char*, size_t); + + void close(); + const std::string& toString() const; + +private: + + void init(); + + const InstancePtr _instance; + SocketAddress _addr; + std::string _desc; +}; +typedef IceUtil::Handle<StreamSocket> StreamSocketPtr; + +} + +#endif + diff --git a/cpp/src/IceBT/TransceiverI.cpp b/cpp/src/IceBT/TransceiverI.cpp new file mode 100644 index 00000000000..8ce1241870a --- /dev/null +++ b/cpp/src/IceBT/TransceiverI.cpp @@ -0,0 +1,117 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <IceBT/TransceiverI.h> +#include <IceBT/ConnectionInfo.h> +#include <IceBT/Engine.h> +#include <IceBT/Instance.h> +#include <IceBT/Util.h> + +#include <Ice/Connection.h> +#include <Ice/LocalException.h> + +#include <IceUtil/DisableWarnings.h> + +using namespace std; +using namespace Ice; +using namespace IceBT; + +IceInternal::NativeInfoPtr +IceBT::TransceiverI::getNativeInfo() +{ + return _stream; +} + +IceInternal::SocketOperation +IceBT::TransceiverI::initialize(IceInternal::Buffer& readBuffer, IceInternal::Buffer& writeBuffer) +{ + return IceInternal::SocketOperationNone; // Already connected. +} + +IceInternal::SocketOperation +IceBT::TransceiverI::closing(bool initiator, const Ice::LocalException&) +{ + // 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(); +} + +IceInternal::SocketOperation +IceBT::TransceiverI::write(IceInternal::Buffer& buf) +{ + return _stream->write(buf); +} + +IceInternal::SocketOperation +IceBT::TransceiverI::read(IceInternal::Buffer& buf) +{ + return _stream->read(buf); +} + +string +IceBT::TransceiverI::protocol() const +{ + return _instance->protocol(); +} + +string +IceBT::TransceiverI::toString() const +{ + return _stream->toString(); +} + +string +IceBT::TransceiverI::toDetailedString() const +{ + return toString(); +} + +Ice::ConnectionInfoPtr +IceBT::TransceiverI::getInfo() const +{ + IceBT::ConnectionInfoPtr info = new IceBT::ConnectionInfo(); + fdToAddressAndChannel(_stream->fd(), info->localAddress, info->localChannel, info->remoteAddress, + info->remoteChannel); + info->uuid = _uuid; + return info; +} + +void +IceBT::TransceiverI::checkSendSize(const IceInternal::Buffer&) +{ +} + +void +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) : + _instance(instance), + _stream(stream), + _connection(conn), + _uuid(uuid) +{ +} + +IceBT::TransceiverI::~TransceiverI() +{ +} diff --git a/cpp/src/IceBT/TransceiverI.h b/cpp/src/IceBT/TransceiverI.h new file mode 100644 index 00000000000..db7460e8a2b --- /dev/null +++ b/cpp/src/IceBT/TransceiverI.h @@ -0,0 +1,59 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICE_BT_TRANSCEIVER_H +#define ICE_BT_TRANSCEIVER_H + +#include <IceBT/InstanceF.h> +#include <IceBT/Engine.h> +#include <IceBT/StreamSocket.h> + +#include <Ice/Transceiver.h> + +namespace IceBT +{ + +class ConnectorI; +class AcceptorI; + +class TransceiverI : public IceInternal::Transceiver +{ +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&); + virtual IceInternal::SocketOperation read(IceInternal::Buffer&); + virtual std::string protocol() const; + virtual std::string toString() const; + virtual std::string toDetailedString() const; + virtual Ice::ConnectionInfoPtr getInfo() const; + virtual void checkSendSize(const IceInternal::Buffer&); + virtual void setBufferSize(int rcvSize, int sndSize); + +private: + + TransceiverI(const InstancePtr&, const StreamSocketPtr&, const ConnectionPtr&, const std::string&); + virtual ~TransceiverI(); + + friend class ConnectorI; + friend class AcceptorI; + + const InstancePtr _instance; + const StreamSocketPtr _stream; + const ConnectionPtr _connection; + const std::string _uuid; +}; + +} + +#endif diff --git a/cpp/src/IceBT/Util.cpp b/cpp/src/IceBT/Util.cpp new file mode 100644 index 00000000000..baf3edff738 --- /dev/null +++ b/cpp/src/IceBT/Util.cpp @@ -0,0 +1,228 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <IceBT/Util.h> + +#include <Ice/LocalException.h> +#include <Ice/Network.h> +#include <IceUtil/StringUtil.h> + +using namespace std; +using namespace Ice; +using namespace IceBT; + +bool +IceBT::isValidDeviceAddress(const string& str) +{ + // + // Format is 01:23:45:67:89:0A + // + + if(str.size() != 17 || str[2] != ':' || str[5] != ':' || str[8] != ':' || str[11] != ':' || str[14] != ':') + { + return false; + } + + for(string::size_type i = 0; i < str.size(); i += 3) + { + if(!isxdigit(str[i]) || !isxdigit(str[i + 1])) + { + return false; + } + } + + return true; +} + +bool +IceBT::parseDeviceAddress(const string& str, DeviceAddress& addr) +{ + uint8_t b0, b1, b2, b3, b4, b5; + + if(isValidDeviceAddress(str) && + sscanf(str.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", &b5, &b4, &b3, &b2, &b1, &b0) == 6) + { + addr.b[0] = b0; + addr.b[1] = b1; + addr.b[2] = b2; + addr.b[3] = b3; + addr.b[4] = b4; + addr.b[5] = b5; + return true; + } + + return false; +} + +string +IceBT::formatDeviceAddress(const DeviceAddress& addr) +{ + char buf[64]; + sprintf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", addr.b[5], addr.b[4], addr.b[3], addr.b[2], addr.b[1], + addr.b[0]); + return IceUtilInternal::toUpper(string(buf)); +} + +string +IceBT::addrToString(const string& addr, Int channel) +{ + ostringstream ostr; + ostr << addr << '#' << channel; + return ostr.str(); +} + +string +IceBT::addrToString(const SocketAddress& addr) +{ + return addrToString(formatDeviceAddress(addr.rc_bdaddr), addr.rc_channel); +} + +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; +} + +namespace +{ + +void +fdToLocalAddress(SOCKET fd, SocketAddress& addr) +{ + socklen_t len = static_cast<socklen_t>(sizeof(SocketAddress)); + if(::getsockname(fd, reinterpret_cast<struct sockaddr*>(&addr), &len) == SOCKET_ERROR) + { + IceInternal::closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = IceInternal::getSocketErrno(); + throw ex; + } +} + +bool +fdToRemoteAddress(SOCKET fd, SocketAddress& addr) +{ + socklen_t len = static_cast<socklen_t>(sizeof(SocketAddress)); + if(::getpeername(fd, reinterpret_cast<struct sockaddr*>(&addr), &len) == SOCKET_ERROR) + { + if(IceInternal::notConnected()) + { + return false; + } + else + { + IceInternal::closeSocketNoThrow(fd); + SocketException ex(__FILE__, __LINE__); + ex.error = IceInternal::getSocketErrno(); + throw ex; + } + } + + return true; +} + +string +addressesToString(const SocketAddress& localAddr, const SocketAddress& remoteAddr, bool peerConnected) +{ + ostringstream s; + s << "local address = " << addrToString(localAddr); + if(peerConnected) + { + s << "\nremote address = " << addrToString(remoteAddr); + } + else + { + s << "\nremote address = <not connected>"; + } + return s.str(); +} + +void +addrToAddressAndChannel(const SocketAddress& addr, string& address, int& channel) +{ + address = formatDeviceAddress(addr.rc_bdaddr); + channel = addr.rc_channel; +} + +} + +string +IceBT::fdToString(SOCKET fd) +{ + if(fd == INVALID_SOCKET) + { + return "<closed>"; + } + + SocketAddress localAddr; + fdToLocalAddress(fd, localAddr); + + SocketAddress remoteAddr; + bool peerConnected = fdToRemoteAddress(fd, remoteAddr); + + return addressesToString(localAddr, remoteAddr, peerConnected); +} + +void +IceBT::fdToAddressAndChannel(SOCKET fd, string& localAddress, int& localChannel, string& remoteAddress, + int& remoteChannel) +{ + if(fd == INVALID_SOCKET) + { + localAddress.clear(); + remoteAddress.clear(); + localChannel = -1; + remoteChannel = -1; + return; + } + + SocketAddress localAddr; + fdToLocalAddress(fd, localAddr); + addrToAddressAndChannel(localAddr, localAddress, localChannel); + + SocketAddress remoteAddr; + if(fdToRemoteAddress(fd, remoteAddr)) + { + addrToAddressAndChannel(remoteAddr, remoteAddress, remoteChannel); + } + else + { + remoteAddress.clear(); + remoteChannel = -1; + } +} + +int +IceBT::compareAddress(const SocketAddress& addr1, const SocketAddress& addr2) +{ + if(addr1.rc_family < addr2.rc_family) + { + return -1; + } + else if(addr2.rc_family < addr1.rc_family) + { + return 1; + } + + if(addr1.rc_channel < addr2.rc_channel) + { + return -1; + } + else if(addr2.rc_channel < addr1.rc_channel) + { + return 1; + } + + return ::memcmp(&addr1.rc_bdaddr, &addr2.rc_bdaddr, sizeof(DeviceAddress)); +} diff --git a/cpp/src/IceBT/Util.h b/cpp/src/IceBT/Util.h new file mode 100644 index 00000000000..1d11baaa8b7 --- /dev/null +++ b/cpp/src/IceBT/Util.h @@ -0,0 +1,35 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICE_BT_UTIL_H +#define ICE_BT_UTIL_H + +#include <IceBT/Config.h> +#include <IceBT/Types.h> + +#include <Ice/Network.h> + +namespace IceBT +{ + +bool isValidDeviceAddress(const std::string&); +bool parseDeviceAddress(const std::string&, DeviceAddress&); +std::string formatDeviceAddress(const DeviceAddress&); + +std::string addrToString(const std::string&, Ice::Int); +std::string addrToString(const SocketAddress&); +SocketAddress createAddr(const std::string&, Ice::Int); + +std::string fdToString(SOCKET); +void fdToAddressAndChannel(SOCKET, std::string&, int&, std::string&, int&); +int compareAddress(const SocketAddress&, const SocketAddress&); + +} + +#endif |