1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #ifndef DBUS_PROPERTY_H_
6 #define DBUS_PROPERTY_H_
15 #include "base/functional/bind.h"
16 #include "base/functional/callback.h"
17 #include "base/memory/raw_ptr.h"
18 #include "dbus/dbus_export.h"
19 #include "dbus/message.h"
20 #include "dbus/object_proxy.h"
22 // D-Bus objects frequently provide sets of properties accessed via a
23 // standard interface of method calls and signals to obtain the current value,
24 // set a new value and be notified of changes to the value. Unfortunately this
25 // interface makes heavy use of variants and dictionaries of variants. The
26 // classes defined here make dealing with properties in a type-safe manner
29 // Client implementation classes should define a Properties structure, deriving
30 // from the PropertySet class defined here. This structure should contain a
31 // member for each property defined as an instance of the Property<> class,
32 // specifying the type to the template. Finally the structure should chain up
33 // to the PropertySet constructor, and then call RegisterProperty() for each
34 // property defined to associate them with their string name.
37 // class ExampleClient {
39 // struct Properties : public dbus::PropertySet {
40 // dbus::Property<std::string> name;
41 // dbus::Property<uint16_t> version;
42 // dbus::Property<dbus::ObjectPath> parent;
43 // dbus::Property<std::vector<std::string>> children;
45 // Properties(dbus::ObjectProxy* object_proxy,
46 // const PropertyChangedCallback callback)
47 // : dbus::PropertySet(object_proxy, "com.example.DBus", callback) {
48 // RegisterProperty("Name", &name);
49 // RegisterProperty("Version", &version);
50 // RegisterProperty("Parent", &parent);
51 // RegisterProperty("Children", &children);
53 // virtual ~Properties() {}
56 // The Properties structure requires a pointer to the object proxy of the
57 // actual object to track, and after construction should have signals
58 // connected to that object and initial values set by calling ConnectSignals()
59 // and GetAll(). The structure should not outlive the object proxy, so it
60 // is recommended that the lifecycle of both be managed together.
62 // Example (continued):
64 // typedef std::map<std::pair<dbus::ObjectProxy*, Properties*>> Object;
65 // typedef std::map<dbus::ObjectPath, Object> ObjectMap;
66 // ObjectMap object_map_;
68 // dbus::ObjectProxy* GetObjectProxy(const dbus::ObjectPath& object_path) {
69 // return GetObject(object_path).first;
72 // Properties* GetProperties(const dbus::ObjectPath& object_path) {
73 // return GetObject(object_path).second;
76 // Object GetObject(const dbus::ObjectPath& object_path) {
77 // ObjectMap::iterator it = object_map_.find(object_path);
78 // if (it != object_map_.end())
81 // dbus::ObjectProxy* object_proxy = bus->GetObjectProxy(...);
82 // // connect signals, etc.
84 // Properties* properties = new Properties(
86 // base::BindRepeating(&PropertyChanged,
87 // weak_ptr_factory_.GetWeakPtr(),
89 // properties->ConnectSignals();
90 // properties->GetAll();
92 // Object object = std::make_pair(object_proxy, properties);
93 // object_map_[object_path] = object;
98 // This now allows code using the client implementation to access properties
99 // in a type-safe manner, and assuming the PropertyChanged callback is
100 // propagated up to observers, be notified of changes. A typical access of
101 // the current value of the name property would be:
103 // ExampleClient::Properties* p = example_client->GetProperties(object_path);
104 // std::string name = p->name.value();
106 // Normally these values are updated from signals emitted by the remote object,
107 // in case an explicit round-trip is needed to obtain the current value, the
108 // Get() method can be used and indicates whether or not the value update was
109 // successful. The updated value can be obtained in the callback using the
112 // p->children.Get(base::BindOnce(&OnGetChildren));
114 // A new value can be set using the Set() method, the callback indicates
115 // success only; it is up to the remote object when (and indeed if) it updates
116 // the property value, and whether it emits a signal or a Get() call is
117 // required to obtain it.
119 // p->version.Set(20, base::BindOnce(&OnSetVersion))
123 // D-Bus Properties interface constants, declared here rather than
124 // in property.cc because template methods use them.
125 const char kPropertiesInterface[] = "org.freedesktop.DBus.Properties";
126 const char kPropertiesGetAll[] = "GetAll";
127 const char kPropertiesGet[] = "Get";
128 const char kPropertiesSet[] = "Set";
129 const char kPropertiesChanged[] = "PropertiesChanged";
133 // PropertyBase is an abstract base-class consisting of the parts of
134 // the Property<> template that are not type-specific, such as the
135 // associated PropertySet, property name, and the type-unsafe parts
136 // used by PropertySet.
137 class CHROME_DBUS_EXPORT PropertyBase {
141 PropertyBase(const PropertyBase&) = delete;
142 PropertyBase& operator=(const PropertyBase&) = delete;
144 virtual ~PropertyBase();
146 // Initializes the |property_set| and property |name| so that method
147 // calls may be made from this class. This method is called by
148 // PropertySet::RegisterProperty() passing |this| for |property_set| so
149 // there should be no need to call it directly. If you do beware that
150 // no ownership or reference to |property_set| is taken so that object
151 // must outlive this one.
152 void Init(PropertySet* property_set, const std::string& name);
154 // Retrieves the name of this property, this may be useful in observers
155 // to avoid specifying the name in more than once place, e.g.
157 // void Client::PropertyChanged(const dbus::ObjectPath& object_path,
158 // const std::string &property_name) {
159 // Properties& properties = GetProperties(object_path);
160 // if (property_name == properties.version.name()) {
161 // // Handle version property changing
164 const std::string& name() const { return name_; }
166 // Returns true if property is valid, false otherwise.
167 bool is_valid() const { return is_valid_; }
169 // Allows to mark Property as valid or invalid.
170 void set_valid(bool is_valid) { is_valid_ = is_valid; }
172 // Method used by PropertySet to retrieve the value from a MessageReader,
173 // no knowledge of the contained type is required, this method returns
174 // true if its expected type was found, false if not.
175 // Implementation provided by specialization.
176 virtual bool PopValueFromReader(MessageReader* reader) = 0;
178 // Method used by PropertySet to append the set value to a MessageWriter,
179 // no knowledge of the contained type is required.
180 // Implementation provided by specialization.
181 virtual void AppendSetValueToWriter(MessageWriter* writer) = 0;
183 // Method used by test and stub implementations of dbus::PropertySet::Set
184 // to replace the property value with the set value without using a
185 // dbus::MessageReader.
186 virtual void ReplaceValueWithSetValue() = 0;
189 // Retrieves the associated property set.
190 PropertySet* property_set() { return property_set_; }
193 // Pointer to the PropertySet instance that this instance is a member of,
194 // no ownership is taken and |property_set_| must outlive this class.
195 raw_ptr<PropertySet> property_set_;
199 // Name of the property.
203 // PropertySet groups a collection of properties for a remote object
204 // together into a single structure, fixing their types and name such
205 // that calls made through it are type-safe.
207 // Clients always sub-class this to add the properties, and should always
208 // provide a constructor that chains up to this and then calls
209 // RegisterProperty() for each property defined.
211 // After creation, client code should call ConnectSignals() and most likely
212 // GetAll() to seed initial values and update as changes occur.
213 class CHROME_DBUS_EXPORT PropertySet {
215 // Callback for changes to cached values of properties, either notified
216 // via signal, or as a result of calls to Get() and GetAll(). The |name|
217 // argument specifies the name of the property changed.
218 using PropertyChangedCallback =
219 base::RepeatingCallback<void(const std::string& name)>;
221 // Constructs a property set, where |object_proxy| specifies the proxy for
222 // the/ remote object that these properties are for, care should be taken to
223 // ensure that this object does not outlive the lifetime of the proxy;
224 // |interface| specifies the D-Bus interface of these properties, and
225 // |property_changed_callback| specifies the callback for when properties
226 // are changed, this may be a NULL callback.
227 PropertySet(ObjectProxy* object_proxy, const std::string& interface,
228 const PropertyChangedCallback& property_changed_callback);
230 PropertySet(const PropertySet&) = delete;
231 PropertySet& operator=(const PropertySet&) = delete;
233 // Destructor; we don't hold on to any references or memory that needs
234 // explicit clean-up, but clang thinks we might.
235 virtual ~PropertySet();
237 // Registers a property, generally called from the subclass constructor;
238 // pass the |name| of the property as used in method calls and signals,
239 // and the pointer to the |property| member of the structure. This will
240 // call the PropertyBase::Init method.
241 void RegisterProperty(const std::string& name, PropertyBase* property);
243 // Connects property change notification signals to the object, generally
244 // called immediately after the object is created and before calls to other
245 // methods. Sub-classes may override to use different D-Bus signals.
246 virtual void ConnectSignals();
248 // Methods connected by ConnectSignals() and called by dbus:: when
249 // a property is changed. Sub-classes may override if the property
250 // changed signal provides different arguments.
251 virtual void ChangedReceived(Signal* signal);
252 virtual void ChangedConnected(const std::string& interface_name,
253 const std::string& signal_name,
256 // Callback for Get() method, |success| indicates whether or not the
257 // value could be retrived, if true the new value can be obtained by
258 // calling value() on the property.
259 using GetCallback = base::OnceCallback<void(bool success)>;
261 // Requests an updated value from the remote object for |property|
262 // incurring a round-trip. |callback| will be called when the new
263 // value is available. This may not be implemented by some interfaces,
264 // and may be overriden by sub-classes if interfaces use different
266 virtual void Get(PropertyBase* property, GetCallback callback);
267 virtual void OnGet(PropertyBase* property, GetCallback callback,
270 // The synchronous version of Get().
271 // This should never be used on an interactive thread.
272 virtual bool GetAndBlock(PropertyBase* property);
274 // Queries the remote object for values of all properties and updates
275 // initial values. Sub-classes may override to use a different D-Bus
276 // method, or if the remote object does not support retrieving all
277 // properties, either ignore or obtain each property value individually.
278 virtual void GetAll();
279 virtual void OnGetAll(Response* response);
281 // Callback for Set() method, |success| indicates whether or not the
282 // new property value was accepted by the remote object.
283 using SetCallback = base::OnceCallback<void(bool success)>;
285 // Requests that the remote object for |property| change the property to
286 // its new value. |callback| will be called to indicate the success or
287 // failure of the request, however the new value may not be available
288 // depending on the remote object. This method may be overridden by
289 // sub-classes if interfaces use different method calls.
290 virtual void Set(PropertyBase* property, SetCallback callback);
291 virtual void OnSet(PropertyBase* property, SetCallback callback,
294 // The synchronous version of Set().
295 // This should never be used on an interactive thread.
296 virtual bool SetAndBlock(PropertyBase* property);
298 // Update properties by reading an array of dictionary entries, each
299 // containing a string with the name and a variant with the value, from
300 // |message_reader|. Returns false if message is in incorrect format.
301 bool UpdatePropertiesFromReader(MessageReader* reader);
303 // Updates a single property by reading a string with the name and a
304 // variant with the value from |message_reader|. Returns false if message
305 // is in incorrect format, or property type doesn't match.
306 bool UpdatePropertyFromReader(MessageReader* reader);
308 // Calls the property changed callback passed to the constructor, used
309 // by sub-classes that do not call UpdatePropertiesFromReader() or
310 // UpdatePropertyFromReader(). Takes the |name| of the changed property.
311 void NotifyPropertyChanged(const std::string& name);
313 // Retrieves the object proxy this property set was initialized with,
314 // provided for sub-classes overriding methods that make D-Bus calls
315 // and for Property<>. Not permitted with const references to this class.
316 ObjectProxy* object_proxy() { return object_proxy_; }
318 // Retrieves the interface of this property set.
319 const std::string& interface() const { return interface_; }
322 // Get a weak pointer to this property set, provided so that sub-classes
323 // overriding methods that make D-Bus calls may use the existing (or
324 // override) callbacks without providing their own weak pointer factory.
325 base::WeakPtr<PropertySet> GetWeakPtr() {
326 return weak_ptr_factory_.GetWeakPtr();
330 // Invalidates properties by reading an array of names, from
331 // |message_reader|. Returns false if message is in incorrect format.
332 bool InvalidatePropertiesFromReader(MessageReader* reader);
334 // Pointer to object proxy for making method calls, no ownership is taken
335 // so this must outlive this class.
336 raw_ptr<ObjectProxy, DanglingUntriaged> object_proxy_;
338 // Interface of property, e.g. "org.chromium.ExampleService", this is
339 // distinct from the interface of the method call itself which is the
340 // general D-Bus Properties interface "org.freedesktop.DBus.Properties".
341 std::string interface_;
343 // Callback for property changes.
344 PropertyChangedCallback property_changed_callback_;
346 // Map of properties (as PropertyBase*) defined in the structure to
347 // names as used in D-Bus method calls and signals. The base pointer
348 // restricts property access via this map to type-unsafe and non-specific
350 typedef std::map<const std::string, PropertyBase*> PropertiesMap;
351 PropertiesMap properties_map_;
353 // Weak pointer factory as D-Bus callbacks may last longer than these
355 base::WeakPtrFactory<PropertySet> weak_ptr_factory_{this};
358 // Property template, this defines the type-specific and type-safe methods
359 // of properties that can be accessed as members of a PropertySet structure.
361 // Properties provide a cached value that has an initial sensible default
362 // until the reply to PropertySet::GetAll() is retrieved and is updated by
363 // all calls to that method, PropertySet::Get() and property changed signals
364 // also handled by PropertySet. It can be obtained by calling value() on the
367 // It is recommended that this cached value be used where necessary, with
368 // code using PropertySet::PropertyChangedCallback to be notified of changes,
369 // rather than incurring a round-trip to the remote object for each property
372 // Where a round-trip is necessary, the Get() method is provided. And to
373 // update the remote object value, the Set() method is also provided; these
374 // both simply call methods on PropertySet.
376 // Handling of particular D-Bus types is performed via specialization,
377 // typically the PopValueFromReader() and AppendSetValueToWriter() methods
378 // will need to be provided, and in rare cases a constructor to provide a
379 // default value. Specializations for basic D-Bus types, strings, object
380 // paths and arrays are provided for you.
382 class CHROME_DBUS_EXPORT Property : public PropertyBase {
385 ~Property() override {}
387 // Retrieves the cached value.
388 const T& value() const { return value_; }
390 // Requests an updated value from the remote object incurring a
391 // round-trip. |callback| will be called when the new value is available.
392 // This may not be implemented by some interfaces.
393 virtual void Get(dbus::PropertySet::GetCallback callback) {
394 property_set()->Get(this, std::move(callback));
397 // The synchronous version of Get().
398 // This should never be used on an interactive thread.
399 virtual bool GetAndBlock() {
400 return property_set()->GetAndBlock(this);
403 // Requests that the remote object change the property value to |value|,
404 // |callback| will be called to indicate the success or failure of the
405 // request, however the new value may not be available depending on the
407 virtual void Set(const T& value, dbus::PropertySet::SetCallback callback) {
409 property_set()->Set(this, std::move(callback));
412 // The synchronous version of Set().
413 // This should never be used on an interactive thread.
414 virtual bool SetAndBlock(const T& value) {
416 return property_set()->SetAndBlock(this);
419 // Method used by PropertySet to retrieve the value from a MessageReader,
420 // no knowledge of the contained type is required, this method returns
421 // true if its expected type was found, false if not.
422 bool PopValueFromReader(MessageReader* reader) override;
424 // Method used by PropertySet to append the set value to a MessageWriter,
425 // no knowledge of the contained type is required.
426 // Implementation provided by specialization.
427 void AppendSetValueToWriter(MessageWriter* writer) override;
429 // Method used by test and stub implementations of dbus::PropertySet::Set
430 // to replace the property value with the set value without using a
431 // dbus::MessageReader.
432 void ReplaceValueWithSetValue() override {
434 property_set()->NotifyPropertyChanged(name());
437 // Method used by test and stub implementations to directly set the
438 // value of a property.
439 void ReplaceValue(const T& value) {
441 property_set()->NotifyPropertyChanged(name());
444 // Method used by test and stub implementations to directly set the
445 // |set_value_| of a property.
446 void ReplaceSetValueForTesting(const T& value) { set_value_ = value; }
448 // Method used by test and stub implementations to retrieve the |set_value|
450 const T& GetSetValueForTesting() const { return set_value_; }
453 // Current cached value of the property.
456 // Replacement value of the property.
460 // Clang and GCC don't agree on how attributes should work for explicitly
461 // instantiated templates. GCC ignores attributes on explicit instantiations
462 // (and emits a warning) while Clang requires the visiblity attribute on the
463 // explicit instantiations for them to be visible to other compilation units.
464 // Hopefully clang and GCC agree one day, and this can be cleaned up:
465 // https://llvm.org/bugs/show_bug.cgi?id=24815
466 #pragma GCC diagnostic push
467 #pragma GCC diagnostic ignored "-Wattributes"
470 CHROME_DBUS_EXPORT Property<uint8_t>::Property();
472 CHROME_DBUS_EXPORT bool Property<uint8_t>::PopValueFromReader(
473 MessageReader* reader);
475 CHROME_DBUS_EXPORT void Property<uint8_t>::AppendSetValueToWriter(
476 MessageWriter* writer);
477 extern template class CHROME_DBUS_EXPORT Property<uint8_t>;
480 CHROME_DBUS_EXPORT Property<bool>::Property();
482 CHROME_DBUS_EXPORT bool Property<bool>::PopValueFromReader(
483 MessageReader* reader);
485 CHROME_DBUS_EXPORT void Property<bool>::AppendSetValueToWriter(
486 MessageWriter* writer);
487 extern template class CHROME_DBUS_EXPORT Property<bool>;
490 CHROME_DBUS_EXPORT Property<int16_t>::Property();
492 CHROME_DBUS_EXPORT bool Property<int16_t>::PopValueFromReader(
493 MessageReader* reader);
495 CHROME_DBUS_EXPORT void Property<int16_t>::AppendSetValueToWriter(
496 MessageWriter* writer);
497 extern template class CHROME_DBUS_EXPORT Property<int16_t>;
500 CHROME_DBUS_EXPORT Property<uint16_t>::Property();
502 CHROME_DBUS_EXPORT bool Property<uint16_t>::PopValueFromReader(
503 MessageReader* reader);
505 CHROME_DBUS_EXPORT void Property<uint16_t>::AppendSetValueToWriter(
506 MessageWriter* writer);
507 extern template class CHROME_DBUS_EXPORT Property<uint16_t>;
510 CHROME_DBUS_EXPORT Property<int32_t>::Property();
512 CHROME_DBUS_EXPORT bool Property<int32_t>::PopValueFromReader(
513 MessageReader* reader);
515 CHROME_DBUS_EXPORT void Property<int32_t>::AppendSetValueToWriter(
516 MessageWriter* writer);
517 extern template class CHROME_DBUS_EXPORT Property<int32_t>;
520 CHROME_DBUS_EXPORT Property<uint32_t>::Property();
522 CHROME_DBUS_EXPORT bool Property<uint32_t>::PopValueFromReader(
523 MessageReader* reader);
525 CHROME_DBUS_EXPORT void Property<uint32_t>::AppendSetValueToWriter(
526 MessageWriter* writer);
527 extern template class CHROME_DBUS_EXPORT Property<uint32_t>;
530 CHROME_DBUS_EXPORT Property<int64_t>::Property();
532 CHROME_DBUS_EXPORT bool Property<int64_t>::PopValueFromReader(
533 MessageReader* reader);
535 CHROME_DBUS_EXPORT void Property<int64_t>::AppendSetValueToWriter(
536 MessageWriter* writer);
537 extern template class CHROME_DBUS_EXPORT Property<int64_t>;
540 CHROME_DBUS_EXPORT Property<uint64_t>::Property();
542 CHROME_DBUS_EXPORT bool Property<uint64_t>::PopValueFromReader(
543 MessageReader* reader);
545 CHROME_DBUS_EXPORT void Property<uint64_t>::AppendSetValueToWriter(
546 MessageWriter* writer);
547 extern template class CHROME_DBUS_EXPORT Property<uint64_t>;
550 CHROME_DBUS_EXPORT Property<double>::Property();
552 CHROME_DBUS_EXPORT bool Property<double>::PopValueFromReader(
553 MessageReader* reader);
555 CHROME_DBUS_EXPORT void Property<double>::AppendSetValueToWriter(
556 MessageWriter* writer);
557 extern template class CHROME_DBUS_EXPORT Property<double>;
560 CHROME_DBUS_EXPORT bool Property<std::string>::PopValueFromReader(
561 MessageReader* reader);
563 CHROME_DBUS_EXPORT void Property<std::string>::AppendSetValueToWriter(
564 MessageWriter* writer);
565 extern template class CHROME_DBUS_EXPORT Property<std::string>;
568 CHROME_DBUS_EXPORT bool Property<ObjectPath>::PopValueFromReader(
569 MessageReader* reader);
571 CHROME_DBUS_EXPORT void Property<ObjectPath>::AppendSetValueToWriter(
572 MessageWriter* writer);
573 extern template class CHROME_DBUS_EXPORT Property<ObjectPath>;
576 CHROME_DBUS_EXPORT bool Property<std::vector<std::string>>::PopValueFromReader(
577 MessageReader* reader);
579 CHROME_DBUS_EXPORT void Property<
580 std::vector<std::string>>::AppendSetValueToWriter(MessageWriter* writer);
581 extern template class CHROME_DBUS_EXPORT Property<std::vector<std::string>>;
584 CHROME_DBUS_EXPORT bool Property<std::vector<ObjectPath>>::PopValueFromReader(
585 MessageReader* reader);
587 CHROME_DBUS_EXPORT void Property<
588 std::vector<ObjectPath>>::AppendSetValueToWriter(MessageWriter* writer);
589 extern template class CHROME_DBUS_EXPORT Property<std::vector<ObjectPath>>;
592 CHROME_DBUS_EXPORT bool Property<std::vector<uint8_t>>::PopValueFromReader(
593 MessageReader* reader);
595 CHROME_DBUS_EXPORT void Property<std::vector<uint8_t>>::AppendSetValueToWriter(
596 MessageWriter* writer);
597 extern template class CHROME_DBUS_EXPORT Property<std::vector<uint8_t>>;
600 CHROME_DBUS_EXPORT bool
601 Property<std::map<std::string, std::string>>::PopValueFromReader(
602 MessageReader* reader);
604 CHROME_DBUS_EXPORT void
605 Property<std::map<std::string, std::string>>::AppendSetValueToWriter(
606 MessageWriter* writer);
607 extern template class CHROME_DBUS_EXPORT
608 Property<std::map<std::string, std::string>>;
611 CHROME_DBUS_EXPORT bool
612 Property<std::vector<std::pair<std::vector<uint8_t>, uint16_t>>>::
613 PopValueFromReader(MessageReader* reader);
615 CHROME_DBUS_EXPORT void
616 Property<std::vector<std::pair<std::vector<uint8_t>, uint16_t>>>::
617 AppendSetValueToWriter(MessageWriter* writer);
618 extern template class CHROME_DBUS_EXPORT
619 Property<std::vector<std::pair<std::vector<uint8_t>, uint16_t>>>;
622 CHROME_DBUS_EXPORT bool
623 Property<std::map<std::string, std::vector<uint8_t>>>::PopValueFromReader(
624 MessageReader* reader);
626 CHROME_DBUS_EXPORT void
627 Property<std::map<std::string, std::vector<uint8_t>>>::AppendSetValueToWriter(
628 MessageWriter* writer);
629 extern template class CHROME_DBUS_EXPORT
630 Property<std::map<std::string, std::vector<uint8_t>>>;
633 CHROME_DBUS_EXPORT bool
634 Property<std::map<uint16_t, std::vector<uint8_t>>>::PopValueFromReader(
635 MessageReader* reader);
637 CHROME_DBUS_EXPORT void
638 Property<std::map<uint16_t, std::vector<uint8_t>>>::AppendSetValueToWriter(
639 MessageWriter* writer);
640 extern template class CHROME_DBUS_EXPORT
641 Property<std::map<uint16_t, std::vector<uint8_t>>>;
643 #pragma GCC diagnostic pop
647 #endif // DBUS_PROPERTY_H_