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