1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297
  298
  299
  300
  301
  302
  303
  304
  305
  306
  307
  308
  309
  310
  311
  312
  313
  314
  315
  316
  317
  318
  319
  320
  321
  322
  323
  324
  325
  326
  327
  328
  329
  330
  331
  332
  333
  334
  335
  336
  337
  338
  339
  340
  341
  342
  343
  344
  345
  346
  347
  348
  349
  350
  351
  352
  353
  354
  355
  356
  357
  358
  359
  360
  361
  362
  363
  364
  365
  366
  367
  368
  369
  370
  371
  372
  373
  374
  375
  376
  377
  378

dbus / object_manager.h [blame]

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef DBUS_OBJECT_MANAGER_H_
#define DBUS_OBJECT_MANAGER_H_

#include <stdint.h>

#include <map>

#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "dbus/object_path.h"
#include "dbus/property.h"

// Newer D-Bus services implement the Object Manager interface to inform other
// clients about the objects they export, the properties of those objects, and
// notification of changes in the set of available objects:
//     http://dbus.freedesktop.org/doc/dbus-specification.html
//       #standard-interfaces-objectmanager
//
// This interface is very closely tied to the Properties interface, and uses
// even more levels of nested dictionaries and variants. In addition to
// simplifying implementation, since there tends to be a single object manager
// per service, spanning the complete set of objects an interfaces available,
// the classes implemented here make dealing with this interface simpler.
//
// Except where noted, use of this class replaces the need for the code
// documented in dbus/property.h
//
// Client implementation classes should begin by deriving from the
// dbus::ObjectManager::Interface class, and defining a Properties structure as
// documented in dbus/property.h.
//
// Example:
//   class ExampleClient : public dbus::ObjectManager::Interface {
//    public:
//     struct Properties : public dbus::PropertySet {
//       dbus::Property<std::string> name;
//       dbus::Property<uint16_t> 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, kExampleInterface, callback) {
//         RegisterProperty("Name", &name);
//         RegisterProperty("Version", &version);
//         RegisterProperty("Parent", &parent);
//         RegisterProperty("Children", &children);
//       }
//       virtual ~Properties() {}
//     };
//
// The link between the implementation class and the object manager is set up
// in the constructor and removed in the destructor; the class should maintain
// a pointer to its object manager for use in other methods and establish
// itself as the implementation class for its interface.
//
// Example:
//   explicit ExampleClient::ExampleClient(dbus::Bus* bus)
//       : bus_(bus),
//         weak_ptr_factory_(this) {
//     object_manager_ = bus_->GetObjectManager(kServiceName, kManagerPath);
//     object_manager_->RegisterInterface(kInterface, this);
//   }
//
//   virtual ExampleClient::~ExampleClient() {
//     object_manager_->UnregisterInterface(kInterface);
//   }
//
// This class calls GetManagedObjects() asynchronously after the remote service
// becomes available and additionally refreshes managed objects after the
// service stops or restarts.
//
// The object manager interface class has one abstract method that must be
// implemented by the class to create Properties structures on demand. As well
// as implementing this, you will want to implement a public GetProperties()
// method.
//
// Example:
//   dbus::PropertySet* CreateProperties(dbus::ObjectProxy* object_proxy,
//                                       const std::string& interface_name)
//       override {
//     Properties* properties = new Properties(
//           object_proxy, interface_name,
//           base::BindRepeating(&PropertyChanged,
//                               weak_ptr_factory_.GetWeakPtr(),
//                               object_path));
//     return static_cast<dbus::PropertySet*>(properties);
//   }
//
//   Properties* GetProperties(const dbus::ObjectPath& object_path) {
//     return static_cast<Properties*>(
//         object_manager_->GetProperties(object_path, kInterface));
//   }
//
// Note that unlike classes that only use dbus/property.h there is no need
// to connect signals or obtain the initial values of properties. The object
// manager class handles that for you.
//
// PropertyChanged is a method of your own to notify your observers of a change
// in your properties, either as a result of a signal from the Properties
// interface or from the Object Manager interface. You may also wish to
// implement the optional ObjectAdded and ObjectRemoved methods of the class
// to likewise notify observers.
//
// When your class needs an object proxy for a given object path, it may
// obtain it from the object manager. Unlike the equivalent method on the bus
// this will return NULL if the object is not known.
//
//   object_proxy = object_manager_->GetObjectProxy(object_path);
//   if (object_proxy) {
//     ...
//   }
//
// There is no need for code using your implementation class to be aware of the
// use of object manager behind the scenes, the rules for updating properties
// documented in dbus/property.h still apply.

namespace dbus {

const char kObjectManagerInterface[] = "org.freedesktop.DBus.ObjectManager";
const char kObjectManagerGetManagedObjects[] = "GetManagedObjects";
const char kObjectManagerInterfacesAdded[] = "InterfacesAdded";
const char kObjectManagerInterfacesRemoved[] = "InterfacesRemoved";

class Bus;
class MessageReader;
class ObjectProxy;
class Response;
class Signal;

// ObjectManager implements both the D-Bus client components of the D-Bus
// Object Manager interface, as internal methods, and a public API for
// client classes to utilize.
class CHROME_DBUS_EXPORT ObjectManager final
    : public base::RefCountedThreadSafe<ObjectManager> {
 public:
  // ObjectManager::Interface must be implemented by any class wishing to have
  // its remote objects managed by an ObjectManager.
  class Interface {
   public:
    virtual ~Interface() {}

    // Called by ObjectManager to create a Properties structure for the remote
    // D-Bus object identified by |object_path| and accessibile through
    // |object_proxy|. The D-Bus interface name |interface_name| is that passed
    // to RegisterInterface() by the implementation class.
    //
    // The implementation class should create and return an instance of its own
    // subclass of dbus::PropertySet; ObjectManager will then connect signals
    // and update the properties from its own internal message reader.
    virtual PropertySet* CreateProperties(
        ObjectProxy *object_proxy,
        const dbus::ObjectPath& object_path,
        const std::string& interface_name) = 0;

    // Called by ObjectManager to inform the implementation class that an
    // object has been added with the path |object_path|. The D-Bus interface
    // name |interface_name| is that passed to RegisterInterface() by the
    // implementation class.
    //
    // If a new object implements multiple interfaces, this method will be
    // called on each interface implementation with differing values of
    // |interface_name| as appropriate. An implementation class will only
    // receive multiple calls if it has registered for multiple interfaces.
    virtual void ObjectAdded(const ObjectPath& object_path,
                             const std::string& interface_name) { }

    // Called by ObjectManager to inform the implementation class than an
    // object with the path |object_path| has been removed. Ths D-Bus interface
    // name |interface_name| is that passed to RegisterInterface() by the
    // implementation class. Multiple interfaces are handled as with
    // ObjectAdded().
    //
    // This method will be called before the Properties structure and the
    // ObjectProxy object for the given interface are cleaned up, it is safe
    // to retrieve them during removal to vary processing.
    virtual void ObjectRemoved(const ObjectPath& object_path,
                               const std::string& interface_name) { }
  };

  // Client code should use Bus::GetObjectManager() instead of this constructor.
  static scoped_refptr<ObjectManager> Create(Bus* bus,
                                             const std::string& service_name,
                                             const ObjectPath& object_path);

  ObjectManager(const ObjectManager&) = delete;
  ObjectManager& operator=(const ObjectManager&) = delete;

  // Register a client implementation class |interface| for the given D-Bus
  // interface named in |interface_name|. That object's CreateProperties()
  // method will be used to create instances of dbus::PropertySet* when
  // required.
  void RegisterInterface(const std::string& interface_name,
                         Interface* interface);

  // Unregister the implementation class for the D-Bus interface named in
  // |interface_name|, objects and properties of this interface will be
  // ignored.
  void UnregisterInterface(const std::string& interface_name);

  // Checks whether an interface is registered.
  bool IsInterfaceRegisteredForTesting(const std::string& interface_name) const;

  // Returns a list of object paths, in an undefined order, of objects known
  // to this manager.
  std::vector<ObjectPath> GetObjects();

  // Returns the list of object paths, in an undefined order, of objects
  // implementing the interface named in |interface_name| known to this manager.
  std::vector<ObjectPath> GetObjectsWithInterface(
      const std::string& interface_name);

  // Returns a ObjectProxy pointer for the given |object_path|. Unlike
  // the equivalent method on Bus this will return NULL if the object
  // manager has not been informed of that object's existence.
  ObjectProxy* GetObjectProxy(const ObjectPath& object_path);

  // Returns a PropertySet* pointer for the given |object_path| and
  // |interface_name|, or NULL if the object manager has not been informed of
  // that object's existence or the interface's properties. The caller should
  // cast the returned pointer to the appropriate type, e.g.:
  //   static_cast<Properties*>(GetProperties(object_path, my_interface));
  PropertySet* GetProperties(const ObjectPath& object_path,
                             const std::string& interface_name);

  // Instructs the object manager to refresh its list of managed objects;
  // automatically called by the D-Bus thread manager, there should never be
  // a need to call this manually.
  void GetManagedObjects();

  // Cleans up any match rules and filter functions added by this ObjectManager.
  // The Bus object will take care of this so you don't have to do it manually.
  //
  // BLOCKING CALL.
  void CleanUp();

 private:
  friend class base::RefCountedThreadSafe<ObjectManager>;

  ObjectManager(Bus* bus,
                const std::string& service_name,
                const ObjectPath& object_path);
  ~ObjectManager();

  // Called from the constructor to add a match rule for PropertiesChanged
  // signals on the D-Bus thread and set up a corresponding filter function.
  bool SetupMatchRuleAndFilter();

  // Called on the origin thread once the match rule and filter have been set
  // up. Connects the InterfacesAdded and InterfacesRemoved signals and
  // refreshes objects if the service is available. |success| is false if an
  // error occurred during setup and true otherwise.
  void OnSetupMatchRuleAndFilterComplete(bool success);

  // Called by dbus:: when a message is received. This is used to filter
  // PropertiesChanged signals from the correct sender and relay the event to
  // the correct PropertySet.
  static DBusHandlerResult HandleMessageThunk(DBusConnection* connection,
                                              DBusMessage* raw_message,
                                              void* user_data);
  DBusHandlerResult HandleMessage(DBusConnection* connection,
                                  DBusMessage* raw_message);

  // Called when a PropertiesChanged signal is received from the sender.
  // This method notifies the relevant PropertySet that it should update its
  // properties based on the received signal. Called from HandleMessage.
  void NotifyPropertiesChanged(const dbus::ObjectPath object_path,
                               Signal* signal);
  void NotifyPropertiesChangedHelper(const dbus::ObjectPath object_path,
                                     Signal* signal);

  // Called by dbus:: in response to the GetManagedObjects() method call.
  void OnGetManagedObjects(Response* response);

  // Called by dbus:: when an InterfacesAdded signal is received and initially
  // connected.
  void InterfacesAddedReceived(Signal* signal);
  void InterfacesAddedConnected(const std::string& interface_name,
                                const std::string& signal_name,
                                bool success);

  // Called by dbus:: when an InterfacesRemoved signal is received and
  // initially connected.
  void InterfacesRemovedReceived(Signal* signal);
  void InterfacesRemovedConnected(const std::string& interface_name,
                                  const std::string& signal_name,
                                  bool success);

  // Updates the map entry for the object with path |object_path| using the
  // D-Bus message in |reader|, which should consist of an dictionary mapping
  // interface names to properties dictionaries as recieved by both the
  // GetManagedObjects() method return and the InterfacesAdded() signal.
  void UpdateObject(const ObjectPath& object_path, MessageReader* reader);

  // Updates the properties structure of the object with path |object_path|
  // for the interface named |interface_name| using the D-Bus message in
  // |reader| which should consist of the properties dictionary for that
  // interface.
  //
  // Called by UpdateObjects() for each interface in the dictionary; this
  // method takes care of both creating the entry in the ObjectMap and
  // ObjectProxy if required, as well as the PropertySet instance for that
  // interface if necessary.
  void AddInterface(const ObjectPath& object_path,
                    const std::string& interface_name,
                    MessageReader* reader);

  // Removes the properties structure of the object with path |object_path|
  // for the interfaces named |interface_name|.
  //
  // If no further interfaces remain, the entry in the ObjectMap is discarded.
  void RemoveInterface(const ObjectPath& object_path,
                       const std::string& interface_name);

  // Removes all objects and interfaces from the object manager when
  // |old_owner| is not the empty string and/or re-requests the set of managed
  // objects when |new_owner| is not the empty string.
  void NameOwnerChanged(const std::string& old_owner,
                        const std::string& new_owner);

  // Write |new_owner| to |service_name_owner_|. This method makes sure write
  // happens on the DBus thread, which is the sole writer to
  // |service_name_owner_|.
  void UpdateServiceNameOwner(const std::string& new_owner);

  // Valid in between the constructor and `CleanUp()`.
  // After Cleanup(), `this` lifetime might exceed Bus's one.
  raw_ptr<Bus> bus_;
  std::string service_name_;
  std::string service_name_owner_;
  std::string match_rule_;
  ObjectPath object_path_;
  raw_ptr<ObjectProxy, AcrossTasksDanglingUntriaged> object_proxy_;
  bool setup_success_ = false;
  bool cleanup_called_ = false;

  // Maps the name of an interface to the implementation class used for
  // instantiating PropertySet structures for that interface's properties.
  typedef std::map<std::string, raw_ptr<Interface, CtnExperimental>>
      InterfaceMap;
  InterfaceMap interface_map_;

  // Each managed object consists of a ObjectProxy used to make calls
  // against that object and a collection of D-Bus interface names and their
  // associated PropertySet structures.
  struct Object {
    Object();
    ~Object();

    raw_ptr<ObjectProxy, AcrossTasksDanglingUntriaged> object_proxy;

    // Maps the name of an interface to the specific PropertySet structure
    // of that interface's properties.
    typedef std::map<const std::string, raw_ptr<PropertySet, CtnExperimental>>
        PropertiesMap;
    PropertiesMap properties_map;
  };

  // Maps the object path of an object to the Object structure.
  typedef std::map<const ObjectPath, raw_ptr<Object, CtnExperimental>>
      ObjectMap;
  ObjectMap object_map_;

  // Weak pointer factory for generating 'this' pointers that might live longer
  // than we do.
  // Note: This should remain the last member so it'll be destroyed and
  // invalidate its weak pointers before any other members are destroyed.
  base::WeakPtrFactory<ObjectManager> weak_ptr_factory_{this};
};

}  // namespace dbus

#endif  // DBUS_OBJECT_MANAGER_H_