• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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