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