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