| // Copyright 2014 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // DBusObject is a special helper class that simplifies the implementation of |
| // D-Bus objects in C++. It provides an easy way to define interfaces with |
| // methods and properties and offloads a lot of work to register the object and |
| // all of its interfaces, to marshal method calls (by converting D-Bus method |
| // parameters to native C++ types and invoking native method handlers), etc. |
| |
| // The basic usage pattern of this class is as follows: |
| /* |
| class MyDbusObject { |
| public: |
| MyDbusObject(ExportedObjectManager* object_manager, |
| const scoped_refptr<dbus::Bus>& bus) |
| : dbus_object_(object_manager, bus, |
| dbus::ObjectPath("/org/chromium/my_obj")) {} |
| |
| void Init(const AsyncEventSequencer::CompletionAction& callback) { |
| DBusInterface* my_interface = |
| dbus_object_.AddOrGetInterface("org.chromium.MyInterface"); |
| my_interface->AddSimpleMethodHandler("Method1", this, |
| &MyDbusObject::Method1); |
| my_interface->AddSimpleMethodHandlerWithError("Method2", this, |
| &MyDbusObject::Method2); |
| my_interface->AddMethodHandler("Method3", this, &MyDbusObject::Method3); |
| my_interface->AddProperty("Property1", &prop1_); |
| my_interface->AddProperty("Property2", &prop2_); |
| prop1_.SetValue("prop1_value"); |
| prop2_.SetValue(50); |
| // Register the object by exporting its methods and properties and |
| // exposing them to D-Bus clients. |
| dbus_object_.RegisterAsync(callback); |
| } |
| |
| private: |
| DBusObject dbus_object_; |
| |
| // Make sure the properties outlive the DBusObject they are registered with. |
| chromeos::dbus_utils::ExportedProperty<std::string> prop1_; |
| chromeos::dbus_utils::ExportedProperty<int> prop2_; |
| int Method1() { return 5; } |
| bool Method2(chromeos::ErrorPtr* error, const std::string& message); |
| void Method3(scoped_ptr<DBusMethodResponse<int_32>> response, |
| const std::string& message) { |
| if (message.empty()) { |
| response->ReplyWithError(chromeos::errors::dbus::kDomain, |
| DBUS_ERROR_INVALID_ARGS, |
| "Message string cannot be empty"); |
| return; |
| } |
| int32_t message_len = message.length(); |
| response->Return(message_len); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(MyDbusObject); |
| }; |
| */ |
| |
| #ifndef LIBCHROMEOS_CHROMEOS_DBUS_DBUS_OBJECT_H_ |
| #define LIBCHROMEOS_CHROMEOS_DBUS_DBUS_OBJECT_H_ |
| |
| #include <map> |
| #include <string> |
| |
| #include <base/bind.h> |
| #include <base/macros.h> |
| #include <base/memory/weak_ptr.h> |
| #include <chromeos/chromeos_export.h> |
| #include <chromeos/dbus/async_event_sequencer.h> |
| #include <chromeos/dbus/dbus_object_internal_impl.h> |
| #include <chromeos/dbus/dbus_signal.h> |
| #include <chromeos/dbus/exported_property_set.h> |
| #include <chromeos/errors/error.h> |
| #include <dbus/bus.h> |
| #include <dbus/exported_object.h> |
| #include <dbus/message.h> |
| #include <dbus/object_path.h> |
| |
| namespace chromeos { |
| namespace dbus_utils { |
| |
| class ExportedObjectManager; |
| class ExportedPropertyBase; |
| class DBusObject; |
| |
| // This is an implementation proxy class for a D-Bus interface of an object. |
| // The important functionality for the users is the ability to add D-Bus method |
| // handlers and define D-Bus object properties. This is achieved by using one |
| // of the overload of AddSimpleMethodHandler()/AddMethodHandler() and |
| // AddProperty() respectively. |
| // There are three overloads for DBusInterface::AddSimpleMethodHandler() and |
| // AddMethodHandler() each: |
| // 1. That takes a handler as base::Callback |
| // 2. That takes a static function |
| // 3. That takes a class instance pointer and a class member function |
| // The signature of the handler for AddSimpleMethodHandler must be one of: |
| // R(Args... args) [IN only] |
| // void(Args... args) [IN/OUT] |
| // The signature of the handler for AddSimpleMethodHandlerWithError must be: |
| // bool(ErrorPtr* error, Args... args) [IN/OUT] |
| // The signature of the handler for AddMethodHandler must be: |
| // void(scoped_ptr<DBusMethodResponse> response, Args... args) [IN only] |
| // There is also an AddRawMethodHandler() call that lets provide a custom |
| // handler that can parse its own input parameter and construct a custom |
| // response. |
| // The signature of the handler for AddRawMethodHandler must be: |
| // void(dbus::MethodCall* method_call, ResponseSender sender) |
| class CHROMEOS_EXPORT DBusInterface final { |
| public: |
| DBusInterface(DBusObject* dbus_object, const std::string& interface_name); |
| |
| // Register sync DBus method handler for |method_name| as base::Callback. |
| template<typename R, typename... Args> |
| inline void AddSimpleMethodHandler( |
| const std::string& method_name, |
| const base::Callback<R(Args...)>& handler) { |
| Handler<SimpleDBusInterfaceMethodHandler<R, Args...>>::Add( |
| this, method_name, handler); |
| } |
| |
| // Register sync D-Bus method handler for |method_name| as a static |
| // function. |
| template<typename R, typename... Args> |
| inline void AddSimpleMethodHandler(const std::string& method_name, |
| R(*handler)(Args...)) { |
| Handler<SimpleDBusInterfaceMethodHandler<R, Args...>>::Add( |
| this, method_name, base::Bind(handler)); |
| } |
| |
| // Register sync D-Bus method handler for |method_name| as a class member |
| // function. |
| template<typename Instance, typename Class, typename R, typename... Args> |
| inline void AddSimpleMethodHandler(const std::string& method_name, |
| Instance instance, |
| R(Class::*handler)(Args...)) { |
| Handler<SimpleDBusInterfaceMethodHandler<R, Args...>>::Add( |
| this, method_name, base::Bind(handler, instance)); |
| } |
| |
| // Same as above but for const-method of a class. |
| template<typename Instance, typename Class, typename R, typename... Args> |
| inline void AddSimpleMethodHandler(const std::string& method_name, |
| Instance instance, |
| R(Class::*handler)(Args...) const) { |
| Handler<SimpleDBusInterfaceMethodHandler<R, Args...>>::Add( |
| this, method_name, base::Bind(handler, instance)); |
| } |
| |
| // Register sync DBus method handler for |method_name| as base::Callback. |
| template<typename... Args> |
| inline void AddSimpleMethodHandlerWithError( |
| const std::string& method_name, |
| const base::Callback<bool(ErrorPtr*, Args...)>& handler) { |
| Handler<SimpleDBusInterfaceMethodHandlerWithError<Args...>>::Add( |
| this, method_name, handler); |
| } |
| |
| // Register sync D-Bus method handler for |method_name| as a static |
| // function. |
| template<typename... Args> |
| inline void AddSimpleMethodHandlerWithError( |
| const std::string& method_name, |
| bool(*handler)(ErrorPtr*, Args...)) { |
| Handler<SimpleDBusInterfaceMethodHandlerWithError<Args...>>::Add( |
| this, method_name, base::Bind(handler)); |
| } |
| |
| // Register sync D-Bus method handler for |method_name| as a class member |
| // function. |
| template<typename Instance, typename Class, typename... Args> |
| inline void AddSimpleMethodHandlerWithError( |
| const std::string& method_name, |
| Instance instance, |
| bool(Class::*handler)(ErrorPtr*, Args...)) { |
| Handler<SimpleDBusInterfaceMethodHandlerWithError<Args...>>::Add( |
| this, method_name, base::Bind(handler, instance)); |
| } |
| |
| // Same as above but for const-method of a class. |
| template<typename Instance, typename Class, typename... Args> |
| inline void AddSimpleMethodHandlerWithError( |
| const std::string& method_name, |
| Instance instance, |
| bool(Class::*handler)(ErrorPtr*, Args...) const) { |
| Handler<SimpleDBusInterfaceMethodHandlerWithError<Args...>>::Add( |
| this, method_name, base::Bind(handler, instance)); |
| } |
| |
| // Register an async DBus method handler for |method_name| as base::Callback. |
| template<typename Response, typename... Args> |
| inline void AddMethodHandler( |
| const std::string& method_name, |
| const base::Callback<void(scoped_ptr<Response>, Args...)>& handler) { |
| static_assert(std::is_base_of<DBusMethodResponseBase, Response>::value, |
| "Response must be DBusMethodResponse<T...>"); |
| Handler<DBusInterfaceMethodHandler<Response, Args...>>::Add( |
| this, method_name, handler); |
| } |
| |
| // Register an async D-Bus method handler for |method_name| as a static |
| // function. |
| template<typename Response, typename... Args> |
| inline void AddMethodHandler(const std::string& method_name, |
| void (*handler)(scoped_ptr<Response>, Args...)) { |
| static_assert(std::is_base_of<DBusMethodResponseBase, Response>::value, |
| "Response must be DBusMethodResponse<T...>"); |
| Handler<DBusInterfaceMethodHandler<Response, Args...>>::Add( |
| this, method_name, base::Bind(handler)); |
| } |
| |
| // Register an async D-Bus method handler for |method_name| as a class member |
| // function. |
| template<typename Response, |
| typename Instance, |
| typename Class, |
| typename... Args> |
| inline void AddMethodHandler( |
| const std::string& method_name, |
| Instance instance, |
| void(Class::*handler)(scoped_ptr<Response>, Args...)) { |
| static_assert(std::is_base_of<DBusMethodResponseBase, Response>::value, |
| "Response must be DBusMethodResponse<T...>"); |
| Handler<DBusInterfaceMethodHandler<Response, Args...>>::Add( |
| this, method_name, base::Bind(handler, instance)); |
| } |
| |
| // Same as above but for const-method of a class. |
| template<typename Response, |
| typename Instance, |
| typename Class, |
| typename... Args> |
| inline void AddMethodHandler( |
| const std::string& method_name, |
| Instance instance, |
| void(Class::*handler)(scoped_ptr<Response>, Args...) const) { |
| static_assert(std::is_base_of<DBusMethodResponseBase, Response>::value, |
| "Response must be DBusMethodResponse<T...>"); |
| Handler<DBusInterfaceMethodHandler<Response, Args...>>::Add( |
| this, method_name, base::Bind(handler, instance)); |
| } |
| |
| // Register a raw D-Bus method handler for |method_name| as base::Callback. |
| inline void AddRawMethodHandler( |
| const std::string& method_name, |
| const base::Callback<void(dbus::MethodCall*, ResponseSender)>& handler) { |
| Handler<RawDBusInterfaceMethodHandler>::Add(this, method_name, handler); |
| } |
| |
| // Register a D-Bus property. |
| void AddProperty(const std::string& property_name, |
| ExportedPropertyBase* prop_base); |
| |
| // Registers a D-Bus signal that has a specified number and types (|Args|) of |
| // arguments. Returns a weak pointer to the DBusSignal object which can be |
| // used to send the signal on this interface when needed: |
| /* |
| DBusInterface* itf = dbus_object->AddOrGetInterface("Interface"); |
| auto signal = itf->RegisterSignal<int, bool>("MySignal"); |
| ... |
| // Send the Interface.MySig(12, true) signal. |
| if (signal.lock()->Send(12, true)) { ... } |
| */ |
| // Or if the signal signature is long or complex, you can alias the |
| // DBusSignal<Args...> signal type and use RegisterSignalOfType method |
| // instead: |
| /* |
| DBusInterface* itf = dbus_object->AddOrGetInterface("Interface"); |
| using MySignal = DBusSignal<int, bool>; |
| auto signal = itf->RegisterSignalOfType<MySignal>("MySignal"); |
| ... |
| // Send the Interface.MySig(12, true) signal. |
| if (signal.lock()->Send(12, true)) { ... } |
| */ |
| // If the signal with the given name was already registered, the existing |
| // copy of the signal proxy object is returned as long as the method signature |
| // of the original signal matches the current call. If it doesn't, the method |
| // aborts. |
| |
| // RegisterSignalOfType can be used to create a signal if the type of the |
| // complete DBusSignal<Args...> class which is pre-defined/aliased earlier. |
| template<typename DBusSignalType> |
| inline std::weak_ptr<DBusSignalType> RegisterSignalOfType( |
| const std::string& signal_name) { |
| auto signal = std::make_shared<DBusSignalType>( |
| dbus_object_, interface_name_, signal_name); |
| AddSignalImpl(signal_name, signal); |
| return signal; |
| } |
| |
| // For simple signal arguments, you can specify their types directly in |
| // RegisterSignal<t1, t2, ...>(): |
| // auto signal = itf->RegisterSignal<int>("SignalName"); |
| // This will create a callback signal object that expects one int argument. |
| template<typename... Args> |
| inline std::weak_ptr<DBusSignal<Args...>> RegisterSignal( |
| const std::string& signal_name) { |
| return RegisterSignalOfType<DBusSignal<Args...>>(signal_name); |
| } |
| |
| private: |
| // Helper to create an instance of DBusInterfaceMethodHandlerInterface-derived |
| // handler and add it to the method handler map of the interface. |
| // This makes the actual AddXXXMethodHandler() methods very light-weight and |
| // easier to provide different overloads for various method handler kinds. |
| // Using struct here to allow partial specialization on HandlerType while |
| // letting the compiler to deduce the type of the callback without explicitly |
| // specifying it. |
| template<typename HandlerType> |
| struct Handler { |
| template<typename CallbackType> |
| inline static void Add(DBusInterface* self, |
| const std::string& method_name, |
| const CallbackType& callback) { |
| std::unique_ptr<DBusInterfaceMethodHandlerInterface> sync_method_handler( |
| new HandlerType(callback)); |
| self->AddHandlerImpl(method_name, std::move(sync_method_handler)); |
| } |
| }; |
| // A generic D-Bus method handler for the interface. It extracts the method |
| // name from |method_call|, looks up a registered handler from |handlers_| |
| // map and dispatched the call to that handler. |
| void HandleMethodCall(dbus::MethodCall* method_call, ResponseSender sender); |
| // Helper to add a handler for method |method_name| to the |handlers_| map. |
| // Not marked CHROMEOS_PRIVATE because it needs to be called by the inline |
| // template functions AddMethodHandler(...) |
| void AddHandlerImpl( |
| const std::string& method_name, |
| std::unique_ptr<DBusInterfaceMethodHandlerInterface> handler); |
| // Helper to add a signal object to the |signals_| map. |
| // Not marked CHROMEOS_PRIVATE because it needs to be called by the inline |
| // template function RegisterSignalOfType(...) |
| void AddSignalImpl(const std::string& signal_name, |
| const std::shared_ptr<DBusSignalBase>& signal); |
| // Exports all the methods and properties of this interface and claims the |
| // D-Bus interface. |
| // object_manager - ExportedObjectManager instance that notifies D-Bus |
| // listeners of a new interface being claimed. |
| // exported_object - instance of D-Bus object the interface is being added to. |
| // object_path - D-Bus object path for the object instance. |
| // interface_name - name of interface being registered. |
| // completion_callback - a callback to be called when the asynchronous |
| // registration operation is completed. |
| CHROMEOS_PRIVATE void ExportAsync( |
| ExportedObjectManager* object_manager, |
| dbus::Bus* bus, |
| dbus::ExportedObject* exported_object, |
| const dbus::ObjectPath& object_path, |
| const AsyncEventSequencer::CompletionAction& completion_callback); |
| |
| // Method registration map. |
| std::map<std::string, std::unique_ptr<DBusInterfaceMethodHandlerInterface>> |
| handlers_; |
| // Signal registration map. |
| std::map<std::string, std::shared_ptr<DBusSignalBase>> signals_; |
| |
| friend class DBusObject; |
| friend class DBusInterfaceTestHelper; |
| DBusObject* dbus_object_; |
| std::string interface_name_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DBusInterface); |
| }; |
| |
| // A D-Bus object implementation class. Manages the interfaces implemented |
| // by this object. |
| class CHROMEOS_EXPORT DBusObject { |
| public: |
| // object_manager - ExportedObjectManager instance that notifies D-Bus |
| // listeners of a new interface being claimed and property |
| // changes on those interfaces. |
| // object_path - D-Bus object path for the object instance. |
| DBusObject(ExportedObjectManager* object_manager, |
| const scoped_refptr<dbus::Bus>& bus, |
| const dbus::ObjectPath& object_path); |
| virtual ~DBusObject(); |
| |
| // Returns an proxy handler for the interface |interface_name|. If the |
| // interface proxy does not exist yet, it will be automatically created. |
| DBusInterface* AddOrGetInterface(const std::string& interface_name); |
| |
| // Finds an interface with the given name. Returns nullptr if there is no |
| // interface registered by this name. |
| DBusInterface* FindInterface(const std::string& interface_name) const; |
| |
| // Registers the object instance with D-Bus. This is an asynchronous call |
| // that will call |completion_callback| when the object and all of its |
| // interfaces are registered. |
| virtual void RegisterAsync( |
| const AsyncEventSequencer::CompletionAction& completion_callback); |
| |
| // Returns the ExportedObjectManager proxy, if any. If DBusObject has been |
| // constructed without an object manager, this method returns an empty |
| // smart pointer (containing nullptr). |
| const base::WeakPtr<ExportedObjectManager>& GetObjectManager() const { |
| return object_manager_; |
| } |
| |
| // Sends a signal from the exported D-Bus object. |
| bool SendSignal(dbus::Signal* signal); |
| |
| private: |
| // A map of all the interfaces added to this object. |
| std::map<std::string, std::unique_ptr<DBusInterface>> interfaces_; |
| // Exported property set for properties registered with the interfaces |
| // implemented by this D-Bus object. |
| ExportedPropertySet property_set_; |
| // Delegate object implementing org.freedesktop.DBus.ObjectManager interface. |
| base::WeakPtr<ExportedObjectManager> object_manager_; |
| // D-Bus bus object. |
| scoped_refptr<dbus::Bus> bus_; |
| // D-Bus object path for this object. |
| dbus::ObjectPath object_path_; |
| // D-Bus object instance once this object is successfully exported. |
| dbus::ExportedObject* exported_object_ = nullptr; // weak; owned by |bus_|. |
| |
| friend class DBusInterface; |
| DISALLOW_COPY_AND_ASSIGN(DBusObject); |
| }; |
| |
| } // namespace dbus_utils |
| } // namespace chromeos |
| |
| #endif // LIBCHROMEOS_CHROMEOS_DBUS_DBUS_OBJECT_H_ |