blob: b1b5633f877306d44a08736200820f6cf2a55f65 [file] [log] [blame]
// 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/bus.h> // For kPropertyInterface
#include <dbus/property.h> // For kPropertyInterface
#include "buffet/async_event_sequencer.h"
#include "buffet/dbus_utils.h"
namespace buffet {
namespace dbus_utils {
namespace {
const char kExportFailedMessage[] = "Failed to register DBus method.";
} // namespace
ExportedPropertySet::ExportedPropertySet(dbus::Bus* bus,
const dbus::ObjectPath& path)
: bus_(bus), exported_object_(bus->GetExportedObject(path)),
weak_ptr_factory_(this) { }
void ExportedPropertySet::Init(const OnInitFinish& cb) {
bus_->AssertOnOriginThread();
scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
exported_object_->ExportMethod(
dbus::kPropertiesInterface, dbus::kPropertiesGetAll,
base::Bind(&ExportedPropertySet::HandleGetAll,
weak_ptr_factory_.GetWeakPtr()),
sequencer->GetExportHandler(
dbus::kPropertiesInterface, dbus::kPropertiesGetAll,
kExportFailedMessage, false));
exported_object_->ExportMethod(
dbus::kPropertiesInterface, dbus::kPropertiesGet,
base::Bind(&ExportedPropertySet::HandleGet,
weak_ptr_factory_.GetWeakPtr()),
sequencer->GetExportHandler(
dbus::kPropertiesInterface, dbus::kPropertiesGet,
kExportFailedMessage, false));
exported_object_->ExportMethod(
dbus::kPropertiesInterface, dbus::kPropertiesSet,
base::Bind(&ExportedPropertySet::HandleSet,
weak_ptr_factory_.GetWeakPtr()),
sequencer->GetExportHandler(
dbus::kPropertiesInterface, dbus::kPropertiesSet,
kExportFailedMessage, false));
sequencer->OnAllTasksCompletedCall({cb});
}
ExportedPropertySet::PropertyWriter ExportedPropertySet::GetPropertyWriter(
const std::string& interface) {
return base::Bind(&ExportedPropertySet::WritePropertiesDictToMessage,
weak_ptr_factory_.GetWeakPtr(),
interface);
}
void ExportedPropertySet::RegisterProperty(
const std::string& interface_name,
const std::string& property_name,
ExportedPropertyBase* exported_property) {
bus_->AssertOnOriginThread();
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) {
bus_->AssertOnOriginThread();
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;
}
scoped_ptr<dbus::Response> response(
dbus::Response::FromMethodCall(method_call));
dbus::MessageWriter resp_writer(response.get());
WritePropertiesDictToMessage(interface_name, &resp_writer);
response_sender.Run(response.Pass());
}
void ExportedPropertySet::WritePropertiesDictToMessage(
const std::string& interface_name,
dbus::MessageWriter* writer) {
dbus::MessageWriter dict_writer(nullptr);
writer->OpenArray("{sv}", &dict_writer);
auto property_map_itr = properties_.find(interface_name);
if (property_map_itr != properties_.end()) {
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);
}
} else {
LOG(WARNING) << "No properties found for interface interface_name";
}
writer->CloseContainer(&dict_writer);
}
void ExportedPropertySet::HandleGet(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
bus_->AssertOnOriginThread();
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) {
bus_->AssertOnOriginThread();
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) {
bus_->AssertOnOriginThread();
dbus::Signal signal(dbus::kPropertiesInterface, dbus::kPropertiesChanged);
dbus::MessageWriter writer(&signal);
dbus::MessageWriter array_writer(nullptr);
dbus::MessageWriter dict_writer(nullptr);
writer.AppendString(interface);
writer.OpenArray("{sv}", &array_writer);
array_writer.OpenDictEntry(&dict_writer);
dict_writer.AppendString(name);
property->AppendValueToWriter(&dict_writer);
array_writer.CloseContainer(&dict_writer);
writer.CloseContainer(&array_writer);
// The interface specification tells us to include this list of properties
// which have changed, but for whom no value is conveyed. Currently, we
// don't do anything interesting here.
writer.OpenArray("s", &array_writer);
writer.CloseContainer(&array_writer);
// This sends the signal asyncronously. However, the raw message inside
// the signal object is ref-counted, so we're fine to allocate the Signal
// object on our local stack.
exported_object_->SendSignal(&signal);
}
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) const {
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