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 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() 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. 198class 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. 352template <class T> 353class 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 409template <> Property<uint8>::Property(); 410template <> bool Property<uint8>::PopValueFromReader(MessageReader* reader); 411template <> void Property<uint8>::AppendSetValueToWriter(MessageWriter* writer); 412extern template class Property<uint8>; 413 414template <> Property<bool>::Property(); 415template <> bool Property<bool>::PopValueFromReader(MessageReader* reader); 416template <> void Property<bool>::AppendSetValueToWriter(MessageWriter* writer); 417extern template class Property<bool>; 418 419template <> Property<int16>::Property(); 420template <> bool Property<int16>::PopValueFromReader(MessageReader* reader); 421template <> void Property<int16>::AppendSetValueToWriter(MessageWriter* writer); 422extern template class Property<int16>; 423 424template <> Property<uint16>::Property(); 425template <> bool Property<uint16>::PopValueFromReader(MessageReader* reader); 426template <> void Property<uint16>::AppendSetValueToWriter( 427 MessageWriter* writer); 428extern template class Property<uint16>; 429 430template <> Property<int32>::Property(); 431template <> bool Property<int32>::PopValueFromReader(MessageReader* reader); 432template <> void Property<int32>::AppendSetValueToWriter(MessageWriter* writer); 433extern template class Property<int32>; 434 435template <> Property<uint32>::Property(); 436template <> bool Property<uint32>::PopValueFromReader(MessageReader* reader); 437template <> void Property<uint32>::AppendSetValueToWriter( 438 MessageWriter* writer); 439extern template class Property<uint32>; 440 441template <> Property<int64>::Property(); 442template <> bool Property<int64>::PopValueFromReader(MessageReader* reader); 443template <> void Property<int64>::AppendSetValueToWriter(MessageWriter* writer); 444extern template class Property<int64>; 445 446template <> Property<uint64>::Property(); 447template <> bool Property<uint64>::PopValueFromReader(MessageReader* reader); 448template <> void Property<uint64>::AppendSetValueToWriter( 449 MessageWriter* writer); 450extern template class Property<uint64>; 451 452template <> Property<double>::Property(); 453template <> bool Property<double>::PopValueFromReader(MessageReader* reader); 454template <> void Property<double>::AppendSetValueToWriter( 455 MessageWriter* writer); 456extern template class Property<double>; 457 458template <> bool Property<std::string>::PopValueFromReader( 459 MessageReader* reader); 460template <> void Property<std::string>::AppendSetValueToWriter( 461 MessageWriter* writer); 462extern template class Property<std::string>; 463 464template <> bool Property<ObjectPath>::PopValueFromReader( 465 MessageReader* reader); 466template <> void Property<ObjectPath>::AppendSetValueToWriter( 467 MessageWriter* writer); 468extern template class Property<ObjectPath>; 469 470template <> bool Property<std::vector<std::string> >::PopValueFromReader( 471 MessageReader* reader); 472template <> void Property<std::vector<std::string> >::AppendSetValueToWriter( 473 MessageWriter* writer); 474extern template class Property<std::vector<std::string> >; 475 476template <> bool Property<std::vector<ObjectPath> >::PopValueFromReader( 477 MessageReader* reader); 478template <> void Property<std::vector<ObjectPath> >::AppendSetValueToWriter( 479 MessageWriter* writer); 480extern template class Property<std::vector<ObjectPath> >; 481 482template <> bool Property<std::vector<uint8> >::PopValueFromReader( 483 MessageReader* reader); 484template <> void Property<std::vector<uint8> >::AppendSetValueToWriter( 485 MessageWriter* writer); 486extern template class Property<std::vector<uint8> >; 487 488} // namespace dbus 489 490#endif // DBUS_PROPERTY_H_ 491