// 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 <brillo/dbus/exported_property_set.h>
#include <base/bind.h>
#include <dbus/bus.h>
#include <dbus/property.h> // For kPropertyInterface
#include <brillo/dbus/async_event_sequencer.h>
#include <brillo/dbus/dbus_object.h>
#include <brillo/errors/error_codes.h>
using brillo::dbus_utils::AsyncEventSequencer;
namespace brillo {
namespace dbus_utils {
ExportedPropertySet::ExportedPropertySet(dbus::Bus* bus)
: bus_(bus), weak_ptr_factory_(this) {
}
void ExportedPropertySet::OnPropertiesInterfaceExported(
DBusInterface* prop_interface) {
signal_properties_changed_ =
prop_interface->RegisterSignalOfType<SignalPropertiesChanged>(
dbus::kPropertiesChanged);
}
ExportedPropertySet::PropertyWriter ExportedPropertySet::GetPropertyWriter(
const std::string& interface_name) {
return base::Bind(&ExportedPropertySet::WritePropertiesToDict,
weak_ptr_factory_.GetWeakPtr(),
interface_name);
}
void ExportedPropertySet::RegisterProperty(
const std::string& interface_name,
const std::string& property_name,
ExportedPropertyBase* exported_property) {
bus_->AssertOnOriginThread();
auto& prop_map = properties_[interface_name];
auto res = prop_map.insert(std::make_pair(property_name, exported_property));
CHECK(res.second) << "Property '" << property_name << "' already exists";
// 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::UnregisterProperty(const std::string& interface_name,
const std::string& property_name) {
bus_->AssertOnOriginThread();
auto& prop_map = properties_[interface_name];
auto prop_iter = prop_map.find(property_name);
CHECK(prop_iter != prop_map.end())
<< "Property '" << property_name << "' doesn't exist";
prop_iter->second->ClearUpdateCallback();
prop_map.erase(prop_iter);
}
VariantDictionary ExportedPropertySet::HandleGetAll(
const std::string& interface_name) {
bus_->AssertOnOriginThread();
return GetInterfaceProperties(interface_name);
}
VariantDictionary ExportedPropertySet::GetInterfaceProperties(
const std::string& interface_name) const {
VariantDictionary properties;
auto property_map_itr = properties_.find(interface_name);
if (property_map_itr != properties_.end()) {
for (const auto& kv : property_map_itr->second)
properties.insert(std::make_pair(kv.first, kv.second->GetValue()));
}
return properties;
}
void ExportedPropertySet::WritePropertiesToDict(
const std::string& interface_name,
VariantDictionary* dict) {
*dict = GetInterfaceProperties(interface_name);
}
bool ExportedPropertySet::HandleGet(brillo::ErrorPtr* error,
const std::string& interface_name,
const std::string& property_name,
brillo::Any* result) {
bus_->AssertOnOriginThread();
auto property_map_itr = properties_.find(interface_name);
if (property_map_itr == properties_.end()) {
brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
DBUS_ERROR_UNKNOWN_INTERFACE,
"No such interface on object.");
return false;
}
LOG(INFO) << "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()) {
brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
DBUS_ERROR_UNKNOWN_PROPERTY,
"No such property on interface.");
return false;
}
*result = property_itr->second->GetValue();
return true;
}
bool ExportedPropertySet::HandleSet(brillo::ErrorPtr* error,
const std::string& interface_name,
const std::string& property_name,
const brillo::Any& value) {
bus_->AssertOnOriginThread();
auto property_map_itr = properties_.find(interface_name);
if (property_map_itr == properties_.end()) {
brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
DBUS_ERROR_UNKNOWN_INTERFACE,
"No such interface on object.");
return false;
}
LOG(INFO) << "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()) {
brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
DBUS_ERROR_UNKNOWN_PROPERTY,
"No such property on interface.");
return false;
}
return property_itr->second->SetValue(error, value);
}
void ExportedPropertySet::HandlePropertyUpdated(
const std::string& interface_name,
const std::string& property_name,
const ExportedPropertyBase* exported_property) {
bus_->AssertOnOriginThread();
// Send signal only if the object has been exported successfully.
// This could happen when a property value is changed (which triggers
// the notification) before D-Bus interface is completely exported/claimed.
auto signal = signal_properties_changed_.lock();
if (!signal)
return;
VariantDictionary changed_properties{
{property_name, exported_property->GetValue()}};
// 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.
std::vector<std::string> invalidated_properties; // empty.
signal->Send(interface_name, changed_properties, invalidated_properties);
}
void ExportedPropertyBase::NotifyPropertyChanged() {
// 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_callback_.is_null()) {
on_update_callback_.Run(this);
}
}
void ExportedPropertyBase::SetUpdateCallback(const OnUpdateCallback& cb) {
on_update_callback_ = cb;
}
void ExportedPropertyBase::ClearUpdateCallback() {
on_update_callback_.Reset();
}
void ExportedPropertyBase::SetAccessMode(
ExportedPropertyBase::Access access_mode) {
access_mode_ = access_mode;
}
ExportedPropertyBase::Access ExportedPropertyBase::GetAccessMode() const {
return access_mode_;
}
} // namespace dbus_utils
} // namespace brillo