blob: 2d2cc82bda86edf8de452b27b990e5415f1f997e [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)
keybuk@chromium.orgf63debc2012-02-18 01:50:42 +090043// : dbus::PropertySet(object_proxy, "com.example.DBus", callback) {
keybuk@chromium.org7e0c4932012-02-15 13:21:08 +090044// RegisterProperty("Name", &name);
45// RegisterProperty("Version", &version);
46// RegisterProperty("Parent", &parent);
47// RegisterProperty("Children", &children);
48// }
keybuk@chromium.orgf63debc2012-02-18 01:50:42 +090049// virtual ~Properties() {}
keybuk@chromium.org7e0c4932012-02-15 13:21:08 +090050// };
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//
keybuk@chromium.orgf63debc2012-02-18 01:50:42 +090060// typedef std::map<std::pair<dbus::ObjectProxy*, Properties*> > Object;
61// typedef std::map<dbus::ObjectPath, Object> ObjectMap;
keybuk@chromium.org7e0c4932012-02-15 13:21:08 +090062// 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(...);
keybuk@chromium.orgf63debc2012-02-18 01:50:42 +090078// // connect signals, etc.
keybuk@chromium.org7e0c4932012-02-15 13:21:08 +090079//
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//
keybuk@chromium.orgf63debc2012-02-18 01:50:42 +090088// Object object = std::make_pair(object_proxy, properties);
89// object_map_[object_path] = object;
90// return object;
keybuk@chromium.org7e0c4932012-02-15 13:21:08 +090091// }
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
117namespace dbus {
118
119// D-Bus Properties interface constants, declared here rather than
120// in property.cc because template methods use them.
121const char kPropertiesInterface[] = "org.freedesktop.DBus.Properties";
122const char kPropertiesGetAll[] = "GetAll";
123const char kPropertiesGet[] = "Get";
124const char kPropertiesSet[] = "Set";
125const char kPropertiesChanged[] = "PropertiesChanged";
126
127class 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.
133class PropertyBase {
134 public:
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(), there should be no need to call it
140 // directly.
141 void Init(PropertySet* property_set, const std::string& name);
142
143 // Retrieves the name of this property, this may be useful in observers
144 // to avoid specifying the name in more than once place, e.g.
145 //
146 // void Client::PropertyChanged(const dbus::ObjectPath& object_path,
147 // const std::string &property_name) {
148 // Properties& properties = GetProperties(object_path);
149 // if (property_name == properties.version.name()) {
150 // // Handle version property changing
151 // }
152 // }
153 const std::string& name() { return name_; }
154
155 // Method used by PropertySet to retrieve the value from a MessageReader,
156 // no knowledge of the contained type is required, this method returns
157 // true if its expected type was found, false if not.
158 virtual bool PopValueFromReader(MessageReader*) = 0;
159
160 protected:
161 // Retrieves the associated property set.
162 PropertySet* property_set() { return property_set_; }
163
164 private:
165 // Pointer to the associated property set.
166 PropertySet* property_set_;
167
168 // Name of the property.
169 std::string name_;
170
171 DISALLOW_COPY_AND_ASSIGN(PropertyBase);
172};
173
174// PropertySet groups a collection of properties for a remote object
175// together into a single structure, fixing their types and name such
176// that calls made through it are type-safe.
177//
178// Clients always sub-class this to add the properties, and should always
179// provide a constructor that chains up to this and then calls
180// RegisterProperty() for each property defined.
181//
182// After creation, client code should call ConnectSignals() and most likely
183// GetAll() to seed initial values and update as changes occur.
184class PropertySet {
185 public:
186 // Callback for changes to cached values of properties, either notified
187 // via signal, or as a result of calls to Get() and GetAll(). The |name|
188 // argument specifies the name of the property changed.
189 typedef base::Callback<void(const std::string& name)> PropertyChangedCallback;
190
191 // Construct a property set, |object_proxy| specifies the proxy for the
192 // remote object that these properties are for, care should be taken to
193 // ensure that this object does not outlive the lifetime of the proxy;
194 // |interface| specifies the D-Bus interface of these properties, and
195 // |property_changed_callback| specifies the callback for when properties
196 // are changed, this may be a NULL callback.
197 PropertySet(ObjectProxy* object_proxy, const std::string& interface,
198 PropertyChangedCallback property_changed_callback);
199
200 // Destructor; we don't hold on to any references or memory that needs
201 // explicit clean-up, but clang thinks we might.
keybuk@chromium.org82e082a2012-02-17 10:01:18 +0900202 virtual ~PropertySet();
keybuk@chromium.org7e0c4932012-02-15 13:21:08 +0900203
204 // Registers a property, generally called from the subclass constructor;
205 // pass the |name| of the property as used in method calls and signals,
206 // and the pointer to the |property| member of the structure. This will
207 // call the PropertyBase::Init method.
208 void RegisterProperty(const std::string& name, PropertyBase* property);
209
210 // Call after construction to connect property change notification
211 // signals. Sub-classes may override to use different D-Bus signals.
212 void ConnectSignals();
213
214 // Methods connected by ConnectSignals() and called by dbus:: when
215 // a property is changed. Sub-classes may override if the property
216 // changed signal provides different arguments.
217 virtual void ChangedReceived(Signal*);
218 virtual void ChangedConnected(const std::string& interface_name,
219 const std::string& signal_name,
220 bool success);
221
keybuk@chromium.orgf63debc2012-02-18 01:50:42 +0900222 // Queries the remote object for values of all properties and updates
keybuk@chromium.org7e0c4932012-02-15 13:21:08 +0900223 // initial values. Sub-classes may override to use a different D-Bus
224 // method, or if the remote object does not support retrieving all
225 // properties, either ignore or obtain each property value individually.
226 void GetAll();
227 virtual void OnGetAll(Response* response);
228
229 // Update properties by reading an array of dictionary entries, each
230 // containing a string with the name and a variant with the value, from
231 // |message_reader|. Returns false if message in incorrect format.
232 bool UpdatePropertiesFromReader(MessageReader* reader);
233
234 // Updates a single property by reading a string with the name and a
235 // variant with the value from |message_reader|. Returns false if message
236 // in incorrect format, or property type doesn't match.
237 bool UpdatePropertyFromReader(MessageReader* reader);
238
239 // Calls the property changed callback passed to the constructor, used
240 // by sub-classes that do not call UpdatePropertiesFromReader() or
241 // UpdatePropertyFromReader(). Takes the |name| of the changed property.
242 void NotifyPropertyChanged(const std::string& name);
243
244 // Retrieves the object proxy this property set was initialized with,
245 // provided for sub-classes overriding methods that make D-Bus calls
246 // and for Property<>.
247 ObjectProxy* object_proxy() { return object_proxy_; }
248
249 // Retrieves the interface of this property set.
250 const std::string& interface() { return interface_; }
251
252 protected:
253 // Get a weak pointer to this property set, provided so that sub-classes
254 // overriding methods that make D-Bus calls may use the existing (or
255 // override) callbacks without providing their own weak pointer factory.
256 base::WeakPtr<PropertySet> GetWeakPtr() {
257 return weak_ptr_factory_.GetWeakPtr();
258 }
259
260 private:
261 // Pointer to object proxy for making method calls.
262 ObjectProxy* object_proxy_;
263
264 // Interface of property, e.g. "org.chromium.ExampleService", this is
265 // distinct from the interface of the method call itself which is the
266 // general D-Bus Properties interface "org.freedesktop.DBus.Properties".
267 std::string interface_;
268
269 // Callback for property changes.
270 PropertyChangedCallback property_changed_callback_;
271
272 // Map of properties (as PropertyBase*) defined in the structure to
273 // names as used in D-Bus method calls and signals. The base pointer
274 // restricts property access via this map to type-unsafe and non-specific
275 // actions only.
276 typedef std::map<const std::string, PropertyBase*> PropertiesMap;
277 PropertiesMap properties_map_;
278
279 // Weak pointer factory as D-Bus callbacks may last longer than these
280 // objects.
281 base::WeakPtrFactory<PropertySet> weak_ptr_factory_;
282
283 DISALLOW_COPY_AND_ASSIGN(PropertySet);
284};
285
286// Property template, this defines the type-specific and type-safe methods
287// of properties that can be accessed as members of a PropertySet structure.
288//
289// Properties provide a cached value that has an initial sensible default
290// until the reply to PropertySet::GetAll() is retrieved and is updated by
291// all calls to that method, Property<>::Get() and property changed signals
292// handled by PropertySet. It can be obtained by calling value() on the
293// property.
294//
295// It is recommended that this cached value be used where necessary, with
296// code using PropertySet::PropertyChangedCallback to be notified of changes,
297// rather than incurring a round-trip to the remote object for each property
298// access.
299//
300// Where a round-trip is necessary, the Get() method is provided. And to
301// update the remote object value, the Set() method is also provided.
302//
303// Handling of particular D-Bus types is performed via specialization,
304// typically the PopValueFromReader() and AppendToWriter() methods will need
305// to be provided, and in rare cases a constructor to provide a default value.
306// Specializations for basic D-Bus types, strings, object paths and arrays
307// are provided for you.
308template <class T>
309class Property : public PropertyBase {
310 public:
311 // Callback for Get() method, |success| indicates whether or not the
312 // value could be retrived, if true the new value can be obtained by
313 // calling value() on the property.
314 typedef base::Callback<void(bool success)> GetCallback;
315
316 // Callback for Set() method, |success| indicates whether or not the
317 // new property value was accepted by the remote object.
318 typedef base::Callback<void(bool success)> SetCallback;
319
320 Property() : weak_ptr_factory_(this) {}
321
322 // Retrieves the cached value.
keybuk@chromium.org5b48ae42012-02-17 16:46:34 +0900323 const T& value() const { return value_; }
keybuk@chromium.org7e0c4932012-02-15 13:21:08 +0900324
325 // Requests an updated value from the remote object incurring a
326 // round-trip. |callback| will be called when the new value is available.
327 // This may not be implemented by some interfaces, and may be overriden by
328 // sub-classes if interfaces use different method calls.
329 void Get(GetCallback callback) {
keybuk@chromium.org7e0c4932012-02-15 13:21:08 +0900330 MethodCall method_call(kPropertiesInterface, kPropertiesGet);
331 MessageWriter writer(&method_call);
332 writer.AppendString(property_set()->interface());
333 writer.AppendString(name());
334
keybuk@chromium.orgf63debc2012-02-18 01:50:42 +0900335 ObjectProxy* object_proxy = property_set()->object_proxy();
keybuk@chromium.org7e0c4932012-02-15 13:21:08 +0900336 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) {
keybuk@chromium.org7e0c4932012-02-15 13:21:08 +0900347 if (!response) {
348 LOG(WARNING) << name() << ": Get: failed.";
349 return;
350 }
351
352 MessageReader reader(response);
353 if (PopValueFromReader(&reader))
354 property_set()->NotifyPropertyChanged(name());
355
356 if (!callback.is_null())
357 callback.Run(response);
358 }
359
360 // Requests that the remote object change the property value to |value|,
361 // |callback| will be called to indicate the success or failure of the
362 // request, however the new value may not be available depending on the
363 // remote object. This method may be overriden by sub-classes if
364 // interfaces use different method calls.
365 void Set(const T& value, SetCallback callback) {
keybuk@chromium.org7e0c4932012-02-15 13:21:08 +0900366 MethodCall method_call(kPropertiesInterface, kPropertiesSet);
367 MessageWriter writer(&method_call);
368 writer.AppendString(property_set()->interface());
369 writer.AppendString(name());
370 AppendToWriter(&writer, value);
371
keybuk@chromium.orgf63debc2012-02-18 01:50:42 +0900372 ObjectProxy* object_proxy = property_set()->object_proxy();
keybuk@chromium.org7e0c4932012-02-15 13:21:08 +0900373 DCHECK(object_proxy);
374 object_proxy->CallMethod(&method_call,
375 ObjectProxy::TIMEOUT_USE_DEFAULT,
376 base::Bind(&Property<T>::OnSet,
377 GetWeakPtr(),
378 callback));
379 }
380
381 // Callback for Set(), may be overriden by sub-classes if interfaces
382 // use different response arguments.
383 virtual void OnSet(SetCallback callback, Response* response) {
keybuk@chromium.org7e0c4932012-02-15 13:21:08 +0900384 LOG_IF(WARNING, !response) << name() << ": Set: failed.";
385 if (!callback.is_null())
386 callback.Run(response);
387 }
388
keybuk@google.comf056b962012-03-22 08:43:45 +0900389 // Updates the cached property value, replacing any previous value
390 // entirely, by popping from |reader| which should be positioned at the
391 // property value, generally of variant type.
392 // Implementation provided by specialization.
keybuk@chromium.org7e0c4932012-02-15 13:21:08 +0900393 virtual bool PopValueFromReader(MessageReader* reader);
394
395 // Appends the passed |value| to |writer|, generally as a variant type.
396 // Implementation provided by specialization.
397 virtual void AppendToWriter(MessageWriter* writer, const T& value);
398
399 protected:
400 // Get a weak pointer to this propertyt, provided so that sub-classes
401 // overriding methods that make D-Bus calls may use the existing (or
402 // override) callbacks without providing their own weak pointer factory.
403 base::WeakPtr<Property<T> > GetWeakPtr() {
404 return weak_ptr_factory_.GetWeakPtr();
405 }
406
407 private:
408 // Current cached value of the property.
409 T value_;
410
411 // Weak pointer factory as D-Bus callbacks may last longer than these
412 // objects.
413 base::WeakPtrFactory<Property<T> > weak_ptr_factory_;
414};
415
416} // namespace dbus
417
418#endif // DBUS_PROPERTY_H_