• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium 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 DBUS_PROPERTY_H_
6 #define DBUS_PROPERTY_H_
7 
8 #include <map>
9 #include <string>
10 
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "dbus/dbus_export.h"
15 #include "dbus/message.h"
16 #include "dbus/object_proxy.h"
17 
18 // D-Bus objects frequently provide sets of properties accessed via a
19 // standard interface of method calls and signals to obtain the current value,
20 // set a new value and be notified of changes to the value. Unfortunately this
21 // interface makes heavy use of variants and dictionaries of variants. The
22 // classes defined here make dealing with properties in a type-safe manner
23 // possible.
24 //
25 // Client implementation classes should define a Properties structure, deriving
26 // from the PropertySet class defined here. This structure should contain a
27 // member for each property defined as an instance of the Property<> class,
28 // specifying the type to the template. Finally the structure should chain up
29 // to the PropertySet constructor, and then call RegisterProperty() for each
30 // property defined to associate them with their string name.
31 //
32 // Example:
33 //   class ExampleClient {
34 //    public:
35 //     struct Properties : public dbus::PropertySet {
36 //       dbus::Property<std::string> name;
37 //       dbus::Property<uint16> version;
38 //       dbus::Property<dbus::ObjectPath> parent;
39 //       dbus::Property<std::vector<std::string> > children;
40 //
41 //       Properties(dbus::ObjectProxy* object_proxy,
42 //                  const PropertyChangedCallback callback)
43 //           : dbus::PropertySet(object_proxy, "com.example.DBus", callback) {
44 //         RegisterProperty("Name", &name);
45 //         RegisterProperty("Version", &version);
46 //         RegisterProperty("Parent", &parent);
47 //         RegisterProperty("Children", &children);
48 //       }
49 //       virtual ~Properties() {}
50 //     };
51 //
52 // The Properties structure requires a pointer to the object proxy of the
53 // actual object to track, and after construction should have signals
54 // connected to that object and initial values set by calling ConnectSignals()
55 // and GetAll(). The structure should not outlive the object proxy, so it
56 // is recommended that the lifecycle of both be managed together.
57 //
58 // Example (continued):
59 //
60 //     typedef std::map<std::pair<dbus::ObjectProxy*, Properties*> > Object;
61 //     typedef std::map<dbus::ObjectPath, Object> ObjectMap;
62 //     ObjectMap object_map_;
63 //
64 //     dbus::ObjectProxy* GetObjectProxy(const dbus::ObjectPath& object_path) {
65 //       return GetObject(object_path).first;
66 //     }
67 //
68 //     Properties* GetProperties(const dbus::ObjectPath& object_path) {
69 //       return GetObject(object_path).second;
70 //     }
71 //
72 //     Object GetObject(const dbus::ObjectPath& object_path) {
73 //       ObjectMap::iterator it = object_map_.find(object_path);
74 //       if (it != object_map_.end())
75 //         return it->second;
76 //
77 //       dbus::ObjectProxy* object_proxy = bus->GetObjectProxy(...);
78 //       // connect signals, etc.
79 //
80 //       Properties* properties = new Properties(
81 //           object_proxy,
82 //           base::Bind(&PropertyChanged,
83 //                      weak_ptr_factory_.GetWeakPtr(),
84 //                      object_path));
85 //       properties->ConnectSignals();
86 //       properties->GetAll();
87 //
88 //       Object object = std::make_pair(object_proxy, properties);
89 //       object_map_[object_path] = object;
90 //       return object;
91 //     }
92 //  };
93 //
94 // This now allows code using the client implementation to access properties
95 // in a type-safe manner, and assuming the PropertyChanged callback is
96 // propogated up to observers, be notified of changes. A typical access of
97 // the current value of the name property would be:
98 //
99 //   ExampleClient::Properties* p = example_client->GetProperties(object_path);
100 //   std::string name = p->name.value();
101 //
102 // Normally these values are updated from signals emitted by the remote object,
103 // in case an explicit round-trip is needed to obtain the current value, the
104 // Get() method can be used and indicates whether or not the value update was
105 // successful. The updated value can be obtained in the callback using the
106 // value() method.
107 //
108 //   p->children.Get(base::Bind(&OnGetChildren));
109 //
110 // A new value can be set using the Set() method, the callback indicates
111 // success only; it is up to the remote object when (and indeed if) it updates
112 // the property value, and whether it emits a signal or a Get() call is
113 // required to obtain it.
114 //
115 //   p->version.Set(20, base::Bind(&OnSetVersion))
116 
117 namespace dbus {
118 
119 // D-Bus Properties interface constants, declared here rather than
120 // in property.cc because template methods use them.
121 const char kPropertiesInterface[] = "org.freedesktop.DBus.Properties";
122 const char kPropertiesGetAll[] = "GetAll";
123 const char kPropertiesGet[] = "Get";
124 const char kPropertiesSet[] = "Set";
125 const char kPropertiesChanged[] = "PropertiesChanged";
126 
127 class PropertySet;
128 
129 // PropertyBase is an abstract base-class consisting of the parts of
130 // the Property<> template that are not type-specific, such as the
131 // associated PropertySet, property name, and the type-unsafe parts
132 // used by PropertySet.
133 class PropertyBase {
134  public:
PropertyBase()135   PropertyBase() : property_set_(NULL) {}
136 
137   // Initializes the |property_set| and property |name| so that method
138   // calls may be made from this class. This method is called by
139   // PropertySet::RegisterProperty() passing |this| for |property_set| so
140   // there should be no need to call it directly. If you do beware that
141   // no ownership or reference to |property_set| is taken so that object
142   // must outlive this one.
143   void Init(PropertySet* property_set, const std::string& name);
144 
145   // Retrieves the name of this property, this may be useful in observers
146   // to avoid specifying the name in more than once place, e.g.
147   //
148   //   void Client::PropertyChanged(const dbus::ObjectPath& object_path,
149   //                                const std::string &property_name) {
150   //     Properties& properties = GetProperties(object_path);
151   //     if (property_name == properties.version.name()) {
152   //       // Handle version property changing
153   //     }
154   //   }
name()155   const std::string& name() const { return name_; }
156 
157   // Method used by PropertySet to retrieve the value from a MessageReader,
158   // no knowledge of the contained type is required, this method returns
159   // true if its expected type was found, false if not.
160   // Implementation provided by specialization.
161   virtual bool PopValueFromReader(MessageReader*) = 0;
162 
163   // Method used by PropertySet to append the set value to a MessageWriter,
164   // no knowledge of the contained type is required.
165   // Implementation provided by specialization.
166   virtual void AppendSetValueToWriter(MessageWriter* writer) = 0;
167 
168   // Method used by test and stub implementations of dbus::PropertySet::Set
169   // to replace the property value with the set value without using a
170   // dbus::MessageReader.
171   virtual void ReplaceValueWithSetValue() = 0;
172 
173  protected:
174   // Retrieves the associated property set.
property_set()175   PropertySet* property_set() { return property_set_; }
176 
177  private:
178   // Pointer to the PropertySet instance that this instance is a member of,
179   // no ownership is taken and |property_set_| must outlive this class.
180   PropertySet* property_set_;
181 
182   // Name of the property.
183   std::string name_;
184 
185   DISALLOW_COPY_AND_ASSIGN(PropertyBase);
186 };
187 
188 // PropertySet groups a collection of properties for a remote object
189 // together into a single structure, fixing their types and name such
190 // that calls made through it are type-safe.
191 //
192 // Clients always sub-class this to add the properties, and should always
193 // provide a constructor that chains up to this and then calls
194 // RegisterProperty() for each property defined.
195 //
196 // After creation, client code should call ConnectSignals() and most likely
197 // GetAll() to seed initial values and update as changes occur.
198 class CHROME_DBUS_EXPORT PropertySet {
199  public:
200   // Callback for changes to cached values of properties, either notified
201   // via signal, or as a result of calls to Get() and GetAll(). The |name|
202   // argument specifies the name of the property changed.
203   typedef base::Callback<void(const std::string& name)> PropertyChangedCallback;
204 
205   // Constructs a property set, where |object_proxy| specifies the proxy for
206   // the/ remote object that these properties are for, care should be taken to
207   // ensure that this object does not outlive the lifetime of the proxy;
208   // |interface| specifies the D-Bus interface of these properties, and
209   // |property_changed_callback| specifies the callback for when properties
210   // are changed, this may be a NULL callback.
211   PropertySet(ObjectProxy* object_proxy, const std::string& interface,
212               const PropertyChangedCallback& property_changed_callback);
213 
214   // Destructor; we don't hold on to any references or memory that needs
215   // explicit clean-up, but clang thinks we might.
216   virtual ~PropertySet();
217 
218   // Registers a property, generally called from the subclass constructor;
219   // pass the |name| of the property as used in method calls and signals,
220   // and the pointer to the |property| member of the structure. This will
221   // call the PropertyBase::Init method.
222   void RegisterProperty(const std::string& name, PropertyBase* property);
223 
224   // Connects property change notification signals to the object, generally
225   // called immediately after the object is created and before calls to other
226   // methods. Sub-classes may override to use different D-Bus signals.
227   virtual void ConnectSignals();
228 
229   // Methods connected by ConnectSignals() and called by dbus:: when
230   // a property is changed. Sub-classes may override if the property
231   // changed signal provides different arguments.
232   virtual void ChangedReceived(Signal*);
233   virtual void ChangedConnected(const std::string& interface_name,
234                                 const std::string& signal_name,
235                                 bool success);
236 
237   // Callback for Get() method, |success| indicates whether or not the
238   // value could be retrived, if true the new value can be obtained by
239   // calling value() on the property.
240   typedef base::Callback<void(bool success)> GetCallback;
241 
242   // Requests an updated value from the remote object for |property|
243   // incurring a round-trip. |callback| will be called when the new
244   // value is available. This may not be implemented by some interfaces,
245   // and may be overriden by sub-classes if interfaces use different
246   // method calls.
247   virtual void Get(PropertyBase* property, GetCallback callback);
248   virtual void OnGet(PropertyBase* property, GetCallback callback,
249                      Response* response);
250 
251   // Queries the remote object for values of all properties and updates
252   // initial values. Sub-classes may override to use a different D-Bus
253   // method, or if the remote object does not support retrieving all
254   // properties, either ignore or obtain each property value individually.
255   virtual void GetAll();
256   virtual void OnGetAll(Response* response);
257 
258   // Callback for Set() method, |success| indicates whether or not the
259   // new property value was accepted by the remote object.
260   typedef base::Callback<void(bool success)> SetCallback;
261 
262   // Requests that the remote object for |property| change the property to
263   // its new value. |callback| will be called to indicate the success or
264   // failure of the request, however the new value may not be available
265   // depending on the remote object. This method may be overridden by
266   // sub-classes if interfaces use different method calls.
267   virtual void Set(PropertyBase* property, SetCallback callback);
268   virtual void OnSet(PropertyBase* property, SetCallback callback,
269                      Response* response);
270 
271   // Update properties by reading an array of dictionary entries, each
272   // containing a string with the name and a variant with the value, from
273   // |message_reader|. Returns false if message is in incorrect format.
274   bool UpdatePropertiesFromReader(MessageReader* reader);
275 
276   // Updates a single property by reading a string with the name and a
277   // variant with the value from |message_reader|. Returns false if message
278   // is in incorrect format, or property type doesn't match.
279   bool UpdatePropertyFromReader(MessageReader* reader);
280 
281   // Calls the property changed callback passed to the constructor, used
282   // by sub-classes that do not call UpdatePropertiesFromReader() or
283   // UpdatePropertyFromReader(). Takes the |name| of the changed property.
284   void NotifyPropertyChanged(const std::string& name);
285 
286   // Retrieves the object proxy this property set was initialized with,
287   // provided for sub-classes overriding methods that make D-Bus calls
288   // and for Property<>. Not permitted with const references to this class.
object_proxy()289   ObjectProxy* object_proxy() { return object_proxy_; }
290 
291   // Retrieves the interface of this property set.
interface()292   const std::string& interface() const { return interface_; }
293 
294  protected:
295   // Get a weak pointer to this property set, provided so that sub-classes
296   // overriding methods that make D-Bus calls may use the existing (or
297   // override) callbacks without providing their own weak pointer factory.
GetWeakPtr()298   base::WeakPtr<PropertySet> GetWeakPtr() {
299     return weak_ptr_factory_.GetWeakPtr();
300   }
301 
302  private:
303   // Pointer to object proxy for making method calls, no ownership is taken
304   // so this must outlive this class.
305   ObjectProxy* object_proxy_;
306 
307   // Interface of property, e.g. "org.chromium.ExampleService", this is
308   // distinct from the interface of the method call itself which is the
309   // general D-Bus Properties interface "org.freedesktop.DBus.Properties".
310   std::string interface_;
311 
312   // Callback for property changes.
313   PropertyChangedCallback property_changed_callback_;
314 
315   // Map of properties (as PropertyBase*) defined in the structure to
316   // names as used in D-Bus method calls and signals. The base pointer
317   // restricts property access via this map to type-unsafe and non-specific
318   // actions only.
319   typedef std::map<const std::string, PropertyBase*> PropertiesMap;
320   PropertiesMap properties_map_;
321 
322   // Weak pointer factory as D-Bus callbacks may last longer than these
323   // objects.
324   base::WeakPtrFactory<PropertySet> weak_ptr_factory_;
325 
326   DISALLOW_COPY_AND_ASSIGN(PropertySet);
327 };
328 
329 // Property template, this defines the type-specific and type-safe methods
330 // of properties that can be accessed as members of a PropertySet structure.
331 //
332 // Properties provide a cached value that has an initial sensible default
333 // until the reply to PropertySet::GetAll() is retrieved and is updated by
334 // all calls to that method, PropertySet::Get() and property changed signals
335 // also handled by PropertySet. It can be obtained by calling value() on the
336 // property.
337 //
338 // It is recommended that this cached value be used where necessary, with
339 // code using PropertySet::PropertyChangedCallback to be notified of changes,
340 // rather than incurring a round-trip to the remote object for each property
341 // access.
342 //
343 // Where a round-trip is necessary, the Get() method is provided. And to
344 // update the remote object value, the Set() method is also provided; these
345 // both simply call methods on PropertySet.
346 //
347 // Handling of particular D-Bus types is performed via specialization,
348 // typically the PopValueFromReader() and AppendSetValueToWriter() methods
349 // will need to be provided, and in rare cases a constructor to provide a
350 // default value. Specializations for basic D-Bus types, strings, object
351 // paths and arrays are provided for you.
352 template <class T>
353 class CHROME_DBUS_EXPORT Property : public PropertyBase {
354  public:
Property()355   Property() {}
356 
357   // Retrieves the cached value.
value()358   const T& value() const { return value_; }
359 
360   // Requests an updated value from the remote object incurring a
361   // round-trip. |callback| will be called when the new value is available.
362   // This may not be implemented by some interfaces.
Get(dbus::PropertySet::GetCallback callback)363   virtual void Get(dbus::PropertySet::GetCallback callback) {
364     property_set()->Get(this, callback);
365   }
366 
367   // Requests that the remote object change the property value to |value|,
368   // |callback| will be called to indicate the success or failure of the
369   // request, however the new value may not be available depending on the
370   // remote object.
Set(const T & value,dbus::PropertySet::SetCallback callback)371   virtual void Set(const T& value, dbus::PropertySet::SetCallback callback) {
372     set_value_ = value;
373     property_set()->Set(this, callback);
374   }
375 
376   // Method used by PropertySet to retrieve the value from a MessageReader,
377   // no knowledge of the contained type is required, this method returns
378   // true if its expected type was found, false if not.
379   virtual bool PopValueFromReader(MessageReader*);
380 
381   // Method used by PropertySet to append the set value to a MessageWriter,
382   // no knowledge of the contained type is required.
383   // Implementation provided by specialization.
384   virtual void AppendSetValueToWriter(MessageWriter* writer);
385 
386   // Method used by test and stub implementations of dbus::PropertySet::Set
387   // to replace the property value with the set value without using a
388   // dbus::MessageReader.
ReplaceValueWithSetValue()389   virtual void ReplaceValueWithSetValue() {
390     value_ = set_value_;
391     property_set()->NotifyPropertyChanged(name());
392   }
393 
394   // Method used by test and stub implementations to directly set the
395   // value of a property.
ReplaceValue(const T & value)396   void ReplaceValue(const T& value) {
397     value_ = value;
398     property_set()->NotifyPropertyChanged(name());
399   }
400 
401  private:
402   // Current cached value of the property.
403   T value_;
404 
405   // Replacement value of the property.
406   T set_value_;
407 };
408 
409 template <> Property<uint8>::Property();
410 template <> bool Property<uint8>::PopValueFromReader(MessageReader* reader);
411 template <> void Property<uint8>::AppendSetValueToWriter(MessageWriter* writer);
412 extern template class Property<uint8>;
413 
414 template <> Property<bool>::Property();
415 template <> bool Property<bool>::PopValueFromReader(MessageReader* reader);
416 template <> void Property<bool>::AppendSetValueToWriter(MessageWriter* writer);
417 extern template class Property<bool>;
418 
419 template <> Property<int16>::Property();
420 template <> bool Property<int16>::PopValueFromReader(MessageReader* reader);
421 template <> void Property<int16>::AppendSetValueToWriter(MessageWriter* writer);
422 extern template class Property<int16>;
423 
424 template <> Property<uint16>::Property();
425 template <> bool Property<uint16>::PopValueFromReader(MessageReader* reader);
426 template <> void Property<uint16>::AppendSetValueToWriter(
427   MessageWriter* writer);
428 extern template class Property<uint16>;
429 
430 template <> Property<int32>::Property();
431 template <> bool Property<int32>::PopValueFromReader(MessageReader* reader);
432 template <> void Property<int32>::AppendSetValueToWriter(MessageWriter* writer);
433 extern template class Property<int32>;
434 
435 template <> Property<uint32>::Property();
436 template <> bool Property<uint32>::PopValueFromReader(MessageReader* reader);
437 template <> void Property<uint32>::AppendSetValueToWriter(
438   MessageWriter* writer);
439 extern template class Property<uint32>;
440 
441 template <> Property<int64>::Property();
442 template <> bool Property<int64>::PopValueFromReader(MessageReader* reader);
443 template <> void Property<int64>::AppendSetValueToWriter(MessageWriter* writer);
444 extern template class Property<int64>;
445 
446 template <> Property<uint64>::Property();
447 template <> bool Property<uint64>::PopValueFromReader(MessageReader* reader);
448 template <> void Property<uint64>::AppendSetValueToWriter(
449   MessageWriter* writer);
450 extern template class Property<uint64>;
451 
452 template <> Property<double>::Property();
453 template <> bool Property<double>::PopValueFromReader(MessageReader* reader);
454 template <> void Property<double>::AppendSetValueToWriter(
455   MessageWriter* writer);
456 extern template class Property<double>;
457 
458 template <> bool Property<std::string>::PopValueFromReader(
459   MessageReader* reader);
460 template <> void Property<std::string>::AppendSetValueToWriter(
461   MessageWriter* writer);
462 extern template class Property<std::string>;
463 
464 template <> bool Property<ObjectPath>::PopValueFromReader(
465   MessageReader* reader);
466 template <> void Property<ObjectPath>::AppendSetValueToWriter(
467   MessageWriter* writer);
468 extern template class Property<ObjectPath>;
469 
470 template <> bool Property<std::vector<std::string> >::PopValueFromReader(
471   MessageReader* reader);
472 template <> void Property<std::vector<std::string> >::AppendSetValueToWriter(
473   MessageWriter* writer);
474 extern template class Property<std::vector<std::string> >;
475 
476 template <> bool Property<std::vector<ObjectPath> >::PopValueFromReader(
477   MessageReader* reader);
478 template <> void Property<std::vector<ObjectPath> >::AppendSetValueToWriter(
479   MessageWriter* writer);
480 extern template class Property<std::vector<ObjectPath> >;
481 
482 template <> bool Property<std::vector<uint8> >::PopValueFromReader(
483   MessageReader* reader);
484 template <> void Property<std::vector<uint8> >::AppendSetValueToWriter(
485   MessageWriter* writer);
486 extern template class Property<std::vector<uint8> >;
487 
488 }  // namespace dbus
489 
490 #endif  // DBUS_PROPERTY_H_
491