blob: 72aad804935fdf221f7bfb1cdd95944e4bb0bf9c [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.
#ifndef LIBCHROMEOS_CHROMEOS_EXPORTED_PROPERTY_SET_H_
#define LIBCHROMEOS_CHROMEOS_EXPORTED_PROPERTY_SET_H_
#include <stdint.h>
#include <map>
#include <string>
#include <vector>
#include <base/memory/weak_ptr.h>
#include <dbus/exported_object.h>
#include <dbus/message.h>
namespace chromeos {
namespace dbus_utils {
// This class may be used to implement the org.freedesktop.DBus.Properties
// interface. It sends the update signal on property updates:
//
// org.freedesktop.DBus.Properties.PropertiesChanged (
// STRING interface_name,
// DICT<STRING,VARIANT> changed_properties,
// ARRAY<STRING> invalidated_properties);
//
//
// and implements the required methods of the interface:
//
// org.freedesktop.DBus.Properties.Get(in STRING interface_name,
// in STRING property_name,
// out VARIANT value);
// org.freedesktop.DBus.Properties.Set(in STRING interface_name,
// in STRING property_name,
// in VARIANT value);
// org.freedesktop.DBus.Properties.GetAll(in STRING interface_name,
// out DICT<STRING,VARIANT> props);
//
// This class is very similar to the PropertySet class in Chrome, except that
// it allows objects to expose properties rather than to consume them.
//
// Example usage:
//
// class ExampleObjectExportingProperties {
// public:
// ExampleObjectExportingProperties(ExportedObject* exported_object)
// : p_(exported_object) {
// // Initialize properties appropriately. Do this before
// // claiming the Properties interface so that daemons watching
// // this object don't see partial or inaccurate state.
// p_.ClaimPropertiesInterface();
// }
//
// private:
// struct Properties : public chromeos::dbus_utils::ExportedPropertySet {
// public:
// chromeos::dbus_utils::ExportedProperty<std::string> name_;
// chromeos::dbus_utils::ExportedProperty<uint16_t> version_;
// chromeos::dbus_utils::ExportedProperty<dbus::ObjectPath> parent_;
// chromeos::dbus_utils::ExportedProperty<std::vector<std::string>>
// children_;
//
// Properties(dbus_utils::ExportedObject* exported_object)
// : chromeos::dbus::ExportedPropertySet(exported_object) {
// RegisterProperty(kExampleInterfaceName, "Name", &name_);
// RegisterProperty(kExampleInterfaceName, "Version", &version_);
// RegisterProperty(kExampleInterfaceName, "Parent", &parent_);
// RegisterProperty(kExampleInterfaceName, "Children", &children_);
// }
// virtual ~Properties() {}
// };
//
// Properties p_;
// };
class ExportedPropertyBase {
public:
ExportedPropertyBase() = default;
virtual ~ExportedPropertyBase() = default;
using OnUpdateCallback = base::Callback<void(const ExportedPropertyBase*)>;
// Called by ExportedPropertySet to register a callback. This callback
// triggers ExportedPropertySet to send a signal from the properties
// interface of the exported object.
virtual void SetUpdateCallback(const OnUpdateCallback& cb);
// Appends a variant of the contained value to the writer. This is
// needed to write out properties to Get and GetAll methods implemented
// by the ExportedPropertySet since it doesn't actually know the type
// of each property.
virtual void AppendValueToWriter(dbus::MessageWriter* writer) const = 0;
protected:
// Notify the listeners of OnUpdateCallback that the property has changed.
void NotifyPropertyChanged();
private:
OnUpdateCallback on_update_callback_;
};
class ExportedPropertySet {
public:
typedef base::Callback<void(bool success)> OnInitFinish;
typedef base::Callback<void(dbus::MessageWriter* writer)> PropertyWriter;
ExportedPropertySet(dbus::Bus* bus, const dbus::ObjectPath& path);
virtual ~ExportedPropertySet() = default;
// Claims the method associated with the org.freedesktop.DBus.Properties
// interface. This needs to be done after all properties are initialized to
// appropriate values. This method will call |cb| when all methods
// are exported to the DBus object. |cb| will be called on the origin
// thread.
void Init(const OnInitFinish& cb);
// Return a callback that knows how to write this property set's properties
// to a message. This writer retains a weak pointer to this, and must
// only be invoked on the same thread as the rest of ExportedPropertySet.
PropertyWriter GetPropertyWriter(const std::string& interface);
protected:
void RegisterProperty(const std::string& interface_name,
const std::string& property_name,
ExportedPropertyBase* exported_property);
private:
// Used to write the dictionary of string->variant to a message.
// This dictionary represents the property name/value pairs for the
// given interface.
void WritePropertiesDictToMessage(const std::string& interface_name,
dbus::MessageWriter* writer);
void HandleGetAll(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
void HandleGet(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
// While Properties.Set has a handler to complete the interface, we don't
// support writable properties. This is almost a feature, since bindings for
// many languages don't support errors coming back from invalid writes.
// Instead, use setters in exposed interfaces.
void HandleSet(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
void HandlePropertyUpdated(const std::string& interface,
const std::string& name,
const ExportedPropertyBase* property);
dbus::Bus* bus_;
dbus::ExportedObject* exported_object_; // weak; owned by the Bus object.
// This is a map from interface name -> property name -> pointer to property.
std::map<std::string,
std::map<std::string, ExportedPropertyBase*>> properties_;
// D-Bus callbacks may last longer the property set exporting those methods.
base::WeakPtrFactory<ExportedPropertySet> weak_ptr_factory_;
friend class ExportedPropertySetTest;
DISALLOW_COPY_AND_ASSIGN(ExportedPropertySet);
};
void AppendPropertyToWriter(dbus::MessageWriter* writer, bool value);
void AppendPropertyToWriter(dbus::MessageWriter* writer, uint8_t value);
void AppendPropertyToWriter(dbus::MessageWriter* writer, int16_t value);
void AppendPropertyToWriter(dbus::MessageWriter* writer, uint16_t value);
void AppendPropertyToWriter(dbus::MessageWriter* writer, int32_t value);
void AppendPropertyToWriter(dbus::MessageWriter* writer, uint32_t value);
void AppendPropertyToWriter(dbus::MessageWriter* writer, int64_t value);
void AppendPropertyToWriter(dbus::MessageWriter* writer, uint64_t value);
void AppendPropertyToWriter(dbus::MessageWriter* writer, double value);
void AppendPropertyToWriter(dbus::MessageWriter* writer,
const std::string& value);
void AppendPropertyToWriter(dbus::MessageWriter* writer,
const dbus::ObjectPath& value);
void AppendPropertyToWriter(dbus::MessageWriter* writer,
const std::vector<std::string>& value);
void AppendPropertyToWriter(dbus::MessageWriter* writer,
const std::vector<dbus::ObjectPath>& value);
void AppendPropertyToWriter(dbus::MessageWriter* writer,
const std::vector<uint8_t>& value);
template <typename T>
class ExportedProperty : public ExportedPropertyBase {
public:
ExportedProperty() = default;
~ExportedProperty() override = default;
// Retrieves the current value.
const T& value() const { return value_; }
// Set the value exposed to remote applications. This triggers notifications
// of changes over the Properties interface.
void SetValue(const T& new_value) {
if (value_ != new_value) {
value_ = new_value;
this->NotifyPropertyChanged();
}
}
// Implementation provided by specialization.
void AppendValueToWriter(dbus::MessageWriter* writer) const override {
AppendPropertyToWriter(writer, value_);
}
private:
T value_{};
DISALLOW_COPY_AND_ASSIGN(ExportedProperty);
};
} // namespace dbus_utils
} // namespace chromeos
#endif // LIBCHROMEOS_CHROMEOS_EXPORTED_PROPERTY_SET_H_