• 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 <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