summaryrefslogtreecommitdiffstats
path: root/dbus
diff options
context:
space:
mode:
authorsatorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-30 19:13:31 +0000
committersatorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-30 19:13:31 +0000
commita9e9149c236b28426c061dc964c3be86571c342f (patch)
tree985d1772b1d0d6b423d274497ac43b33391ab24a /dbus
parent08bb1e7204b84f029d78db6871497bac5597e3a6 (diff)
downloadchromium_src-a9e9149c236b28426c061dc964c3be86571c342f.zip
chromium_src-a9e9149c236b28426c061dc964c3be86571c342f.tar.gz
chromium_src-a9e9149c236b28426c061dc964c3be86571c342f.tar.bz2
Implement classes used for manipulating D-Bus messages.
Message/MethodCall/Response classes wrap around DBusMessage. MessageReader and Message Writer provide API to read and write D-Bus messages in a type safe fashion. BUG=90036 TEST=The code is not yet used in Chrome. Run unit tests. Review URL: http://codereview.chromium.org/7492029 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@94845 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'dbus')
-rw-r--r--dbus/dbus.gyp19
-rw-r--r--dbus/message.cc664
-rw-r--r--dbus/message.h333
-rw-r--r--dbus/message_unittest.cc372
4 files changed, 1388 insertions, 0 deletions
diff --git a/dbus/dbus.gyp b/dbus/dbus.gyp
index f2f842b..c1fd9fe 100644
--- a/dbus/dbus.gyp
+++ b/dbus/dbus.gyp
@@ -15,6 +15,25 @@
'../build/linux/system.gyp:dbus',
],
'sources': [
+ 'message.cc',
+ 'message.h',
+ ],
+ },
+ {
+ 'target_name': 'dbus_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ 'dbus',
+ '../base/base.gyp:test_support_base',
+ '../testing/gtest.gyp:gtest',
+ '../build/linux/system.gyp:dbus',
+ ],
+ 'sources': [
+ '../base/test/run_all_unittests.cc',
+ 'message_unittest.cc',
+ ],
+ 'include_dirs': [
+ '..',
],
},
],
diff --git a/dbus/message.cc b/dbus/message.cc
new file mode 100644
index 0000000..28f8960
--- /dev/null
+++ b/dbus/message.cc
@@ -0,0 +1,664 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "dbus/message.h"
+
+#include "base/basictypes.h"
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+
+namespace dbus {
+
+Message::Message()
+ : raw_message_(NULL) {
+}
+
+Message::~Message() {
+ if (raw_message_)
+ dbus_message_unref(raw_message_);
+}
+
+Message::MessageType Message::GetMessageType() {
+ if (!raw_message_)
+ return MESSAGE_INVALID;
+ const int type = dbus_message_get_type(raw_message_);
+ return static_cast<Message::MessageType>(type);
+}
+
+void Message::reset_raw_message(DBusMessage* raw_message) {
+ if (raw_message_)
+ dbus_message_unref(raw_message_);
+ raw_message_ = raw_message;
+}
+
+std::string Message::ToStringInternal(const std::string& indent,
+ MessageReader* reader) {
+ const char* kBrokenMessage = "[broken message]";
+ std::string output;
+ while (reader->HasMoreData()) {
+ const DataType type = reader->GetDataType();
+ switch (type) {
+ case BYTE: {
+ uint8 value = 0;
+ if (!reader->PopByte(&value))
+ return kBrokenMessage;
+ output += indent + "byte " + base::StringPrintf("%d", value) + "\n";
+ break;
+ }
+ case BOOL: {
+ bool value = false;
+ if (!reader->PopBool(&value))
+ return kBrokenMessage;
+ output += indent + "bool " + (value ? "true" : "false") + "\n";
+ break;
+ }
+ case INT16: {
+ int16 value = 0;
+ if (!reader->PopInt16(&value))
+ return kBrokenMessage;
+ output += indent + "int16 " + base::StringPrintf("%d", value) + "\n";
+ break;
+ }
+ case UINT16: {
+ uint16 value = 0;
+ if (!reader->PopUint16(&value))
+ return kBrokenMessage;
+ output += indent + "uint16 " + base::StringPrintf("%d", value) + "\n";
+ break;
+ }
+ case INT32: {
+ int32 value = 0;
+ if (!reader->PopInt32(&value))
+ return kBrokenMessage;
+ output += indent + "int32 " + base::StringPrintf("%d", value) + "\n";
+ break;
+ }
+ case UINT32: {
+ uint32 value = 0;
+ if (!reader->PopUint32(&value))
+ return kBrokenMessage;
+ output += indent + "uint32 " + base::StringPrintf("%u", value) + "\n";
+ break;
+ }
+ case INT64: {
+ int64 value = 0;
+ if (!reader->PopInt64(&value))
+ return kBrokenMessage;
+ output += (indent + "int64 " +
+ base::StringPrintf("%" PRId64, value) + "\n");
+ break;
+ }
+ case UINT64: {
+ uint64 value = 0;
+ if (!reader->PopUint64(&value))
+ return kBrokenMessage;
+ output += (indent + "uint64 " +
+ base::StringPrintf("%" PRIu64, value) + "\n");
+ break;
+ }
+ case DOUBLE: {
+ double value = 0;
+ if (!reader->PopDouble(&value))
+ return kBrokenMessage;
+ output += indent + "double " + base::StringPrintf("%f", value) + "\n";
+ break;
+ }
+ case STRING: {
+ std::string value;
+ if (!reader->PopString(&value))
+ return kBrokenMessage;
+ output += indent + "string \"" + value + "\"\n";
+ break;
+ }
+ case OBJECT_PATH: {
+ std::string value;
+ if (!reader->PopObjectPath(&value))
+ return kBrokenMessage;
+ output += indent + "object_path \"" + value + "\"\n";
+ break;
+ }
+ case ARRAY: {
+ MessageReader sub_reader(this);
+ if (!reader->PopArray(&sub_reader))
+ return kBrokenMessage;
+ output += indent + "array [\n";
+ output += ToStringInternal(indent + " ", &sub_reader);
+ output += indent + "]\n";
+ break;
+ }
+ case STRUCT: {
+ MessageReader sub_reader(this);
+ if (!reader->PopStruct(&sub_reader))
+ return kBrokenMessage;
+ output += indent + "struct {\n";
+ output += ToStringInternal(indent + " ", &sub_reader);
+ output += indent + "}\n";
+ break;
+ }
+ case DICT_ENTRY: {
+ MessageReader sub_reader(this);
+ if (!reader->PopDictEntry(&sub_reader))
+ return kBrokenMessage;
+ output += indent + "dict entry {\n";
+ output += ToStringInternal(indent + " ", &sub_reader);
+ output += indent + "}\n";
+ break;
+ }
+ case VARIANT: {
+ MessageReader sub_reader(this);
+ if (!reader->PopVariant(&sub_reader))
+ return kBrokenMessage;
+ output += indent + "variant ";
+ output += ToStringInternal(indent + " ", &sub_reader);
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unknown type: " << type;
+ }
+ }
+ return output;
+}
+
+// The returned string consists of message headers such as
+// destination if any, followed by a blank line, and the message
+// payload. For example, a MethodCall's ToString() will look like:
+//
+// destination: com.example.Service
+// path: /com/example/Object
+// interface: com.example.Interface
+// member: SomeMethod
+//
+// string \"payload\"
+// ...
+std::string Message::ToString() {
+ if (!raw_message_)
+ return "";
+
+ // Generate headers first.
+ std::string headers;
+ const char* destination = dbus_message_get_destination(raw_message_);
+ if (destination)
+ headers += base::StringPrintf("destination: %s\n", destination);
+ const char* path = dbus_message_get_path(raw_message_);
+ if (path)
+ headers += base::StringPrintf("path: %s\n", path);
+ const char* interface = dbus_message_get_interface(raw_message_);
+ if (interface)
+ headers += base::StringPrintf("interface: %s\n", interface);
+ const char* member = dbus_message_get_member(raw_message_);
+ if (member)
+ headers += base::StringPrintf("member: %s\n", member);
+ const char* error_name = dbus_message_get_error_name(raw_message_);
+ if (error_name)
+ headers += base::StringPrintf("error_name: %s\n", error_name);
+ const char* sender = dbus_message_get_sender(raw_message_);
+ if (sender)
+ headers += base::StringPrintf("sender: %s\n", sender);
+ const char* signature = dbus_message_get_signature(raw_message_);
+ if (signature)
+ headers += base::StringPrintf("signature: %s\n", signature);
+
+ // Generate the payload.
+ MessageReader reader(this);
+ return headers + "\n" + ToStringInternal("", &reader);
+}
+
+//
+// MethodCall implementation.
+//
+
+MethodCall::MethodCall(const std::string& interface_name,
+ const std::string& method_name)
+ : Message(),
+ interface_name_(interface_name),
+ method_name_(method_name) {
+ reset_raw_message(dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL));
+
+ bool success = dbus_message_set_interface(raw_message(),
+ interface_name.c_str());
+ CHECK(success) << "Unable to allocate memory";
+
+ success = dbus_message_set_member(raw_message(), method_name.c_str());
+ CHECK(success) << "Unable to allocate memory";
+}
+
+void MethodCall::SetServiceName(const std::string& service_name) {
+ const bool success = dbus_message_set_destination(raw_message(),
+ service_name.c_str());
+ CHECK(success) << "Unable to allocate memory";
+}
+
+void MethodCall::SetObjectPath(const std::string& object_path) {
+ const bool success = dbus_message_set_path(raw_message(),
+ object_path.c_str());
+ CHECK(success) << "Unable to allocate memory";
+}
+
+//
+// Response implementation.
+//
+
+Response::Response() : Message() {
+}
+
+//
+// MessageWriter implementation.
+//
+
+MessageWriter::MessageWriter(Message* message) :
+ message_(message),
+ container_is_open_(false) {
+ dbus_message_iter_init_append(message_->raw_message(), &raw_message_iter_);
+}
+
+MessageWriter::~MessageWriter() {
+}
+
+void MessageWriter::AppendByte(uint8 value) {
+ AppendBasic(DBUS_TYPE_BYTE, &value);
+}
+
+void MessageWriter::AppendBool(bool value) {
+ AppendBasic(DBUS_TYPE_BOOLEAN, &value);
+}
+
+void MessageWriter::AppendInt16(int16 value) {
+ AppendBasic(DBUS_TYPE_INT16, &value);
+}
+
+void MessageWriter::AppendUint16(uint16 value) {
+ AppendBasic(DBUS_TYPE_UINT16, &value);
+}
+
+void MessageWriter::AppendInt32(int32 value) {
+ AppendBasic(DBUS_TYPE_INT32, &value);
+}
+
+void MessageWriter::AppendUint32(uint32 value) {
+ AppendBasic(DBUS_TYPE_UINT32, &value);
+}
+
+void MessageWriter::AppendInt64(int64 value) {
+ AppendBasic(DBUS_TYPE_INT64, &value);
+}
+
+void MessageWriter::AppendUint64(uint64 value) {
+ AppendBasic(DBUS_TYPE_UINT64, &value);
+}
+
+void MessageWriter::AppendDouble(double value) {
+ AppendBasic(DBUS_TYPE_DOUBLE, &value);
+}
+
+void MessageWriter::AppendString(const std::string& value) {
+ const char* pointer = value.c_str();
+ AppendBasic(DBUS_TYPE_STRING, &pointer);
+ // TODO(satorux): It may make sense to return an error here, as the
+ // input string can be large. If needed, we could add something like
+ // bool AppendStringWithErrorChecking().
+}
+
+void MessageWriter::AppendObjectPath(const std::string& value) {
+ const char* pointer = value.c_str();
+ AppendBasic(DBUS_TYPE_OBJECT_PATH, &pointer);
+}
+
+// Ideally, client shouldn't need to supply the signature string, but
+// the underlying D-Bus library requires us to supply this before
+// appending contents to array and variant. It's technically possible
+// for us to design API that doesn't require the signature but it will
+// complicate the implementation so we decided to have the signature
+// parameter. Hopefully, variants are less used in request messages from
+// client side than response message from server side, so this should
+// not be a big issue.
+void MessageWriter::OpenArray(const std::string& signature,
+ MessageWriter* writer) {
+ DCHECK(!container_is_open_);
+
+ const bool success = dbus_message_iter_open_container(
+ &raw_message_iter_,
+ DBUS_TYPE_ARRAY,
+ signature.c_str(),
+ &writer->raw_message_iter_);
+ CHECK(success) << "Unable to allocate memory";
+ container_is_open_ = true;
+}
+
+void MessageWriter::OpenVariant(const std::string& signature,
+ MessageWriter* writer) {
+ DCHECK(!container_is_open_);
+
+ const bool success = dbus_message_iter_open_container(
+ &raw_message_iter_,
+ DBUS_TYPE_VARIANT,
+ signature.c_str(),
+ &writer->raw_message_iter_);
+ CHECK(success) << "Unable to allocate memory";
+ container_is_open_ = true;
+}
+
+void MessageWriter::OpenStruct(MessageWriter* writer) {
+ DCHECK(!container_is_open_);
+
+ const bool success = dbus_message_iter_open_container(
+ &raw_message_iter_,
+ DBUS_TYPE_STRUCT,
+ NULL, // Signature should be NULL.
+ &writer->raw_message_iter_);
+ CHECK(success) << "Unable to allocate memory";
+ container_is_open_ = true;
+}
+
+void MessageWriter::OpenDictEntry(MessageWriter* writer) {
+ DCHECK(!container_is_open_);
+
+ const bool success = dbus_message_iter_open_container(
+ &raw_message_iter_,
+ DBUS_TYPE_DICT_ENTRY,
+ NULL, // Signature should be NULL.
+ &writer->raw_message_iter_);
+ CHECK(success) << "Unable to allocate memory";
+ container_is_open_ = true;
+}
+
+void MessageWriter::CloseContainer(MessageWriter* writer) {
+ DCHECK(container_is_open_);
+
+ const bool success = dbus_message_iter_close_container(
+ &raw_message_iter_, &writer->raw_message_iter_);
+ CHECK(success) << "Unable to allocate memory";
+ container_is_open_ = false;
+}
+
+void MessageWriter::AppendArrayOfBytes(const uint8* values, size_t length) {
+ DCHECK(!container_is_open_);
+ MessageWriter array_writer(message_);
+ OpenArray("y", &array_writer);
+ const bool success = dbus_message_iter_append_fixed_array(
+ &(array_writer.raw_message_iter_),
+ DBUS_TYPE_BYTE,
+ &values,
+ static_cast<int>(length));
+ CHECK(success) << "Unable to allocate memory";
+ CloseContainer(&array_writer);
+}
+
+void MessageWriter::AppendVariantOfByte(uint8 value) {
+ AppendVariantOfBasic(DBUS_TYPE_BYTE, &value);
+}
+
+void MessageWriter::AppendVariantOfBool(bool value) {
+ AppendVariantOfBasic(DBUS_TYPE_BOOLEAN, &value);
+}
+
+void MessageWriter::AppendVariantOfInt16(int16 value) {
+ AppendVariantOfBasic(DBUS_TYPE_INT16, &value);
+}
+
+void MessageWriter::AppendVariantOfUint16(uint16 value) {
+ AppendVariantOfBasic(DBUS_TYPE_UINT16, &value);
+}
+
+void MessageWriter::AppendVariantOfInt32(int32 value) {
+ AppendVariantOfBasic(DBUS_TYPE_INT32, &value);
+}
+
+void MessageWriter::AppendVariantOfUint32(uint32 value) {
+ AppendVariantOfBasic(DBUS_TYPE_UINT32, &value);
+}
+
+void MessageWriter::AppendVariantOfInt64(int64 value) {
+ AppendVariantOfBasic(DBUS_TYPE_INT64, &value);
+}
+
+void MessageWriter::AppendVariantOfUint64(uint64 value) {
+ AppendVariantOfBasic(DBUS_TYPE_UINT64, &value);
+}
+
+void MessageWriter::AppendVariantOfDouble(double value) {
+ AppendVariantOfBasic(DBUS_TYPE_DOUBLE, &value);
+}
+
+void MessageWriter::AppendVariantOfString(const std::string& value) {
+ const char* pointer = value.c_str();
+ AppendVariantOfBasic(DBUS_TYPE_STRING, &pointer);
+}
+
+void MessageWriter::AppendVariantOfObjectPath(const std::string& value) {
+ const char* pointer = value.c_str();
+ AppendVariantOfBasic(DBUS_TYPE_OBJECT_PATH, &pointer);
+}
+
+void MessageWriter::AppendBasic(int dbus_type, const void* value) {
+ DCHECK(!container_is_open_);
+
+ const bool success = dbus_message_iter_append_basic(
+ &raw_message_iter_, dbus_type, value);
+ // dbus_message_iter_append_basic() fails only when there is not enough
+ // memory. We don't return this error as there is nothing we can do when
+ // it fails to allocate memory for a byte etc.
+ CHECK(success) << "Unable to allocate memory";
+}
+
+void MessageWriter::AppendVariantOfBasic(int dbus_type, const void* value) {
+ const std::string signature = base::StringPrintf("%c", dbus_type);
+ MessageWriter variant_writer(message_);
+ OpenVariant(signature, &variant_writer);
+ variant_writer.AppendBasic(dbus_type, value);
+ CloseContainer(&variant_writer);
+}
+
+//
+// MessageReader implementation.
+//
+
+MessageReader::MessageReader(Message* message)
+ : message_(message) {
+ dbus_message_iter_init(message_->raw_message(), &raw_message_iter_);
+}
+
+
+MessageReader::~MessageReader() {
+}
+
+bool MessageReader::HasMoreData() {
+ const int dbus_type = dbus_message_iter_get_arg_type(&raw_message_iter_);
+ return dbus_type != DBUS_TYPE_INVALID;
+}
+
+bool MessageReader::PopByte(uint8* value) {
+ return PopBasic(DBUS_TYPE_BYTE, value);
+}
+
+bool MessageReader::PopBool(bool* value) {
+ return PopBasic(DBUS_TYPE_BOOLEAN, value);
+}
+
+bool MessageReader::PopInt16(int16* value) {
+ return PopBasic(DBUS_TYPE_INT16, value);
+}
+
+bool MessageReader::PopUint16(uint16* value) {
+ return PopBasic(DBUS_TYPE_UINT16, value);
+}
+
+bool MessageReader::PopInt32(int32* value) {
+ return PopBasic(DBUS_TYPE_INT32, value);
+}
+
+bool MessageReader::PopUint32(uint32* value) {
+ return PopBasic(DBUS_TYPE_UINT32, value);
+}
+
+bool MessageReader::PopInt64(int64* value) {
+ return PopBasic(DBUS_TYPE_INT64, value);
+}
+
+bool MessageReader::PopUint64(uint64* value) {
+ return PopBasic(DBUS_TYPE_UINT64, value);
+}
+
+bool MessageReader::PopDouble(double* value) {
+ return PopBasic(DBUS_TYPE_DOUBLE, value);
+}
+
+bool MessageReader::PopString(std::string* value) {
+ char* tmp_value = NULL;
+ const bool success = PopBasic(DBUS_TYPE_STRING, &tmp_value);
+ if (success)
+ value->assign(tmp_value);
+ return success;
+}
+
+bool MessageReader::PopObjectPath(std::string* value) {
+ char* tmp_value = NULL;
+ const bool success = PopBasic(DBUS_TYPE_OBJECT_PATH, &tmp_value);
+ if (success)
+ value->assign(tmp_value);
+ return success;
+}
+
+bool MessageReader::PopArray(MessageReader* sub_reader) {
+ return PopContainer(DBUS_TYPE_ARRAY, sub_reader);
+}
+
+bool MessageReader::PopStruct(MessageReader* sub_reader) {
+ return PopContainer(DBUS_TYPE_STRUCT, sub_reader);
+}
+
+bool MessageReader::PopDictEntry(MessageReader* sub_reader) {
+ return PopContainer(DBUS_TYPE_DICT_ENTRY, sub_reader);
+}
+
+bool MessageReader::PopVariant(MessageReader* sub_reader) {
+ return PopContainer(DBUS_TYPE_VARIANT, sub_reader);
+}
+
+bool MessageReader::PopArrayOfBytes(uint8** bytes, size_t* length) {
+ MessageReader array_reader(message_);
+ if (!PopArray(&array_reader))
+ return false;
+ if (!array_reader.CheckDataType(DBUS_TYPE_BYTE))
+ return false;
+ int int_length = 0;
+ dbus_message_iter_get_fixed_array(&array_reader.raw_message_iter_,
+ bytes,
+ &int_length);
+ *length = static_cast<int>(int_length);
+ return bytes != NULL;
+}
+
+bool MessageReader::PopArrayOfObjectPaths(
+ std::vector<std::string> *object_paths) {
+ MessageReader array_reader(message_);
+ if (!PopArray(&array_reader))
+ return false;
+ while (array_reader.HasMoreData()) {
+ std::string object_path;
+ if (!array_reader.PopObjectPath(&object_path))
+ return false;
+ object_paths->push_back(object_path);
+ }
+ return true;
+}
+
+bool MessageReader::PopVariantOfByte(uint8* value) {
+ return PopVariantOfBasic(DBUS_TYPE_BYTE, value);
+}
+
+bool MessageReader::PopVariantOfBool(bool* value) {
+ return PopVariantOfBasic(DBUS_TYPE_BOOLEAN, value);
+}
+
+bool MessageReader::PopVariantOfInt16(int16* value) {
+ return PopVariantOfBasic(DBUS_TYPE_INT16, value);
+}
+
+bool MessageReader::PopVariantOfUint16(uint16* value) {
+ return PopVariantOfBasic(DBUS_TYPE_UINT16, value);
+}
+
+bool MessageReader::PopVariantOfInt32(int32* value) {
+ return PopVariantOfBasic(DBUS_TYPE_INT32, value);
+}
+
+bool MessageReader::PopVariantOfUint32(uint32* value) {
+ return PopVariantOfBasic(DBUS_TYPE_UINT32, value);
+}
+
+bool MessageReader::PopVariantOfInt64(int64* value) {
+ return PopVariantOfBasic(DBUS_TYPE_INT64, value);
+}
+
+bool MessageReader::PopVariantOfUint64(uint64* value) {
+ return PopVariantOfBasic(DBUS_TYPE_UINT64, value);
+}
+
+bool MessageReader::PopVariantOfDouble(double* value) {
+ return PopVariantOfBasic(DBUS_TYPE_DOUBLE, value);
+}
+
+bool MessageReader::PopVariantOfString(std::string* value) {
+ char* tmp_value = NULL;
+ const bool success = PopVariantOfBasic(DBUS_TYPE_STRING, &tmp_value);
+ if (success)
+ value->assign(tmp_value);
+ return success;
+}
+
+bool MessageReader::PopVariantOfObjectPath(std::string* value) {
+ char* tmp_value = NULL;
+ const bool success = PopVariantOfBasic(DBUS_TYPE_OBJECT_PATH, &tmp_value);
+ if (success)
+ value->assign(tmp_value);
+ return success;
+}
+
+Message::DataType MessageReader::GetDataType() {
+ const int dbus_type = dbus_message_iter_get_arg_type(&raw_message_iter_);
+ return static_cast<Message::DataType>(dbus_type);
+}
+
+bool MessageReader::CheckDataType(int dbus_type) {
+ const int actual_type = dbus_message_iter_get_arg_type(&raw_message_iter_);
+ if (actual_type != dbus_type) {
+ VLOG(1) << "Type " << dbus_type << " is expected but got "
+ << actual_type;
+ return false;
+ }
+ return true;
+}
+
+bool MessageReader::PopBasic(int dbus_type, void* value) {
+ if (!CheckDataType(dbus_type))
+ return false;
+ // dbus_message_iter_get_basic() here should always work, as we have
+ // already checked the next item's data type in CheckDataType(). Note
+ // that dbus_message_iter_get_basic() is a void function.
+ dbus_message_iter_get_basic(&raw_message_iter_, value);
+ DCHECK(value);
+ dbus_message_iter_next(&raw_message_iter_);
+ return true;
+}
+
+bool MessageReader::PopContainer(int dbus_type, MessageReader* sub_reader) {
+ DCHECK_NE(this, sub_reader);
+
+ if (!CheckDataType(dbus_type))
+ return false;
+ dbus_message_iter_recurse(&raw_message_iter_,
+ &sub_reader->raw_message_iter_);
+ dbus_message_iter_next(&raw_message_iter_);
+ return true;
+}
+
+bool MessageReader::PopVariantOfBasic(int dbus_type, void* value) {
+ dbus::MessageReader variant_reader(message_);
+ if (!PopVariant(&variant_reader))
+ return false;
+ return variant_reader.PopBasic(dbus_type, value);
+}
+
+} // namespace dbus
diff --git a/dbus/message.h b/dbus/message.h
new file mode 100644
index 0000000..501f99a
--- /dev/null
+++ b/dbus/message.h
@@ -0,0 +1,333 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DBUS_MESSAGE_H_
+#define DBUS_MESSAGE_H_
+#pragma once
+
+#include <string>
+#include <vector>
+#include <dbus/dbus.h>
+
+#include "base/basictypes.h"
+
+namespace dbus {
+
+class MessageWriter;
+class MessageReader;
+
+// Message is the base class of D-Bus message types. Client code should
+// usually use sub classes such as MethodCall and Response instead.
+//
+// The class name Message is very generic, but there should be no problem
+// as the class is inside 'dbus' namespace. We chose to name this way, as
+// libdbus defines lots of types starting with DBus, such as
+// DBusMessage. We should avoid confusion and conflict with these types.
+class Message {
+ public:
+ // The message type used in D-Bus. Redefined here so client code
+ // doesn't need to use raw D-Bus macros. DBUS_MESSAGE_TYPE_INVALID
+ // etc. are #define macros. Having an enum type here makes code a bit
+ // more type-safe.
+ enum MessageType {
+ MESSAGE_INVALID = DBUS_MESSAGE_TYPE_INVALID,
+ MESSAGE_METHOD_CALL = DBUS_MESSAGE_TYPE_METHOD_CALL,
+ MESSAGE_METHOD_RETURN = DBUS_MESSAGE_TYPE_METHOD_RETURN,
+ MESSAGE_SIGNAL = DBUS_MESSAGE_TYPE_SIGNAL,
+ MESSAGE_ERROR = DBUS_MESSAGE_TYPE_ERROR,
+ };
+
+ // The data type used in the D-Bus type system. See the comment at
+ // MessageType for why we are redefining data types here.
+ enum DataType {
+ INVALID_DATA = DBUS_TYPE_INVALID,
+ BYTE = DBUS_TYPE_BYTE,
+ BOOL = DBUS_TYPE_BOOLEAN,
+ INT16 = DBUS_TYPE_INT16,
+ UINT16 = DBUS_TYPE_UINT16,
+ INT32 = DBUS_TYPE_INT32,
+ UINT32 = DBUS_TYPE_UINT32,
+ INT64 = DBUS_TYPE_INT64,
+ UINT64 = DBUS_TYPE_UINT64,
+ DOUBLE = DBUS_TYPE_DOUBLE,
+ STRING = DBUS_TYPE_STRING,
+ OBJECT_PATH = DBUS_TYPE_OBJECT_PATH,
+ ARRAY = DBUS_TYPE_ARRAY,
+ STRUCT = DBUS_TYPE_STRUCT,
+ DICT_ENTRY = DBUS_TYPE_DICT_ENTRY,
+ VARIANT = DBUS_TYPE_VARIANT,
+ };
+
+ // Creates a Message. The internal raw message is NULL until it's set
+ // from outside by reset_raw_message().
+ Message();
+ virtual ~Message();
+
+ // Returns the type of the message. Returns MESSAGE_INVALID if
+ // raw_message_ is NULL.
+ MessageType GetMessageType();
+
+ DBusMessage* raw_message() { return raw_message_; }
+
+ // Resets raw_message_ with the given raw message. Takes the ownership
+ // of raw_message. raw_message_ will be unref'ed in the destructor.
+ void reset_raw_message(DBusMessage* raw_message);
+
+ // Returns the string representation of this message. Useful for
+ // debugging.
+ std::string ToString();
+
+ private:
+ // Helper function used in ToString().
+ std::string ToStringInternal(const std::string& indent,
+ MessageReader* reader);
+
+ DBusMessage* raw_message_;
+ DISALLOW_COPY_AND_ASSIGN(Message);
+};
+
+// MessageCall is a type of message used for calling a method via D-Bus.
+class MethodCall : public Message {
+ public:
+ // Creates a method call message for the specified interface name and
+ // the method name.
+ //
+ // For instance, to call "Get" method of DBUS_INTERFACE_INTROSPECTABLE
+ // interface ("org.freedesktop.DBus.Introspectable"), create a method
+ // call like this:
+ //
+ // MethodCall method_call(DBUS_INTERFACE_INTROSPECTABLE, "Get");
+ //
+ // The constructor creates the internal raw_message_, so the client
+ // doesn't need to set this with reset_raw_message().
+ MethodCall(const std::string& interface_name,
+ const std::string& method_name);
+
+ const std::string& interface_name() { return interface_name_; }
+ const std::string& method_name() { return method_name_; }
+
+ // Sets the service name. This will be handled by the object proxy.
+ void SetServiceName(const std::string& service_name);
+ // Sets the object path. This will be handled by the object proxy.
+ void SetObjectPath(const std::string& object_path);
+
+ std::string interface_name_;
+ std::string method_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(MethodCall);
+};
+
+// Response is a type of message used for receiving a response from a
+// method via D-Bus.
+class Response : public Message {
+ public:
+ // Creates a Response message. The internal raw message is NULL.
+ // Classes that implment method calls need to set the raw message once a
+ // response is received from the server. See object_proxy.h.
+ Response();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Response);
+};
+
+// MessageWriter is used to write outgoing messages for calling methods
+// and sending signals.
+//
+// The main design goal of MessageReader and MessageWriter classes is to
+// provide a type safe API. In the past, there was a Chrome OS blocker
+// bug, that took days to fix, that would have been prevented if the API
+// was type-safe.
+//
+// For instance, instead of doing something like:
+//
+// // We shouldn't add '&' to str here, but it compiles with '&' added.
+// dbus_g_proxy_call(..., G_TYPE_STRING, str, G_TYPE_INVALID, ...)
+//
+// We want to do something like:
+//
+// writer.AppendString(str);
+//
+class MessageWriter {
+ public:
+ // Data added with Append* will be written to |message|.
+ MessageWriter(Message* message);
+ ~MessageWriter();
+
+ // Appends a byte to the message.
+ void AppendByte(uint8 value);
+ void AppendBool(bool value);
+ void AppendInt16(int16 value);
+ void AppendUint16(uint16 value);
+ void AppendInt32(int32 value);
+ void AppendUint32(uint32 value);
+ void AppendInt64(int64 value);
+ void AppendUint64(uint64 value);
+ void AppendDouble(double value);
+ void AppendString(const std::string& value);
+ void AppendObjectPath(const std::string& value);
+
+ // Opens an array. The array contents can be added to the array with
+ // |sub_writer|. The client code must close the array with
+ // CloseContainer(), once all contents are added.
+ //
+ // |signature| parameter is used to supply the D-Bus type signature of
+ // the array contents. For instance, if you want an array of strings,
+ // then you pass "s" as the signature.
+ //
+ // See the spec for details about the type signatures.
+ // http://dbus.freedesktop.org/doc/dbus-specification.html
+ // #message-protocol-signatures
+ //
+ void OpenArray(const std::string& signature, MessageWriter* sub_writer);
+ // Do the same for a variant.
+ void OpenVariant(const std::string& signature, MessageWriter* sub_writer);
+ // Do the same for Struct and dict entry. They don't need the signature.
+ void OpenStruct(MessageWriter* sub_writer);
+ void OpenDictEntry(MessageWriter* sub_writer);
+
+ // Close the container for a array/variant/struct/dict entry.
+ void CloseContainer(MessageWriter* sub_writer);
+
+ // Appends the array of bytes. Arrays of bytes are often used for
+ // exchanging binary blobs hence it's worth having a specialized
+ // function.
+ void AppendArrayOfBytes(const uint8* values, size_t length);
+
+ // Appends the byte wrapped in a variant data container. Variants are
+ // widely used in D-Bus services so it's worth having a specialized
+ // function. For instance, The third parameter of
+ // "org.freedesktop.DBus.Properties.Set" is a variant.
+ void AppendVariantOfByte(uint8 value);
+ void AppendVariantOfBool(bool value);
+ void AppendVariantOfInt16(int16 value);
+ void AppendVariantOfUint16(uint16 value);
+ void AppendVariantOfInt32(int32 value);
+ void AppendVariantOfUint32(uint32 value);
+ void AppendVariantOfInt64(int64 value);
+ void AppendVariantOfUint64(uint64 value);
+ void AppendVariantOfDouble(double value);
+ void AppendVariantOfString(const std::string& value);
+ void AppendVariantOfObjectPath(const std::string& value);
+
+ private:
+ // Helper function used to implement AppendByte etc.
+ void AppendBasic(int dbus_type, const void* value);
+
+ // Helper function used to implement AppendVariantOfByte() etc.
+ void AppendVariantOfBasic(int dbus_type, const void* value);
+
+ Message* message_;
+ DBusMessageIter raw_message_iter_;
+ bool container_is_open_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageWriter);
+};
+
+// MessageReader is used to read incoming messages such as responses for
+// method calls.
+//
+// MessageReader manages an internal iterator to read data. All functions
+// starting with Pop advance the iterator on success.
+class MessageReader {
+ public:
+ // The data will be read from the given message.
+ MessageReader(Message* message);
+ ~MessageReader();
+
+ // Returns true if the reader has more data to read. The function is
+ // used to iterate contents in a container like:
+ //
+ // while (reader.HasMoreData())
+ // reader.PopString(&value);
+ bool HasMoreData();
+
+ // Gets the byte at the current iterator position.
+ // Returns true and advances the iterator on success.
+ // Returns false if the data type is not a byte.
+ bool PopByte(uint8* value);
+ bool PopBool(bool* value);
+ bool PopInt16(int16* value);
+ bool PopUint16(uint16* value);
+ bool PopInt32(int32* value);
+ bool PopUint32(uint32* value);
+ bool PopInt64(int64* value);
+ bool PopUint64(uint64* value);
+ bool PopDouble(double* value);
+ bool PopString(std::string* value);
+ bool PopObjectPath(std::string* value);
+
+ // Sets up the given message reader to read an array at the current
+ // iterator position.
+ // Returns true and advances the iterator on success.
+ // Returns false if the data type is not an array
+ bool PopArray(MessageReader* sub_reader);
+ bool PopStruct(MessageReader* sub_reader);
+ bool PopDictEntry(MessageReader* sub_reader);
+ bool PopVariant(MessageReader* sub_reader);
+
+ // Gets the array of bytes at the current iterator position.
+ // Returns true and advances the iterator on success.
+ //
+ // Arrays of bytes are often used for exchanging binary blobs hence it's
+ // worth having a specialized function.
+ //
+ // |bytes| must be copied if the contents will be referenced after the
+ // |MessageReader is destroyed.
+ bool PopArrayOfBytes(uint8** bytes, size_t* length);
+
+ // Gets the array of object paths at the current iterator position.
+ // Returns true and advances the iterator on success.
+ //
+ // Arrays of object paths are often used to communicate with D-Bus
+ // services like NetworkManager, hence it's worth having a specialized
+ // function.
+ bool PopArrayOfObjectPaths(std::vector<std::string>* object_paths);
+
+ // Gets the byte from the variant data container at the current iterator
+ // position.
+ // Returns true and advances the iterator on success.
+ //
+ // Variants are widely used in D-Bus services so it's worth having a
+ // specialized function. For instance, The return value type of
+ // "org.freedesktop.DBus.Properties.Get" is a variant.
+ bool PopVariantOfByte(uint8* value);
+ bool PopVariantOfBool(bool* value);
+ bool PopVariantOfInt16(int16* value);
+ bool PopVariantOfUint16(uint16* value);
+ bool PopVariantOfInt32(int32* value);
+ bool PopVariantOfUint32(uint32* value);
+ bool PopVariantOfInt64(int64* value);
+ bool PopVariantOfUint64(uint64* value);
+ bool PopVariantOfDouble(double* value);
+ bool PopVariantOfString(std::string* value);
+ bool PopVariantOfObjectPath(std::string* value);
+
+ // Get the data type of the value at the current iterator
+ // position. INVALID_DATA will be returned if the iterator points to the
+ // end of the message.
+ Message::DataType GetDataType();
+
+ private:
+ // Returns true if the data type at the current iterator position
+ // matches the given D-Bus type, such as DBUS_TYPE_BYTE.
+ bool CheckDataType(int dbus_type);
+
+ // Helper function used to implement PopByte() etc.
+ bool PopBasic(int dbus_type, void *value);
+
+ // Helper function used to implement PopArray() etc.
+ bool PopContainer(int dbus_type, MessageReader* sub_reader);
+
+ // Helper function used to implement PopVariantOfByte() etc.
+ bool PopVariantOfBasic(int dbus_type, void* value);
+
+ Message* message_;
+ DBusMessageIter raw_message_iter_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageReader);
+};
+
+} // namespace dbus
+
+#endif // DBUS_MESSAGE_H_
diff --git a/dbus/message_unittest.cc b/dbus/message_unittest.cc
new file mode 100644
index 0000000..e7aaba1
--- /dev/null
+++ b/dbus/message_unittest.cc
@@ -0,0 +1,372 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "dbus/message.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Test that a byte can be properly written and read. We only have this
+// test for byte, as repeating this for other basic types is too redundant.
+TEST(MessageTest, AppendAndPopByte) {
+ dbus::Message message;
+ message.reset_raw_message(dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL));
+ dbus::MessageWriter writer(&message);
+ writer.AppendByte(123); // The input is 123.
+
+ dbus::MessageReader reader(&message);
+ ASSERT_TRUE(reader.HasMoreData()); // Should have data to read.
+ ASSERT_EQ(dbus::Message::BYTE, reader.GetDataType());
+
+ bool bool_value = false;
+ // Should fail as the type is not bool here.
+ ASSERT_FALSE(reader.PopBool(&bool_value));
+
+ uint8 byte_value = 0;
+ ASSERT_TRUE(reader.PopByte(&byte_value));
+ EXPECT_EQ(123, byte_value); // Should match with the input.
+ ASSERT_FALSE(reader.HasMoreData()); // Should not have more data to read.
+
+ // Try to get another byte. Should fail.
+ ASSERT_FALSE(reader.PopByte(&byte_value));
+}
+
+// Check all basic types can be properly written and read.
+TEST(MessageTest, AppendAndPopBasicDataTypes) {
+ dbus::Message message;
+ message.reset_raw_message(dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL));
+ dbus::MessageWriter writer(&message);
+
+ // Append 0, 1, 2, 3, 4, 5, 6, 7, 8, "string", "/object/path".
+ writer.AppendByte(0);
+ writer.AppendBool(true);
+ writer.AppendInt16(2);
+ writer.AppendUint16(3);
+ writer.AppendInt32(4);
+ writer.AppendUint32(5);
+ writer.AppendInt64(6);
+ writer.AppendUint64(7);
+ writer.AppendDouble(8.0);
+ writer.AppendString("string");
+ writer.AppendObjectPath("/object/path");
+
+ uint8 byte_value = 0;
+ bool bool_value = false;
+ int16 int16_value = 0;
+ uint16 uint16_value = 0;
+ int32 int32_value = 0;
+ uint32 uint32_value = 0;
+ int64 int64_value = 0;
+ uint64 uint64_value = 0;
+ double double_value = 0;
+ std::string string_value;
+ std::string object_path_value;
+
+ dbus::MessageReader reader(&message);
+ ASSERT_TRUE(reader.HasMoreData());
+ ASSERT_TRUE(reader.PopByte(&byte_value));
+ ASSERT_TRUE(reader.PopBool(&bool_value));
+ ASSERT_TRUE(reader.PopInt16(&int16_value));
+ ASSERT_TRUE(reader.PopUint16(&uint16_value));
+ ASSERT_TRUE(reader.PopInt32(&int32_value));
+ ASSERT_TRUE(reader.PopUint32(&uint32_value));
+ ASSERT_TRUE(reader.PopInt64(&int64_value));
+ ASSERT_TRUE(reader.PopUint64(&uint64_value));
+ ASSERT_TRUE(reader.PopDouble(&double_value));
+ ASSERT_TRUE(reader.PopString(&string_value));
+ ASSERT_TRUE(reader.PopObjectPath(&object_path_value));
+ ASSERT_FALSE(reader.HasMoreData());
+
+ // 0, 1, 2, 3, 4, 5, 6, 7, 8, "string", "/object/path" should be returned.
+ EXPECT_EQ(0, byte_value);
+ EXPECT_EQ(true, bool_value);
+ EXPECT_EQ(2, int16_value);
+ EXPECT_EQ(3U, uint16_value);
+ EXPECT_EQ(4, int32_value);
+ EXPECT_EQ(5U, uint32_value);
+ EXPECT_EQ(6, int64_value);
+ EXPECT_EQ(7U, uint64_value);
+ EXPECT_DOUBLE_EQ(8.0, double_value);
+ EXPECT_EQ("string", string_value);
+ EXPECT_EQ("/object/path", object_path_value);
+}
+
+// Check all variant types can be properly written and read.
+TEST(MessageTest, AppendAndPopVariantDataTypes) {
+ dbus::Message message;
+ message.reset_raw_message(dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL));
+ dbus::MessageWriter writer(&message);
+
+ // Append 0, 1, 2, 3, 4, 5, 6, 7, 8, "string", "/object/path".
+ writer.AppendVariantOfByte(0);
+ writer.AppendVariantOfBool(true);
+ writer.AppendVariantOfInt16(2);
+ writer.AppendVariantOfUint16(3);
+ writer.AppendVariantOfInt32(4);
+ writer.AppendVariantOfUint32(5);
+ writer.AppendVariantOfInt64(6);
+ writer.AppendVariantOfUint64(7);
+ writer.AppendVariantOfDouble(8.0);
+ writer.AppendVariantOfString("string");
+ writer.AppendVariantOfObjectPath("/object/path");
+
+ uint8 byte_value = 0;
+ bool bool_value = false;
+ int16 int16_value = 0;
+ uint16 uint16_value = 0;
+ int32 int32_value = 0;
+ uint32 uint32_value = 0;
+ int64 int64_value = 0;
+ uint64 uint64_value = 0;
+ double double_value = 0;
+ std::string string_value;
+ std::string object_path_value;
+
+ dbus::MessageReader reader(&message);
+ ASSERT_TRUE(reader.HasMoreData());
+ ASSERT_TRUE(reader.PopVariantOfByte(&byte_value));
+ ASSERT_TRUE(reader.PopVariantOfBool(&bool_value));
+ ASSERT_TRUE(reader.PopVariantOfInt16(&int16_value));
+ ASSERT_TRUE(reader.PopVariantOfUint16(&uint16_value));
+ ASSERT_TRUE(reader.PopVariantOfInt32(&int32_value));
+ ASSERT_TRUE(reader.PopVariantOfUint32(&uint32_value));
+ ASSERT_TRUE(reader.PopVariantOfInt64(&int64_value));
+ ASSERT_TRUE(reader.PopVariantOfUint64(&uint64_value));
+ ASSERT_TRUE(reader.PopVariantOfDouble(&double_value));
+ ASSERT_TRUE(reader.PopVariantOfString(&string_value));
+ ASSERT_TRUE(reader.PopVariantOfObjectPath(&object_path_value));
+ ASSERT_FALSE(reader.HasMoreData());
+
+ // 0, 1, 2, 3, 4, 5, 6, 7, 8, "string", "/object/path" should be returned.
+ EXPECT_EQ(0, byte_value);
+ EXPECT_EQ(true, bool_value);
+ EXPECT_EQ(2, int16_value);
+ EXPECT_EQ(3U, uint16_value);
+ EXPECT_EQ(4, int32_value);
+ EXPECT_EQ(5U, uint32_value);
+ EXPECT_EQ(6, int64_value);
+ EXPECT_EQ(7U, uint64_value);
+ EXPECT_DOUBLE_EQ(8.0, double_value);
+ EXPECT_EQ("string", string_value);
+ EXPECT_EQ("/object/path", object_path_value);
+}
+
+TEST(MessageTest, ArrayOfBytes) {
+ dbus::Message message;
+ message.reset_raw_message(dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL));
+ dbus::MessageWriter writer(&message);
+ std::vector<uint8> bytes;
+ bytes.push_back(1);
+ bytes.push_back(2);
+ bytes.push_back(3);
+ writer.AppendArrayOfBytes(bytes.data(), bytes.size());
+
+ dbus::MessageReader reader(&message);
+ uint8* output_bytes = NULL;
+ size_t length = 0;
+ ASSERT_TRUE(reader.PopArrayOfBytes(&output_bytes, &length));
+ ASSERT_FALSE(reader.HasMoreData());
+ ASSERT_EQ(3U, length);
+ ASSERT_EQ(1, output_bytes[0]);
+ ASSERT_EQ(2, output_bytes[1]);
+ ASSERT_EQ(3, output_bytes[2]);
+}
+
+// Test that an array can be properly written and read. We only have this
+// test for array, as repeating this for other container types is too
+// redundant.
+TEST(MessageTest, OpenArrayAndPopArray) {
+ dbus::Message message;
+ message.reset_raw_message(dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL));
+ dbus::MessageWriter writer(&message);
+ dbus::MessageWriter array_writer(&message);
+ writer.OpenArray("s", &array_writer); // Open an array of strings.
+ array_writer.AppendString("foo");
+ array_writer.AppendString("bar");
+ array_writer.AppendString("baz");
+ writer.CloseContainer(&array_writer);
+
+ dbus::MessageReader reader(&message);
+ ASSERT_EQ(dbus::Message::ARRAY, reader.GetDataType());
+ dbus::MessageReader array_reader(&message);
+ ASSERT_TRUE(reader.PopArray(&array_reader));
+ ASSERT_FALSE(reader.HasMoreData()); // Should not have more data to read.
+
+ std::string string_value;
+ ASSERT_TRUE(array_reader.PopString(&string_value));
+ EXPECT_EQ("foo", string_value);
+ ASSERT_TRUE(array_reader.PopString(&string_value));
+ EXPECT_EQ("bar", string_value);
+ ASSERT_TRUE(array_reader.PopString(&string_value));
+ EXPECT_EQ("baz", string_value);
+ // Should not have more data to read.
+ ASSERT_FALSE(array_reader.HasMoreData());
+}
+
+// Create a complex message using array, struct, variant, dict entry, and
+// make sure it can be read properly.
+TEST(MessageTest, CreateComplexMessageAndReadIt) {
+ dbus::Message message;
+ message.reset_raw_message(dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL));
+ dbus::MessageWriter writer(&message);
+ {
+ dbus::MessageWriter array_writer(&message);
+ // Open an array of variants.
+ writer.OpenArray("v", &array_writer);
+ {
+ // The first value in the array.
+ {
+ dbus::MessageWriter variant_writer(&message);
+ // Open a variant of a boolean.
+ array_writer.OpenVariant("b", &variant_writer);
+ variant_writer.AppendBool(true);
+ array_writer.CloseContainer(&variant_writer);
+ }
+
+ // The second value in the array.
+ {
+ dbus::MessageWriter variant_writer(&message);
+ // Open a variant of a struct that contains a string and an int32.
+ array_writer.OpenVariant("(si)", &variant_writer);
+ {
+ dbus::MessageWriter struct_writer(&message);
+ variant_writer.OpenStruct(&struct_writer);
+ struct_writer.AppendString("string");
+ struct_writer.AppendInt32(123);
+ variant_writer.CloseContainer(&struct_writer);
+ }
+ array_writer.CloseContainer(&variant_writer);
+ }
+
+ // The third value in the array.
+ {
+ dbus::MessageWriter variant_writer(&message);
+ // Open a variant of an array of string-to-int64 dict entries.
+ array_writer.OpenVariant("a{sx}", &variant_writer);
+ {
+ // Opens an array of string-to-int64 dict entries.
+ dbus::MessageWriter dict_array_writer(&message);
+ variant_writer.OpenArray("{sx}", &dict_array_writer);
+ {
+ // Opens a string-to-int64 dict entries.
+ dbus::MessageWriter dict_entry_writer(&message);
+ dict_array_writer.OpenDictEntry(&dict_entry_writer);
+ dict_entry_writer.AppendString("foo");
+ dict_entry_writer.AppendInt64(1234567890123456789);
+ dict_array_writer.CloseContainer(&dict_entry_writer);
+ }
+ variant_writer.CloseContainer(&dict_array_writer);
+ }
+ array_writer.CloseContainer(&variant_writer);
+ }
+ }
+ writer.CloseContainer(&array_writer);
+ }
+ // What we have created looks like this:
+ EXPECT_EQ("signature: av\n"
+ "\n"
+ "array [\n"
+ " variant bool true\n"
+ " variant struct {\n"
+ " string \"string\"\n"
+ " int32 123\n"
+ " }\n"
+ " variant array [\n"
+ " dict entry {\n"
+ " string \"foo\"\n"
+ " int64 1234567890123456789\n"
+ " }\n"
+ " ]\n"
+ "]\n",
+ message.ToString());
+
+ dbus::MessageReader reader(&message);
+ dbus::MessageReader array_reader(&message);
+ ASSERT_TRUE(reader.PopArray(&array_reader));
+
+ // The first value in the array.
+ bool bool_value = false;
+ ASSERT_TRUE(array_reader.PopVariantOfBool(&bool_value));
+ EXPECT_EQ(true, bool_value);
+
+ // The second value in the array.
+ {
+ dbus::MessageReader variant_reader(&message);
+ ASSERT_TRUE(array_reader.PopVariant(&variant_reader));
+ {
+ dbus::MessageReader struct_reader(&message);
+ ASSERT_TRUE(variant_reader.PopStruct(&struct_reader));
+ std::string string_value;
+ ASSERT_TRUE(struct_reader.PopString(&string_value));
+ EXPECT_EQ("string", string_value);
+ int32 int32_value = 0;
+ ASSERT_TRUE(struct_reader.PopInt32(&int32_value));
+ EXPECT_EQ(123, int32_value);
+ ASSERT_FALSE(struct_reader.HasMoreData());
+ }
+ ASSERT_FALSE(variant_reader.HasMoreData());
+ }
+
+ // The third value in the array.
+ {
+ dbus::MessageReader variant_reader(&message);
+ ASSERT_TRUE(array_reader.PopVariant(&variant_reader));
+ {
+ dbus::MessageReader dict_array_reader(&message);
+ ASSERT_TRUE(variant_reader.PopArray(&dict_array_reader));
+ {
+ dbus::MessageReader dict_entry_reader(&message);
+ ASSERT_TRUE(dict_array_reader.PopDictEntry(&dict_entry_reader));
+ std::string string_value;
+ ASSERT_TRUE(dict_entry_reader.PopString(&string_value));
+ EXPECT_EQ("foo", string_value);
+ int64 int64_value = 0;
+ ASSERT_TRUE(dict_entry_reader.PopInt64(&int64_value));
+ EXPECT_EQ(1234567890123456789, int64_value);
+ }
+ ASSERT_FALSE(dict_array_reader.HasMoreData());
+ }
+ ASSERT_FALSE(variant_reader.HasMoreData());
+ }
+ ASSERT_FALSE(array_reader.HasMoreData());
+ ASSERT_FALSE(reader.HasMoreData());
+}
+
+TEST(MessageTest, Message) {
+ dbus::Message message;
+ EXPECT_TRUE(message.raw_message() == NULL);
+ EXPECT_EQ(dbus::Message::MESSAGE_INVALID, message.GetMessageType());
+}
+
+TEST(MessageTest, MethodCall) {
+ dbus::MethodCall method_call("com.example.Interface", "SomeMethod");
+ EXPECT_TRUE(method_call.raw_message() != NULL);
+ EXPECT_EQ(dbus::Message::MESSAGE_METHOD_CALL, method_call.GetMessageType());
+ method_call.SetServiceName("com.example.Service");
+ method_call.SetObjectPath("/com/example/Object");
+
+ dbus::MessageWriter writer(&method_call);
+ writer.AppendString("payload");
+
+ EXPECT_EQ("destination: com.example.Service\n"
+ "path: /com/example/Object\n"
+ "interface: com.example.Interface\n"
+ "member: SomeMethod\n"
+ "signature: s\n"
+ "\n"
+ "string \"payload\"\n",
+ method_call.ToString());
+}
+
+TEST(MessageTest, Response) {
+ dbus::Response response;
+ EXPECT_TRUE(response.raw_message() == NULL);
+ response.reset_raw_message(
+ dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN));
+ EXPECT_EQ(dbus::Message::MESSAGE_METHOD_RETURN, response.GetMessageType());
+}
+
+TEST(MessageTest, ToString_EmptyMessage) {
+ dbus::Message message;
+ EXPECT_EQ("", message.ToString());
+}