普通文本  |  190行  |  7.04 KB

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