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