| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef DBUS_PROPERTY_H_ |
| #define DBUS_PROPERTY_H_ |
| |
| #include <map> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/basictypes.h" |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "dbus/dbus_export.h" |
| #include "dbus/message.h" |
| #include "dbus/object_proxy.h" |
| |
| // D-Bus objects frequently provide sets of properties accessed via a |
| // standard interface of method calls and signals to obtain the current value, |
| // set a new value and be notified of changes to the value. Unfortunately this |
| // interface makes heavy use of variants and dictionaries of variants. The |
| // classes defined here make dealing with properties in a type-safe manner |
| // possible. |
| // |
| // Client implementation classes should define a Properties structure, deriving |
| // from the PropertySet class defined here. This structure should contain a |
| // member for each property defined as an instance of the Property<> class, |
| // specifying the type to the template. Finally the structure should chain up |
| // to the PropertySet constructor, and then call RegisterProperty() for each |
| // property defined to associate them with their string name. |
| // |
| // Example: |
| // class ExampleClient { |
| // public: |
| // struct Properties : public dbus::PropertySet { |
| // dbus::Property<std::string> name; |
| // dbus::Property<uint16> version; |
| // dbus::Property<dbus::ObjectPath> parent; |
| // dbus::Property<std::vector<std::string> > children; |
| // |
| // Properties(dbus::ObjectProxy* object_proxy, |
| // const PropertyChangedCallback callback) |
| // : dbus::PropertySet(object_proxy, "com.example.DBus", callback) { |
| // RegisterProperty("Name", &name); |
| // RegisterProperty("Version", &version); |
| // RegisterProperty("Parent", &parent); |
| // RegisterProperty("Children", &children); |
| // } |
| // virtual ~Properties() {} |
| // }; |
| // |
| // The Properties structure requires a pointer to the object proxy of the |
| // actual object to track, and after construction should have signals |
| // connected to that object and initial values set by calling ConnectSignals() |
| // and GetAll(). The structure should not outlive the object proxy, so it |
| // is recommended that the lifecycle of both be managed together. |
| // |
| // Example (continued): |
| // |
| // typedef std::map<std::pair<dbus::ObjectProxy*, Properties*> > Object; |
| // typedef std::map<dbus::ObjectPath, Object> ObjectMap; |
| // ObjectMap object_map_; |
| // |
| // dbus::ObjectProxy* GetObjectProxy(const dbus::ObjectPath& object_path) { |
| // return GetObject(object_path).first; |
| // } |
| // |
| // Properties* GetProperties(const dbus::ObjectPath& object_path) { |
| // return GetObject(object_path).second; |
| // } |
| // |
| // Object GetObject(const dbus::ObjectPath& object_path) { |
| // ObjectMap::iterator it = object_map_.find(object_path); |
| // if (it != object_map_.end()) |
| // return it->second; |
| // |
| // dbus::ObjectProxy* object_proxy = bus->GetObjectProxy(...); |
| // // connect signals, etc. |
| // |
| // Properties* properties = new Properties( |
| // object_proxy, |
| // base::Bind(&PropertyChanged, |
| // weak_ptr_factory_.GetWeakPtr(), |
| // object_path)); |
| // properties->ConnectSignals(); |
| // properties->GetAll(); |
| // |
| // Object object = std::make_pair(object_proxy, properties); |
| // object_map_[object_path] = object; |
| // return object; |
| // } |
| // }; |
| // |
| // This now allows code using the client implementation to access properties |
| // in a type-safe manner, and assuming the PropertyChanged callback is |
| // propogated up to observers, be notified of changes. A typical access of |
| // the current value of the name property would be: |
| // |
| // ExampleClient::Properties* p = example_client->GetProperties(object_path); |
| // std::string name = p->name.value(); |
| // |
| // Normally these values are updated from signals emitted by the remote object, |
| // in case an explicit round-trip is needed to obtain the current value, the |
| // Get() method can be used and indicates whether or not the value update was |
| // successful. The updated value can be obtained in the callback using the |
| // value() method. |
| // |
| // p->children.Get(base::Bind(&OnGetChildren)); |
| // |
| // A new value can be set using the Set() method, the callback indicates |
| // success only; it is up to the remote object when (and indeed if) it updates |
| // the property value, and whether it emits a signal or a Get() call is |
| // required to obtain it. |
| // |
| // p->version.Set(20, base::Bind(&OnSetVersion)) |
| |
| namespace dbus { |
| |
| // D-Bus Properties interface constants, declared here rather than |
| // in property.cc because template methods use them. |
| const char kPropertiesInterface[] = "org.freedesktop.DBus.Properties"; |
| const char kPropertiesGetAll[] = "GetAll"; |
| const char kPropertiesGet[] = "Get"; |
| const char kPropertiesSet[] = "Set"; |
| const char kPropertiesChanged[] = "PropertiesChanged"; |
| |
| class PropertySet; |
| |
| // PropertyBase is an abstract base-class consisting of the parts of |
| // the Property<> template that are not type-specific, such as the |
| // associated PropertySet, property name, and the type-unsafe parts |
| // used by PropertySet. |
| class CHROME_DBUS_EXPORT PropertyBase { |
| public: |
| PropertyBase(); |
| virtual ~PropertyBase(); |
| |
| // Initializes the |property_set| and property |name| so that method |
| // calls may be made from this class. This method is called by |
| // PropertySet::RegisterProperty() passing |this| for |property_set| so |
| // there should be no need to call it directly. If you do beware that |
| // no ownership or reference to |property_set| is taken so that object |
| // must outlive this one. |
| void Init(PropertySet* property_set, const std::string& name); |
| |
| // Retrieves the name of this property, this may be useful in observers |
| // to avoid specifying the name in more than once place, e.g. |
| // |
| // void Client::PropertyChanged(const dbus::ObjectPath& object_path, |
| // const std::string &property_name) { |
| // Properties& properties = GetProperties(object_path); |
| // if (property_name == properties.version.name()) { |
| // // Handle version property changing |
| // } |
| // } |
| const std::string& name() const { return name_; } |
| |
| // Returns true if property is valid, false otherwise. |
| bool is_valid() const { return is_valid_; } |
| |
| // Allows to mark Property as valid or invalid. |
| void set_valid(bool is_valid) { is_valid_ = is_valid; } |
| |
| // Method used by PropertySet to retrieve the value from a MessageReader, |
| // no knowledge of the contained type is required, this method returns |
| // true if its expected type was found, false if not. |
| // Implementation provided by specialization. |
| virtual bool PopValueFromReader(MessageReader* reader) = 0; |
| |
| // Method used by PropertySet to append the set value to a MessageWriter, |
| // no knowledge of the contained type is required. |
| // Implementation provided by specialization. |
| virtual void AppendSetValueToWriter(MessageWriter* writer) = 0; |
| |
| // Method used by test and stub implementations of dbus::PropertySet::Set |
| // to replace the property value with the set value without using a |
| // dbus::MessageReader. |
| virtual void ReplaceValueWithSetValue() = 0; |
| |
| protected: |
| // Retrieves the associated property set. |
| PropertySet* property_set() { return property_set_; } |
| |
| private: |
| // Pointer to the PropertySet instance that this instance is a member of, |
| // no ownership is taken and |property_set_| must outlive this class. |
| PropertySet* property_set_; |
| |
| bool is_valid_; |
| |
| // Name of the property. |
| std::string name_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PropertyBase); |
| }; |
| |
| // PropertySet groups a collection of properties for a remote object |
| // together into a single structure, fixing their types and name such |
| // that calls made through it are type-safe. |
| // |
| // Clients always sub-class this to add the properties, and should always |
| // provide a constructor that chains up to this and then calls |
| // RegisterProperty() for each property defined. |
| // |
| // After creation, client code should call ConnectSignals() and most likely |
| // GetAll() to seed initial values and update as changes occur. |
| class CHROME_DBUS_EXPORT PropertySet { |
| public: |
| // Callback for changes to cached values of properties, either notified |
| // via signal, or as a result of calls to Get() and GetAll(). The |name| |
| // argument specifies the name of the property changed. |
| typedef base::Callback<void(const std::string& name)> PropertyChangedCallback; |
| |
| // Constructs a property set, where |object_proxy| specifies the proxy for |
| // the/ remote object that these properties are for, care should be taken to |
| // ensure that this object does not outlive the lifetime of the proxy; |
| // |interface| specifies the D-Bus interface of these properties, and |
| // |property_changed_callback| specifies the callback for when properties |
| // are changed, this may be a NULL callback. |
| PropertySet(ObjectProxy* object_proxy, const std::string& interface, |
| const PropertyChangedCallback& property_changed_callback); |
| |
| // Destructor; we don't hold on to any references or memory that needs |
| // explicit clean-up, but clang thinks we might. |
| virtual ~PropertySet(); |
| |
| // Registers a property, generally called from the subclass constructor; |
| // pass the |name| of the property as used in method calls and signals, |
| // and the pointer to the |property| member of the structure. This will |
| // call the PropertyBase::Init method. |
| void RegisterProperty(const std::string& name, PropertyBase* property); |
| |
| // Connects property change notification signals to the object, generally |
| // called immediately after the object is created and before calls to other |
| // methods. Sub-classes may override to use different D-Bus signals. |
| virtual void ConnectSignals(); |
| |
| // Methods connected by ConnectSignals() and called by dbus:: when |
| // a property is changed. Sub-classes may override if the property |
| // changed signal provides different arguments. |
| virtual void ChangedReceived(Signal* signal); |
| virtual void ChangedConnected(const std::string& interface_name, |
| const std::string& signal_name, |
| bool success); |
| |
| // Callback for Get() method, |success| indicates whether or not the |
| // value could be retrived, if true the new value can be obtained by |
| // calling value() on the property. |
| typedef base::Callback<void(bool success)> GetCallback; |
| |
| // Requests an updated value from the remote object for |property| |
| // incurring a round-trip. |callback| will be called when the new |
| // value is available. This may not be implemented by some interfaces, |
| // and may be overriden by sub-classes if interfaces use different |
| // method calls. |
| virtual void Get(PropertyBase* property, GetCallback callback); |
| virtual void OnGet(PropertyBase* property, GetCallback callback, |
| Response* response); |
| |
| // Queries the remote object for values of all properties and updates |
| // initial values. Sub-classes may override to use a different D-Bus |
| // method, or if the remote object does not support retrieving all |
| // properties, either ignore or obtain each property value individually. |
| virtual void GetAll(); |
| virtual void OnGetAll(Response* response); |
| |
| // Callback for Set() method, |success| indicates whether or not the |
| // new property value was accepted by the remote object. |
| typedef base::Callback<void(bool success)> SetCallback; |
| |
| // Requests that the remote object for |property| change the property to |
| // its new value. |callback| will be called to indicate the success or |
| // failure of the request, however the new value may not be available |
| // depending on the remote object. This method may be overridden by |
| // sub-classes if interfaces use different method calls. |
| virtual void Set(PropertyBase* property, SetCallback callback); |
| virtual void OnSet(PropertyBase* property, SetCallback callback, |
| Response* response); |
| |
| // Update properties by reading an array of dictionary entries, each |
| // containing a string with the name and a variant with the value, from |
| // |message_reader|. Returns false if message is in incorrect format. |
| bool UpdatePropertiesFromReader(MessageReader* reader); |
| |
| // Updates a single property by reading a string with the name and a |
| // variant with the value from |message_reader|. Returns false if message |
| // is in incorrect format, or property type doesn't match. |
| bool UpdatePropertyFromReader(MessageReader* reader); |
| |
| // Calls the property changed callback passed to the constructor, used |
| // by sub-classes that do not call UpdatePropertiesFromReader() or |
| // UpdatePropertyFromReader(). Takes the |name| of the changed property. |
| void NotifyPropertyChanged(const std::string& name); |
| |
| // Retrieves the object proxy this property set was initialized with, |
| // provided for sub-classes overriding methods that make D-Bus calls |
| // and for Property<>. Not permitted with const references to this class. |
| ObjectProxy* object_proxy() { return object_proxy_; } |
| |
| // Retrieves the interface of this property set. |
| const std::string& interface() const { return interface_; } |
| |
| protected: |
| // Get a weak pointer to this property set, provided so that sub-classes |
| // overriding methods that make D-Bus calls may use the existing (or |
| // override) callbacks without providing their own weak pointer factory. |
| base::WeakPtr<PropertySet> GetWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| private: |
| // Invalidates properties by reading an array of names, from |
| // |message_reader|. Returns false if message is in incorrect format. |
| bool InvalidatePropertiesFromReader(MessageReader* reader); |
| |
| // Pointer to object proxy for making method calls, no ownership is taken |
| // so this must outlive this class. |
| ObjectProxy* object_proxy_; |
| |
| // Interface of property, e.g. "org.chromium.ExampleService", this is |
| // distinct from the interface of the method call itself which is the |
| // general D-Bus Properties interface "org.freedesktop.DBus.Properties". |
| std::string interface_; |
| |
| // Callback for property changes. |
| PropertyChangedCallback property_changed_callback_; |
| |
| // Map of properties (as PropertyBase*) defined in the structure to |
| // names as used in D-Bus method calls and signals. The base pointer |
| // restricts property access via this map to type-unsafe and non-specific |
| // actions only. |
| typedef std::map<const std::string, PropertyBase*> PropertiesMap; |
| PropertiesMap properties_map_; |
| |
| // Weak pointer factory as D-Bus callbacks may last longer than these |
| // objects. |
| base::WeakPtrFactory<PropertySet> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PropertySet); |
| }; |
| |
| // Property template, this defines the type-specific and type-safe methods |
| // of properties that can be accessed as members of a PropertySet structure. |
| // |
| // Properties provide a cached value that has an initial sensible default |
| // until the reply to PropertySet::GetAll() is retrieved and is updated by |
| // all calls to that method, PropertySet::Get() and property changed signals |
| // also handled by PropertySet. It can be obtained by calling value() on the |
| // property. |
| // |
| // It is recommended that this cached value be used where necessary, with |
| // code using PropertySet::PropertyChangedCallback to be notified of changes, |
| // rather than incurring a round-trip to the remote object for each property |
| // access. |
| // |
| // Where a round-trip is necessary, the Get() method is provided. And to |
| // update the remote object value, the Set() method is also provided; these |
| // both simply call methods on PropertySet. |
| // |
| // Handling of particular D-Bus types is performed via specialization, |
| // typically the PopValueFromReader() and AppendSetValueToWriter() methods |
| // will need to be provided, and in rare cases a constructor to provide a |
| // default value. Specializations for basic D-Bus types, strings, object |
| // paths and arrays are provided for you. |
| template <class T> |
| class CHROME_DBUS_EXPORT Property : public PropertyBase { |
| public: |
| Property() {} |
| ~Property() override {} |
| |
| // Retrieves the cached value. |
| const T& value() const { return value_; } |
| |
| // Requests an updated value from the remote object incurring a |
| // round-trip. |callback| will be called when the new value is available. |
| // This may not be implemented by some interfaces. |
| virtual void Get(dbus::PropertySet::GetCallback callback) { |
| property_set()->Get(this, callback); |
| } |
| |
| // Requests that the remote object change the property value to |value|, |
| // |callback| will be called to indicate the success or failure of the |
| // request, however the new value may not be available depending on the |
| // remote object. |
| virtual void Set(const T& value, dbus::PropertySet::SetCallback callback) { |
| set_value_ = value; |
| property_set()->Set(this, callback); |
| } |
| |
| // Method used by PropertySet to retrieve the value from a MessageReader, |
| // no knowledge of the contained type is required, this method returns |
| // true if its expected type was found, false if not. |
| bool PopValueFromReader(MessageReader* reader) override; |
| |
| // Method used by PropertySet to append the set value to a MessageWriter, |
| // no knowledge of the contained type is required. |
| // Implementation provided by specialization. |
| void AppendSetValueToWriter(MessageWriter* writer) override; |
| |
| // Method used by test and stub implementations of dbus::PropertySet::Set |
| // to replace the property value with the set value without using a |
| // dbus::MessageReader. |
| void ReplaceValueWithSetValue() override { |
| value_ = set_value_; |
| property_set()->NotifyPropertyChanged(name()); |
| } |
| |
| // Method used by test and stub implementations to directly set the |
| // value of a property. |
| void ReplaceValue(const T& value) { |
| value_ = value; |
| property_set()->NotifyPropertyChanged(name()); |
| } |
| |
| // Method used by test and stub implementations to directly set the |
| // |set_value_| of a property. |
| void ReplaceSetValueForTesting(const T& value) { set_value_ = value; } |
| |
| private: |
| // Current cached value of the property. |
| T value_; |
| |
| // Replacement value of the property. |
| T set_value_; |
| }; |
| |
| // Clang and GCC don't agree on how attributes should work for explicitly |
| // instantiated templates. GCC ignores attributes on explicit instantiations |
| // (and emits a warning) while Clang requires the visiblity attribute on the |
| // explicit instantiations for them to be visible to other compilation units. |
| // Hopefully clang and GCC agree one day, and this can be cleaned up: |
| // https://llvm.org/bugs/show_bug.cgi?id=24815 |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wattributes" |
| |
| template <> |
| CHROME_DBUS_EXPORT Property<uint8>::Property(); |
| template <> |
| CHROME_DBUS_EXPORT bool Property<uint8>::PopValueFromReader( |
| MessageReader* reader); |
| template <> |
| CHROME_DBUS_EXPORT void Property<uint8>::AppendSetValueToWriter( |
| MessageWriter* writer); |
| extern template class CHROME_DBUS_EXPORT Property<uint8>; |
| |
| template <> |
| CHROME_DBUS_EXPORT Property<bool>::Property(); |
| template <> |
| CHROME_DBUS_EXPORT bool Property<bool>::PopValueFromReader( |
| MessageReader* reader); |
| template <> |
| CHROME_DBUS_EXPORT void Property<bool>::AppendSetValueToWriter( |
| MessageWriter* writer); |
| extern template class CHROME_DBUS_EXPORT Property<bool>; |
| |
| template <> |
| CHROME_DBUS_EXPORT Property<int16>::Property(); |
| template <> |
| CHROME_DBUS_EXPORT bool Property<int16>::PopValueFromReader( |
| MessageReader* reader); |
| template <> |
| CHROME_DBUS_EXPORT void Property<int16>::AppendSetValueToWriter( |
| MessageWriter* writer); |
| extern template class CHROME_DBUS_EXPORT Property<int16>; |
| |
| template <> |
| CHROME_DBUS_EXPORT Property<uint16>::Property(); |
| template <> |
| CHROME_DBUS_EXPORT bool Property<uint16>::PopValueFromReader( |
| MessageReader* reader); |
| template <> |
| CHROME_DBUS_EXPORT void Property<uint16>::AppendSetValueToWriter( |
| MessageWriter* writer); |
| extern template class CHROME_DBUS_EXPORT Property<uint16>; |
| |
| template <> |
| CHROME_DBUS_EXPORT Property<int32>::Property(); |
| template <> |
| CHROME_DBUS_EXPORT bool Property<int32>::PopValueFromReader( |
| MessageReader* reader); |
| template <> |
| CHROME_DBUS_EXPORT void Property<int32>::AppendSetValueToWriter( |
| MessageWriter* writer); |
| extern template class CHROME_DBUS_EXPORT Property<int32>; |
| |
| template <> |
| CHROME_DBUS_EXPORT Property<uint32>::Property(); |
| template <> |
| CHROME_DBUS_EXPORT bool Property<uint32>::PopValueFromReader( |
| MessageReader* reader); |
| template <> |
| CHROME_DBUS_EXPORT void Property<uint32>::AppendSetValueToWriter( |
| MessageWriter* writer); |
| extern template class CHROME_DBUS_EXPORT Property<uint32>; |
| |
| template <> |
| CHROME_DBUS_EXPORT Property<int64>::Property(); |
| template <> |
| CHROME_DBUS_EXPORT bool Property<int64>::PopValueFromReader( |
| MessageReader* reader); |
| template <> |
| CHROME_DBUS_EXPORT void Property<int64>::AppendSetValueToWriter( |
| MessageWriter* writer); |
| extern template class CHROME_DBUS_EXPORT Property<int64>; |
| |
| template <> |
| CHROME_DBUS_EXPORT Property<uint64>::Property(); |
| template <> |
| CHROME_DBUS_EXPORT bool Property<uint64>::PopValueFromReader( |
| MessageReader* reader); |
| template <> |
| CHROME_DBUS_EXPORT void Property<uint64>::AppendSetValueToWriter( |
| MessageWriter* writer); |
| extern template class CHROME_DBUS_EXPORT Property<uint64>; |
| |
| template <> |
| CHROME_DBUS_EXPORT Property<double>::Property(); |
| template <> |
| CHROME_DBUS_EXPORT bool Property<double>::PopValueFromReader( |
| MessageReader* reader); |
| template <> |
| CHROME_DBUS_EXPORT void Property<double>::AppendSetValueToWriter( |
| MessageWriter* writer); |
| extern template class CHROME_DBUS_EXPORT Property<double>; |
| |
| template <> |
| CHROME_DBUS_EXPORT bool Property<std::string>::PopValueFromReader( |
| MessageReader* reader); |
| template <> |
| CHROME_DBUS_EXPORT void Property<std::string>::AppendSetValueToWriter( |
| MessageWriter* writer); |
| extern template class CHROME_DBUS_EXPORT Property<std::string>; |
| |
| template <> |
| CHROME_DBUS_EXPORT bool Property<ObjectPath>::PopValueFromReader( |
| MessageReader* reader); |
| template <> |
| CHROME_DBUS_EXPORT void Property<ObjectPath>::AppendSetValueToWriter( |
| MessageWriter* writer); |
| extern template class CHROME_DBUS_EXPORT Property<ObjectPath>; |
| |
| template <> |
| CHROME_DBUS_EXPORT bool Property<std::vector<std::string>>::PopValueFromReader( |
| MessageReader* reader); |
| template <> |
| CHROME_DBUS_EXPORT void Property< |
| std::vector<std::string>>::AppendSetValueToWriter(MessageWriter* writer); |
| extern template class CHROME_DBUS_EXPORT Property<std::vector<std::string>>; |
| |
| template <> |
| CHROME_DBUS_EXPORT bool Property<std::vector<ObjectPath>>::PopValueFromReader( |
| MessageReader* reader); |
| template <> |
| CHROME_DBUS_EXPORT void Property< |
| std::vector<ObjectPath>>::AppendSetValueToWriter(MessageWriter* writer); |
| extern template class CHROME_DBUS_EXPORT Property<std::vector<ObjectPath>>; |
| |
| template <> |
| CHROME_DBUS_EXPORT bool Property<std::vector<uint8>>::PopValueFromReader( |
| MessageReader* reader); |
| template <> |
| CHROME_DBUS_EXPORT void Property<std::vector<uint8>>::AppendSetValueToWriter( |
| MessageWriter* writer); |
| extern template class CHROME_DBUS_EXPORT Property<std::vector<uint8>>; |
| |
| template <> |
| CHROME_DBUS_EXPORT bool |
| Property<std::map<std::string, std::string>>::PopValueFromReader( |
| MessageReader* reader); |
| template <> |
| CHROME_DBUS_EXPORT void |
| Property<std::map<std::string, std::string>>::AppendSetValueToWriter( |
| MessageWriter* writer); |
| extern template class CHROME_DBUS_EXPORT |
| Property<std::map<std::string, std::string>>; |
| |
| template <> |
| CHROME_DBUS_EXPORT bool |
| Property<std::vector<std::pair<std::vector<uint8_t>, uint16_t>>>:: |
| PopValueFromReader(MessageReader* reader); |
| template <> |
| CHROME_DBUS_EXPORT void |
| Property<std::vector<std::pair<std::vector<uint8_t>, uint16_t>>>:: |
| AppendSetValueToWriter(MessageWriter* writer); |
| extern template class CHROME_DBUS_EXPORT |
| Property<std::vector<std::pair<std::vector<uint8_t>, uint16_t>>>; |
| |
| #pragma GCC diagnostic pop |
| |
| } // namespace dbus |
| |
| #endif // DBUS_PROPERTY_H_ |