buffet: Add ExportedPropertySet delegate
This object makes it easy to export an org.freedesktop.DBus.Properties
interface.
BUG=chromium:356368
TEST=When integrated with the Manager, this correctly exposes
properties. This can be tested with buffet_BasicDBusAPI.
Change-Id: I6c871ebbd225b6305ca9d4a309fb7b47ed305f9b
Reviewed-on: https://chromium-review.googlesource.com/192001
Tested-by: Christopher Wiley <wiley@chromium.org>
Reviewed-by: Chris Sosa <sosa@chromium.org>
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Christopher Wiley <wiley@chromium.org>
diff --git a/buffet/exported_property_set.cc b/buffet/exported_property_set.cc
new file mode 100644
index 0000000..fe1ef65
--- /dev/null
+++ b/buffet/exported_property_set.cc
@@ -0,0 +1,283 @@
+// Copyright 2014 The Chromium OS 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 "buffet/exported_property_set.h"
+
+#include <base/bind.h>
+#include <dbus/property.h> // For kPropertyInterface
+
+#include "buffet/dbus_utils.h"
+
+namespace buffet {
+
+namespace dbus_utils {
+
+ExportedPropertySet::ExportedPropertySet(dbus::ExportedObject* exported_object)
+ : exported_object_(exported_object), weak_ptr_factory_(this) { }
+
+void ExportedPropertySet::ClaimPropertiesInterface() {
+ exported_object_->ExportMethodAndBlock(
+ dbus::kPropertiesInterface, dbus::kPropertiesGetAll,
+ base::Bind(&ExportedPropertySet::HandleGetAll,
+ weak_ptr_factory_.GetWeakPtr()));
+ exported_object_->ExportMethodAndBlock(
+ dbus::kPropertiesInterface, dbus::kPropertiesGet,
+ base::Bind(&ExportedPropertySet::HandleGet,
+ weak_ptr_factory_.GetWeakPtr()));
+ exported_object_->ExportMethodAndBlock(
+ dbus::kPropertiesInterface, dbus::kPropertiesSet,
+ base::Bind(&ExportedPropertySet::HandleSet,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+ExportedPropertySet::~ExportedPropertySet() { }
+
+void ExportedPropertySet::RegisterProperty(
+ const std::string& interface_name,
+ const std::string& property_name,
+ ExportedPropertyBase* exported_property) {
+ properties_[interface_name][property_name] = exported_property;
+ // Technically, the property set exists longer than the properties themselves,
+ // so we could use Unretained here rather than a weak pointer.
+ ExportedPropertyBase::OnUpdateCallback cb = base::Bind(
+ &ExportedPropertySet::HandlePropertyUpdated,
+ weak_ptr_factory_.GetWeakPtr(),
+ interface_name, property_name);
+ exported_property->SetUpdateCallback(cb);
+}
+
+void ExportedPropertySet::HandleGetAll(
+ dbus::MethodCall* method_call,
+ dbus::ExportedObject::ResponseSender response_sender) {
+ dbus::MessageReader reader(method_call);
+ std::string interface_name;
+ if (!reader.PopString(&interface_name)) {
+ response_sender.Run(
+ GetBadArgsError(method_call, "No interface name specified."));
+ return;
+ }
+ if (reader.HasMoreData()) {
+ response_sender.Run(
+ GetBadArgsError(method_call, "Too many arguments to GetAll."));
+ return;
+ }
+ auto property_map_itr = properties_.find(interface_name);
+ if (property_map_itr == properties_.end()) {
+ response_sender.Run(
+ GetBadArgsError(method_call, "No such interface on object."));
+ return;
+ }
+ scoped_ptr<dbus::Response> response(
+ dbus::Response::FromMethodCall(method_call));
+ dbus::MessageWriter resp_writer(response.get());
+ dbus::MessageWriter dict_writer(nullptr);
+ resp_writer.OpenArray("{sv}", &dict_writer);
+ for (const auto& kv : property_map_itr->second) {
+ dbus::MessageWriter entry_writer(nullptr);
+ dict_writer.OpenDictEntry(&entry_writer);
+ entry_writer.AppendString(kv.first);
+ kv.second->AppendValueToWriter(&entry_writer);
+ dict_writer.CloseContainer(&entry_writer);
+ }
+ resp_writer.CloseContainer(&dict_writer);
+ response_sender.Run(response.Pass());
+}
+
+void ExportedPropertySet::HandleGet(
+ dbus::MethodCall* method_call,
+ dbus::ExportedObject::ResponseSender response_sender) {
+ dbus::MessageReader reader(method_call);
+ std::string interface_name, property_name;
+ if (!reader.PopString(&interface_name)) {
+ response_sender.Run(
+ GetBadArgsError(method_call, "No interface name specified."));
+ return;
+ }
+ if (!reader.PopString(&property_name)) {
+ response_sender.Run(
+ GetBadArgsError(method_call, "No property name specified."));
+ return;
+ }
+ if (reader.HasMoreData()) {
+ response_sender.Run(
+ GetBadArgsError(method_call, "Too many arguments to Get."));
+ return;
+ }
+ auto property_map_itr = properties_.find(interface_name);
+ if (property_map_itr == properties_.end()) {
+ response_sender.Run(
+ GetBadArgsError(method_call, "No such interface on object."));
+ return;
+ }
+ LOG(ERROR) << "Looking for " << property_name << " on " << interface_name;
+ auto property_itr = property_map_itr->second.find(property_name);
+ if (property_itr == property_map_itr->second.end()) {
+ response_sender.Run(
+ GetBadArgsError(method_call, "No such property on interface."));
+ return;
+ }
+ scoped_ptr<dbus::Response> response(
+ dbus::Response::FromMethodCall(method_call));
+ dbus::MessageWriter resp_writer(response.get());
+ property_itr->second->AppendValueToWriter(&resp_writer);
+ response_sender.Run(response.Pass());
+}
+
+void ExportedPropertySet::HandleSet(
+ dbus::MethodCall* method_call,
+ dbus::ExportedObject::ResponseSender response_sender) {
+ scoped_ptr<dbus::ErrorResponse> error_resp(
+ dbus::ErrorResponse::FromMethodCall(
+ method_call, "org.freedesktop.DBus.Error.NotSupported", ""));
+ scoped_ptr<dbus::Response> response(error_resp.release());
+ response_sender.Run(response.Pass());
+}
+
+void ExportedPropertySet::HandlePropertyUpdated(
+ const std::string& interface,
+ const std::string& name,
+ const ExportedPropertyBase* property) {
+ // TODO(wiley): Send a signal from the exported object here.
+}
+
+template <typename T>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const T& value);
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const bool& value) {
+ writer->AppendVariantOfBool(value);
+}
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const uint8& value) {
+ writer->AppendVariantOfByte(value);
+}
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const int16& value) {
+ writer->AppendVariantOfInt16(value);
+}
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const uint16& value) {
+ writer->AppendVariantOfUint16(value);
+}
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const int32& value) {
+ writer->AppendVariantOfInt32(value);
+}
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const uint32& value) {
+ writer->AppendVariantOfUint32(value);
+}
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const int64& value) {
+ writer->AppendVariantOfInt64(value);
+}
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const uint64& value) {
+ writer->AppendVariantOfUint64(value);
+}
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const double& value) {
+ writer->AppendVariantOfDouble(value);
+}
+
+template <>
+void AppendPropertyToWriter(
+ dbus::MessageWriter* writer, const std::string& value) {
+ writer->AppendVariantOfString(value);
+}
+
+template <>
+void AppendPropertyToWriter(
+ dbus::MessageWriter* writer, const dbus::ObjectPath& value) {
+ writer->AppendVariantOfObjectPath(value);
+}
+
+template <>
+void AppendPropertyToWriter(
+ dbus::MessageWriter* writer, const std::vector<std::string>& value) {
+ dbus::MessageWriter variant_writer(nullptr);
+ writer->OpenVariant("as", &variant_writer);
+ variant_writer.AppendArrayOfStrings(value);
+ writer->CloseContainer(&variant_writer);
+}
+
+template <>
+void AppendPropertyToWriter(
+ dbus::MessageWriter* writer, const std::vector<dbus::ObjectPath>& value) {
+ dbus::MessageWriter variant_writer(nullptr);
+ writer->OpenVariant("ao", &variant_writer);
+ variant_writer.AppendArrayOfObjectPaths(value);
+ writer->CloseContainer(&variant_writer);
+}
+
+template <>
+void AppendPropertyToWriter(
+ dbus::MessageWriter* writer, const std::vector<uint8>& value) {
+ dbus::MessageWriter variant_writer(nullptr);
+ writer->OpenVariant("ay", &variant_writer);
+ variant_writer.AppendArrayOfBytes(value.data(), value.size());
+ writer->CloseContainer(&variant_writer);
+}
+
+template <typename T>
+ExportedProperty<T>::ExportedProperty() {}
+
+template <typename T>
+ExportedProperty<T>::~ExportedProperty() {}
+
+template <typename T>
+const T& ExportedProperty<T>::value() const { return value_; }
+
+template <typename T>
+void ExportedProperty<T>::SetValue(const T& new_value) {
+ if (value_ == new_value) {
+ return;
+ }
+ value_ = new_value;
+ // These is a brief period after the construction of an ExportedProperty
+ // when this callback is not initialized because the property has not
+ // been registered with the parent ExportedPropertySet. During this period
+ // users should be initializing values via SetValue, and no notifications
+ // should be triggered by the ExportedPropertySet.
+ if (!on_update_.is_null()) {
+ on_update_.Run(this);
+ }
+}
+
+template <typename T>
+void ExportedProperty<T>::SetUpdateCallback(const OnUpdateCallback& cb) {
+ on_update_ = cb;
+}
+
+template <typename T>
+void ExportedProperty<T>::AppendValueToWriter(dbus::MessageWriter* writer) {
+ AppendPropertyToWriter(writer, value_);
+}
+
+template class ExportedProperty<bool>;
+template class ExportedProperty<uint8>;
+template class ExportedProperty<int16>;
+template class ExportedProperty<uint16>;
+template class ExportedProperty<int32>;
+template class ExportedProperty<uint32>;
+template class ExportedProperty<int64>;
+template class ExportedProperty<uint64>;
+template class ExportedProperty<double>;
+template class ExportedProperty<std::string>;
+template class ExportedProperty<dbus::ObjectPath>;
+template class ExportedProperty<std::vector<std::string>>;
+template class ExportedProperty<std::vector<dbus::ObjectPath>>;
+template class ExportedProperty<std::vector<uint8>>;
+
+} // namespace dbus_utils
+
+} // namespace buffet