summaryrefslogtreecommitdiff
path: root/cpp/src/IceBT/Engine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/IceBT/Engine.cpp')
-rw-r--r--cpp/src/IceBT/Engine.cpp1017
1 files changed, 1017 insertions, 0 deletions
diff --git a/cpp/src/IceBT/Engine.cpp b/cpp/src/IceBT/Engine.cpp
new file mode 100644
index 00000000000..6d7417c14e0
--- /dev/null
+++ b/cpp/src/IceBT/Engine.cpp
@@ -0,0 +1,1017 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 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 <Ice/UUID.h>
+#include <IceXML/Parser.h>
+
+#include <stack>
+
+using namespace std;
+using namespace Ice;
+using namespace IceBT;
+
+IceUtil::Shared* IceBT::upCast(IceBT::Engine* p) { return p; }
+
+typedef map<string, DBus::VariantValuePtr> VariantMap;
+
+namespace
+{
+
+void extractProperties(const DBus::ValuePtr& v, VariantMap& vm)
+{
+ //
+ // The given value is a dictionary structured like this:
+ //
+ // Key: Property name
+ // Value: Property value (variant)
+ //
+
+ assert(v->getType()->getKind() == DBus::Type::KindArray);
+ DBus::ArrayValuePtr props = DBus::ArrayValuePtr::dynamicCast(v);
+ for(vector<DBus::ValuePtr>::const_iterator s = props->elements.begin(); s != props->elements.end(); ++s)
+ {
+ assert((*s)->getType()->getKind() == DBus::Type::KindDictEntry);
+ DBus::DictEntryValuePtr pe = DBus::DictEntryValuePtr::dynamicCast(*s);
+ assert(pe->key->getType()->getKind() == DBus::Type::KindString);
+ DBus::StringValuePtr propName = DBus::StringValuePtr::dynamicCast(pe->key);
+ assert(pe->value->getType()->getKind() == DBus::Type::KindVariant);
+ vm[propName->v] = DBus::VariantValuePtr::dynamicCast(pe->value);
+ }
+}
+
+}
+
+namespace IceBT
+{
+
+//
+// Engine delegates to BluetoothService.
+//
+class BluetoothService : public DBus::Filter
+{
+public:
+
+ typedef map<string, VariantMap> InterfacePropertiesMap;
+
+ BluetoothService() :
+ _destroyed(false)
+ {
+ DBus::initThreads();
+
+ try
+ {
+ //
+ // Block while we establish a DBus connection.
+ //
+ _dbusConnection = DBus::Connection::getSystemBus();
+ }
+ catch(const DBus::Exception& ex)
+ {
+ throw BluetoothException(__FILE__, __LINE__, ex.reason);
+ }
+
+ _dbusConnection->addFilter(this);
+
+ try
+ {
+ //
+ // Use a call DefaultAdapter() to verify that the Bluetooth daemon is present and
+ // uses the expected version (BlueZ 4). If the system is running BlueZ 5, this call
+ // will return the error org.freedesktop.DBus.Error.UnknownMethod.
+ //
+ call("/", "org.bluez.Manager", "DefaultAdapter");
+ }
+ catch(const DBus::Exception& ex)
+ {
+ if(ex.reason.find("UnknownMethod") != string::npos)
+ {
+ throw BluetoothException(__FILE__, __LINE__, "Bluetooth daemon uses an unsupported version");
+ }
+ else
+ {
+ throw BluetoothException(__FILE__, __LINE__, ex.reason);
+ }
+ }
+ }
+
+ //
+ // From DBus::Filter.
+ //
+ virtual bool handleMessage(const DBus::ConnectionPtr&, const DBus::MessagePtr& msg)
+ {
+ if(!msg->isSignal())
+ {
+ return false; // Not handled.
+ }
+
+ string intf = msg->getInterface();
+ string member = msg->getMember();
+
+ if(intf == "org.bluez.Adapter" && member == "DeviceFound")
+ {
+ //
+ // The DeviceFound signal contains two values:
+ //
+ // STRING address
+ // DICT<STRING,VARIANT> properties
+ //
+ vector<DBus::ValuePtr> values = msg->readAll();
+ assert(values.size() == 2);
+ DBus::StringValuePtr addr = DBus::StringValuePtr::dynamicCast(values[0]);
+ assert(addr);
+ VariantMap props;
+ extractProperties(values[1], props);
+
+#ifdef ICE_CPP11_MAPPING
+ vector<function<void(const string&, const PropertyMap&)>> callbacks;
+#else
+ vector<DiscoveryCallbackPtr> callbacks;
+#endif
+
+ {
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+#ifdef ICE_CPP11_MAPPING
+ auto p = _discoveryCallbacks.find(msg->getPath());
+#else
+ map<string, vector<DiscoveryCallbackPtr> >::iterator p = _discoveryCallbacks.find(msg->getPath());
+#endif
+ if(p != _discoveryCallbacks.end())
+ {
+ callbacks = p->second;
+ }
+ }
+
+ if(!callbacks.empty())
+ {
+ PropertyMap pm; // Convert to string-string map.
+ for(VariantMap::const_iterator p = props.begin(); p != props.end(); ++p)
+ {
+ pm[p->first] = p->second->toString();
+ }
+#ifdef ICE_CPP11_MAPPING
+ for(const auto& discovered : callbacks)
+ {
+ try
+ {
+ discovered(addr->v, pm);
+ }
+ catch(...)
+ {
+ }
+ }
+#else
+ for(vector<DiscoveryCallbackPtr>::iterator p = callbacks.begin(); p != callbacks.end(); ++p)
+ {
+ try
+ {
+ (*p)->discovered(addr->v, pm);
+ }
+ catch(...)
+ {
+ }
+ }
+#endif
+ }
+
+ return true;
+ }
+ else if(intf == "org.bluez.Adapter" && member == "PropertyChanged")
+ {
+ //
+ // The PropertyChanged signal contains two values:
+ //
+ // STRING name
+ // VARIANT value
+ //
+ vector<DBus::ValuePtr> values = msg->readAll();
+ assert(values.size() == 2);
+ DBus::StringValuePtr name = DBus::StringValuePtr::dynamicCast(values[0]);
+ assert(name);
+ if(name->v == "Discovering")
+ {
+ DBus::VariantValuePtr v = DBus::VariantValuePtr::dynamicCast(values[1]);
+ assert(v);
+ DBus::BooleanValuePtr b = DBus::BooleanValuePtr::dynamicCast(v->v);
+ assert(b);
+ if(!b->v)
+ {
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+
+#ifdef ICE_CPP11_MAPPING
+ auto p = _discoveryCallbacks.find(msg->getPath());
+#else
+ map<string, vector<DiscoveryCallbackPtr> >::iterator p = _discoveryCallbacks.find(msg->getPath());
+#endif
+ if(p != _discoveryCallbacks.end())
+ {
+ _discoveryCallbacks.erase(p);
+ }
+ }
+ }
+ return false;
+ }
+
+ return false;
+ }
+
+ string getDefaultAdapterAddress() const
+ {
+ string path = getDefaultAdapter();
+ VariantMap props = getAdapterProperties(path);
+
+ VariantMap::const_iterator i = props.find("Address");
+ if(i != props.end())
+ {
+ DBus::StringValuePtr str = DBus::StringValuePtr::dynamicCast(i->second->v);
+ assert(str);
+ return IceUtilInternal::toUpper(str->v);
+ }
+
+ throw BluetoothException(__FILE__, __LINE__, "no default Bluetooth adapter found");
+ }
+
+ bool adapterExists(const string& addr) const
+ {
+ string path = findAdapter(addr);
+ return !path.empty();
+ }
+
+ unsigned int addService(const string& addr, const string& name, const string& uuid, int channel)
+ {
+ string path = findAdapter(addr);
+
+ if(path.empty())
+ {
+ throw BluetoothException(__FILE__, __LINE__, "no Bluetooth adapter found matching address " + addr);
+ }
+
+ //
+ // Compose an XML record.
+ //
+ ostringstream ostr;
+ ostr << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" << endl
+ << "<record>" << endl
+ << " <attribute id=\"0x0001\">" << endl // UUID
+ << " <sequence>" << endl
+ << " <uuid value=\"" << uuid << "\" />" << endl
+ << " </sequence>" << endl
+ << " </attribute>" << endl
+ << " <attribute id=\"0x0004\">" << endl
+ << " <sequence>" << endl
+ << " <sequence>" << endl
+ << " <uuid value=\"0x0100\" />" << endl // L2CAP
+ << " </sequence>" << endl
+ << " <sequence>" << endl
+ << " <uuid value=\"0x0003\" />" << endl // RFCOMM channel
+ << " <uint8 value=\"0x" << hex << channel << dec << "\" />" << endl
+ << " </sequence>" << endl
+ << " </sequence>" << endl
+ << " </attribute>" << endl
+ << " <attribute id=\"0x0005\">" << endl
+ << " <sequence>" << endl
+ << " <uuid value=\"0x1002\" />" << endl
+ << " </sequence>" << endl
+ << " </attribute>" << endl;
+ if(!name.empty())
+ {
+ ostr << " <attribute id=\"0x0100\">" << endl
+ << " <text value=\"" << name << "\" />" << endl
+ << " </attribute>" << endl;
+ }
+ ostr << "</record>";
+
+ try
+ {
+ DBus::MessagePtr reply = call(path, "org.bluez.Service", "AddRecord", new DBus::StringValue(ostr.str()));
+ DBus::ValuePtr v = reply->read();
+ DBus::Uint32ValuePtr handle = DBus::Uint32ValuePtr::dynamicCast(v);
+ return handle->v;
+ }
+ catch(const DBus::Exception& ex)
+ {
+ throw BluetoothException(__FILE__, __LINE__, ex.reason);
+ }
+ }
+
+ void findService(const string& addr, const string& uuid, const FindServiceCallbackPtr& cb)
+ {
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+
+ if(_destroyed)
+ {
+ cb->exception(CommunicatorDestroyedException(__FILE__, __LINE__));
+ }
+ else
+ {
+ IceUtil::ThreadPtr t = new FindServiceThread(this, addr, uuid, cb);
+ _threads.push_back(t);
+ t->start();
+ }
+ }
+
+ void removeService(const string& addr, unsigned int handle)
+ {
+ string path = findAdapter(addr);
+
+ if(path.empty())
+ {
+ throw BluetoothException(__FILE__, __LINE__, "no Bluetooth adapter found matching address " + addr);
+ }
+
+ try
+ {
+ call(path, "org.bluez.Service", "RemoveRecord", new DBus::Uint32Value(handle));
+ }
+ catch(const DBus::Exception& ex)
+ {
+ if(ex.reason.find("DoesNotExist") != string::npos)
+ {
+ throw BluetoothException(__FILE__, __LINE__, ex.reason);
+ }
+ }
+ }
+
+#ifdef ICE_CPP11_MAPPING
+ void startDiscovery(const string& addr, function<void(const string&, const PropertyMap&)> cb)
+#else
+ void startDiscovery(const string& addr, const DiscoveryCallbackPtr& cb)
+#endif
+ {
+ string path;
+ if(addr.empty())
+ {
+ path = getDefaultAdapter();
+ }
+ else
+ {
+ path = findAdapter(addr);
+ }
+
+ if(path.empty())
+ {
+ throw BluetoothException(__FILE__, __LINE__, "no Bluetooth adapter found matching address " + addr);
+ }
+
+ {
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+
+#ifdef ICE_CPP11_MAPPING
+ auto p = _discoveryCallbacks.find(path);
+ if(p == _discoveryCallbacks.end())
+ {
+ _discoveryCallbacks[path] = vector<function<void(const string&, const PropertyMap&)>>();
+ }
+ _discoveryCallbacks[path].push_back(move(cb));
+#else
+ map<string, vector<DiscoveryCallbackPtr> >::iterator p = _discoveryCallbacks.find(path);
+ if(p == _discoveryCallbacks.end())
+ {
+ _discoveryCallbacks[path] = vector<DiscoveryCallbackPtr>();
+ }
+ _discoveryCallbacks[path].push_back(cb);
+#endif
+ }
+
+ try
+ {
+ call(path, "org.bluez.Adapter", "StartDiscovery");
+ }
+ catch(const DBus::Exception& ex)
+ {
+ throw BluetoothException(__FILE__, __LINE__, ex.reason);
+ }
+ }
+
+ void stopDiscovery(const string& addr)
+ {
+ string path;
+ if(addr.empty())
+ {
+ path = getDefaultAdapter();
+ }
+ else
+ {
+ path = findAdapter(addr);
+ }
+
+ if(path.empty())
+ {
+ throw BluetoothException(__FILE__, __LINE__, "no Bluetooth adapter found matching address " + addr);
+ }
+
+ try
+ {
+ call(path, "org.bluez.Adapter", "StopDiscovery");
+ }
+ catch(const DBus::Exception& ex)
+ {
+ throw BluetoothException(__FILE__, __LINE__, ex.reason);
+ }
+ }
+
+ void destroy()
+ {
+ vector<IceUtil::ThreadPtr> threads;
+
+ {
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+
+ _destroyed = true;
+ threads.swap(_threads);
+ }
+
+ for(vector<IceUtil::ThreadPtr>::iterator p = threads.begin(); p != threads.end(); ++p)
+ {
+ (*p)->getThreadControl().join();
+ }
+
+ if(_dbusConnection)
+ {
+ try
+ {
+ _dbusConnection->close();
+ }
+ catch(const DBus::Exception& ex)
+ {
+ }
+ }
+ }
+
+ string getDefaultAdapter() const
+ {
+ try
+ {
+ //
+ // The call to DefaultAdapter returns OBJ_PATH.
+ //
+ DBus::MessagePtr reply = call("/", "org.bluez.Manager", "DefaultAdapter");
+ DBus::ValuePtr v = reply->read();
+ DBus::ObjectPathValuePtr path = DBus::ObjectPathValuePtr::dynamicCast(v);
+ assert(path);
+ return path->v;
+ }
+ catch(const DBus::Exception& ex)
+ {
+ if(ex.reason.find("NoSuchAdapter") == string::npos)
+ {
+ throw BluetoothException(__FILE__, __LINE__, ex.reason);
+ }
+ }
+
+ return string();
+ }
+
+ string findAdapter(const string& addr) const
+ {
+ try
+ {
+ //
+ // The call to FindAdapter returns OBJ_PATH.
+ //
+ DBus::MessagePtr reply = call("/", "org.bluez.Manager", "FindAdapter", new DBus::StringValue(addr));
+ DBus::ValuePtr v = reply->read();
+ DBus::ObjectPathValuePtr path = DBus::ObjectPathValuePtr::dynamicCast(v);
+ assert(path);
+ return path->v;
+ }
+ catch(const DBus::Exception& ex)
+ {
+ if(ex.reason.find("NoSuchAdapter") == string::npos)
+ {
+ throw BluetoothException(__FILE__, __LINE__, ex.reason);
+ }
+ }
+
+ return string();
+ }
+
+ VariantMap getAdapterProperties(const string& path) const
+ {
+ //
+ // The call to GetProperties returns
+ //
+ // DICT<STRING,VARIANT> properties
+ //
+ DBus::MessagePtr reply = call(path, "org.bluez.Adapter", "GetProperties");
+ DBus::ValuePtr v = reply->read();
+ VariantMap props;
+ extractProperties(v, props);
+ return props;
+ }
+
+ IceXML::NodePtr findChild(const IceXML::NodePtr& parent, const string& name) const
+ {
+ IceXML::NodeList l = parent->getChildren();
+ for(IceXML::NodeList::iterator p = l.begin(); p != l.end(); ++p)
+ {
+ if((*p)->getName() == name)
+ {
+ return *p;
+ }
+ }
+
+ return 0;
+ }
+
+ IceXML::NodePtr findAttribute(const IceXML::NodePtr& record, int id) const
+ {
+ IceXML::NodeList l = record->getChildren();
+ for(IceXML::NodeList::iterator p = l.begin(); p != l.end(); ++p)
+ {
+ if((*p)->getName() == "attribute")
+ {
+ string sid = (*p)->getAttribute("id");
+ int i;
+ if(sscanf(sid.c_str(), "%x", &i) == 1 && i == id)
+ {
+ return *p;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ IceXML::NodePtr findSequence(const IceXML::NodePtr& parent, const string& uuid) const
+ {
+ IceXML::NodeList l = parent->getChildren();
+ for(IceXML::NodeList::iterator p = l.begin(); p != l.end(); ++p)
+ {
+ if((*p)->getName() == "sequence")
+ {
+ IceXML::NodePtr u = findChild(*p, "uuid");
+ if(u)
+ {
+ string val = u->getAttribute("value");
+ if(IceUtilInternal::toUpper(val) == IceUtilInternal::toUpper(uuid))
+ {
+ return *p;
+ }
+ }
+
+ //
+ // Recursively search for nested <sequence> elements.
+ //
+ IceXML::NodePtr n = findSequence(*p, uuid);
+ if(n)
+ {
+ return n;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ IceXML::NodePtr findSequence(const IceXML::NodePtr& parent, int uuid) const
+ {
+ IceXML::NodeList l = parent->getChildren();
+ for(IceXML::NodeList::iterator p = l.begin(); p != l.end(); ++p)
+ {
+ if((*p)->getName() == "sequence")
+ {
+ IceXML::NodePtr u = findChild(*p, "uuid");
+ if(u)
+ {
+ string val = u->getAttribute("value");
+ int i;
+ if(sscanf(val.c_str(), "%x", &i) == 1 && i == uuid)
+ {
+ return *p;
+ }
+ }
+
+ //
+ // Recursively search for nested <sequence> elements.
+ //
+ IceXML::NodePtr n = findSequence(*p, uuid);
+ if(n)
+ {
+ return n;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ DBus::MessagePtr call(const string& path, const string& intf, const string& member,
+ const DBus::ValuePtr& arg = 0) const
+ {
+ DBus::MessagePtr msg = DBus::Message::createCall("org.bluez", path, intf, member);
+ if(arg)
+ {
+ msg->write(arg);
+ }
+ DBus::AsyncResultPtr r = _dbusConnection->callAsync(msg);
+ DBus::MessagePtr reply = r->waitUntilFinished();
+ if(reply->isError())
+ {
+ reply->throwException();
+ }
+ return reply;
+ }
+
+ void runFindService(const IceUtil::ThreadPtr& thread, const string& addr, const string& uuid,
+ const FindServiceCallbackPtr& cb)
+ {
+ vector<int> channels;
+ bool failed = false;
+
+ try
+ {
+ const string a = IceUtilInternal::toUpper(addr);
+ const string adapter = getDefaultAdapter();
+
+ string devicePath;
+ bool first = true;
+
+ while(devicePath.empty())
+ {
+ try
+ {
+ DBus::MessagePtr reply = call(adapter, "org.bluez.Adapter", "FindDevice", new DBus::StringValue(a));
+ DBus::ValuePtr v = reply->read();
+ DBus::ObjectPathValuePtr path = DBus::ObjectPathValuePtr::dynamicCast(v);
+ assert(path);
+ devicePath = path->v;
+ }
+ catch(const DBus::Exception& ex)
+ {
+ if(ex.reason.find("DoesNotExist") != string::npos)
+ {
+ if(first)
+ {
+ first = false;
+ try
+ {
+ DBus::MessagePtr reply = call(adapter, "org.bluez.Adapter", "CreateDevice",
+ new DBus::StringValue(a));
+ DBus::ValuePtr v = reply->read();
+ DBus::ObjectPathValuePtr path = DBus::ObjectPathValuePtr::dynamicCast(v);
+ assert(path);
+ devicePath = path->v;
+ }
+ catch(const DBus::Exception&)
+ {
+ // Try FindDevice one more time.
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ else
+ {
+ throw BluetoothException(__FILE__, __LINE__, ex.reason);
+ }
+ }
+ }
+
+ if(devicePath.empty())
+ {
+ //
+ // Couldn't get the object path for the device.
+ //
+ throw BluetoothException(__FILE__, __LINE__, "unable to create device for address " + addr);
+ }
+
+ DBus::ValuePtr services;
+
+ for(int iter = 0; iter < 50; ++iter)
+ {
+ {
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+
+ if(_destroyed)
+ {
+ throw CommunicatorDestroyedException(__FILE__, __LINE__);
+ }
+ }
+
+ try
+ {
+ //
+ // Try to call DiscoverServices on the device to get the XML service records.
+ // The return value is a DICT<UINT32, STRING>.
+ //
+ DBus::MessagePtr reply = call(devicePath, "org.bluez.Device", "DiscoverServices",
+ new DBus::StringValue(""));
+ services = reply->read();
+ break;
+ }
+ catch(const DBus::Exception& ex)
+ {
+ if(ex.reason.find("InProgress") == string::npos)
+ {
+ throw BluetoothException(__FILE__, __LINE__, ex.reason);
+ }
+
+ //
+ // Another call to DiscoverServices is already in progress for the target device.
+ // Sleep a little and try again.
+ //
+ IceUtil::ThreadControl::sleep(IceUtil::Time::milliSeconds(100));
+ }
+ }
+
+ if(!services)
+ {
+ throw BluetoothException(__FILE__, __LINE__, "query for services failed for address " + addr);
+ }
+
+ //
+ // The returned dictionary contains an XML record for each service advertised by the
+ // remote device. We have to parse the XML and look for certain elements. Specifically,
+ // we're looking for a UUID that matches the one supplied by the caller. If we find
+ // that, we look in the same record for an RFCOMM channel.
+ //
+ IceXML::DocumentPtr doc;
+ assert(services->getType()->getKind() == DBus::Type::KindArray);
+ DBus::ArrayValuePtr d = DBus::ArrayValuePtr::dynamicCast(services);
+ for(vector<DBus::ValuePtr>::const_iterator p = d->elements.begin(); p != d->elements.end(); ++p)
+ {
+ assert((*p)->getType()->getKind() == DBus::Type::KindDictEntry);
+ DBus::DictEntryValuePtr pe = DBus::DictEntryValuePtr::dynamicCast(*p);
+ assert(pe->value->getType()->getKind() == DBus::Type::KindString);
+ DBus::StringValuePtr rec = DBus::StringValuePtr::dynamicCast(pe->value);
+
+ try
+ {
+ //
+ // This is what we're processing:
+ //
+ // <record>
+ // <attribute id="0x0001">
+ // <sequence>
+ // <uuid value="f6d289b4-1596-4294-ac34-f08e8adbfe5b" />
+ // </sequence>
+ // </attribute>
+ // <attribute id="0x0004">
+ // <sequence>
+ // <sequence>
+ // <uuid value="0x0100" />
+ // </sequence>
+ // <sequence>
+ // <uuid value="0x0003" />
+ // <uint8 value="0x01" />
+ // </sequence>
+ // </sequence>
+ // </attribute>
+ // ...
+ // </record>
+ //
+
+ istringstream istr(rec->v);
+ doc = IceXML::Parser::parse(istr);
+ IceXML::NodePtr record = findChild(doc, "record");
+ if(!record)
+ {
+ throw BluetoothException(__FILE__, __LINE__, "malformed XML - can't find <record>");
+ }
+
+ //
+ // Attribute 0x0001 holds the UUID. See if it matches the target UUID.
+ //
+ IceXML::NodePtr attrib = findAttribute(record, 1);
+ IceXML::NodePtr seq;
+ if(attrib)
+ {
+ seq = findSequence(attrib, uuid);
+ }
+ if(seq)
+ {
+ //
+ // We found a matching service UUID. Now look for the RFCOMM channel in the
+ // protocol descriptor list.
+ //
+ attrib = findAttribute(record, 4);
+ if(attrib)
+ {
+ seq = findSequence(attrib, 3);
+ if(seq)
+ {
+ IceXML::NodePtr ch = findChild(seq, "uint8");
+ if(ch)
+ {
+ string val = ch->getAttribute("value");
+ int channel;
+ if(sscanf(val.c_str(), "%x", &channel) == 1)
+ {
+ channels.push_back(channel);
+ }
+ }
+ }
+ }
+ }
+ }
+ catch(const IceXML::ParserException& ex)
+ {
+ if(doc)
+ {
+ doc->destroy();
+ }
+ ostringstream ostr;
+ ostr << ex.reason() << endl << rec->v;
+ throw BluetoothException(__FILE__, __LINE__, ostr.str());
+ }
+ catch(...)
+ {
+ if(doc)
+ {
+ doc->destroy();
+ }
+ throw;
+ }
+ }
+
+ if(doc)
+ {
+ doc->destroy();
+ }
+
+ {
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+
+ if(_destroyed)
+ {
+ throw CommunicatorDestroyedException(__FILE__, __LINE__);
+ }
+ }
+ }
+ catch(const DBus::Exception& ex)
+ {
+ cb->exception(BluetoothException(__FILE__, __LINE__, ex.reason));
+ failed = true;
+ }
+ catch(const LocalException& ex)
+ {
+ cb->exception(ex);
+ failed = true;
+ }
+ catch(const std::exception& ex)
+ {
+ cb->exception(UnknownException(__FILE__, __LINE__, ex.what()));
+ failed = true;
+ }
+ catch(...)
+ {
+ cb->exception(UnknownException(__FILE__, __LINE__, "unknown C++ exception"));
+ failed = true;
+ }
+
+ if(!failed)
+ {
+ if(channels.empty())
+ {
+ //
+ // No service found for the UUID at the remote address. We treat this as if the
+ // server is not running.
+ //
+ cb->exception(ConnectFailedException(__FILE__, __LINE__, ECONNREFUSED));
+ }
+ else
+ {
+ cb->completed(channels);
+ }
+ }
+
+ {
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
+
+ vector<IceUtil::ThreadPtr>::iterator p = find(_threads.begin(), _threads.end(), thread);
+ if(p != _threads.end())
+ {
+ _threads.erase(p);
+ }
+ }
+ }
+
+ class FindServiceThread : public IceUtil::Thread
+ {
+ public:
+
+ FindServiceThread(const BluetoothServicePtr& service, const string& addr, const string& uuid,
+ const FindServiceCallbackPtr& cb) :
+ _service(service),
+ _addr(addr),
+ _uuid(uuid),
+ _cb(cb)
+ {
+ }
+
+ virtual void run()
+ {
+ _service->runFindService(this, _addr, _uuid, _cb);
+ }
+
+ BluetoothServicePtr _service;
+ string _addr;
+ string _uuid;
+ FindServiceCallbackPtr _cb;
+ };
+
+ IceUtil::Monitor<IceUtil::Mutex> _lock;
+ bool _destroyed;
+ DBus::ConnectionPtr _dbusConnection;
+ vector<IceUtil::ThreadPtr> _threads;
+#ifdef ICE_CPP11_MAPPING
+ map<string, vector<function<void(const string&, const PropertyMap&)>>> _discoveryCallbacks;
+#else
+ map<string, vector<DiscoveryCallbackPtr> > _discoveryCallbacks;
+#endif
+};
+
+}
+
+IceUtil::Shared* IceBT::upCast(IceBT::BluetoothService* 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()
+{
+ _service = new BluetoothService;
+ _initialized = true;
+}
+
+bool
+IceBT::Engine::initialized() const
+{
+ return _initialized;
+}
+
+string
+IceBT::Engine::getDefaultAdapterAddress() const
+{
+ return _service->getDefaultAdapterAddress();
+}
+
+bool
+IceBT::Engine::adapterExists(const string& addr) const
+{
+ return _service->adapterExists(addr);
+}
+
+unsigned int
+IceBT::Engine::addService(const string& address, const string& name, const string& uuid, int channel)
+{
+ return _service->addService(address, name, uuid, channel);
+}
+
+void
+IceBT::Engine::findService(const string& address, const string& uuid, const FindServiceCallbackPtr& cb)
+{
+ _service->findService(address, uuid, cb);
+}
+
+void
+IceBT::Engine::removeService(const string& address, unsigned int handle)
+{
+ _service->removeService(address, handle);
+}
+
+void
+#ifdef ICE_CPP11_MAPPING
+IceBT::Engine::startDiscovery(const string& address, function<void(const string&, const PropertyMap&)> cb)
+#else
+IceBT::Engine::startDiscovery(const string& address, const DiscoveryCallbackPtr& cb)
+#endif
+{
+ _service->startDiscovery(address, cb);
+}
+
+void
+IceBT::Engine::stopDiscovery(const string& address)
+{
+ _service->stopDiscovery(address);
+}
+
+void
+IceBT::Engine::destroy()
+{
+ _service->destroy();
+}