Home | History | Annotate | Download | only in dbus
      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:
    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   //   }
    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.
    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.
    289   ObjectProxy* object_proxy() { return object_proxy_; }
    290 
    291   // Retrieves the interface of this property set.
    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.
    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:
    355   Property() {}
    356 
    357   // Retrieves the cached 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.
    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.
    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.
    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.
    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 
    413 template <> Property<bool>::Property();
    414 template <> bool Property<bool>::PopValueFromReader(MessageReader* reader);
    415 template <> void Property<bool>::AppendSetValueToWriter(MessageWriter* writer);
    416 
    417 template <> Property<int16>::Property();
    418 template <> bool Property<int16>::PopValueFromReader(MessageReader* reader);
    419 template <> void Property<int16>::AppendSetValueToWriter(MessageWriter* writer);
    420 
    421 template <> Property<uint16>::Property();
    422 template <> bool Property<uint16>::PopValueFromReader(MessageReader* reader);
    423 template <> void Property<uint16>::AppendSetValueToWriter(
    424   MessageWriter* writer);
    425 
    426 template <> Property<int32>::Property();
    427 template <> bool Property<int32>::PopValueFromReader(MessageReader* reader);
    428 template <> void Property<int32>::AppendSetValueToWriter(MessageWriter* writer);
    429 
    430 template <> Property<uint32>::Property();
    431 template <> bool Property<uint32>::PopValueFromReader(MessageReader* reader);
    432 template <> void Property<uint32>::AppendSetValueToWriter(
    433   MessageWriter* writer);
    434 
    435 template <> Property<int64>::Property();
    436 template <> bool Property<int64>::PopValueFromReader(MessageReader* reader);
    437 template <> void Property<int64>::AppendSetValueToWriter(MessageWriter* writer);
    438 
    439 template <> Property<uint64>::Property();
    440 template <> bool Property<uint64>::PopValueFromReader(MessageReader* reader);
    441 template <> void Property<uint64>::AppendSetValueToWriter(
    442   MessageWriter* writer);
    443 
    444 template <> Property<double>::Property();
    445 template <> bool Property<double>::PopValueFromReader(MessageReader* reader);
    446 template <> void Property<double>::AppendSetValueToWriter(
    447   MessageWriter* writer);
    448 
    449 template <> bool Property<std::string>::PopValueFromReader(
    450   MessageReader* reader);
    451 template <> void Property<std::string>::AppendSetValueToWriter(
    452   MessageWriter* writer);
    453 
    454 template <> bool Property<ObjectPath>::PopValueFromReader(
    455   MessageReader* reader);
    456 template <> void Property<ObjectPath>::AppendSetValueToWriter(
    457   MessageWriter* writer);
    458 
    459 template <> bool Property<std::vector<std::string> >::PopValueFromReader(
    460   MessageReader* reader);
    461 template <> void Property<std::vector<std::string> >::AppendSetValueToWriter(
    462   MessageWriter* writer);
    463 
    464 template <> bool Property<std::vector<ObjectPath> >::PopValueFromReader(
    465   MessageReader* reader);
    466 template <> void Property<std::vector<ObjectPath> >::AppendSetValueToWriter(
    467   MessageWriter* writer);
    468 
    469 }  // namespace dbus
    470 
    471 #endif  // DBUS_PROPERTY_H_
    472