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 // 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 93 class 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 162 template<typename T> 163 class ExportedProperty : public ExportedPropertyBase { 164 public: 165 ExportedProperty() = default; 166 ~ExportedProperty() override = default; 167 168 // Retrieves the current value. 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. SetValue(const T & new_value)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. SetValidator(const base::Callback<bool (brillo::ErrorPtr *,const T &)> & validator)182 void SetValidator( 183 const base::Callback<bool(brillo::ErrorPtr*, const T&)>& validator) { 184 validator_ = validator; 185 } 186 187 // Implementation provided by specialization. GetValue()188 brillo::Any GetValue() const override { return value_; } 189 SetValue(brillo::ErrorPtr * error,const brillo::Any & value)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