// 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