1// Copyright 2014 The Chromium OS Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifndef LIBBRILLO_BRILLO_DBUS_EXPORTED_PROPERTY_SET_H_ 6#define LIBBRILLO_BRILLO_DBUS_EXPORTED_PROPERTY_SET_H_ 7 8#include <stdint.h> 9 10#include <map> 11#include <string> 12#include <vector> 13 14#include <base/memory/weak_ptr.h> 15#include <brillo/any.h> 16#include <brillo/brillo_export.h> 17#include <brillo/dbus/dbus_signal.h> 18#include <brillo/errors/error.h> 19#include <brillo/errors/error_codes.h> 20#include <brillo/variant_dictionary.h> 21#include <dbus/exported_object.h> 22#include <dbus/message.h> 23 24namespace brillo { 25 26namespace dbus_utils { 27 28// This class may be used to implement the org.freedesktop.DBus.Properties 29// interface. It sends the update signal on property updates: 30// 31// org.freedesktop.DBus.Properties.PropertiesChanged ( 32// STRING interface_name, 33// DICT<STRING,VARIANT> changed_properties, 34// ARRAY<STRING> invalidated_properties); 35// 36// 37// and implements the required methods of the interface: 38// 39// org.freedesktop.DBus.Properties.Get(in STRING interface_name, 40// in STRING property_name, 41// out VARIANT value); 42// org.freedesktop.DBus.Properties.Set(in STRING interface_name, 43// in STRING property_name, 44// in VARIANT value); 45// org.freedesktop.DBus.Properties.GetAll(in STRING interface_name, 46// out DICT<STRING,VARIANT> props); 47// 48// This class is very similar to the PropertySet class in Chrome, except that 49// it allows objects to expose properties rather than to consume them. 50// It is used as part of DBusObject to implement D-Bus object properties on 51// registered interfaces. See description of DBusObject class for more details. 52 53class DBusInterface; 54class DBusObject; 55 56class BRILLO_EXPORT ExportedPropertyBase { 57 public: 58 enum class Access { 59 kReadOnly, 60 kWriteOnly, 61 kReadWrite, 62 }; 63 64 ExportedPropertyBase() = default; 65 virtual ~ExportedPropertyBase() = default; 66 67 using OnUpdateCallback = base::Callback<void(const ExportedPropertyBase*)>; 68 69 // Called by ExportedPropertySet to register a callback. This callback 70 // triggers ExportedPropertySet to send a signal from the properties 71 // interface of the exported object. 72 virtual void SetUpdateCallback(const OnUpdateCallback& cb); 73 74 // Returns the contained value as Any. 75 virtual brillo::Any GetValue() const = 0; 76 77 virtual bool SetValue(brillo::ErrorPtr* error, 78 const brillo::Any& value) = 0; 79 80 void SetAccessMode(Access access_mode); 81 Access GetAccessMode() const; 82 83 protected: 84 // Notify the listeners of OnUpdateCallback that the property has changed. 85 void NotifyPropertyChanged(); 86 87 private: 88 OnUpdateCallback on_update_callback_; 89 // Default to read-only. 90 Access access_mode_{Access::kReadOnly}; 91}; 92 93class BRILLO_EXPORT ExportedPropertySet { 94 public: 95 using PropertyWriter = base::Callback<void(VariantDictionary* dict)>; 96 97 explicit ExportedPropertySet(dbus::Bus* bus); 98 virtual ~ExportedPropertySet() = default; 99 100 // Called to notify ExportedPropertySet that the Properties interface of the 101 // D-Bus object has been exported successfully and property notification 102 // signals can be sent out. 103 void OnPropertiesInterfaceExported(DBusInterface* prop_interface); 104 105 // Return a callback that knows how to write this property set's properties 106 // to a message. This writer retains a weak pointer to this, and must 107 // only be invoked on the same thread as the rest of ExportedPropertySet. 108 PropertyWriter GetPropertyWriter(const std::string& interface_name); 109 110 void RegisterProperty(const std::string& interface_name, 111 const std::string& property_name, 112 ExportedPropertyBase* exported_property); 113 114 // D-Bus methods for org.freedesktop.DBus.Properties interface. 115 VariantDictionary HandleGetAll(const std::string& interface_name); 116 bool HandleGet(brillo::ErrorPtr* error, 117 const std::string& interface_name, 118 const std::string& property_name, 119 brillo::Any* result); 120 // While Properties.Set has a handler to complete the interface, we don't 121 // support writable properties. This is almost a feature, since bindings for 122 // many languages don't support errors coming back from invalid writes. 123 // Instead, use setters in exposed interfaces. 124 bool HandleSet(brillo::ErrorPtr* error, 125 const std::string& interface_name, 126 const std::string& property_name, 127 const brillo::Any& value); 128 // Returns a string-to-variant map of all the properties for the given 129 // interface and their values. 130 VariantDictionary GetInterfaceProperties( 131 const std::string& interface_name) const; 132 133 private: 134 // Used to write the dictionary of string->variant to a message. 135 // This dictionary represents the property name/value pairs for the 136 // given interface. 137 BRILLO_PRIVATE void WritePropertiesToDict(const std::string& interface_name, 138 VariantDictionary* dict); 139 BRILLO_PRIVATE void HandlePropertyUpdated( 140 const std::string& interface_name, 141 const std::string& property_name, 142 const ExportedPropertyBase* exported_property); 143 144 dbus::Bus* bus_; // weak; owned by outer DBusObject containing this object. 145 // This is a map from interface name -> property name -> pointer to property. 146 std::map<std::string, std::map<std::string, ExportedPropertyBase*>> 147 properties_; 148 149 // D-Bus callbacks may last longer the property set exporting those methods. 150 base::WeakPtrFactory<ExportedPropertySet> weak_ptr_factory_; 151 152 using SignalPropertiesChanged = 153 DBusSignal<std::string, VariantDictionary, std::vector<std::string>>; 154 155 std::weak_ptr<SignalPropertiesChanged> signal_properties_changed_; 156 157 friend class DBusObject; 158 friend class ExportedPropertySetTest; 159 DISALLOW_COPY_AND_ASSIGN(ExportedPropertySet); 160}; 161 162template<typename T> 163class ExportedProperty : public ExportedPropertyBase { 164 public: 165 ExportedProperty() = default; 166 ~ExportedProperty() override = default; 167 168 // Retrieves the current value. 169 const T& value() const { return value_; } 170 171 // Set the value exposed to remote applications. This triggers notifications 172 // of changes over the Properties interface. 173 void SetValue(const T& new_value) { 174 if (value_ != new_value) { 175 value_ = new_value; 176 this->NotifyPropertyChanged(); 177 } 178 } 179 180 // Set the validator for value checking when setting the property by remote 181 // application. 182 void SetValidator( 183 const base::Callback<bool(brillo::ErrorPtr*, const T&)>& validator) { 184 validator_ = validator; 185 } 186 187 // Implementation provided by specialization. 188 brillo::Any GetValue() const override { return value_; } 189 190 bool SetValue(brillo::ErrorPtr* error, 191 const brillo::Any& value) override { 192 if (GetAccessMode() == ExportedPropertyBase::Access::kReadOnly) { 193 brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain, 194 DBUS_ERROR_PROPERTY_READ_ONLY, 195 "Property is read-only."); 196 return false; 197 } 198 if (!value.IsTypeCompatible<T>()) { 199 brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain, 200 DBUS_ERROR_INVALID_ARGS, 201 "Argument type mismatched."); 202 return false; 203 } 204 if (value_ == value.Get<T>()) { 205 // No change to the property value, nothing to be done. 206 return true; 207 } 208 if (!validator_.is_null() && !validator_.Run(error, value.Get<T>())) { 209 return false; 210 } 211 value_ = value.Get<T>(); 212 return true; 213 } 214 215 private: 216 T value_{}; 217 base::Callback<bool(brillo::ErrorPtr*, const T&)> validator_; 218 219 DISALLOW_COPY_AND_ASSIGN(ExportedProperty); 220}; 221 222} // namespace dbus_utils 223 224} // namespace brillo 225 226#endif // LIBBRILLO_BRILLO_DBUS_EXPORTED_PROPERTY_SET_H_ 227