summaryrefslogtreecommitdiff
path: root/cpp/src/IceBT/DBus.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/IceBT/DBus.cpp')
-rw-r--r--cpp/src/IceBT/DBus.cpp1320
1 files changed, 1320 insertions, 0 deletions
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();
+}