summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config/PropertyNames.xml8
-rw-r--r--cpp/include/IceBT/.gitignore6
-rw-r--r--cpp/src/Ice/PropertyNames.cpp15
-rw-r--r--cpp/src/Ice/PropertyNames.h3
-rw-r--r--cpp/src/Ice/ProtocolInstance.cpp24
-rw-r--r--cpp/src/Ice/ProtocolInstance.h23
-rw-r--r--cpp/src/IceBT/.gitignore6
-rw-r--r--cpp/src/IceBT/AcceptorI.cpp233
-rw-r--r--cpp/src/IceBT/AcceptorI.h63
-rw-r--r--cpp/src/IceBT/Config.h28
-rw-r--r--cpp/src/IceBT/ConnectorI.cpp156
-rw-r--r--cpp/src/IceBT/ConnectorI.h54
-rw-r--r--cpp/src/IceBT/DBus.cpp1320
-rw-r--r--cpp/src/IceBT/DBus.h558
-rw-r--r--cpp/src/IceBT/EndpointI.cpp732
-rw-r--r--cpp/src/IceBT/EndpointI.h150
-rw-r--r--cpp/src/IceBT/Engine.cpp831
-rw-r--r--cpp/src/IceBT/Engine.h91
-rw-r--r--cpp/src/IceBT/EngineF.h29
-rw-r--r--cpp/src/IceBT/Instance.cpp33
-rw-r--r--cpp/src/IceBT/Instance.h41
-rw-r--r--cpp/src/IceBT/InstanceF.h33
-rw-r--r--cpp/src/IceBT/Makefile67
-rw-r--r--cpp/src/IceBT/PluginI.cpp70
-rw-r--r--cpp/src/IceBT/PluginI.h39
-rw-r--r--cpp/src/IceBT/StreamSocket.cpp268
-rw-r--r--cpp/src/IceBT/StreamSocket.h57
-rw-r--r--cpp/src/IceBT/TransceiverI.cpp117
-rw-r--r--cpp/src/IceBT/TransceiverI.h59
-rw-r--r--cpp/src/IceBT/Util.cpp228
-rw-r--r--cpp/src/IceBT/Util.h35
-rw-r--r--csharp/src/Ice/PropertyNames.cs12
-rw-r--r--java/src/Ice/src/main/java/IceInternal/PropertyNames.java12
-rw-r--r--java/src/IceBT/src/main/java/IceBT/EndpointI.java16
-rw-r--r--js/src/Ice/PropertyNames.js2
-rw-r--r--slice/IceBT/Types.ice34
36 files changed, 5389 insertions, 64 deletions
diff --git a/config/PropertyNames.xml b/config/PropertyNames.xml
index 22b7734ba56..22add8a6df1 100644
--- a/config/PropertyNames.xml
+++ b/config/PropertyNames.xml
@@ -536,7 +536,7 @@ generated from the section label.
<property name="Registry.Trace.Topic"/>
<property name="Registry.Trace.TopicManager"/>
<property name="Registry.UserAccounts" />
-</section>
+ </section>
<section name="IcePatch2">
<property class="objectadapter" />
@@ -606,6 +606,12 @@ generated from the section label.
<property name="Port" />
</section>
+ <section name="IceBT">
+ <property name="DefaultAddress" />
+ <property name="RcvSize" />
+ <property name="SndSize" />
+ </section>
+
<section name="Glacier2">
<property name="AddConnectionContext"/>
<property name="Client" class="objectadapter"/>
diff --git a/cpp/include/IceBT/.gitignore b/cpp/include/IceBT/.gitignore
new file mode 100644
index 00000000000..cf344c483fc
--- /dev/null
+++ b/cpp/include/IceBT/.gitignore
@@ -0,0 +1,6 @@
+// Generated by makegitignore.py
+
+// IMPORTANT: Do not edit this file -- any edits made here will be lost!
+EndpointInfo.h
+ConnectionInfo.h
+Types.h
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
diff --git a/csharp/src/Ice/PropertyNames.cs b/csharp/src/Ice/PropertyNames.cs
index b09f83f673a..0aabda56109 100644
--- a/csharp/src/Ice/PropertyNames.cs
+++ b/csharp/src/Ice/PropertyNames.cs
@@ -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!
@@ -1019,6 +1019,14 @@ namespace IceInternal
null
};
+ public static Property[] IceBTProps =
+ {
+ new Property(@"^IceBT\.DefaultAddress$", false, null),
+ new Property(@"^IceBT\.RcvSize$", false, null),
+ new Property(@"^IceBT\.SndSize$", false, null),
+ null
+ };
+
public static Property[] Glacier2Props =
{
new Property(@"^Glacier2\.AddConnectionContext$", false, null),
@@ -1218,6 +1226,7 @@ namespace IceInternal
IcePatch2ClientProps,
IceSSLProps,
IceStormAdminProps,
+ IceBTProps,
Glacier2Props,
Glacier2CryptPermissionsVerifierProps,
FreezeProps,
@@ -1238,6 +1247,7 @@ namespace IceInternal
"IcePatch2Client",
"IceSSL",
"IceStormAdmin",
+ "IceBT",
"Glacier2",
"Glacier2CryptPermissionsVerifier",
"Freeze",
diff --git a/java/src/Ice/src/main/java/IceInternal/PropertyNames.java b/java/src/Ice/src/main/java/IceInternal/PropertyNames.java
index 401fc14ae66..929bb424f48 100644
--- a/java/src/Ice/src/main/java/IceInternal/PropertyNames.java
+++ b/java/src/Ice/src/main/java/IceInternal/PropertyNames.java
@@ -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!
@@ -1019,6 +1019,14 @@ public final class PropertyNames
null
};
+ public static final Property IceBTProps[] =
+ {
+ new Property("IceBT\\.DefaultAddress", false, null),
+ new Property("IceBT\\.RcvSize", false, null),
+ new Property("IceBT\\.SndSize", false, null),
+ null
+ };
+
public static final Property Glacier2Props[] =
{
new Property("Glacier2\\.AddConnectionContext", false, null),
@@ -1219,6 +1227,7 @@ public final class PropertyNames
IcePatch2ClientProps,
IceSSLProps,
IceStormAdminProps,
+ IceBTProps,
Glacier2Props,
Glacier2CryptPermissionsVerifierProps,
FreezeProps,
@@ -1239,6 +1248,7 @@ public final class PropertyNames
"IcePatch2Client",
"IceSSL",
"IceStormAdmin",
+ "IceBT",
"Glacier2",
"Glacier2CryptPermissionsVerifier",
"Freeze",
diff --git a/java/src/IceBT/src/main/java/IceBT/EndpointI.java b/java/src/IceBT/src/main/java/IceBT/EndpointI.java
index 0830e757a0d..4c18fbdb687 100644
--- a/java/src/IceBT/src/main/java/IceBT/EndpointI.java
+++ b/java/src/IceBT/src/main/java/IceBT/EndpointI.java
@@ -274,6 +274,17 @@ final class EndpointI extends IceInternal.EndpointI
{
super.initWithOptions(args);
+ if(_addr.length() == 0)
+ {
+ _addr = _instance.properties().getProperty("IceBT.DefaultAddress");
+ }
+
+ if(!oaEndpoint && _addr.length() == 0)
+ {
+ throw new Ice.EndpointParseException(
+ "a device address must be specified using the -a option or IceBT.DefaultAddress");
+ }
+
if(_name.length() == 0)
{
_name = "Ice Service";
@@ -284,11 +295,6 @@ final class EndpointI extends IceInternal.EndpointI
throw new Ice.EndpointParseException("a UUID must be specified using the -u option");
}
- if(!oaEndpoint && _addr.length() == 0)
- {
- throw new Ice.EndpointParseException("a device address must be specified using the -a option");
- }
-
hashInit();
}
diff --git a/js/src/Ice/PropertyNames.js b/js/src/Ice/PropertyNames.js
index 2f33bf9a869..ce70cca5d39 100644
--- a/js/src/Ice/PropertyNames.js
+++ b/js/src/Ice/PropertyNames.js
@@ -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!
diff --git a/slice/IceBT/Types.ice b/slice/IceBT/Types.ice
index ca042b3e73f..712896530f4 100644
--- a/slice/IceBT/Types.ice
+++ b/slice/IceBT/Types.ice
@@ -32,38 +32,4 @@ local exception BluetoothException
string reason;
};
-/**
- *
- * Provides information about a Bluetooth device.
- *
- **/
-local struct DeviceInfo
-{
- /** The device's Bluetooth address. */
- string address;
- /** The device's display name, or an empty string if no name is available. */
- string name;
-};
-/** A list of device information structures. */
-local sequence<DeviceInfo> DeviceInfoSeq;
-
-/**
- *
- * Provides information about a service offered by a Bluetooth device.
- *
- **/
-local struct ServiceInfo
-{
- /** The Bluetooth address of the device on which the service is running. */
- string address;
- /** The UUID of the service. */
- string uuid;
- /** The display name of the service, or an empty string if no name is available. */
- string name;
- /** The RFCOMM channel on which the service is running. */
- int channel;
-};
-/** A list of service information structures. */
-local sequence<ServiceInfo> ServiceInfoSeq;
-
};