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 24 namespace brillo { 25 26 namespace 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 53 class DBusInterface; 54 class DBusObject; 55 56 class 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 // Clears the update callback that was previously set with SetUpdateCallback. 75 virtual void ClearUpdateCallback(); 76 77 // Returns the contained value as Any. 78 virtual brillo::Any GetValue() const = 0; 79 80 virtual bool SetValue(brillo::ErrorPtr* error, 81 const brillo::Any& value) = 0; 82 83 void SetAccessMode(Access access_mode); 84 Access GetAccessMode() const; 85 86 protected: 87 // Notify the listeners of OnUpdateCallback that the property has changed. 88 void NotifyPropertyChanged(); 89 90 private: 91 OnUpdateCallback on_update_callback_; 92 // Default to read-only. 93 Access access_mode_{Access::kReadOnly}; 94 }; 95 96 class BRILLO_EXPORT ExportedPropertySet { 97 public: 98 using PropertyWriter = base::Callback<void(VariantDictionary* dict)>; 99 100 explicit ExportedPropertySet(dbus::Bus* bus); 101 virtual ~ExportedPropertySet() = default; 102 103 // Called to notify ExportedPropertySet that the Properties interface of the 104 // D-Bus object has been exported successfully and property notification 105 // signals can be sent out. 106 void OnPropertiesInterfaceExported(DBusInterface* prop_interface); 107 108 // Return a callback that knows how to write this property set's properties 109 // to a message. This writer retains a weak pointer to this, and must 110 // only be invoked on the same thread as the rest of ExportedPropertySet. 111 PropertyWriter GetPropertyWriter(const std::string& interface_name); 112 113 void RegisterProperty(const std::string& interface_name, 114 const std::string& property_name, 115 ExportedPropertyBase* exported_property); 116 117 // Unregisters a property from this exported property set. 118 void UnregisterProperty(const std::string& interface_name, 119 const std::string& property_name); 120 121 // D-Bus methods for org.freedesktop.DBus.Properties interface. 122 VariantDictionary HandleGetAll(const std::string& interface_name); 123 bool HandleGet(brillo::ErrorPtr* error, 124 const std::string& interface_name, 125 const std::string& property_name, 126 brillo::Any* result); 127 // While Properties.Set has a handler to complete the interface, we don't 128 // support writable properties. This is almost a feature, since bindings for 129 // many languages don't support errors coming back from invalid writes. 130 // Instead, use setters in exposed interfaces. 131 bool HandleSet(brillo::ErrorPtr* error, 132 const std::string& interface_name, 133 const std::string& property_name, 134 const brillo::Any& value); 135 // Returns a string-to-variant map of all the properties for the given 136 // interface and their values. 137 VariantDictionary GetInterfaceProperties( 138 const std::string& interface_name) const; 139 140 private: 141 // Used to write the dictionary of string->variant to a message. 142 // This dictionary represents the property name/value pairs for the 143 // given interface. 144 BRILLO_PRIVATE void WritePropertiesToDict(const std::string& interface_name, 145 VariantDictionary* dict); 146 BRILLO_PRIVATE void HandlePropertyUpdated( 147 const std::string& interface_name, 148 const std::string& property_name, 149 const ExportedPropertyBase* exported_property); 150 151 dbus::Bus* bus_; // weak; owned by outer DBusObject containing this object. 152 // This is a map from interface name -> property name -> pointer to property. 153 std::map<std::string, std::map<std::string, ExportedPropertyBase*>> 154 properties_; 155 156 // D-Bus callbacks may last longer the property set exporting those methods. 157 base::WeakPtrFactory<ExportedPropertySet> weak_ptr_factory_; 158 159 using SignalPropertiesChanged = 160 DBusSignal<std::string, VariantDictionary, std::vector<std::string>>; 161 162 std::weak_ptr<SignalPropertiesChanged> signal_properties_changed_; 163 164 friend class DBusObject; 165 friend class ExportedPropertySetTest; 166 DISALLOW_COPY_AND_ASSIGN(ExportedPropertySet); 167 }; 168 169 template<typename T> 170 class ExportedProperty : public ExportedPropertyBase { 171 public: 172 ExportedProperty() = default; 173 ~ExportedProperty() override = default; 174 175 // Retrieves the current value. value()176 const T& value() const { return value_; } 177 178 // Set the value exposed to remote applications. This triggers notifications 179 // of changes over the Properties interface. SetValue(const T & new_value)180 void SetValue(const T& new_value) { 181 if (value_ != new_value) { 182 value_ = new_value; 183 this->NotifyPropertyChanged(); 184 } 185 } 186 187 // Set the validator for value checking when setting the property by remote 188 // application. SetValidator(const base::Callback<bool (brillo::ErrorPtr *,const T &)> & validator)189 void SetValidator( 190 const base::Callback<bool(brillo::ErrorPtr*, const T&)>& validator) { 191 validator_ = validator; 192 } 193 194 // Implementation provided by specialization. GetValue()195 brillo::Any GetValue() const override { return value_; } 196 SetValue(brillo::ErrorPtr * error,const brillo::Any & value)197 bool SetValue(brillo::ErrorPtr* error, 198 const brillo::Any& value) override { 199 if (GetAccessMode() == ExportedPropertyBase::Access::kReadOnly) { 200 brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain, 201 DBUS_ERROR_PROPERTY_READ_ONLY, 202 "Property is read-only."); 203 return false; 204 } 205 if (!value.IsTypeCompatible<T>()) { 206 brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain, 207 DBUS_ERROR_INVALID_ARGS, 208 "Argument type mismatched."); 209 return false; 210 } 211 if (value_ == value.Get<T>()) { 212 // No change to the property value, nothing to be done. 213 return true; 214 } 215 if (!validator_.is_null() && !validator_.Run(error, value.Get<T>())) { 216 return false; 217 } 218 SetValue(value.Get<T>()); 219 return true; 220 } 221 222 private: 223 T value_{}; 224 base::Callback<bool(brillo::ErrorPtr*, const T&)> validator_; 225 226 DISALLOW_COPY_AND_ASSIGN(ExportedProperty); 227 }; 228 229 } // namespace dbus_utils 230 231 } // namespace brillo 232 233 #endif // LIBBRILLO_BRILLO_DBUS_EXPORTED_PROPERTY_SET_H_ 234