diff options
Diffstat (limited to 'include/CommonAPI/DBus')
40 files changed, 5425 insertions, 0 deletions
diff --git a/include/CommonAPI/DBus/DBusAddress.hpp b/include/CommonAPI/DBus/DBusAddress.hpp new file mode 100644 index 0000000..926b11b --- /dev/null +++ b/include/CommonAPI/DBus/DBusAddress.hpp @@ -0,0 +1,53 @@ +// Copyright (C) 2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_ADDRESS_HPP_ +#define COMMONAPI_DBUS_ADDRESS_HPP_ + +#include <iostream> +#include <map> + +#include <CommonAPI/Export.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusAddress { +public: + COMMONAPI_EXPORT DBusAddress(const std::string &_service = "", + const std::string &_objectPath = "", + const std::string &_interface = ""); + COMMONAPI_EXPORT DBusAddress(const DBusAddress &_source); + COMMONAPI_EXPORT virtual ~DBusAddress(); + + COMMONAPI_EXPORT bool operator==(const DBusAddress &_other) const; + COMMONAPI_EXPORT bool operator!=(const DBusAddress &_other) const; + COMMONAPI_EXPORT bool operator<(const DBusAddress &_other) const; + + COMMONAPI_EXPORT const std::string &getInterface() const; + COMMONAPI_EXPORT void setInterface(const std::string &_interface); + + COMMONAPI_EXPORT const std::string &getObjectPath() const; + COMMONAPI_EXPORT void setObjectPath(const std::string &_objectPath); + + COMMONAPI_EXPORT const std::string &getService() const; + COMMONAPI_EXPORT void setService(const std::string &_service); + +private: + std::string service_; + std::string objectPath_; + std::string interface_; + +friend std::ostream &operator<<(std::ostream &_out, const DBusAddress &_dbusAddress); +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_ADDRESS_HPP_ diff --git a/include/CommonAPI/DBus/DBusAddressTranslator.hpp b/include/CommonAPI/DBus/DBusAddressTranslator.hpp new file mode 100644 index 0000000..6d950e9 --- /dev/null +++ b/include/CommonAPI/DBus/DBusAddressTranslator.hpp @@ -0,0 +1,61 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_ADDRESSTRANSLATOR_HPP_ +#define COMMONAPI_DBUS_ADDRESSTRANSLATOR_HPP_ + +#include <map> +#include <memory> +#include <mutex> + +#include <CommonAPI/Address.hpp> +#include <CommonAPI/DBus/DBusAddress.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusAddressTranslator { +public: + COMMONAPI_EXPORT static std::shared_ptr<DBusAddressTranslator> get(); + + COMMONAPI_EXPORT DBusAddressTranslator(); + + COMMONAPI_EXPORT void init(); + + COMMONAPI_EXPORT bool translate(const std::string &_key, DBusAddress &_value); + COMMONAPI_EXPORT bool translate(const CommonAPI::Address &_key, DBusAddress &_value); + + COMMONAPI_EXPORT bool translate(const DBusAddress &_key, std::string &_value); + COMMONAPI_EXPORT bool translate(const DBusAddress &_key, CommonAPI::Address &_value); + + COMMONAPI_EXPORT void insert(const std::string &_address, + const std::string &_service, const std::string &_path, const std::string &_interface); + +private: + COMMONAPI_EXPORT bool readConfiguration(); + + COMMONAPI_EXPORT bool isValid(const std::string &, const char, + bool = false, bool = false, bool = false) const; + +private: + bool isDefault_; + + std::string defaultConfig_; + std::string defaultDomain_; + + std::map<CommonAPI::Address, DBusAddress> forwards_; + std::map<DBusAddress, CommonAPI::Address> backwards_; + + std::mutex mutex_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_ADDRESSTRANSLATOR_HPP_ diff --git a/include/CommonAPI/DBus/DBusAttribute.hpp b/include/CommonAPI/DBus/DBusAttribute.hpp new file mode 100644 index 0000000..3d9170d --- /dev/null +++ b/include/CommonAPI/DBus/DBusAttribute.hpp @@ -0,0 +1,156 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUS_ATTRIBUTE_HPP_ +#define COMMONAPI_DBUS_DBUS_ATTRIBUTE_HPP_ + +#include <cassert> +#include <cstdint> +#include <tuple> + +#include <CommonAPI/DBus/DBusConfig.hpp> +#include <CommonAPI/DBus/DBusEvent.hpp> +#include <CommonAPI/DBus/DBusProxyHelper.hpp> + +namespace CommonAPI { +namespace DBus { + +template <typename _AttributeType, typename _AttributeDepl = EmptyDeployment> +class DBusReadonlyAttribute: public _AttributeType { +public: + typedef typename _AttributeType::ValueType ValueType; + typedef _AttributeDepl ValueTypeDepl; + typedef typename _AttributeType::AttributeAsyncCallback AttributeAsyncCallback; + + DBusReadonlyAttribute(DBusProxy &_proxy, + const char *setMethodSignature, const char *getMethodName, + _AttributeDepl *_depl = nullptr) + : proxy_(_proxy), + getMethodName_(getMethodName), + setMethodSignature_(setMethodSignature), + depl_(_depl) { + assert(getMethodName); + } + + void getValue(CommonAPI::CallStatus &_status, ValueType &_value, const CommonAPI::CallInfo *_info) const { + CommonAPI::Deployable<ValueType, _AttributeDepl> deployedValue(depl_); + DBusProxyHelper< + DBusSerializableArguments< + >, + DBusSerializableArguments< + CommonAPI::Deployable< + ValueType, + _AttributeDepl + > + > + >::callMethodWithReply(proxy_, getMethodName_, "", (_info ? _info : &defaultCallInfo), _status, deployedValue); + _value = deployedValue.getValue(); + } + + std::future<CallStatus> getValueAsync(AttributeAsyncCallback _callback, const CommonAPI::CallInfo *_info) { + CommonAPI::Deployable<ValueType, _AttributeDepl> deployedValue(depl_); + return DBusProxyHelper< + DBusSerializableArguments<>, + DBusSerializableArguments<CommonAPI::Deployable<ValueType, _AttributeDepl>> + >::callMethodAsync(proxy_, getMethodName_, "", (_info ? _info : &defaultCallInfo), + [_callback](CommonAPI::CallStatus _status, CommonAPI::Deployable<ValueType, _AttributeDepl> _response) { + _callback(_status, _response.getValue()); + }, + std::make_tuple(deployedValue)); + } + + protected: + DBusProxy &proxy_; + const char *getMethodName_; + const char *setMethodSignature_; + _AttributeDepl *depl_; +}; + +template <typename _AttributeType, typename _AttributeDepl = EmptyDeployment> +class DBusAttribute: public DBusReadonlyAttribute<_AttributeType, _AttributeDepl> { +public: + typedef typename _AttributeType::ValueType ValueType; + typedef typename _AttributeType::AttributeAsyncCallback AttributeAsyncCallback; + + DBusAttribute(DBusProxy &_proxy, + const char *_setMethodName, const char *_setMethodSignature, const char *_getMethodName, + _AttributeDepl *_depl = nullptr) + : DBusReadonlyAttribute<_AttributeType, _AttributeDepl>(_proxy, _setMethodSignature, _getMethodName, _depl), + setMethodName_(_setMethodName), + setMethodSignature_(_setMethodSignature) { + assert(_setMethodName); + assert(_setMethodSignature); + } + + void setValue(const ValueType &_request, CommonAPI::CallStatus &_status, ValueType &_response, const CommonAPI::CallInfo *_info) { + CommonAPI::Deployable<ValueType, _AttributeDepl> deployedRequest(_request, this->depl_); + CommonAPI::Deployable<ValueType, _AttributeDepl> deployedResponse(this->depl_); + DBusProxyHelper<DBusSerializableArguments<CommonAPI::Deployable<ValueType, _AttributeDepl>>, + DBusSerializableArguments<CommonAPI::Deployable<ValueType, _AttributeDepl>> >::callMethodWithReply( + this->proxy_, + setMethodName_, + setMethodSignature_, + (_info ? _info : &defaultCallInfo), + deployedRequest, + _status, + deployedResponse); + _response = deployedResponse.getValue(); + } + + + std::future<CallStatus> setValueAsync(const ValueType &_request, AttributeAsyncCallback _callback, const CommonAPI::CallInfo *_info) { + CommonAPI::Deployable<ValueType, _AttributeDepl> deployedRequest(_request, this->depl_); + CommonAPI::Deployable<ValueType, _AttributeDepl> deployedResponse(this->depl_); + return DBusProxyHelper<DBusSerializableArguments<CommonAPI::Deployable<ValueType, _AttributeDepl>>, + DBusSerializableArguments<CommonAPI::Deployable<ValueType, _AttributeDepl>> >::callMethodAsync( + this->proxy_, + setMethodName_, + setMethodSignature_, + (_info ? _info : &defaultCallInfo), + deployedRequest, + [_callback](CommonAPI::CallStatus _status, CommonAPI::Deployable<ValueType, _AttributeDepl> _response) { + _callback(_status, _response.getValue()); + }, + std::make_tuple(deployedResponse)); + } + + protected: + const char* setMethodName_; + const char* setMethodSignature_; +}; + +template <typename _AttributeType> +class DBusObservableAttribute: public _AttributeType { +public: + typedef typename _AttributeType::ValueType ValueType; + typedef typename _AttributeType::ValueTypeDepl ValueTypeDepl; + typedef typename _AttributeType::AttributeAsyncCallback AttributeAsyncCallback; + typedef typename _AttributeType::ChangedEvent ChangedEvent; + + template <typename... _AttributeTypeArguments> + DBusObservableAttribute(DBusProxy &_proxy, + const char *_changedEventName, + _AttributeTypeArguments... arguments) + : _AttributeType(_proxy, arguments...), + changedEvent_(_proxy, _changedEventName, this->setMethodSignature_, + std::make_tuple(CommonAPI::Deployable<ValueType, ValueTypeDepl>(this->depl_))) { + } + + ChangedEvent &getChangedEvent() { + return changedEvent_; + } + + protected: + DBusEvent<ChangedEvent, CommonAPI::Deployable<ValueType, ValueTypeDepl> > changedEvent_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUS_ATTRIBUTE_HPP_ diff --git a/include/CommonAPI/DBus/DBusClientId.hpp b/include/CommonAPI/DBus/DBusClientId.hpp new file mode 100644 index 0000000..72dc1c5 --- /dev/null +++ b/include/CommonAPI/DBus/DBusClientId.hpp @@ -0,0 +1,48 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSCLIENTID_HPP_ +#define COMMONAPI_DBUS_DBUSCLIENTID_HPP_ + +#include <CommonAPI/Export.hpp> +#include <CommonAPI/Types.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusMessage; + +/** + * \brief Implementation of CommonAPI::ClientId for DBus + * + * This class represents the DBus specific implementation of CommonAPI::ClientId. + * It internally uses a string to identify clients. This string is the unique sender id used by dbus. + */ +class DBusClientId + : public CommonAPI::ClientId { + friend struct std::hash<DBusClientId>; + +public: + COMMONAPI_EXPORT DBusClientId(std::string dbusId); + + COMMONAPI_EXPORT bool operator==(CommonAPI::ClientId& clientIdToCompare); + COMMONAPI_EXPORT bool operator==(DBusClientId& clientIdToCompare); + COMMONAPI_EXPORT size_t hashCode(); + + COMMONAPI_EXPORT const char * getDBusId(); + + COMMONAPI_EXPORT DBusMessage createMessage(const std::string objectPath, const std::string interfaceName, const std::string signalName) const; +protected: + std::string dbusId_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // DBUSCLIENTID_HPP_ diff --git a/include/CommonAPI/DBus/DBusConfig.hpp b/include/CommonAPI/DBus/DBusConfig.hpp new file mode 100644 index 0000000..dae6b7a --- /dev/null +++ b/include/CommonAPI/DBus/DBusConfig.hpp @@ -0,0 +1,24 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSCONFIG_HPP_ +#define COMMONAPI_DBUS_DBUSCONFIG_HPP_ + +#include <CommonAPI/CallInfo.hpp> + +namespace CommonAPI { +namespace DBus { + +static const Timeout_t DEFAULT_SEND_TIMEOUT_MS = 5000; +static CommonAPI::CallInfo defaultCallInfo(DEFAULT_SEND_TIMEOUT_MS); + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSCONFIG_HPP_ diff --git a/include/CommonAPI/DBus/DBusConnection.hpp b/include/CommonAPI/DBus/DBusConnection.hpp new file mode 100644 index 0000000..436cfab --- /dev/null +++ b/include/CommonAPI/DBus/DBusConnection.hpp @@ -0,0 +1,246 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUS_CONNECTION_HPP_ +#define COMMONAPI_DBUS_DBUS_CONNECTION_HPP_ + +#include <atomic> + +#include <dbus/dbus.h> + +#include <CommonAPI/DBus/DBusConfig.hpp> +#include <CommonAPI/DBus/DBusDaemonProxy.hpp> +#include <CommonAPI/DBus/DBusMainLoopContext.hpp> +#include <CommonAPI/DBus/DBusObjectManager.hpp> +#include <CommonAPI/DBus/DBusProxyConnection.hpp> +#include <CommonAPI/DBus/DBusServiceRegistry.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusObjectManager; + +class DBusConnectionStatusEvent + : public DBusProxyConnection::ConnectionStatusEvent { +public: + DBusConnectionStatusEvent(DBusConnection* dbusConnection); + virtual ~DBusConnectionStatusEvent() {} + + protected: + virtual void onListenerAdded(const Listener& listener); + + // TODO: change to std::weak_ptr<DBusConnection> connection_; + DBusConnection* dbusConnection_; + +friend class DBusConnection; +}; + +struct WatchContext { + WatchContext(std::weak_ptr<MainLoopContext> mainLoopContext, DispatchSource* dispatchSource) : + mainLoopContext_(mainLoopContext), dispatchSource_(dispatchSource) { + } + + std::weak_ptr<MainLoopContext> mainLoopContext_; + DispatchSource* dispatchSource_; +}; + +class DBusConnection + : public DBusProxyConnection, + public std::enable_shared_from_this<DBusConnection> { +public: + COMMONAPI_EXPORT static std::shared_ptr<DBusConnection> getBus(const DBusType_t &_type); + COMMONAPI_EXPORT static std::shared_ptr<DBusConnection> wrap(::DBusConnection *_connection); + + COMMONAPI_EXPORT DBusConnection(DBusType_t _type); + COMMONAPI_EXPORT DBusConnection(const DBusConnection&) = delete; + COMMONAPI_EXPORT DBusConnection(::DBusConnection* libDbusConnection); + COMMONAPI_EXPORT virtual ~DBusConnection(); + + COMMONAPI_EXPORT DBusConnection& operator=(const DBusConnection&) = delete; + + COMMONAPI_EXPORT DBusType_t getBusType() const; + + COMMONAPI_EXPORT bool connect(bool startDispatchThread = true); + COMMONAPI_EXPORT bool connect(DBusError& dbusError, bool startDispatchThread = true); + COMMONAPI_EXPORT void disconnect(); + + COMMONAPI_EXPORT virtual bool isConnected() const; + + COMMONAPI_EXPORT virtual ConnectionStatusEvent& getConnectionStatusEvent(); + + COMMONAPI_EXPORT virtual bool requestServiceNameAndBlock(const std::string& serviceName) const; + COMMONAPI_EXPORT virtual bool releaseServiceName(const std::string& serviceName) const; + + COMMONAPI_EXPORT bool sendDBusMessage(const DBusMessage& dbusMessage/*, uint32_t* allocatedSerial = NULL*/) const; + + COMMONAPI_EXPORT std::future<CallStatus> sendDBusMessageWithReplyAsync( + const DBusMessage& dbusMessage, + std::unique_ptr<DBusMessageReplyAsyncHandler> dbusMessageReplyAsyncHandler, + const CommonAPI::CallInfo *_info) const; + + COMMONAPI_EXPORT DBusMessage sendDBusMessageWithReplyAndBlock(const DBusMessage& dbusMessage, + DBusError& dbusError, + const CommonAPI::CallInfo *_info) const; + + COMMONAPI_EXPORT virtual bool addObjectManagerSignalMemberHandler(const std::string& dbusBusName, + DBusSignalHandler* dbusSignalHandler); + COMMONAPI_EXPORT virtual bool removeObjectManagerSignalMemberHandler(const std::string& dbusBusName, + DBusSignalHandler* dbusSignalHandler); + + COMMONAPI_EXPORT DBusSignalHandlerToken addSignalMemberHandler(const std::string& objectPath, + const std::string& interfaceName, + const std::string& interfaceMemberName, + const std::string& inuint32_tterfaceMemberSignature, + DBusSignalHandler* dbusSignalHandler, + const bool justAddFilter = false); + + COMMONAPI_EXPORT DBusProxyConnection::DBusSignalHandlerToken subscribeForSelectiveBroadcast(bool& subscriptionAccepted, + const std::string& objectPath, + const std::string& interfaceName, + const std::string& interfaceMemberName, + const std::string& interfaceMemberSignature, + DBusSignalHandler* dbusSignalHandler, + DBusProxy* callingProxy); + + COMMONAPI_EXPORT void unsubscribeFromSelectiveBroadcast(const std::string& eventName, + DBusProxyConnection::DBusSignalHandlerToken subscription, + DBusProxy* callingProxy, + const DBusSignalHandler* dbusSignalHandler); + + COMMONAPI_EXPORT void registerObjectPath(const std::string& objectPath); + COMMONAPI_EXPORT void unregisterObjectPath(const std::string& objectPath); + + COMMONAPI_EXPORT bool removeSignalMemberHandler(const DBusSignalHandlerToken& dbusSignalHandlerToken, + const DBusSignalHandler* dbusSignalHandler = NULL); + COMMONAPI_EXPORT bool readWriteDispatch(int timeoutMilliseconds = -1); + + COMMONAPI_EXPORT virtual const std::shared_ptr<DBusObjectManager> getDBusObjectManager(); + + COMMONAPI_EXPORT void setObjectPathMessageHandler(DBusObjectPathMessageHandler); + COMMONAPI_EXPORT bool isObjectPathMessageHandlerSet(); + + COMMONAPI_EXPORT virtual bool attachMainLoopContext(std::weak_ptr<MainLoopContext>); + + COMMONAPI_EXPORT bool isDispatchReady(); + COMMONAPI_EXPORT bool singleDispatch(); + + typedef std::tuple<std::string, std::string, std::string> DBusSignalMatchRuleTuple; + typedef std::pair<uint32_t, std::string> DBusSignalMatchRuleMapping; + typedef std::unordered_map<DBusSignalMatchRuleTuple, DBusSignalMatchRuleMapping> DBusSignalMatchRulesMap; + private: + COMMONAPI_EXPORT void dispatch(); + COMMONAPI_EXPORT void suspendDispatching() const; + COMMONAPI_EXPORT void resumeDispatching() const; + + std::thread* dispatchThread_; + bool stopDispatching_; + + std::weak_ptr<MainLoopContext> mainLoopContext_; + DispatchSource* dispatchSource_; + WatchContext* watchContext_; + + mutable std::recursive_mutex sendLock_; + mutable bool pauseDispatching_; + mutable std::mutex dispatchSuspendLock_; + + COMMONAPI_EXPORT void addLibdbusSignalMatchRule(const std::string& objectPath, + const std::string& interfaceName, + const std::string& interfaceMemberName, + const bool justAddFilter = false); + + COMMONAPI_EXPORT void removeLibdbusSignalMatchRule(const std::string& objectPath, + const std::string& interfaceName, + const std::string& interfaceMemberName); + + COMMONAPI_EXPORT void initLibdbusSignalFilterAfterConnect(); + ::DBusHandlerResult onLibdbusSignalFilter(::DBusMessage* libdbusMessage); + + COMMONAPI_EXPORT void initLibdbusObjectPathHandlerAfterConnect(); + ::DBusHandlerResult onLibdbusObjectPathMessage(::DBusMessage* libdbusMessage); + + COMMONAPI_EXPORT static void onLibdbusPendingCallNotifyThunk(::DBusPendingCall* libdbusPendingCall, void* userData); + COMMONAPI_EXPORT static void onLibdbusDataCleanup(void* userData); + + COMMONAPI_EXPORT static ::DBusHandlerResult onLibdbusObjectPathMessageThunk(::DBusConnection* libdbusConnection, + ::DBusMessage* libdbusMessage, + void* userData); + + COMMONAPI_EXPORT static ::DBusHandlerResult onLibdbusSignalFilterThunk(::DBusConnection* libdbusConnection, + ::DBusMessage* libdbusMessage, + void* userData); + + COMMONAPI_EXPORT static dbus_bool_t onAddWatch(::DBusWatch* libdbusWatch, void* data); + COMMONAPI_EXPORT static void onRemoveWatch(::DBusWatch* libdbusWatch, void* data); + COMMONAPI_EXPORT static void onToggleWatch(::DBusWatch* libdbusWatch, void* data); + + COMMONAPI_EXPORT static dbus_bool_t onAddTimeout(::DBusTimeout* dbus_timeout, void* data); + COMMONAPI_EXPORT static void onRemoveTimeout(::DBusTimeout* dbus_timeout, void* data); + COMMONAPI_EXPORT static void onToggleTimeout(::DBusTimeout* dbus_timeout, void* data); + + COMMONAPI_EXPORT static void onWakeupMainContext(void* data); + + COMMONAPI_EXPORT void enforceAsynchronousTimeouts() const; + COMMONAPI_EXPORT static const DBusObjectPathVTable* getDBusObjectPathVTable(); + + ::DBusConnection* connection_; + mutable std::mutex connectionGuard_; + + std::mutex signalGuard_; + std::mutex objectManagerGuard_; + std::mutex serviceRegistryGuard_; + + DBusType_t busType_; + + std::shared_ptr<DBusObjectManager> dbusObjectManager_; + + DBusConnectionStatusEvent dbusConnectionStatusEvent_; + + DBusSignalMatchRulesMap dbusSignalMatchRulesMap_; + + DBusSignalHandlerTable dbusSignalHandlerTable_; + + std::unordered_map<std::string, size_t> dbusObjectManagerSignalMatchRulesMap_; + std::unordered_multimap<std::string, DBusSignalHandler*> dbusObjectManagerSignalHandlerTable_; + std::mutex dbusObjectManagerSignalGuard_; + + COMMONAPI_EXPORT bool addObjectManagerSignalMatchRule(const std::string& dbusBusName); + COMMONAPI_EXPORT bool removeObjectManagerSignalMatchRule(const std::string& dbusBusName); + + COMMONAPI_EXPORT bool addLibdbusSignalMatchRule(const std::string& dbusMatchRule); + COMMONAPI_EXPORT bool removeLibdbusSignalMatchRule(const std::string& dbusMatchRule); + + std::atomic_size_t libdbusSignalMatchRulesCount_; + + // objectPath, referenceCount + typedef std::unordered_map<std::string, uint32_t> LibdbusRegisteredObjectPathHandlersTable; + LibdbusRegisteredObjectPathHandlersTable libdbusRegisteredObjectPaths_; + + DBusObjectPathMessageHandler dbusObjectMessageHandler_; + + mutable std::unordered_map<std::string, uint16_t> connectionNameCount_; + + typedef std::pair<DBusPendingCall*, std::tuple<int, DBusMessageReplyAsyncHandler*, DBusMessage> > TimeoutMapElement; + mutable std::map<DBusPendingCall*, std::tuple<int, DBusMessageReplyAsyncHandler*, DBusMessage>> timeoutMap_; + + typedef std::pair<DBusMessageReplyAsyncHandler *, DBusMessage> MainloopTimeout_t; + mutable std::list<MainloopTimeout_t> mainloopTimeouts_; + + mutable std::mutex enforceTimeoutMutex_; + mutable std::condition_variable enforceTimeoutCondition_; + + mutable std::shared_ptr<std::thread> enforcerThread_; + mutable std::mutex enforcerThreadMutex_; + bool enforcerThreadCancelled_; +}; + + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUS_CONNECTION_HPP_ diff --git a/include/CommonAPI/DBus/DBusDaemonProxy.hpp b/include/CommonAPI/DBus/DBusDaemonProxy.hpp new file mode 100644 index 0000000..1333548 --- /dev/null +++ b/include/CommonAPI/DBus/DBusDaemonProxy.hpp @@ -0,0 +1,94 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUS_DAEMON_PROXY_HPP_ +#define COMMONAPI_DBUS_DBUS_DAEMON_PROXY_HPP_ + +#include <functional> +#include <string> +#include <vector> + +#include <CommonAPI/Address.hpp> + +#include <CommonAPI/DBus/DBusAttribute.hpp> +#include <CommonAPI/DBus/DBusEvent.hpp> +#include <CommonAPI/DBus/DBusProxyBase.hpp> + +namespace CommonAPI { +namespace DBus { + +class StaticInterfaceVersionAttribute: public InterfaceVersionAttribute { + public: + StaticInterfaceVersionAttribute(const uint32_t& majorValue, const uint32_t& minorValue); + + void getValue(CommonAPI::CallStatus& callStatus, Version &_version, const CommonAPI::CallInfo *_info) const; + std::future<CommonAPI::CallStatus> getValueAsync(AttributeAsyncCallback _callback, const CommonAPI::CallInfo *_info); + + private: + Version version_; +}; + + +class DBusDaemonProxy : public DBusProxyBase { + public: + typedef Event<std::string, std::string, std::string> NameOwnerChangedEvent; + + typedef std::unordered_map<std::string, int> PropertyDictStub; + typedef std::unordered_map<std::string, PropertyDictStub> InterfaceToPropertyDict; + typedef std::unordered_map<std::string, InterfaceToPropertyDict> DBusObjectToInterfaceDict; + + typedef std::function<void(const CommonAPI::CallStatus&, std::vector<std::string>)> ListNamesAsyncCallback; + typedef std::function<void(const CommonAPI::CallStatus&, bool)> NameHasOwnerAsyncCallback; + typedef std::function<void(const CommonAPI::CallStatus&, DBusObjectToInterfaceDict)> GetManagedObjectsAsyncCallback; + typedef std::function<void(const CommonAPI::CallStatus&, std::string)> GetNameOwnerAsyncCallback; + + COMMONAPI_EXPORT DBusDaemonProxy(const std::shared_ptr<DBusProxyConnection>& dbusConnection); + COMMONAPI_EXPORT virtual ~DBusDaemonProxy() {} + + COMMONAPI_EXPORT virtual bool isAvailable() const; + COMMONAPI_EXPORT virtual bool isAvailableBlocking() const; + COMMONAPI_EXPORT virtual ProxyStatusEvent& getProxyStatusEvent(); + + COMMONAPI_EXPORT virtual InterfaceVersionAttribute& getInterfaceVersionAttribute(); + + COMMONAPI_EXPORT void init(); + + COMMONAPI_EXPORT static const char* getInterfaceId(); + + COMMONAPI_EXPORT NameOwnerChangedEvent& getNameOwnerChangedEvent(); + + COMMONAPI_EXPORT void listNames(CommonAPI::CallStatus& callStatus, std::vector<std::string>& busNames) const; + COMMONAPI_EXPORT std::future<CallStatus> listNamesAsync(ListNamesAsyncCallback listNamesAsyncCallback) const; + + COMMONAPI_EXPORT void nameHasOwner(const std::string& busName, CommonAPI::CallStatus& callStatus, bool& hasOwner) const; + COMMONAPI_EXPORT std::future<CallStatus> nameHasOwnerAsync(const std::string& busName, + NameHasOwnerAsyncCallback nameHasOwnerAsyncCallback) const; + + COMMONAPI_EXPORT std::future<CallStatus> getManagedObjectsAsync(const std::string& forDBusServiceName, + GetManagedObjectsAsyncCallback) const; + + /** + * Get the unique connection/bus name of the primary owner of the name given + * + * @param busName Name to get the owner of + * @param getNameOwnerAsyncCallback callback functor + * + * @return CallStatus::REMOTE_ERROR if the name is unknown, otherwise CallStatus::SUCCESS and the uniq name of the owner + */ + std::future<CallStatus> getNameOwnerAsync(const std::string& busName, GetNameOwnerAsyncCallback getNameOwnerAsyncCallback) const; + + private: + DBusEvent<NameOwnerChangedEvent, std::string, std::string, std::string> nameOwnerChangedEvent_; + StaticInterfaceVersionAttribute interfaceVersionAttribute_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUS_DAEMON_PROXY_HPP_ diff --git a/include/CommonAPI/DBus/DBusDeployment.hpp b/include/CommonAPI/DBus/DBusDeployment.hpp new file mode 100644 index 0000000..21ad78a --- /dev/null +++ b/include/CommonAPI/DBus/DBusDeployment.hpp @@ -0,0 +1,37 @@ +// Copyright (C) 2014-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSDEPLOYMENTS_HPP_ +#define COMMONAPI_DBUS_DBUSDEPLOYMENTS_HPP_ + +#include <string> +#include <unordered_map> + +#include <CommonAPI/Deployment.hpp> +#include <CommonAPI/Export.hpp> + +namespace CommonAPI { +namespace DBus { + +template<typename... _Types> +struct VariantDeployment : CommonAPI::Deployment<_Types...> { + VariantDeployment(bool _isFreeDesktop, _Types*... _t) + : CommonAPI::Deployment<_Types...>(_t...), + isFreeDesktop_(_isFreeDesktop) { + } + + bool isFreeDesktop_; +}; + +extern COMMONAPI_IMPORT_EXPORT VariantDeployment<> freedesktopVariant; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSDEPLOYMENTS_HPP_ diff --git a/include/CommonAPI/DBus/DBusError.hpp b/include/CommonAPI/DBus/DBusError.hpp new file mode 100644 index 0000000..6e8f170 --- /dev/null +++ b/include/CommonAPI/DBus/DBusError.hpp @@ -0,0 +1,45 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUS_ERROR_HPP_ +#define COMMONAPI_DBUS_DBUS_ERROR_HPP_ + +#include <string> +#include <dbus/dbus.h> + +#include <CommonAPI/Export.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusConnection; + + +class COMMONAPI_EXPORT DBusError { + public: + DBusError(); + ~DBusError(); + + operator bool() const; + + void clear(); + + std::string getName() const; + std::string getMessage() const; + + private: + ::DBusError libdbusError_; + + friend class DBusConnection; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUS_ERROR_HPP_ diff --git a/include/CommonAPI/DBus/DBusEvent.hpp b/include/CommonAPI/DBus/DBusEvent.hpp new file mode 100644 index 0000000..a3bfe01 --- /dev/null +++ b/include/CommonAPI/DBus/DBusEvent.hpp @@ -0,0 +1,91 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUS_EVENT_HPP_ +#define COMMONAPI_DBUS_DBUS_EVENT_HPP_ + +#include <CommonAPI/Event.hpp> +#include <CommonAPI/DBus/DBusAddress.hpp> +#include <CommonAPI/DBus/DBusHelper.hpp> +#include <CommonAPI/DBus/DBusMessage.hpp> +#include <CommonAPI/DBus/DBusProxyBase.hpp> +#include <CommonAPI/DBus/DBusProxyConnection.hpp> +#include <CommonAPI/DBus/DBusSerializableArguments.hpp> + +namespace CommonAPI { +namespace DBus { + +template <typename _Event, typename... _Arguments> +class DBusEvent: public _Event, public DBusProxyConnection::DBusSignalHandler { +public: + typedef typename _Event::Listener Listener; + + DBusEvent(DBusProxyBase &_proxy, + const std::string &_name, const std::string &_signature, + std::tuple<_Arguments...> _arguments) + : proxy_(_proxy), + name_(_name), signature_(_signature), + arguments_(_arguments) { + + interface_ = proxy_.getDBusAddress().getInterface(); + path_ = proxy_.getDBusAddress().getObjectPath(); + } + + DBusEvent(DBusProxyBase &_proxy, + const std::string &_name, const std::string &_signature, + const std::string &_path, const std::string &_interface, + std::tuple<_Arguments...> _arguments) + : proxy_(_proxy), + name_(_name), signature_(_signature), + path_(_path), interface_(_interface), + arguments_(_arguments) { + } + + virtual ~DBusEvent() { + proxy_.removeSignalMemberHandler(subscription_, this); + } + + virtual void onSignalDBusMessage(const DBusMessage &_message) { + handleSignalDBusMessage(_message, typename make_sequence<sizeof...(_Arguments)>::type()); + } + protected: + virtual void onFirstListenerAdded(const Listener&) { + subscription_ = proxy_.addSignalMemberHandler( + path_, interface_, name_, signature_, this); + } + + virtual void onLastListenerRemoved(const Listener&) { + proxy_.removeSignalMemberHandler(subscription_, this); + } + + template<int ... _Indices> + inline void handleSignalDBusMessage(const DBusMessage &_message, index_sequence<_Indices...>) { + DBusInputStream input(_message); + if (DBusSerializableArguments< + _Arguments... + >::deserialize(input, std::get<_Indices>(arguments_)...)) { + this->notifyListeners(std::get<_Indices>(arguments_)...); + } + } + + DBusProxyBase &proxy_; + + std::string name_; + std::string signature_; + std::string path_; + std::string interface_; + + DBusProxyConnection::DBusSignalHandlerToken subscription_; + std::tuple<_Arguments...> arguments_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUS_EVENT_HPP_ diff --git a/include/CommonAPI/DBus/DBusFactory.hpp b/include/CommonAPI/DBus/DBusFactory.hpp new file mode 100644 index 0000000..fa0808b --- /dev/null +++ b/include/CommonAPI/DBus/DBusFactory.hpp @@ -0,0 +1,114 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_FACTORY_HPP_ +#define COMMONAPI_DBUS_FACTORY_HPP_ + +#include <map> + +#include <CommonAPI/Export.hpp> +#include <CommonAPI/Factory.hpp> +#include <CommonAPI/DBus/DBusTypes.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusAddress; +class DBusProxy; +class DBusProxyConnection; +class DBusStubAdapter; + +typedef std::shared_ptr<DBusProxy> +(*ProxyCreateFunction)(const DBusAddress &_address, + const std::shared_ptr<DBusProxyConnection> &_connection); + +typedef std::shared_ptr<DBusStubAdapter> +(*StubAdapterCreateFunction) (const DBusAddress &_address, + const std::shared_ptr<DBusProxyConnection> &_connection, + const std::shared_ptr<StubBase> &_stub); + +class Factory : public CommonAPI::Factory { +public: + COMMONAPI_EXPORT static std::shared_ptr<Factory> get(); + + COMMONAPI_EXPORT Factory(); + COMMONAPI_EXPORT virtual ~Factory(); + + COMMONAPI_EXPORT void registerProxyCreateMethod(const std::string &_address, + ProxyCreateFunction _function); + + COMMONAPI_EXPORT void registerStubAdapterCreateMethod(const std::string &_address, + StubAdapterCreateFunction _function); + + + COMMONAPI_EXPORT std::shared_ptr<Proxy> createProxy(const std::string &_domain, + const std::string &_interface, + const std::string &_instance, + const ConnectionId_t &_connectionId); + + COMMONAPI_EXPORT std::shared_ptr<Proxy> createProxy(const std::string &_domain, + const std::string &_interface, + const std::string &_instance, + std::shared_ptr<MainLoopContext> _context); + + COMMONAPI_EXPORT bool registerStub(const std::string &_domain, + const std::string &_interface, + const std::string &_instance, + std::shared_ptr<StubBase> _stub, + const ConnectionId_t &_connectionId); + + COMMONAPI_EXPORT bool registerStub(const std::string &_domain, + const std::string &_interface, + const std::string &_instance, + std::shared_ptr<StubBase> _stub, + std::shared_ptr<MainLoopContext> _context); + + COMMONAPI_EXPORT bool unregisterStub(const std::string &_domain, + const std::string &_interface, + const std::string &_instance); + + // Services + COMMONAPI_EXPORT std::shared_ptr<DBusStubAdapter> getRegisteredService(const std::string &_address); + + // Managed services + COMMONAPI_EXPORT std::shared_ptr<DBusStubAdapter> createDBusStubAdapter(const std::shared_ptr<StubBase> &_stub, + const std::string &_interface, + const DBusAddress &_address, + const std::shared_ptr<DBusProxyConnection> &_connection); + COMMONAPI_EXPORT bool registerManagedService(const std::shared_ptr<DBusStubAdapter> &_adapter); + COMMONAPI_EXPORT bool unregisterManagedService(const std::string &_address); + +private: + COMMONAPI_EXPORT std::shared_ptr<DBusConnection> getConnection(const ConnectionId_t &); + COMMONAPI_EXPORT std::shared_ptr<DBusConnection> getConnection(std::shared_ptr<MainLoopContext>); + COMMONAPI_EXPORT bool registerStubAdapter(std::shared_ptr<DBusStubAdapter>); + COMMONAPI_EXPORT bool unregisterStubAdapter(std::shared_ptr<DBusStubAdapter>); + + // Managed services + typedef std::unordered_map<std::string, std::shared_ptr<DBusStubAdapter>> ServicesMap; + COMMONAPI_EXPORT bool unregisterManagedService(const ServicesMap::iterator &); + +private: + static std::shared_ptr<Factory> theFactory; + + std::map<ConnectionId_t, std::shared_ptr<DBusConnection>> connections_; + std::map<MainLoopContext *, std::shared_ptr<DBusConnection>> contextConnections_; + + std::map<std::string, ProxyCreateFunction> proxyCreateFunctions_; + std::map<std::string, StubAdapterCreateFunction> stubAdapterCreateFunctions_; + + ServicesMap services_; + + DBusType_t defaultBusType_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_FACTORY_HPP_ diff --git a/include/CommonAPI/DBus/DBusFreedesktopAttribute.hpp b/include/CommonAPI/DBus/DBusFreedesktopAttribute.hpp new file mode 100644 index 0000000..dc485cb --- /dev/null +++ b/include/CommonAPI/DBus/DBusFreedesktopAttribute.hpp @@ -0,0 +1,451 @@ +// Copyright (C) 2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUS_FREEDESKTOPATTRIBUTE_HPP_ +#define COMMONAPI_DBUS_DBUS_FREEDESKTOPATTRIBUTE_HPP_ + +#include <CommonAPI/DBus/DBusDeployment.hpp> + +namespace CommonAPI { +namespace DBus { + +template <typename _AttributeType> +class DBusFreedesktopReadonlyAttribute: public _AttributeType { +public: + typedef typename _AttributeType::ValueType ValueType; + typedef typename _AttributeType::AttributeAsyncCallback AttributeAsyncCallback; + + DBusFreedesktopReadonlyAttribute(DBusProxy &_proxy, const std::string &_interfaceName, const std::string &_propertyName) + : proxy_(_proxy), + interfaceName_(_interfaceName), + propertyName_(_propertyName) { + } + + void getValue(CommonAPI::CallStatus &_status, ValueType &_value, const CommonAPI::CallInfo *_info) const { + CommonAPI::Deployable<Variant<ValueType>, VariantDeployment<>> deployedValue(&freedesktopVariant); + DBusProxyHelper< + DBusSerializableArguments< + std::string, std::string + >, + DBusSerializableArguments< + CommonAPI::Deployable<Variant<ValueType>, VariantDeployment<>> + > + >::callMethodWithReply( + proxy_, + "org.freedesktop.DBus.Properties", + "Get", + "ss", + (_info ? _info : &defaultCallInfo), + interfaceName_, + propertyName_, + _status, + deployedValue); + + _value = deployedValue.getValue().template get<ValueType>(); + } + + std::future<CallStatus> getValueAsync(AttributeAsyncCallback _callback, const CommonAPI::CallInfo *_info) { + CommonAPI::Deployable<Variant<ValueType>, VariantDeployment<>> deployedValue(&freedesktopVariant); + return DBusProxyHelper< + DBusSerializableArguments< + std::string, std::string + >, + DBusSerializableArguments< + CommonAPI::Deployable<Variant<ValueType>, VariantDeployment<>> + > + >::callMethodAsync( + proxy_, + "org.freedesktop.DBus.Properties", + "Get", + "ss", + (_info ? _info : &defaultCallInfo), + interfaceName_, + propertyName_, + [_callback](CommonAPI::CallStatus _status, CommonAPI::Deployable<Variant<ValueType>, VariantDeployment<>> _value) { + _callback(_status, _value.getValue().template get<ValueType>()); + }, + std::make_tuple(deployedValue) + ); + } + +protected: + DBusProxy &proxy_; + std::string interfaceName_; + std::string propertyName_; +}; + +template <typename _AttributeType> +class DBusFreedesktopUnionReadonlyAttribute: public _AttributeType { +public: + typedef typename _AttributeType::ValueType ValueType; + typedef typename _AttributeType::AttributeAsyncCallback AttributeAsyncCallback; + + DBusFreedesktopUnionReadonlyAttribute(DBusProxy &_proxy, const std::string &_interfaceName, const std::string &_propertyName) + : proxy_(_proxy), + interfaceName_(_interfaceName), + propertyName_(_propertyName) { + } + + void getValue(CommonAPI::CallStatus &_status, ValueType &_value, const CommonAPI::CallInfo *_info) const { + CommonAPI::Deployable<ValueType, VariantDeployment<>> deployedValue(&freedesktopVariant); + DBusProxyHelper< + DBusSerializableArguments< + std::string, std::string + >, + DBusSerializableArguments< + CommonAPI::Deployable<ValueType, VariantDeployment<>> + > + >::callMethodWithReply( + proxy_, + "org.freedesktop.DBus.Properties", + "Get", + "ss", + (_info ? _info : &defaultCallInfo), + interfaceName_, + propertyName_, + _status, + deployedValue); + + _value = deployedValue.getValue().template get<ValueType>(); + } + + std::future<CommonAPI::CallStatus> getValueAsync(AttributeAsyncCallback _callback, const CommonAPI::CallInfo *_info) { + CommonAPI::Deployable<ValueType, VariantDeployment<>> deployedValue(&freedesktopVariant); + return DBusProxyHelper< + DBusSerializableArguments< + std::string, std::string + >, + DBusSerializableArguments< + CommonAPI::Deployable<ValueType, VariantDeployment<>> + > + >::callMethodAsync( + proxy_, + "org.freedesktop.DBus.Properties", + "Get", + "ss", + (_info ? _info : &defaultCallInfo), + interfaceName_, + propertyName_, + [_callback](CommonAPI::CallStatus _status, CommonAPI::Deployable<ValueType, VariantDeployment<>> _value) { + _callback(_status, _value.getValue().template get<ValueType>()); + }, + std::make_tuple(deployedValue) + ); + } + +protected: + DBusProxy &proxy_; + std::string interfaceName_; + std::string propertyName_; +}; + +template <typename _AttributeType> +class DBusFreedesktopAttribute + : public DBusFreedesktopReadonlyAttribute<_AttributeType> { + public: + typedef typename _AttributeType::ValueType ValueType; + typedef typename _AttributeType::AttributeAsyncCallback AttributeAsyncCallback; + typedef typename _AttributeType::ChangedEvent ChangedEvent; + + DBusFreedesktopAttribute(DBusProxy &_proxy, const std::string &_interfaceName, const std::string &_propertyName) + : DBusFreedesktopReadonlyAttribute<_AttributeType>(_proxy, _interfaceName, _propertyName) { + } + + void setValue(const ValueType &_request, CommonAPI::CallStatus &_status, ValueType &_response, const CommonAPI::CallInfo *_info) { + CommonAPI::Deployable<Variant<ValueType>, VariantDeployment<>> deployedVariant(_request, &freedesktopVariant); + DBusProxyHelper< + DBusSerializableArguments< + std::string, std::string, CommonAPI::Deployable<Variant<ValueType>, VariantDeployment<>> + >, + DBusSerializableArguments< + > + >::callMethodWithReply( + DBusFreedesktopReadonlyAttribute<_AttributeType>::proxy_, + "org.freedesktop.DBus.Properties", + "Set", + "ssv", + (_info ? _info : &defaultCallInfo), + DBusFreedesktopReadonlyAttribute<_AttributeType>::interfaceName_, + DBusFreedesktopReadonlyAttribute<_AttributeType>::propertyName_, + deployedVariant, + _status); + _response = _request; + } + + std::future<CommonAPI::CallStatus> setValueAsync(const ValueType &_request, AttributeAsyncCallback _callback, const CommonAPI::CallInfo *_info) { + CommonAPI::Deployable<Variant<ValueType>, VariantDeployment<>> deployedVariant(_request, &freedesktopVariant); + return DBusProxyHelper< + DBusSerializableArguments< + std::string, std::string, CommonAPI::Deployable<Variant<ValueType>, VariantDeployment<>> + >, + DBusSerializableArguments< + > + >::callMethodAsync( + DBusFreedesktopReadonlyAttribute<_AttributeType>::proxy_, + "org.freedesktop.DBus.Properties", + "Set", + "ssv", + (_info ? _info : &defaultCallInfo), + DBusFreedesktopReadonlyAttribute<_AttributeType>::interfaceName_, + DBusFreedesktopReadonlyAttribute<_AttributeType>::propertyName_, + deployedVariant, + [_callback, deployedVariant](CommonAPI::CallStatus _status) { + _callback(_status, deployedVariant.getValue().template get<ValueType>()); + }, + std::tuple<>()); + } +}; + +template <typename _AttributeType> +class DBusFreedesktopUnionAttribute + : public DBusFreedesktopReadonlyAttribute<_AttributeType> { + public: + typedef typename _AttributeType::ValueType ValueType; + typedef typename _AttributeType::AttributeAsyncCallback AttributeAsyncCallback; + + DBusFreedesktopUnionAttribute(DBusProxy &_proxy, const std::string &_interfaceName, const std::string &_propertyName) + : DBusFreedesktopUnionReadonlyAttribute<_AttributeType>(_proxy, _interfaceName, _propertyName) { + } + + void setValue(const ValueType &_request, CommonAPI::CallStatus &_status, ValueType &_response, const CommonAPI::CallInfo *_info) { + CommonAPI::Deployable<ValueType, VariantDeployment<>> deployedVariant(_request, &freedesktopVariant); + DBusProxyHelper< + DBusSerializableArguments< + std::string, std::string, CommonAPI::Deployable<ValueType, VariantDeployment<>> + >, + DBusSerializableArguments< + > + >::callMethodWithReply( + DBusFreedesktopReadonlyAttribute<_AttributeType>::proxy_, + "org.freedesktop.DBus.Properties", + "Set", + "ssv", + (_info ? _info : &defaultCallInfo), + DBusFreedesktopReadonlyAttribute<_AttributeType>::interfaceName_, + DBusFreedesktopReadonlyAttribute<_AttributeType>::propertyName_, + deployedVariant, + _status); + _response = _request; + } + + std::future<CallStatus> setValueAsync(const ValueType &_request, AttributeAsyncCallback _callback, const CommonAPI::CallInfo *_info) { + CommonAPI::Deployable<ValueType, VariantDeployment<>> deployedVariant(_request, &freedesktopVariant); + return DBusProxyHelper< + DBusSerializableArguments< + std::string, std::string, CommonAPI::Deployable<ValueType, VariantDeployment<>> + >, + DBusSerializableArguments< + > + >::callMethodAsync( + DBusFreedesktopReadonlyAttribute<_AttributeType>::proxy_, + "org.freedesktop.DBus.Properties", + "Set", + "ssv", + (_info ? _info : &defaultCallInfo), + DBusFreedesktopReadonlyAttribute<_AttributeType>::interfaceName_, + DBusFreedesktopReadonlyAttribute<_AttributeType>::propertyName_, + deployedVariant, + [_callback](CommonAPI::CallStatus _status, CommonAPI::Deployable<ValueType, VariantDeployment<>> _value) { + _callback(_status, _value.getValue().template get<ValueType>()); + }, + std::make_tuple(deployedVariant)); + } +}; + +template<class, class> +class LegacyEvent; + +template <template <class...> class _Type, class _Types, class _Variant> +class LegacyEvent<_Type<_Types>, _Variant>: public _Type<_Types> { +public: + typedef _Types ValueType; + typedef typename _Type<ValueType>::Listener Listener; + typedef std::unordered_map<std::string, _Variant> PropertyMap; + typedef MapDeployment<EmptyDeployment, VariantDeployment<>> PropertyMapDeployment; + typedef Deployable<PropertyMap, PropertyMapDeployment> DeployedPropertyMap; + typedef std::vector<std::string> InvalidArray; + typedef Event<std::string, DeployedPropertyMap, InvalidArray> SignalEvent; + + LegacyEvent(DBusProxy &_proxy, const std::string &_interfaceName, const std::string &_propertyName) + : interfaceName_(_interfaceName), + propertyName_(_propertyName), + isSubcriptionSet_(false), + internalEvent_(_proxy, + "PropertiesChanged", + "sa{sv}as", + _proxy.getDBusAddress().getObjectPath(), + "org.freedesktop.DBus.Properties", + std::make_tuple("", getDeployedMap(), InvalidArray())) { + } + +protected: + void onFirstListenerAdded(const Listener &) { + if (!isSubcriptionSet_) { + subscription_ = internalEvent_.subscribe( + [this](const std::string &_interfaceName, + const PropertyMap &_properties, + const InvalidArray &_invalid) { + if (interfaceName_ == _interfaceName) { + auto iter = _properties.find(propertyName_); + if (iter != _properties.end()) { + const ValueType &value = iter->second.template get<ValueType>(); + this->notifyListeners(value); + } + } + }); + + isSubcriptionSet_ = true; + } + } + + void onLastListenerRemoved(const Listener &) { + if (isSubcriptionSet_) { + internalEvent_.unsubscribe(subscription_); + isSubcriptionSet_ = false; + } + } + + std::string interfaceName_; + std::string propertyName_; + + typename DBusEvent<SignalEvent, std::string, DeployedPropertyMap, InvalidArray>::Subscription subscription_; + bool isSubcriptionSet_; + + DBusEvent<SignalEvent, std::string, DeployedPropertyMap, InvalidArray> internalEvent_; + +private: + static DeployedPropertyMap &getDeployedMap() { + static PropertyMapDeployment itsDeployment(nullptr, &freedesktopVariant); + static DeployedPropertyMap itsDeployedMap(&itsDeployment); + return itsDeployedMap; + } +}; + +template <typename _AttributeType, typename _Variant> +class DBusFreedesktopObservableAttribute: public _AttributeType { + public: + typedef typename _AttributeType::ValueType ValueType; + typedef typename _AttributeType::AttributeAsyncCallback AttributeAsyncCallback; + typedef typename _AttributeType::ChangedEvent ChangedEvent; + + template <typename... _AttributeTypeArguments> + DBusFreedesktopObservableAttribute(DBusProxy &_proxy, + const std::string &_interfaceName, + const std::string &_propertyName, + _AttributeTypeArguments... _arguments) + : _AttributeType(_proxy, _interfaceName, _propertyName, _arguments...), + interfaceName_(_interfaceName), + propertyName_(_propertyName), + externalChangedEvent_(_proxy, _interfaceName, _propertyName) { + } + + ChangedEvent &getChangedEvent() { + return externalChangedEvent_; + } + + protected: + std::string interfaceName_; + std::string propertyName_; + LegacyEvent<ChangedEvent, _Variant> externalChangedEvent_; +}; + +template<class, class> +class LegacyUnionEvent; + +template <template <class...> class _Type, class _Types, class _Variant> +class LegacyUnionEvent<_Type<_Types>, _Variant>: public _Type<_Types> { +public: + typedef _Types ValueType; + typedef typename _Type<ValueType>::Listener Listener; + typedef std::unordered_map<std::string, _Variant> PropertyMap; + typedef MapDeployment<EmptyDeployment, VariantDeployment<>> PropertyMapDeployment; + typedef CommonAPI::Deployable<PropertyMap, PropertyMapDeployment> DeployedPropertyMap; + typedef std::vector<std::string> InvalidArray; + typedef Event<std::string, DeployedPropertyMap, InvalidArray> SignalEvent; + + LegacyUnionEvent(DBusProxy &_proxy, const std::string &_interfaceName, const std::string &_propertyName) + : interfaceName_(_interfaceName), + propertyName_(_propertyName), + isSubcriptionSet_(false), + internalEvent_(_proxy, + "PropertiesChanged", + "sa{sv}as", + _proxy.getDBusAddress().getObjectPath(), + "org.freedesktop.DBus.Properties", + std::make_tuple("", getDeployedMap(), InvalidArray())) { + } + +protected: + void onFirstListenerAdded(const Listener &) { + if (isSubcriptionSet_) { + subscription_ = internalEvent_.subscribe( + [this](const std::string &_interfaceName, + const PropertyMap &_properties, + const std::vector<std::string> &_invalid) { + if (interfaceName_ == _interfaceName) { + auto iter = _properties.find(propertyName_); + if (iter != _properties.end()) { + this->notifyListeners(iter->second.template get<ValueType>()); + } + } + }); + isSubcriptionSet_ = true; + } + } + + void onLastListenerRemoved(const Listener &) { + if (isSubcriptionSet_) { + internalEvent_.unsubscribe(subscription_); + isSubcriptionSet_ = false; + } + } + + DBusEvent<SignalEvent, ValueType> internalEvent_; + std::string interfaceName_; + std::string propertyName_; + + typename DBusEvent<SignalEvent>::Subscription subscription_; + bool isSubcriptionSet_; + +private: + static DeployedPropertyMap &getDeployedMap() { + static PropertyMapDeployment itsDeployment(nullptr, &freedesktopVariant); + static DeployedPropertyMap itsDeployedMap(&itsDeployment); + return itsDeployedMap; + } +}; + +template <typename _AttributeType, typename _Variant> +class DBusFreedesktopUnionObservableAttribute: public _AttributeType { + public: + typedef typename _AttributeType::ValueType ValueType; + typedef typename _AttributeType::AttributeAsyncCallback AttributeAsyncCallback; + typedef typename _AttributeType::ChangedEvent ChangedEvent; + + template <typename... _AttributeTypeArguments> + DBusFreedesktopUnionObservableAttribute(DBusProxy &_proxy, + const std::string &_interfaceName, + const std::string &_propertyName, + _AttributeTypeArguments... _arguments) + : _AttributeType(_proxy, _interfaceName, _propertyName, _arguments...), + externalChangedEvent_(_proxy, _interfaceName, _propertyName) { + } + + ChangedEvent &getChangedEvent() { + return externalChangedEvent_; + } + + protected: + LegacyUnionEvent<ChangedEvent, _Variant> externalChangedEvent_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUS_FREEDESKTOPATTRIBUTE_HPP_ diff --git a/include/CommonAPI/DBus/DBusFreedesktopPropertiesStub.hpp b/include/CommonAPI/DBus/DBusFreedesktopPropertiesStub.hpp new file mode 100644 index 0000000..f7e2cdf --- /dev/null +++ b/include/CommonAPI/DBus/DBusFreedesktopPropertiesStub.hpp @@ -0,0 +1,59 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUS_FREEDESKTOPPROPERTIESSTUB_HPP_ +#define COMMONAPI_DBUS_DBUS_FREEDESKTOPPROPERTIESSTUB_HPP_ + +#include <memory> +#include <mutex> +#include <string> + +#include <CommonAPI/DBus/DBusInterfaceHandler.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusStubAdapter; + +/** + * Stub for standard <a href="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties">org.freedesktop.dbus.Properties</a> interface. + * + * DBusFreedesktopPropertiesStub gets the DBusStubAdapter for handling the actual properties with instantiation. + */ +class DBusFreedesktopPropertiesStub: public DBusInterfaceHandler { +public: + DBusFreedesktopPropertiesStub(const std::string &_path, + const std::string &_interface, + const std::shared_ptr<DBusProxyConnection> &_connection, + const std::shared_ptr<DBusStubAdapter> &_adapter); + + virtual ~DBusFreedesktopPropertiesStub(); + + const std::string &getObjectPath() const; + static const std::string &getInterface(); + + virtual const char* getMethodsDBusIntrospectionXmlData() const; + virtual bool onInterfaceDBusMessage(const DBusMessage &_message); + virtual const bool hasFreedesktopProperties(); + +private: + std::string path_; + std::weak_ptr<DBusProxyConnection> connection_; + std::shared_ptr<DBusStubAdapter> adapter_; + + typedef std::unordered_map<std::string, std::shared_ptr<DBusStubAdapter>> DBusInterfacesMap; + DBusInterfacesMap managedInterfaces_; + + std::mutex dbusInterfacesLock_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUS_FREEDESKTOPPROPERTIESSTUB_HPP_ diff --git a/include/CommonAPI/DBus/DBusFreedesktopStubAdapterHelper.hpp b/include/CommonAPI/DBus/DBusFreedesktopStubAdapterHelper.hpp new file mode 100644 index 0000000..5c0bffa --- /dev/null +++ b/include/CommonAPI/DBus/DBusFreedesktopStubAdapterHelper.hpp @@ -0,0 +1,194 @@ +// Copyright (C) 2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSFREEDESKTOPSTUBADAPTERHELPER_HPP_ +#define COMMONAPI_DBUS_DBUSFREEDESKTOPSTUBADAPTERHELPER_HPP_ + +#include <CommonAPI/Struct.hpp> +#include <CommonAPI/DBus/DBusStubAdapterHelper.hpp> + +namespace CommonAPI { +namespace DBus { + +template <typename _StubClass> +class DBusGetFreedesktopAttributeStubDispatcherBase { +public: + virtual ~DBusGetFreedesktopAttributeStubDispatcherBase() {} + virtual void dispatchDBusMessageAndAppendReply(const DBusMessage &_message, + const std::shared_ptr<_StubClass> &_stub, + DBusOutputStream &_output, + const std::shared_ptr<DBusClientId> &_clientId) = 0; +}; + +template <typename _StubClass, typename _AttributeType> +class DBusGetFreedesktopAttributeStubDispatcher + : public virtual DBusGetAttributeStubDispatcher<_StubClass, _AttributeType>, + public virtual DBusGetFreedesktopAttributeStubDispatcherBase<_StubClass> { +public: + typedef DBusStubAdapterHelper<_StubClass> DBusStubAdapterHelperType; + typedef typename DBusGetAttributeStubDispatcher<_StubClass, _AttributeType>::GetStubFunctor GetStubFunctor; + + DBusGetFreedesktopAttributeStubDispatcher(GetStubFunctor _getStubFunctor) + : DBusGetAttributeStubDispatcher<_StubClass, _AttributeType>(_getStubFunctor, "v") { + } + + virtual ~DBusGetFreedesktopAttributeStubDispatcher() {}; + + void dispatchDBusMessageAndAppendReply(const DBusMessage &_message, + const std::shared_ptr<_StubClass> &_stub, + DBusOutputStream &_output, + const std::shared_ptr<DBusClientId> &_clientId) { + CommonAPI::Deployable<CommonAPI::Variant<_AttributeType>, VariantDeployment<>> deployedVariant( + (_stub.get()->*(DBusGetAttributeStubDispatcher<_StubClass, _AttributeType>::getStubFunctor_))(_clientId), &freedesktopVariant); + + _output << deployedVariant; + } + +protected: + virtual bool sendAttributeValueReply(const DBusMessage &_message, const std::shared_ptr<_StubClass> &_stub, DBusStubAdapterHelperType &_helper) { + DBusMessage reply = _message.createMethodReturn(DBusGetAttributeStubDispatcher<_StubClass, _AttributeType>::signature_); + + std::shared_ptr<DBusClientId> clientId = std::make_shared<DBusClientId>(std::string(_message.getSender())); + CommonAPI::Deployable<CommonAPI::Variant<_AttributeType>, VariantDeployment<>> deployedVariant( + (_stub.get()->*(DBusGetAttributeStubDispatcher<_StubClass, _AttributeType>::getStubFunctor_))(clientId), &freedesktopVariant); + + DBusOutputStream output(reply); + output << deployedVariant; + output.flush(); + + return _helper.getDBusConnection()->sendDBusMessage(reply); + } +}; + +template <typename _StubClass, typename _AttributeType> +class DBusSetFreedesktopAttributeStubDispatcher + : public virtual DBusGetFreedesktopAttributeStubDispatcher<_StubClass, _AttributeType>, + public virtual DBusSetAttributeStubDispatcher<_StubClass, _AttributeType> { +public: + typedef typename DBusGetAttributeStubDispatcher<_StubClass, _AttributeType>::GetStubFunctor GetStubFunctor; + typedef typename DBusGetAttributeStubDispatcher<_StubClass, _AttributeType>::DBusStubAdapterHelperType DBusStubAdapterHelperType; + typedef typename DBusStubAdapterHelperType::RemoteEventHandlerType RemoteEventHandlerType; + typedef bool (RemoteEventHandlerType::*OnRemoteSetFunctor)(std::shared_ptr<CommonAPI::ClientId>, _AttributeType); + typedef void (RemoteEventHandlerType::*OnRemoteChangedFunctor)(); + + DBusSetFreedesktopAttributeStubDispatcher( + GetStubFunctor _getStubFunctor, + OnRemoteSetFunctor _onRemoteSetFunctor, + OnRemoteChangedFunctor _onRemoteChangedFunctor) + : DBusGetAttributeStubDispatcher<_StubClass, _AttributeType>(_getStubFunctor, "v"), + DBusGetFreedesktopAttributeStubDispatcher<_StubClass, _AttributeType>(_getStubFunctor), + DBusSetAttributeStubDispatcher<_StubClass, _AttributeType>(_getStubFunctor, _onRemoteSetFunctor, _onRemoteChangedFunctor, "v") { + } + + virtual ~DBusSetFreedesktopAttributeStubDispatcher() {}; + +protected: + virtual _AttributeType retreiveAttributeValue(const DBusMessage &_message, bool &_error) { + std::string interfaceName, attributeName; + DBusInputStream input(_message); + CommonAPI::Deployable<CommonAPI::Variant<_AttributeType>, VariantDeployment<>> deployedVariant(&freedesktopVariant); + input >> interfaceName; // skip over interface and attribute name + input >> attributeName; + input >> deployedVariant; + _error = input.hasError(); + _AttributeType attributeValue = deployedVariant.getValue().template get<_AttributeType>() ; + return attributeValue; + } +}; + +template <typename _StubClass, typename _AttributeType> +class DBusSetFreedesktopObservableAttributeStubDispatcher + : public virtual DBusSetFreedesktopAttributeStubDispatcher<_StubClass, _AttributeType>, + public virtual DBusSetObservableAttributeStubDispatcher<_StubClass, _AttributeType> { +public: + typedef typename DBusSetFreedesktopAttributeStubDispatcher<_StubClass, _AttributeType>::DBusStubAdapterHelperType DBusStubAdapterHelperType; + typedef typename DBusStubAdapterHelperType::StubAdapterType StubAdapterType; + typedef typename DBusSetFreedesktopAttributeStubDispatcher<_StubClass, _AttributeType>::GetStubFunctor GetStubFunctor; + typedef typename DBusSetFreedesktopAttributeStubDispatcher<_StubClass, _AttributeType>::OnRemoteSetFunctor OnRemoteSetFunctor; + typedef typename DBusSetFreedesktopAttributeStubDispatcher<_StubClass, _AttributeType>::OnRemoteChangedFunctor OnRemoteChangedFunctor; + typedef void (StubAdapterType::*FireChangedFunctor)(const _AttributeType&); + + DBusSetFreedesktopObservableAttributeStubDispatcher( + GetStubFunctor _getStubFunctor, + OnRemoteSetFunctor _onRemoteSetFunctor, + OnRemoteChangedFunctor _onRemoteChangedFunctor, + FireChangedFunctor _fireChangedFunctor) + : DBusGetAttributeStubDispatcher<_StubClass, _AttributeType>(_getStubFunctor, "v"), + DBusGetFreedesktopAttributeStubDispatcher<_StubClass, _AttributeType>(_getStubFunctor), + DBusSetAttributeStubDispatcher<_StubClass, _AttributeType>(_getStubFunctor, _onRemoteSetFunctor, _onRemoteChangedFunctor, "v"), + DBusSetFreedesktopAttributeStubDispatcher<_StubClass, _AttributeType>(_getStubFunctor, _onRemoteSetFunctor, _onRemoteChangedFunctor), + DBusSetObservableAttributeStubDispatcher<_StubClass, _AttributeType>(_getStubFunctor, _onRemoteSetFunctor, _onRemoteChangedFunctor, _fireChangedFunctor, "v") { + } +}; + +template<class> +struct DBusStubFreedesktopPropertiesSignalHelper; + +template<template<class ...> class _In, class _InArg> +struct DBusStubFreedesktopPropertiesSignalHelper<_In<DBusInputStream, DBusOutputStream, _InArg>> { + + template <typename _ValueType> + struct DBusPropertiesEntry + : public CommonAPI::Struct<std::string, CommonAPI::Variant<_ValueType>> { + + DBusPropertiesEntry() = default; + DBusPropertiesEntry(const std::string &_propertyName, + const _ValueType &_propertyValue) { + std::get<0>(this->values_) = _propertyName; + std::get<1>(this->values_) = _propertyValue; + }; + + const std::string &getPropertyName() const { return std::get<0>(this->values_); } + void setPropertyName(const std::string &_value) { std::get<0>(this->values_) = _value; } + + const _ValueType getPropertyValue() const { return std::get<1>(this->values_); } + void setPropertyValue(const _ValueType &_value) { std::get<1>(this->values_) = _value; } + }; + + typedef std::vector<DBusPropertiesEntry<_InArg>> PropertiesArray; + typedef CommonAPI::Deployment<CommonAPI::EmptyDeployment, VariantDeployment<>> PropertyDeployment; + typedef CommonAPI::ArrayDeployment<PropertyDeployment> PropertiesDeployment; + typedef CommonAPI::Deployable<PropertiesArray, PropertiesDeployment> DeployedPropertiesArray; + + template <typename _StubClass> + static bool sendPropertiesChangedSignal(const _StubClass &_stub, const std::string &_propertyName, const _InArg &_inArg) { + const std::vector<std::string> invalidatedProperties; + PropertiesArray changedProperties; + DBusPropertiesEntry<_InArg> entry(_propertyName, _inArg); + changedProperties.push_back(entry); + + PropertyDeployment propertyDeployment(nullptr, &freedesktopVariant); + PropertiesDeployment changedPropertiesDeployment(&propertyDeployment); + + DeployedPropertiesArray deployedChangedProperties(changedProperties, &changedPropertiesDeployment); + + return DBusStubSignalHelper< + _In< + DBusInputStream, + DBusOutputStream, + const std::string, + DeployedPropertiesArray, + std::vector<std::string> + > + >::sendSignal( + _stub.getDBusAddress().getObjectPath().c_str(), + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + "sa{sv}as", + _stub.getDBusConnection(), + _stub.getInterface(), + deployedChangedProperties, + invalidatedProperties); + } +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSFREEDESKTOPSTUBADAPTERHELPER_HPP_ diff --git a/include/CommonAPI/DBus/DBusFreedesktopVariant.hpp b/include/CommonAPI/DBus/DBusFreedesktopVariant.hpp new file mode 100644 index 0000000..31490d2 --- /dev/null +++ b/include/CommonAPI/DBus/DBusFreedesktopVariant.hpp @@ -0,0 +1,74 @@ +// Copyright (C) 2014-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.h> can be included directly, this file may disappear or change contents." +#endif + +#include <CommonAPI/DBus/DBusTypeOutputStream.hpp> + +#ifndef COMMONAPI_DBUS_FREEDESKTOPVARIANT_HPP_ +#define COMMONAPI_DBUS_FREEDESKTOPVARIANT_HPP_ + +namespace CommonAPI { +namespace DBus { + +template<class Visitor, class Variant, typename ... _Types> +struct ApplyTypeCompareVisitor; + +template<class Visitor, class Variant> +struct ApplyTypeCompareVisitor<Visitor, Variant> { + static const uint8_t index = 0; + + static uint8_t visit(Visitor&, const Variant&) { + // won't be called if the variant contains the requested type + assert(false); + return 0; + } +}; + +template<class Visitor, class Variant, typename _Type, typename ... _Types> +struct ApplyTypeCompareVisitor<Visitor, Variant, _Type, _Types...> { + static const uint8_t index + = ApplyTypeCompareVisitor<Visitor, Variant, _Types...>::index + 1; + + static uint8_t visit(Visitor &_visitor, const Variant &_variant) { + DBusTypeOutputStream output; + _Type current; + output.writeType(current); +#ifdef WIN32 + if (_visitor.operator()<_Type>(output.getSignature())) { +#else + if (_visitor.template operator()<_Type>(output.getSignature())) { +#endif + return index; + } else { + return ApplyTypeCompareVisitor< + Visitor, Variant, _Types... + >::visit(_visitor, _variant); + } + } +}; + +template<typename ... _Types> +struct TypeCompareVisitor { +public: + TypeCompareVisitor(const std::string &_type) + : type_(_type) { + } + + template<typename _Type> + bool operator()(const std::string &_type) const { + return (_type == type_); + } + +private: + const std::string type_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_FREEDESKTOPVARIANT_HPP_ diff --git a/include/CommonAPI/DBus/DBusFunctionalHash.hpp b/include/CommonAPI/DBus/DBusFunctionalHash.hpp new file mode 100644 index 0000000..78848c7 --- /dev/null +++ b/include/CommonAPI/DBus/DBusFunctionalHash.hpp @@ -0,0 +1,81 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUS_FUNCTIONAL_HASH_HPP_ +#define COMMONAPI_DBUS_DBUS_FUNCTIONAL_HASH_HPP_ + +#include <functional> +#include <string> +#include <tuple> + +#include <CommonAPI/Export.hpp> + +namespace std { + +template<> +struct COMMONAPI_EXPORT hash<pair<const char*, const char*> > + : public unary_function<pair<const char*, const char*>, size_t> { + + size_t operator()(const pair<const char*, const char*>& t) const; +}; + +template<> +struct COMMONAPI_EXPORT hash<const char*> + : public unary_function<const char*, size_t> { + + size_t operator()(const char* const t) const; +}; + +template<> +struct COMMONAPI_EXPORT hash<pair<string, string> > + : public unary_function<pair<string, string>, size_t> { + + size_t operator()(const pair<string, string>& t) const; +}; + +template<> +struct COMMONAPI_EXPORT hash<tuple<string, string, string> > + : public unary_function<tuple<string, string, string>, size_t> { + + size_t operator()(const tuple<string, string, string>& t) const; +}; + +template<> +struct COMMONAPI_EXPORT hash<tuple<string, string, string, bool> > + : public unary_function<tuple<string, string, string, bool>, size_t> { + + size_t operator()(const tuple<string, string, string, bool>& t) const; +}; + +template<> +struct COMMONAPI_EXPORT hash<tuple<string, string, string, int> > + : public unary_function<tuple<string, string, string, int>, size_t> { + + size_t operator()(const tuple<string, string, string, int>& t) const; +}; + +template<> +struct COMMONAPI_EXPORT hash<tuple<string, string, string, string> > + : public std::unary_function<tuple<string, string, string, string>, size_t> { + + size_t operator()(const tuple<string, string, string, string>& t) const; +}; + +template<> +struct COMMONAPI_EXPORT equal_to<pair<const char*, const char*> > + : public binary_function<pair<const char*, const char*>, + pair<const char*, const char*>, + bool> { + + bool operator()(const pair<const char*, const char*>& a, const pair<const char*, const char*>& b) const; +}; + +} // namespace std + +#endif // COMMONAPI_DBUS_DBUS_FUNCTIONAL_HASH_HPP_ diff --git a/include/CommonAPI/DBus/DBusHelper.hpp b/include/CommonAPI/DBus/DBusHelper.hpp new file mode 100644 index 0000000..53a5625 --- /dev/null +++ b/include/CommonAPI/DBus/DBusHelper.hpp @@ -0,0 +1,55 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUS_HELPER_HPP_ +#define COMMONAPI_DBUS_DBUS_HELPER_HPP_ + +#include <unordered_map> +#include <vector> + +namespace CommonAPI { +namespace DBus { + +template <int ...> +struct index_sequence {}; + + +template <int N, int ...S> +struct make_sequence : make_sequence<N-1, N-1, S...> {}; + +template <int ...S> +struct make_sequence<0, S...> { + typedef index_sequence<S...> type; +}; + + +template <int N, int _Offset, int ...S> +struct make_sequence_range : make_sequence_range<N-1, _Offset, N-1+_Offset, S...> {}; + +template <int _Offset, int ...S> +struct make_sequence_range<0, _Offset, S...> { + typedef index_sequence<S...> type; +}; + +template<typename _Type> +struct is_std_vector { static const bool value = false; }; + +template<typename _Type> +struct is_std_vector<std::vector<_Type> > { static const bool value = true; }; + +template<typename _Type> +struct is_std_unordered_map { static const bool value = false; }; + +template<typename _Key, typename _Value, typename _Hash> +struct is_std_unordered_map<std::unordered_map<_Key, _Value, _Hash>> { static const bool value = true; }; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUS_HELPER_HPP_ diff --git a/include/CommonAPI/DBus/DBusInputStream.hpp b/include/CommonAPI/DBus/DBusInputStream.hpp new file mode 100644 index 0000000..389cb68 --- /dev/null +++ b/include/CommonAPI/DBus/DBusInputStream.hpp @@ -0,0 +1,426 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSINPUTSTREAM_HPP_ +#define COMMONAPI_DBUS_DBUSINPUTSTREAM_HPP_ + +#include <iostream> +#include <iomanip> +#include <sstream> + +#include <cassert> +#include <cstdint> +#include <stack> +#include <string> +#include <vector> + +#include <CommonAPI/Export.hpp> +#include <CommonAPI/InputStream.hpp> +#include <CommonAPI/Struct.hpp> +#include <CommonAPI/DBus/DBusDeployment.hpp> +#include <CommonAPI/DBus/DBusError.hpp> +#include <CommonAPI/DBus/DBusFreedesktopVariant.hpp> +#include <CommonAPI/DBus/DBusHelper.hpp> +#include <CommonAPI/DBus/DBusMessage.hpp> + +namespace CommonAPI { +namespace DBus { + +// Used to mark the position of a pointer within an array of bytes. +typedef uint32_t position_t; + +/** + * @class DBusInputMessageStream + * + * Used to deserialize and read data from a #DBusMessage. For all data types that can be read from a #DBusMessage, a ">>"-operator should be defined to handle the reading + * (this operator is predefined for all basic data types and for vectors). + */ +class DBusInputStream + : public InputStream<DBusInputStream> { +public: + COMMONAPI_EXPORT bool hasError() const { + return isErrorSet(); + } + + COMMONAPI_EXPORT InputStream &readValue(bool &_value, const EmptyDeployment *_depl); + + COMMONAPI_EXPORT InputStream &readValue(int8_t &_value, const EmptyDeployment *_depl); + COMMONAPI_EXPORT InputStream &readValue(int16_t &_value, const EmptyDeployment *_depl); + COMMONAPI_EXPORT InputStream &readValue(int32_t &_value, const EmptyDeployment *_depl); + COMMONAPI_EXPORT InputStream &readValue(int64_t &_value, const EmptyDeployment *_depl); + + COMMONAPI_EXPORT InputStream &readValue(uint8_t &_value, const EmptyDeployment *_depl); + COMMONAPI_EXPORT InputStream &readValue(uint16_t &_value, const EmptyDeployment *_depl); + COMMONAPI_EXPORT InputStream &readValue(uint32_t &_value, const EmptyDeployment *_depl); + COMMONAPI_EXPORT InputStream &readValue(uint64_t &_value, const EmptyDeployment *_depl); + + COMMONAPI_EXPORT InputStream &readValue(float &_value, const EmptyDeployment *_depl); + COMMONAPI_EXPORT InputStream &readValue(double &_value, const EmptyDeployment *_depl); + + COMMONAPI_EXPORT InputStream &readValue(std::string &_value, const EmptyDeployment *_depl); + + COMMONAPI_EXPORT InputStream &readValue(Version &_value, const EmptyDeployment *_depl); + + template<class _Deployment, typename _Base> + COMMONAPI_EXPORT InputStream &readValue(Enumeration<_Base> &_value, const _Deployment *_depl) { + _Base tmpValue; + readValue(tmpValue, _depl); + _value = tmpValue; + return (*this); + } + + template<class _Deployment, typename... _Types> + COMMONAPI_EXPORT InputStream &readValue(Struct<_Types...> &_value, const _Deployment *_depl) { + align(8); + const auto itsSize(std::tuple_size<std::tuple<_Types...>>::value); + StructReader<itsSize-1, DBusInputStream, Struct<_Types...>, _Deployment>{}( + (*this), _value, _depl); + return (*this); + } + + template<class _Deployment, class _PolymorphicStruct> + COMMONAPI_EXPORT InputStream &readValue(std::shared_ptr<_PolymorphicStruct> &_value, + const _Deployment *_depl) { + uint32_t serial; + align(8); + _readValue(serial); + skipSignature(); + align(8); + if (!hasError()) { + _value = _PolymorphicStruct::create(serial); + _value->template readValue<>(*this, _depl); + } + + return (*this); + } + + template<typename... _Types> + COMMONAPI_EXPORT InputStream &readValue(Variant<_Types...> &_value, const CommonAPI::EmptyDeployment *_depl = nullptr) { + if(_value.hasValue()) { + DeleteVisitor<_value.maxSize> visitor(_value.valueStorage_); + ApplyVoidVisitor<DeleteVisitor<_value.maxSize>, + Variant<_Types...>, _Types... >::visit(visitor, _value); + } + + align(8); + readValue(_value.valueType_, static_cast<EmptyDeployment *>(nullptr)); + skipSignature(); + + InputStreamReadVisitor<DBusInputStream, _Types...> visitor((*this), _value); + ApplyVoidVisitor<InputStreamReadVisitor<DBusInputStream, _Types... >, + Variant<_Types...>, _Types...>::visit(visitor, _value); + + return (*this); + } + + template<typename _Deployment, typename... _Types> + COMMONAPI_EXPORT InputStream &readValue(Variant<_Types...> &_value, const _Deployment *_depl) { + if(_value.hasValue()) { + DeleteVisitor<_value.maxSize> visitor(_value.valueStorage_); + ApplyVoidVisitor<DeleteVisitor<_value.maxSize>, + Variant<_Types...>, _Types... >::visit(visitor, _value); + } + + if (_depl != nullptr && _depl->isFreeDesktop_) { + // Read signature + uint8_t signatureLength; + readValue(signatureLength, static_cast<EmptyDeployment *>(nullptr)); + std::string signature(_readRaw(signatureLength+1), signatureLength); + + // Determine index (value type) from signature + TypeCompareVisitor<_Types...> visitor(signature); + _value.valueType_ = ApplyTypeCompareVisitor< + TypeCompareVisitor<_Types...>, + Variant<_Types...>, + _Types... + >::visit(visitor, _value); + } else { + align(8); + readValue(_value.valueType_, static_cast<EmptyDeployment *>(nullptr)); + skipSignature(); + } + + + InputStreamReadVisitor<DBusInputStream, _Types...> visitor((*this), _value); + ApplyVoidVisitor<InputStreamReadVisitor<DBusInputStream, _Types... >, + Variant<_Types...>, _Types...>::visit(visitor, _value); + + return (*this); + } + + template<typename _ElementType> + COMMONAPI_EXPORT InputStream &readValue(std::vector<_ElementType> &_value, const EmptyDeployment *_depl) { + uint32_t itsSize; + _readValue(itsSize); + pushSize(itsSize); + + alignVector<_ElementType>(); + + pushPosition(); + + _value.clear(); + while (sizes_.top() > current_ - positions_.top()) { + _ElementType itsElement; + readValue(itsElement, static_cast<EmptyDeployment *>(nullptr)); + + if (hasError()) { + break; + } + + _value.push_back(std::move(itsElement)); + } + + popSize(); + popPosition(); + + return (*this); + } + + template<class _Deployment, typename _ElementType> + COMMONAPI_EXPORT InputStream &readValue(std::vector<_ElementType> &_value, const _Deployment *_depl) { + uint32_t itsSize; + _readValue(itsSize); + pushSize(itsSize); + + alignVector<_ElementType>(); + + pushPosition(); + + _value.clear(); + while (sizes_.top() > current_ - positions_.top()) { + _ElementType itsElement; + readValue(itsElement, _depl->elementDepl_); + + if (hasError()) { + break; + } + + _value.push_back(std::move(itsElement)); + } + + popSize(); + popPosition(); + + return (*this); + } + + template<typename _KeyType, typename _ValueType, typename _HasherType> + COMMONAPI_EXPORT InputStream &readValue(std::unordered_map<_KeyType, _ValueType, _HasherType> &_value, + const EmptyDeployment *_depl) { + + typedef typename std::unordered_map<_KeyType, _ValueType, _HasherType>::value_type MapElement; + + uint32_t itsSize; + _readValue(itsSize); + pushSize(itsSize); + + align(8); + pushPosition(); + + _value.clear(); + while (sizes_.top() > current_ - positions_.top()) { + _KeyType itsKey; + _ValueType itsValue; + + align(8); + readValue(itsKey, _depl); + readValue(itsValue, _depl); + + if (hasError()) { + break; + } + + _value.insert(MapElement(std::move(itsKey), std::move(itsValue))); + } + + (void)popSize(); + (void)popPosition(); + + return (*this); + } + + template<class _Deployment, typename _KeyType, typename _ValueType, typename _HasherType> + COMMONAPI_EXPORT InputStream &readValue(std::unordered_map<_KeyType, _ValueType, _HasherType> &_value, + const _Deployment *_depl) { + + typedef typename std::unordered_map<_KeyType, _ValueType, _HasherType>::value_type MapElement; + + uint32_t itsSize; + _readValue(itsSize); + pushSize(itsSize); + + align(8); + pushPosition(); + + _value.clear(); + while (sizes_.top() > current_ - positions_.top()) { + _KeyType itsKey; + _ValueType itsValue; + + align(8); + readValue(itsKey, _depl->key_); + readValue(itsValue, _depl->value_); + + if (hasError()) { + break; + } + + _value.insert(MapElement(std::move(itsKey), std::move(itsValue))); + } + + (void)popSize(); + (void)popPosition(); + + return (*this); + } + + /** + * Creates a #DBusInputMessageStream which can be used to deserialize and read data from the given #DBusMessage. + * As no message-signature is checked, the user is responsible to ensure that the correct data types are read in the correct order. + * + * @param message the #DBusMessage from which data should be read. + */ + COMMONAPI_EXPORT DBusInputStream(const CommonAPI::DBus::DBusMessage &_message); + COMMONAPI_EXPORT DBusInputStream(const DBusInputStream &_stream) = delete; + + /** + * Destructor; does not call the destructor of the referred #DBusMessage. Make sure to maintain a reference to the + * #DBusMessage outside of the stream if you intend to make further use of the message. + */ + COMMONAPI_EXPORT ~DBusInputStream(); + + // Marks the stream as erroneous. + COMMONAPI_EXPORT void setError(); + + /** + * @return An instance of #DBusError if this stream is in an erroneous state, NULL otherwise + */ + COMMONAPI_EXPORT const DBusError &getError() const; + + /** + * @return true if this stream is in an erroneous state, false otherwise. + */ + COMMONAPI_EXPORT bool isErrorSet() const; + + // Marks the state of the stream as cleared from all errors. Further reading is possible afterwards. + // The stream will have maintained the last valid position from before its state became erroneous. + COMMONAPI_EXPORT void clearError(); + + /** + * Aligns the stream to the given byte boundary, i.e. the stream skips as many bytes as are necessary to execute the next read + * starting from the given boundary. + * + * @param _boundary the byte boundary to which the stream needs to be aligned. + */ + COMMONAPI_EXPORT void align(const size_t _boundary); + + /** + * Reads the given number of bytes and returns them as an array of characters. + * + * Actually, for performance reasons this command only returns a pointer to the current position in the stream, + * and then increases the position of this pointer by the number of bytes indicated by the given parameter. + * It is the user's responsibility to actually use only the number of bytes he indicated he would use. + * It is assumed the user knows what kind of value is stored next in the #DBusMessage the data is streamed from. + * Using a reinterpret_cast on the returned pointer should then restore the original value. + * + * Example use case: + * @code + * ... + * inputMessageStream.alignForBasicType(sizeof(int32_t)); + * char* const dataPtr = inputMessageStream.read(sizeof(int32_t)); + * int32_t val = *(reinterpret_cast<int32_t*>(dataPtr)); + * ... + * @endcode + */ + COMMONAPI_EXPORT char *_readRaw(const size_t _size); + + /** + * Handles all reading of basic types from a given #DBusInputMessageStream. + * Basic types in this context are: uint8_t, uint16_t, uint32_t, uint64_t, int8_t, int16_t, int32_t, int64_t, float, double. + * Any types not listed here (especially all complex types, e.g. structs, unions etc.) need to provide a + * specialized implementation of this operator. + * + * @tparam _Type The type of the value that is to be read from the given stream. + * @param _value The variable in which the retrieved value is to be stored + * @return The given inputMessageStream to allow for successive reading + */ + template<typename _Type> + COMMONAPI_EXPORT DBusInputStream &_readValue(_Type &_value) { + if (sizeof(_value) > 1) + align(sizeof(_Type)); + + _value = *(reinterpret_cast<_Type *>(_readRaw(sizeof(_Type)))); + + return (*this); + } + + COMMONAPI_EXPORT DBusInputStream &_readValue(float &_value) { + align(sizeof(double)); + + _value = (float) (*(reinterpret_cast<double*>(_readRaw(sizeof(double))))); + return (*this); + } + +private: + COMMONAPI_EXPORT void pushPosition(); + COMMONAPI_EXPORT size_t popPosition(); + + COMMONAPI_EXPORT void pushSize(size_t _size); + COMMONAPI_EXPORT size_t popSize(); + + inline void skipSignature() { + uint8_t length; + _readValue(length); + _readRaw(length + 1); + } + + template<typename _Type> + COMMONAPI_EXPORT void alignVector(typename std::enable_if<!std::is_class<_Type>::value>::type * = nullptr, + typename std::enable_if<!is_std_vector<_Type>::value>::type * = nullptr, + typename std::enable_if<!is_std_unordered_map<_Type>::value>::type * = nullptr) { + if (4 < sizeof(_Type)) align(8); + } + + template<typename _Type> + COMMONAPI_EXPORT void alignVector(typename std::enable_if<!std::is_same<_Type, std::string>::value>::type * = nullptr, + typename std::enable_if<std::is_class<_Type>::value>::type * = nullptr, + typename std::enable_if<!is_std_vector<_Type>::value>::type * = nullptr, + typename std::enable_if<!is_std_unordered_map<_Type>::value>::type * = nullptr) { + align(8); + } + + template<typename _Type> + COMMONAPI_EXPORT void alignVector(typename std::enable_if<std::is_same<_Type, std::string>::value>::type * = nullptr) { + // Intentionally do nothing + } + + template<typename _Type> + COMMONAPI_EXPORT void alignVector(typename std::enable_if<is_std_vector<_Type>::value>::type * = nullptr) { + // Intentionally do nothing + } + + template<typename _Type> + COMMONAPI_EXPORT void alignVector(typename std::enable_if<is_std_unordered_map<_Type>::value>::type * = nullptr) { + align(4); + } + + char *begin_; + size_t current_; + size_t size_; + CommonAPI::DBus::DBusError* exception_; + CommonAPI::DBus::DBusMessage message_; + + std::stack<uint32_t> sizes_; + std::stack<size_t> positions_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUS_INPUTSTREAM_HPP_ diff --git a/include/CommonAPI/DBus/DBusInstanceAvailabilityStatusChangedEvent.hpp b/include/CommonAPI/DBus/DBusInstanceAvailabilityStatusChangedEvent.hpp new file mode 100644 index 0000000..d520034 --- /dev/null +++ b/include/CommonAPI/DBus/DBusInstanceAvailabilityStatusChangedEvent.hpp @@ -0,0 +1,135 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSINSTANCEAVAILABILITYSTATUSCHANGED_EVENT_HPP_ +#define COMMONAPI_DBUS_DBUSINSTANCEAVAILABILITYSTATUSCHANGED_EVENT_HPP_ + +#include <functional> +#include <future> +#include <string> +#include <vector> + +#include <CommonAPI/ProxyManager.hpp> +#include <CommonAPI/DBus/DBusAddressTranslator.hpp> +#include <CommonAPI/DBus/DBusProxy.hpp> +#include <CommonAPI/DBus/DBusObjectManagerStub.hpp> +#include <CommonAPI/DBus/DBusInstanceAvailabilityStatusChangedEvent.hpp> +#include <CommonAPI/DBus/DBusTypes.hpp> + +namespace CommonAPI { +namespace DBus { + +// TODO Check to move logic to DBusServiceRegistry, now every proxy will deserialize the messages! +class DBusInstanceAvailabilityStatusChangedEvent: + public ProxyManager::InstanceAvailabilityStatusChangedEvent, + public DBusProxyConnection::DBusSignalHandler { + public: + DBusInstanceAvailabilityStatusChangedEvent(DBusProxy &_proxy, const std::string &_interfaceName) : + proxy_(_proxy), + observedInterfaceName_(_interfaceName) { + } + + virtual ~DBusInstanceAvailabilityStatusChangedEvent() { + proxy_.removeSignalMemberHandler(interfacesAddedSubscription_); + proxy_.removeSignalMemberHandler(interfacesRemovedSubscription_); + } + + virtual void onSignalDBusMessage(const DBusMessage& dbusMessage) { + if (dbusMessage.hasMemberName("InterfacesAdded")) { + onInterfacesAddedSignal(dbusMessage); + } else if (dbusMessage.hasMemberName("InterfacesRemoved")) { + onInterfacesRemovedSignal(dbusMessage); + } + } + + protected: + virtual void onFirstListenerAdded(const Listener&) { + interfacesAddedSubscription_ = proxy_.addSignalMemberHandler( + proxy_.getDBusAddress().getObjectPath(), + DBusObjectManagerStub::getInterfaceName(), + "InterfacesAdded", + "oa{sa{sv}}", + this); + + interfacesRemovedSubscription_ = proxy_.addSignalMemberHandler( + proxy_.getDBusAddress().getObjectPath(), + DBusObjectManagerStub::getInterfaceName(), + "InterfacesRemoved", + "oas", + this); + } + + virtual void onLastListenerRemoved(const Listener&) { + proxy_.removeSignalMemberHandler(interfacesAddedSubscription_); + proxy_.removeSignalMemberHandler(interfacesRemovedSubscription_); + } + + private: + inline void onInterfacesAddedSignal(const DBusMessage &_message) { + DBusInputStream dbusInputStream(_message); + std::string dbusObjectPath; + DBusInterfacesAndPropertiesDict dbusInterfacesAndPropertiesDict; + + dbusInputStream >> dbusObjectPath; + assert(!dbusInputStream.hasError()); + + dbusInputStream >> dbusInterfacesAndPropertiesDict; + assert(!dbusInputStream.hasError()); + + for (const auto& dbusInterfaceIterator : dbusInterfacesAndPropertiesDict) { + const std::string& dbusInterfaceName = dbusInterfaceIterator.first; + + if(dbusInterfaceName == observedInterfaceName_) { + notifyInterfaceStatusChanged(dbusObjectPath, dbusInterfaceName, AvailabilityStatus::AVAILABLE); + } + } + } + + inline void onInterfacesRemovedSignal(const DBusMessage &_message) { + DBusInputStream dbusInputStream(_message); + std::string dbusObjectPath; + std::vector<std::string> dbusInterfaceNames; + + dbusInputStream >> dbusObjectPath; + assert(!dbusInputStream.hasError()); + + dbusInputStream >> dbusInterfaceNames; + assert(!dbusInputStream.hasError()); + + for (const auto& dbusInterfaceName : dbusInterfaceNames) { + if(dbusInterfaceName == observedInterfaceName_) { + notifyInterfaceStatusChanged(dbusObjectPath, dbusInterfaceName, AvailabilityStatus::NOT_AVAILABLE); + } + } + } + + void notifyInterfaceStatusChanged(const std::string &_objectPath, + const std::string &_interfaceName, + const AvailabilityStatus &_availability) { + CommonAPI::Address itsAddress; + DBusAddress itsDBusAddress(proxy_.getDBusAddress().getService(), + _objectPath, + _interfaceName); + + DBusAddressTranslator::get()->translate(itsDBusAddress, itsAddress); + + notifyListeners(itsAddress.getAddress(), _availability); + } + + + DBusProxy &proxy_; + std::string observedInterfaceName_; + DBusProxyConnection::DBusSignalHandlerToken interfacesAddedSubscription_; + DBusProxyConnection::DBusSignalHandlerToken interfacesRemovedSubscription_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSINSTANCEAVAILABILITYSTATUSCHANGEDEVENT_HPP_ diff --git a/include/CommonAPI/DBus/DBusInterfaceHandler.hpp b/include/CommonAPI/DBus/DBusInterfaceHandler.hpp new file mode 100644 index 0000000..c570975 --- /dev/null +++ b/include/CommonAPI/DBus/DBusInterfaceHandler.hpp @@ -0,0 +1,31 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef COMMONAPI_DBUS_DBUSINTERFACEHANDLER_HPP_ +#define COMMONAPI_DBUS_DBUSINTERFACEHANDLER_HPP_ + +#include <memory> + +#include <CommonAPI/DBus/DBusProxyConnection.hpp> +#include <CommonAPI/DBus/DBusMessage.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusInterfaceHandler { + public: + virtual ~DBusInterfaceHandler() {} + + virtual const char* getMethodsDBusIntrospectionXmlData() const = 0; + + virtual bool onInterfaceDBusMessage(const DBusMessage& dbusMessage) = 0; + + virtual const bool hasFreedesktopProperties() = 0; +}; + +} // namespace dbus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSINTERFACEHANDLER_HPP_ diff --git a/include/CommonAPI/DBus/DBusMainLoopContext.hpp b/include/CommonAPI/DBus/DBusMainLoopContext.hpp new file mode 100644 index 0000000..9230cd2 --- /dev/null +++ b/include/CommonAPI/DBus/DBusMainLoopContext.hpp @@ -0,0 +1,87 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSMAINLOOPCONTEXT_HPP_ +#define COMMONAPI_DBUS_DBUSMAINLOOPCONTEXT_HPP_ + +#include <list> +#include <memory> + +#include <dbus/dbus.h> + +#include <CommonAPI/MainLoopContext.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusConnection; + +class DBusDispatchSource: public DispatchSource { + public: + DBusDispatchSource(DBusConnection* dbusConnection); + ~DBusDispatchSource(); + + bool prepare(int64_t& timeout); + bool check(); + bool dispatch(); + + private: + DBusConnection* dbusConnection_; +}; + +class DBusWatch: public Watch { + public: + DBusWatch(::DBusWatch* libdbusWatch, std::weak_ptr<MainLoopContext>& mainLoopContext); + + bool isReadyToBeWatched(); + void startWatching(); + void stopWatching(); + + void dispatch(unsigned int eventFlags); + + const pollfd& getAssociatedFileDescriptor(); + + const std::vector<DispatchSource*>& getDependentDispatchSources(); + void addDependentDispatchSource(DispatchSource* dispatchSource); + private: + bool isReady(); + + ::DBusWatch* libdbusWatch_; + pollfd pollFileDescriptor_; + std::vector<DispatchSource*> dependentDispatchSources_; + + std::weak_ptr<MainLoopContext> mainLoopContext_; +}; + + +class DBusTimeout: public Timeout { + public: + DBusTimeout(::DBusTimeout* libdbusTimeout, std::weak_ptr<MainLoopContext>& mainLoopContext); + + bool isReadyToBeMonitored(); + void startMonitoring(); + void stopMonitoring(); + + bool dispatch(); + + int64_t getTimeoutInterval() const; + int64_t getReadyTime() const; + private: + void recalculateDueTime(); + + int64_t dueTimeInMs_; + ::DBusTimeout* libdbusTimeout_; + std::weak_ptr<MainLoopContext> mainLoopContext_; +}; + + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSMAINLOOPCONTEXT_HPP_ diff --git a/include/CommonAPI/DBus/DBusMessage.hpp b/include/CommonAPI/DBus/DBusMessage.hpp new file mode 100644 index 0000000..dc5d72e --- /dev/null +++ b/include/CommonAPI/DBus/DBusMessage.hpp @@ -0,0 +1,101 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSMESSAGE_HPP_ +#define COMMONAPI_DBUS_DBUSMESSAGE_HPP_ + +#include <string> + +#include <dbus/dbus.h> + +#include <CommonAPI/Export.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusAddress; +class DBusConnection; + +class COMMONAPI_EXPORT DBusMessage { +public: + enum class Type: int { + Invalid = DBUS_MESSAGE_TYPE_INVALID, + MethodCall = DBUS_MESSAGE_TYPE_METHOD_CALL, + MethodReturn = DBUS_MESSAGE_TYPE_METHOD_RETURN, + Error = DBUS_MESSAGE_TYPE_ERROR, + Signal = DBUS_MESSAGE_TYPE_SIGNAL + }; + + DBusMessage(); + DBusMessage(::DBusMessage* libdbusMessage); + DBusMessage(::DBusMessage* libdbusMessage, bool _reference); + DBusMessage(const DBusMessage &_source); + DBusMessage(DBusMessage &&_source); + + ~DBusMessage(); + + DBusMessage &operator=(const DBusMessage &_source); + DBusMessage &operator=(DBusMessage &&_source); + operator bool() const; + + static DBusMessage createOrgFreedesktopOrgMethodCall(const std::string &_method, + const std::string &_signature = ""); + + static DBusMessage createMethodCall(const DBusAddress &_address, + const std::string &_method, const std::string &_signature = ""); + + DBusMessage createMethodReturn(const std::string &_signature) const; + + DBusMessage createMethodError(const std::string &_name, const std::string &_reason = "") const; + + static DBusMessage createSignal(const std::string& objectPath, + const std::string& interfaceName, + const std::string& signalName, + const std::string& signature = ""); + + const char* getSender() const; + const char* getObjectPath() const; + const char* getInterface() const; + const char* getMember() const; + const char* getSignature() const; + const char* getError() const; + const char* getDestination() const; + const uint32_t getSerial() const; + + bool hasObjectPath(const std::string& objectPath) const; + + bool hasObjectPath(const char* objectPath) const; + bool hasInterfaceName(const char* interfaceName) const; + bool hasMemberName(const char* memberName) const; + bool hasSignature(const char* signature) const; + + const Type getType() const; + bool isInvalidType() const; + bool isMethodCallType() const; + bool isMethodReturnType() const; + bool isErrorType() const; + bool isSignalType() const; + + char* getBodyData() const; + int getBodyLength() const; + int getBodySize() const; + + bool setBodyLength(const int bodyLength); + bool setDestination(const char* destination); + +private: + ::DBusMessage *message_; + + friend class DBusConnection; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSMESSAGE_HPP_ diff --git a/include/CommonAPI/DBus/DBusMultiEvent.hpp b/include/CommonAPI/DBus/DBusMultiEvent.hpp new file mode 100644 index 0000000..8215d4f --- /dev/null +++ b/include/CommonAPI/DBus/DBusMultiEvent.hpp @@ -0,0 +1,118 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSMULTIEVENT_HPP_ +#define COMMONAPI_DBUS_DBUSMULTIEVENT_HPP_ + +#include <string> +#include <unordered_map> + +#include <CommonAPI/Event.hpp> + +namespace CommonAPI { +namespace DBus { + +template <typename... _Arguments> +class DBusMultiEvent { + public: + typedef std::function<SubscriptionStatus(const std::string&, const _Arguments&...)> Listener; + typedef std::unordered_multimap<std::string, Listener> ListenersMap; + typedef typename ListenersMap::iterator Subscription; + + virtual ~DBusMultiEvent() {} + + Subscription subscribeAll(const Listener& listener); + Subscription subscribe(const std::string& eventName, const Listener& listener); + + void unsubscribe(Subscription listenerSubscription); + + protected: + SubscriptionStatus notifyListeners(const std::string& name, const _Arguments&... eventArguments); + + virtual void onFirstListenerAdded(const std::string& name, const Listener& listener) { } + virtual void onListenerAdded(const std::string& name, const Listener& listener) { } + + virtual void onListenerRemoved(const std::string& name, const Listener& listener) { } + virtual void onLastListenerRemoved(const std::string& name, const Listener& listener) { } + + private: + typedef std::pair<typename ListenersMap::iterator, typename ListenersMap::iterator> IteratorRange; + SubscriptionStatus notifyListenersRange(const std::string& name, IteratorRange listenersRange, const _Arguments&... eventArguments); + + ListenersMap listenersMap_; +}; + +template <typename... _Arguments> +typename DBusMultiEvent<_Arguments...>::Subscription +DBusMultiEvent<_Arguments...>::subscribeAll(const Listener& listener) { + return subscribe(std::string(), listener); +} + +template <typename... _Arguments> +typename DBusMultiEvent<_Arguments...>::Subscription +DBusMultiEvent<_Arguments...>::subscribe(const std::string& eventName, const Listener& listener) { + const bool firstListenerAdded = listenersMap_.empty(); + + auto listenerSubscription = listenersMap_.insert({eventName, listener}); + + if (firstListenerAdded) { + onFirstListenerAdded(eventName, listener); + } + + onListenerAdded(eventName, listener); + + return listenerSubscription; +} + +template <typename... _Arguments> +void DBusMultiEvent<_Arguments...>::unsubscribe(Subscription listenerSubscription) { + const std::string name = listenerSubscription->first; + const Listener listener = listenerSubscription->second; + + listenersMap_.erase(listenerSubscription); + + onListenerRemoved(name, listener); + + const bool lastListenerRemoved = listenersMap_.empty(); + if (lastListenerRemoved) + onLastListenerRemoved(name, listener); +} + +template <typename... _Arguments> +SubscriptionStatus DBusMultiEvent<_Arguments...>::notifyListeners(const std::string& name, const _Arguments&... eventArguments) { + const SubscriptionStatus subscriptionStatus = notifyListenersRange(name, listenersMap_.equal_range(name), eventArguments...); + + if (subscriptionStatus == SubscriptionStatus::CANCEL) + return SubscriptionStatus::CANCEL; + + return notifyListenersRange(name, listenersMap_.equal_range(std::string()), eventArguments...); +} + +template <typename... _Arguments> +SubscriptionStatus DBusMultiEvent<_Arguments...>::notifyListenersRange( + const std::string& name, + IteratorRange listenersRange, + const _Arguments&... eventArguments) { + for (auto iterator = listenersRange.first; iterator != listenersRange.second; iterator++) { + const Listener& listener = iterator->second; + const SubscriptionStatus listenerSubcriptionStatus = listener(name, eventArguments...); + + if (listenerSubcriptionStatus == SubscriptionStatus::CANCEL) { + auto listenerIterator = iterator; + listenersMap_.erase(listenerIterator); + } + } + + return listenersMap_.empty() ? SubscriptionStatus::CANCEL : SubscriptionStatus::RETAIN; +} + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSMULTIEVENT_HPP_ diff --git a/include/CommonAPI/DBus/DBusObjectManager.hpp b/include/CommonAPI/DBus/DBusObjectManager.hpp new file mode 100644 index 0000000..8c7225b --- /dev/null +++ b/include/CommonAPI/DBus/DBusObjectManager.hpp @@ -0,0 +1,69 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSOBJECTMANAGER_HPP_ +#define COMMONAPI_DBUS_DBUSOBJECTMANAGER_HPP_ + +#include <CommonAPI/DBus/DBusProxyConnection.hpp> +#include <CommonAPI/DBus/DBusMessage.hpp> +#include <CommonAPI/DBus/DBusObjectManagerStub.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusStubAdapter; +class DBusInterfaceHandler; + +class DBusObjectManager { + public: + COMMONAPI_EXPORT DBusObjectManager(const std::shared_ptr<DBusProxyConnection>&); + COMMONAPI_EXPORT ~DBusObjectManager(); + + COMMONAPI_EXPORT bool registerDBusStubAdapter(std::shared_ptr<DBusStubAdapter> dbusStubAdapter); + COMMONAPI_EXPORT bool unregisterDBusStubAdapter(std::shared_ptr<DBusStubAdapter> dbusStubAdapter); + + //Zusammenfassbar mit "registerDBusStubAdapter"? + COMMONAPI_EXPORT bool exportManagedDBusStubAdapter(const std::string& parentObjectPath, std::shared_ptr<DBusStubAdapter> dbusStubAdapter); + COMMONAPI_EXPORT bool unexportManagedDBusStubAdapter(const std::string& parentObjectPath, std::shared_ptr<DBusStubAdapter> dbusStubAdapter); + + COMMONAPI_EXPORT bool handleMessage(const DBusMessage&); + + COMMONAPI_EXPORT std::shared_ptr<DBusObjectManagerStub> getRootDBusObjectManagerStub(); + + private: + // objectPath, interfaceName + typedef std::pair<std::string, std::string> DBusInterfaceHandlerPath; + + COMMONAPI_EXPORT bool addDBusInterfaceHandler(const DBusInterfaceHandlerPath& dbusInterfaceHandlerPath, + std::shared_ptr<DBusInterfaceHandler> dbusInterfaceHandler); + + COMMONAPI_EXPORT bool removeDBusInterfaceHandler(const DBusInterfaceHandlerPath& dbusInterfaceHandlerPath, + std::shared_ptr<DBusInterfaceHandler> dbusInterfaceHandler); + + COMMONAPI_EXPORT bool onIntrospectableInterfaceDBusMessage(const DBusMessage& callMessage); + COMMONAPI_EXPORT bool onFreedesktopPropertiesDBusMessage(const DBusMessage& callMessage); + + + typedef std::unordered_map<DBusInterfaceHandlerPath, std::shared_ptr<DBusInterfaceHandler>> DBusRegisteredObjectsTable; + DBusRegisteredObjectsTable dbusRegisteredObjectsTable_; + + std::shared_ptr<DBusObjectManagerStub> rootDBusObjectManagerStub_; + + typedef std::pair<std::shared_ptr<DBusObjectManagerStub>, uint32_t> ReferenceCountedDBusObjectManagerStub; + typedef std::unordered_map<std::string, ReferenceCountedDBusObjectManagerStub> RegisteredObjectManagersTable; + RegisteredObjectManagersTable managerStubs_; + + std::weak_ptr<DBusProxyConnection> dbusConnection_; + std::recursive_mutex objectPathLock_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSOBJECTMANAGER_HPP_ diff --git a/include/CommonAPI/DBus/DBusObjectManagerStub.hpp b/include/CommonAPI/DBus/DBusObjectManagerStub.hpp new file mode 100644 index 0000000..a16578b --- /dev/null +++ b/include/CommonAPI/DBus/DBusObjectManagerStub.hpp @@ -0,0 +1,125 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef COMMONAPI_DBUS_DBUSFREEDESKTOPOBJECTMANAGERSTUB_HPP_ +#define COMMONAPI_DBUS_DBUSFREEDESKTOPOBJECTMANAGERSTUB_HPP_ + +#include <memory> +#include <mutex> +#include <string> + +#include <CommonAPI/DBus/DBusInterfaceHandler.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusStubAdapter; + +/** + * Stub for standard <a href="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager">org.freedesktop.dbus.ObjectManager</a> interface. + * + * Instantiated within a manager stub and it must hold reference to all registered objects. + * Whenever the manager gets destroyed all references to registered objects are lost too. + * This duplicates the semantic of the CommonAPI::ServicePublisher class. + * + * Only one DBusStubAdapter instance could be registered per DBusObjectManagerStub instance. + * + * The owner of the DBusObjectManagerStub instance must take care of registering and unregistering it. + * + * Example stub life cycle: + * - create CommonAPI::ServicePublisher + * - create stub A + * - register stub A to CommonAPI::ServicePublisher + * - create stub B + * - register stub B with stub A as object manager + * - drop all references to stub B, stub A keeps a reference to stub B + * - drop all references to stub A, CommonAPI::ServicePublisher keeps a reference to stub A + * - reference overview: Application > CommonAPI::ServicePublisher > Stub A > Stub B + * - drop all references to CommonAPI::ServicePublisher causes all object references to be dropped + */ +class DBusObjectManagerStub : public DBusInterfaceHandler { +public: + // serialization trick: use bool instead of variant since we never serialize it + typedef std::unordered_map<std::string, bool> DBusPropertiesChangedDict; + typedef std::unordered_map<std::string, DBusPropertiesChangedDict> DBusInterfacesAndPropertiesDict; + typedef std::unordered_map<std::string, DBusInterfacesAndPropertiesDict> DBusObjectPathAndInterfacesDict; + +public: + COMMONAPI_EXPORT DBusObjectManagerStub(const std::string& dbusObjectPath, const std::shared_ptr<DBusProxyConnection>&); + + /** + * Unregisters all currently registered DBusStubAdapter instances from the DBusServicePublisher + */ + COMMONAPI_EXPORT virtual ~DBusObjectManagerStub(); + + /** + * Export DBusStubAdapter instance with the current DBusObjectManagerStub instance. + * + * The DBusStubAdapter must be registered with the DBusServicePublisher! + * + * On registering a + * <a href="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager">InsterfaceAdded</a> + * signal will be emitted with the DBusObjectManagerStub instance's current D-Bus object path. + * + * @param dbusStubAdapter a refernce to DBusStubAdapter instance + * + * @return false if the @a dbusStubAdapter instance was already registered + * @return false if sending the InterfaceAdded signal fails + * + * @see ~DBusObjectManagerStub() + * @see CommonAPI::ServicePublisher + * @see DBusObjectManager + */ + COMMONAPI_EXPORT bool exportManagedDBusStubAdapter(std::shared_ptr<DBusStubAdapter> dbusStubAdapter); + + /** + * Unexport DBusStubAdapter instance from this DBusObjectManagerStub instance. + * + * On unregistering a + * <a href="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager">InsterfaceRemoved</a> + * signal will be emitted with the DBusObjectManagerStub instance's current D-Bus object path. + * + * @param dbusStubAdapter + * + * @return false if @a dbusStubAdapter wasn't registered + * @return true even if sending the InterfaceRemoved signal fails + * + * @see exportDBusStubAdapter() + */ + COMMONAPI_EXPORT bool unexportManagedDBusStubAdapter(std::shared_ptr<DBusStubAdapter> dbusStubAdapter); + + COMMONAPI_EXPORT bool isDBusStubAdapterExported(std::shared_ptr<DBusStubAdapter> dbusStubAdapter); + + COMMONAPI_EXPORT const std::string& getDBusObjectPath() const; + COMMONAPI_EXPORT static const char* getInterfaceName(); + + COMMONAPI_EXPORT virtual const char* getMethodsDBusIntrospectionXmlData() const; + COMMONAPI_EXPORT virtual bool onInterfaceDBusMessage(const DBusMessage& dbusMessage); + COMMONAPI_EXPORT virtual const bool hasFreedesktopProperties(); + + private: + COMMONAPI_EXPORT bool registerDBusStubAdapter(std::shared_ptr<DBusStubAdapter> dbusStubAdapter); + COMMONAPI_EXPORT bool unregisterDBusStubAdapter(std::shared_ptr<DBusStubAdapter> dbusStubAdapter); + + COMMONAPI_EXPORT bool emitInterfacesAddedSignal(std::shared_ptr<DBusStubAdapter> dbusStubAdapter, + const std::shared_ptr<DBusProxyConnection>& dbusConnection) const; + + COMMONAPI_EXPORT bool emitInterfacesRemovedSignal(std::shared_ptr<DBusStubAdapter> dbusStubAdapter, + const std::shared_ptr<DBusProxyConnection>& dbusConnection) const; + + std::string dbusObjectPath_; + std::weak_ptr<DBusProxyConnection> dbusConnection_; + + typedef std::unordered_map<std::string, std::shared_ptr<DBusStubAdapter>> DBusInterfacesMap; + typedef std::unordered_map<std::string, DBusInterfacesMap> DBusObjectPathsMap; + DBusObjectPathsMap registeredDBusObjectPathsMap_; + + std::mutex dbusObjectManagerStubLock_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSFREEDESKTOPOBJECTMANAGERSTUB_HPP_ diff --git a/include/CommonAPI/DBus/DBusOutputStream.hpp b/include/CommonAPI/DBus/DBusOutputStream.hpp new file mode 100644 index 0000000..1148f3e --- /dev/null +++ b/include/CommonAPI/DBus/DBusOutputStream.hpp @@ -0,0 +1,398 @@ +// Copyright (C) 2014-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSOUTPUTSTREAM_HPP_ +#define COMMONAPI_DBUS_DBUSOUTPUTSTREAM_HPP_ + +#include <cassert> +#include <cstring> +#include <memory> +#include <stack> +#include <string> +#include <vector> + +#include <CommonAPI/Export.hpp> +#include <CommonAPI/Logger.hpp> +#include <CommonAPI/OutputStream.hpp> +#include <CommonAPI/DBus/DBusDeployment.hpp> +#include <CommonAPI/DBus/DBusError.hpp> +#include <CommonAPI/DBus/DBusHelper.hpp> +#include <CommonAPI/DBus/DBusMessage.hpp> +#include <CommonAPI/DBus/DBusTypeOutputStream.hpp> + +namespace CommonAPI { +namespace DBus { + +/** + * @class DBusOutputMessageStream + * + * Used to serialize and write data into a #DBusMessage. For all data types that may be written to a #DBusMessage, a "<<"-operator should be defined to handle the writing + * (this operator is predefined for all basic data types and for vectors). The signature that has to be written to the #DBusMessage separately is assumed + * to match the actual data that is inserted via the #DBusOutputMessageStream. + */ +class DBusOutputStream: public OutputStream<DBusOutputStream> { +public: + + /** + * Creates a #DBusOutputMessageStream which can be used to serialize and write data into the given #DBusMessage. Any data written is buffered within the stream. + * Remember to call flush() when you are done with writing: Only then the data actually is written to the #DBusMessage. + * + * @param dbusMessage The #DBusMessage any data pushed into this stream should be written to. + */ + COMMONAPI_EXPORT DBusOutputStream(DBusMessage dbusMessage); + + COMMONAPI_EXPORT OutputStream &writeValue(const bool &_value, const EmptyDeployment *_depl) { + uint32_t tmp = (_value ? 1 : 0); + return _writeValue(tmp); + } + + COMMONAPI_EXPORT OutputStream &writeValue(const int8_t &_value, const EmptyDeployment *_depl) { + return _writeValue(_value); + } + + COMMONAPI_EXPORT OutputStream &writeValue(const int16_t &_value, const EmptyDeployment *_depl) { + return _writeValue(_value); + } + + COMMONAPI_EXPORT OutputStream &writeValue(const int32_t &_value, const EmptyDeployment *_depl) { + return _writeValue(_value); + } + + COMMONAPI_EXPORT OutputStream &writeValue(const int64_t &_value, const EmptyDeployment *_depl) { + return _writeValue(_value); + } + + COMMONAPI_EXPORT OutputStream &writeValue(const uint8_t &_value, const EmptyDeployment *_depl) { + return _writeValue(_value); + } + + COMMONAPI_EXPORT OutputStream &writeValue(const uint16_t &_value, const EmptyDeployment *_depl) { + return _writeValue(_value); + } + + COMMONAPI_EXPORT OutputStream &writeValue(const uint32_t &_value, const EmptyDeployment *_depl) { + return _writeValue(_value); + } + + COMMONAPI_EXPORT OutputStream &writeValue(const uint64_t &_value, const EmptyDeployment *_depl) { + return _writeValue(_value); + } + + COMMONAPI_EXPORT OutputStream &writeValue(const float &_value, const EmptyDeployment *_depl) { + return _writeValue(static_cast<double>(_value)); + } + + COMMONAPI_EXPORT OutputStream &writeValue(const double &_value, const EmptyDeployment *_depl) { + return _writeValue(_value); + } + + COMMONAPI_EXPORT OutputStream &writeValue(const std::string &_value, const EmptyDeployment * = nullptr) { + return writeString(_value.c_str(), _value.length()); + } + + COMMONAPI_EXPORT OutputStream &writeValue(const Version &_value, const EmptyDeployment *_depl = nullptr) { + align(8); + writeValue(_value.Major, _depl); + writeValue(_value.Minor, _depl); + return (*this); + } + + template<class _Deployment, typename _Base> + COMMONAPI_EXPORT OutputStream &writeValue(const Enumeration<_Base> &_value, const _Deployment *_depl = nullptr) { + return writeValue(static_cast<_Base>(_value), _depl); + } + + template<class _Deployment, typename... _Types> + COMMONAPI_EXPORT OutputStream &writeValue(const Struct<_Types...> &_value, const _Deployment *_depl = nullptr) { + align(8); + + const auto itsSize(std::tuple_size<std::tuple<_Types...>>::value); + StructWriter<itsSize-1, DBusOutputStream, Struct<_Types...>, _Deployment>{}((*this), _value, _depl); + + return (*this); + } + + template<class _Deployment, class _PolymorphicStruct> + COMMONAPI_EXPORT OutputStream &writeValue(const std::shared_ptr<_PolymorphicStruct> &_value, const _Deployment *_depl = nullptr) { + align(8); + _writeValue(_value->getSerial()); + + DBusTypeOutputStream typeOutput; + typeOutput.writeType(_value); + writeSignature(typeOutput.getSignature()); + + align(8); + _value->template writeValue<>((*this), _depl); + + return (*this); + } + + template<typename... _Types> + COMMONAPI_EXPORT OutputStream &writeValue(const Variant<_Types...> &_value, const CommonAPI::EmptyDeployment *_depl = nullptr) { + align(8); + writeValue(_value.getValueType(), static_cast<EmptyDeployment *>(nullptr)); + + DBusTypeOutputStream typeOutput; + TypeOutputStreamWriteVisitor<DBusTypeOutputStream> typeVisitor(typeOutput); + ApplyVoidVisitor<TypeOutputStreamWriteVisitor<DBusTypeOutputStream>, + Variant<_Types...>, _Types...>::visit(typeVisitor, _value); + writeSignature(typeOutput.getSignature()); + + OutputStreamWriteVisitor<DBusOutputStream> valueVisitor(*this); + ApplyVoidVisitor<OutputStreamWriteVisitor<DBusOutputStream>, + Variant<_Types...>, _Types...>::visit(valueVisitor, _value); + + return (*this); + } + + template<typename _Deployment, typename... _Types> + COMMONAPI_EXPORT OutputStream &writeValue(const Variant<_Types...> &_value, const _Deployment *_depl = nullptr) { + if (_depl != nullptr && _depl->isFreeDesktop_) { + align(1); + } else { + align(8); + writeValue(_value.getValueType(), static_cast<EmptyDeployment *>(nullptr)); + } + + DBusTypeOutputStream typeOutput; + TypeOutputStreamWriteVisitor<DBusTypeOutputStream> typeVisitor(typeOutput); + ApplyVoidVisitor<TypeOutputStreamWriteVisitor<DBusTypeOutputStream>, + Variant<_Types...>, _Types...>::visit(typeVisitor, _value); + writeSignature(typeOutput.getSignature()); + + OutputStreamWriteVisitor<DBusOutputStream> valueVisitor(*this); + ApplyVoidVisitor<OutputStreamWriteVisitor<DBusOutputStream>, + Variant<_Types...>, _Types...>::visit(valueVisitor, _value); + + return (*this); + } + + template<typename _ElementType> + COMMONAPI_EXPORT OutputStream &writeValue(const std::vector<_ElementType> &_value, + const EmptyDeployment *_depl) { + align(sizeof(uint32_t)); + pushPosition(); + _writeValue(static_cast<uint32_t>(0)); // Placeholder + + alignVector<_ElementType>(); + pushPosition(); // Start of vector data + + for (auto i : _value) { + writeValue(i, _depl); + if (hasError()) { + break; + } + } + + // Write number of written bytes to placeholder position + uint32_t length = getPosition() - popPosition(); + _writeValueAt(popPosition(), length); + + return (*this); + } + + template<class _Deployment, typename _ElementType> + COMMONAPI_EXPORT OutputStream &writeValue(const std::vector<_ElementType> &_value, + const _Deployment *_depl) { + align(sizeof(uint32_t)); + pushPosition(); + _writeValue(static_cast<uint32_t>(0)); // Placeholder + + alignVector<_ElementType>(); + pushPosition(); // Start of vector data + + for (auto i : _value) { + writeValue(i, _depl->elementDepl_); + if (hasError()) { + break; + } + } + + // Write number of written bytes to placeholder position + uint32_t length = getPosition() - popPosition(); + _writeValueAt(popPosition(), length); + + return (*this); + } + + template<typename _KeyType, typename _ValueType, typename _HasherType> + COMMONAPI_EXPORT OutputStream &writeValue(const std::unordered_map<_KeyType, _ValueType, _HasherType> &_value, + const EmptyDeployment *_depl) { + align(sizeof(uint32_t)); + pushPosition(); + _writeValue(static_cast<uint32_t>(0)); // Placeholder + + align(8); + pushPosition(); // Start of map data + + for (auto v : _value) { + align(8); + writeValue(v.first, static_cast<EmptyDeployment *>(nullptr)); + writeValue(v.second, static_cast<EmptyDeployment *>(nullptr)); + + if (hasError()) { + return (*this); + } + } + + // Write number of written bytes to placeholder position + uint32_t length = getPosition() - popPosition(); + _writeValueAt(popPosition(), length); + return (*this); + } + + template<class _Deployment, typename _KeyType, typename _ValueType, typename _HasherType> + COMMONAPI_EXPORT OutputStream &writeValue(const std::unordered_map<_KeyType, _ValueType, _HasherType> &_value, + const _Deployment *_depl) { + align(sizeof(uint32_t)); + pushPosition(); + _writeValue(static_cast<uint32_t>(0)); // Placeholder + + align(8); + pushPosition(); // Start of map data + + for (auto v : _value) { + align(8); + writeValue(v.first, _depl->key_); + writeValue(v.second, _depl->value_); + + if (hasError()) { + return (*this); + } + } + + // Write number of written bytes to placeholder position + uint32_t length = getPosition() - popPosition(); + _writeValueAt(popPosition(), length); + return (*this); + } + + /** + * Writes the data that was buffered within this #DBusOutputMessageStream to the #DBusMessage that was given to the constructor. Each call to flush() + * will completely override the data that currently is contained in the #DBusMessage. The data that is buffered in this #DBusOutputMessageStream is + * not deleted by calling flush(). + */ + COMMONAPI_EXPORT void flush(); + + COMMONAPI_EXPORT bool hasError() const; + +private: + COMMONAPI_EXPORT size_t getPosition(); + COMMONAPI_EXPORT void pushPosition(); + COMMONAPI_EXPORT size_t popPosition(); + + template<typename _Type> + COMMONAPI_EXPORT void alignVector(typename std::enable_if<!std::is_class<_Type>::value>::type * = nullptr, + typename std::enable_if<!is_std_vector<_Type>::value>::type * = nullptr, + typename std::enable_if<!is_std_unordered_map<_Type>::value>::type * = nullptr) { + if (4 < sizeof(_Type)) align(8); + } + + template<typename _Type> + COMMONAPI_EXPORT void alignVector(typename std::enable_if<!std::is_same<_Type, std::string>::value>::type * = nullptr, + typename std::enable_if<std::is_class<_Type>::value>::type * = nullptr, + typename std::enable_if<!is_std_vector<_Type>::value>::type * = nullptr, + typename std::enable_if<!is_std_unordered_map<_Type>::value>::type * = nullptr) { + align(8); + } + + template<typename _Type> + COMMONAPI_EXPORT void alignVector(typename std::enable_if<std::is_same<_Type, std::string>::value>::type * = nullptr) { + // Intentionally do nothing + } + + template<typename _Type> + COMMONAPI_EXPORT void alignVector(typename std::enable_if<is_std_vector<_Type>::value>::type * = nullptr) { + // Intentionally do nothing + } + + template<typename _Type> + COMMONAPI_EXPORT void alignVector(typename std::enable_if<is_std_unordered_map<_Type>::value>::type * = nullptr) { + align(4); + } + + COMMONAPI_EXPORT void setError(); + + /** + * Reserves the given number of bytes for writing, thereby negating the need to dynamically allocate memory while writing. + * Use this method for optimization: If possible, reserve as many bytes as you need for your data before doing any writing. + * + * @param numOfBytes The number of bytes that should be reserved for writing. + */ + COMMONAPI_EXPORT void reserveMemory(size_t numOfBytes); + + template<typename _Type> + COMMONAPI_EXPORT DBusOutputStream &_writeValue(const _Type &_value) { + if (sizeof(_Type) > 1) + align(sizeof(_Type)); + + _writeRaw(reinterpret_cast<const char*>(&_value), sizeof(_Type)); + return (*this); + } + + template<typename _Type> + COMMONAPI_EXPORT void _writeValueAt(size_t _position, const _Type &_value) { + assert(_position + sizeof(_Type) <= payload_.size()); + _writeRawAt(reinterpret_cast<const char *>(&_value), + sizeof(_Type), _position); + } + + COMMONAPI_EXPORT DBusOutputStream &writeString(const char *_data, const uint32_t &_length); + + /** + * Fills the stream with 0-bytes to make the next value be aligned to the boundary given. + * This means that as many 0-bytes are written to the buffer as are necessary + * to make the next value start with the given alignment. + * + * @param alignBoundary The byte-boundary to which the next value should be aligned. + */ + COMMONAPI_EXPORT void align(const size_t _boundary); + + /** + * Takes sizeInByte characters, starting from the character which val points to, and stores them for later writing. + * When calling flush(), all values that were written to this stream are copied into the payload of the #DBusMessage. + * + * The array of characters might be created from a pointer to a given value by using a reinterpret_cast. Example: + * @code + * ... + * int32_t val = 15; + * outputMessageStream.alignForBasicType(sizeof(int32_t)); + * const char* const reinterpreted = reinterpret_cast<const char*>(&val); + * outputMessageStream.writeValue(reinterpreted, sizeof(int32_t)); + * ... + * @endcode + * + * @param _data The array of chars that should serve as input + * @param _size The number of bytes that should be written + * @return true if writing was successful, false otherwise. + * + * @see DBusOutputMessageStream() + * @see flush() + */ + COMMONAPI_EXPORT void _writeRaw(const char *_data, const size_t _size); + COMMONAPI_EXPORT void _writeRawAt(const char *_data, const size_t _size, size_t _position); + +protected: + std::string payload_; + +private: + COMMONAPI_EXPORT void writeSignature(const std::string& signature); + + COMMONAPI_EXPORT size_t getCurrentStreamPosition(); + + DBusError dbusError_; + DBusMessage dbusMessage_; + + std::stack<size_t> positions_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSOUTPUTSTREAM_PPH_ diff --git a/include/CommonAPI/DBus/DBusProxy.hpp b/include/CommonAPI/DBus/DBusProxy.hpp new file mode 100644 index 0000000..af40863 --- /dev/null +++ b/include/CommonAPI/DBus/DBusProxy.hpp @@ -0,0 +1,84 @@ +// Copyright (C) 2014-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSPROXY_HPP_ +#define COMMONAPI_DBUS_DBUSPROXY_HPP_ + +#include <functional> +#include <memory> +#include <string> + +#include <CommonAPI/Export.hpp> +#include <CommonAPI/DBus/DBusAttribute.hpp> +#include <CommonAPI/DBus/DBusServiceRegistry.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusProxyStatusEvent + : public ProxyStatusEvent { + friend class DBusProxy; + + public: + DBusProxyStatusEvent(DBusProxy* dbusProxy); + virtual ~DBusProxyStatusEvent() {} + + protected: + virtual void onListenerAdded(const Listener& listener); + + DBusProxy* dbusProxy_; +}; + + +class DBusProxy + : public DBusProxyBase { +public: + COMMONAPI_EXPORT DBusProxy(const DBusAddress &_address, + const std::shared_ptr<DBusProxyConnection> &_connection); + COMMONAPI_EXPORT virtual ~DBusProxy(); + + COMMONAPI_EXPORT virtual ProxyStatusEvent& getProxyStatusEvent(); + COMMONAPI_EXPORT virtual InterfaceVersionAttribute& getInterfaceVersionAttribute(); + + COMMONAPI_EXPORT virtual bool isAvailable() const; + COMMONAPI_EXPORT virtual bool isAvailableBlocking() const; + + COMMONAPI_EXPORT DBusProxyConnection::DBusSignalHandlerToken subscribeForSelectiveBroadcastOnConnection( + bool& subscriptionAccepted, + const std::string& objectPath, + const std::string& interfaceName, + const std::string& interfaceMemberName, + const std::string& interfaceMemberSignature, + DBusProxyConnection::DBusSignalHandler* dbusSignalHandler); + COMMONAPI_EXPORT void unsubscribeFromSelectiveBroadcast(const std::string& eventName, + DBusProxyConnection::DBusSignalHandlerToken subscription, + const DBusProxyConnection::DBusSignalHandler* dbusSignalHandler); + + COMMONAPI_EXPORT void init(); + +private: + COMMONAPI_EXPORT DBusProxy(const DBusProxy &) = delete; + + COMMONAPI_EXPORT void onDBusServiceInstanceStatus(const AvailabilityStatus& availabilityStatus); + + DBusProxyStatusEvent dbusProxyStatusEvent_; + DBusServiceRegistry::DBusServiceSubscription dbusServiceRegistrySubscription_; + AvailabilityStatus availabilityStatus_; + + DBusReadonlyAttribute<InterfaceVersionAttribute> interfaceVersionAttribute_; + + std::shared_ptr<DBusServiceRegistry> dbusServiceRegistry_; +}; + + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSPROXY_HPP_ + diff --git a/include/CommonAPI/DBus/DBusProxyAsyncCallbackHandler.hpp b/include/CommonAPI/DBus/DBusProxyAsyncCallbackHandler.hpp new file mode 100644 index 0000000..eac96d2 --- /dev/null +++ b/include/CommonAPI/DBus/DBusProxyAsyncCallbackHandler.hpp @@ -0,0 +1,78 @@ +// Copyright (C) 2014-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSPROXYASYNCCALLBACKHANDLER_HPP_ +#define COMMONAPI_DBUS_DBUSPROXYASYNCCALLBACKHANDLER_HPP_ + +#include <functional> +#include <future> +#include <memory> + +#include <CommonAPI/DBus/DBusHelper.hpp> +#include <CommonAPI/DBus/DBusMessage.hpp> +#include <CommonAPI/DBus/DBusProxyConnection.hpp> +#include <CommonAPI/DBus/DBusSerializableArguments.hpp> + +namespace CommonAPI { +namespace DBus { + +template<typename ... _ArgTypes> +class DBusProxyAsyncCallbackHandler: public DBusProxyConnection::DBusMessageReplyAsyncHandler { + public: + typedef std::function<void(CallStatus, _ArgTypes...)> FunctionType; + + static std::unique_ptr<DBusProxyConnection::DBusMessageReplyAsyncHandler> create( + FunctionType&& callback, std::tuple<_ArgTypes...> args) { + return std::unique_ptr<DBusProxyConnection::DBusMessageReplyAsyncHandler>( + new DBusProxyAsyncCallbackHandler(std::move(callback), args)); + } + + DBusProxyAsyncCallbackHandler() = delete; + DBusProxyAsyncCallbackHandler(FunctionType&& callback, std::tuple<_ArgTypes...> args): + callback_(std::move(callback)), args_(args) { + } + virtual ~DBusProxyAsyncCallbackHandler() {} + + virtual std::future<CallStatus> getFuture() { + return promise_.get_future(); + } + + virtual void onDBusMessageReply(const CallStatus& dbusMessageCallStatus, const DBusMessage& dbusMessage) { + promise_.set_value(handleDBusMessageReply(dbusMessageCallStatus, dbusMessage, typename make_sequence<sizeof...(_ArgTypes)>::type(), args_)); + } + + private: + template <int... _ArgIndices> + inline CallStatus handleDBusMessageReply(const CallStatus dbusMessageCallStatus, const DBusMessage& dbusMessage, index_sequence<_ArgIndices...>, std::tuple<_ArgTypes...> argTuple) const { + CallStatus callStatus = dbusMessageCallStatus; + + if (dbusMessageCallStatus == CallStatus::SUCCESS) { + if (!dbusMessage.isErrorType()) { + DBusInputStream dbusInputStream(dbusMessage); + const bool success = DBusSerializableArguments<_ArgTypes...>::deserialize(dbusInputStream, std::get<_ArgIndices>(argTuple)...); + if (!success) + callStatus = CallStatus::REMOTE_ERROR; + } else { + callStatus = CallStatus::REMOTE_ERROR; + } + } + + callback_(callStatus, std::move(std::get<_ArgIndices>(argTuple))...); + return callStatus; + } + + std::promise<CallStatus> promise_; + const FunctionType callback_; + std::tuple<_ArgTypes...> args_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSPROXYASYNCCALLBACKHANDLER_HPP_ diff --git a/include/CommonAPI/DBus/DBusProxyBase.hpp b/include/CommonAPI/DBus/DBusProxyBase.hpp new file mode 100644 index 0000000..85c746c --- /dev/null +++ b/include/CommonAPI/DBus/DBusProxyBase.hpp @@ -0,0 +1,72 @@ +// Copyright (C) 2014-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSPROXYBASE_HPP_ +#define COMMONAPI_DBUS_DBUSPROXYBASE_HPP_ + +#include <functional> +#include <memory> +#include <string> + +#include <CommonAPI/Export.hpp> +#include <CommonAPI/Proxy.hpp> +#include <CommonAPI/Types.hpp> + +#include <CommonAPI/DBus/DBusAddress.hpp> +#include <CommonAPI/DBus/DBusProxyConnection.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusAddress; + +class DBusProxyBase + : public virtual CommonAPI::Proxy { +public: + COMMONAPI_EXPORT DBusProxyBase(const DBusAddress &_address, + const std::shared_ptr<DBusProxyConnection> &_connection); + COMMONAPI_EXPORT virtual ~DBusProxyBase() {} + + COMMONAPI_EXPORT const DBusAddress &getDBusAddress() const; + COMMONAPI_EXPORT const std::shared_ptr<DBusProxyConnection> &getDBusConnection() const; + + COMMONAPI_EXPORT DBusMessage createMethodCall(const std::string &_method, + const std::string &_signature = "") const; + + COMMONAPI_EXPORT DBusProxyConnection::DBusSignalHandlerToken addSignalMemberHandler( + const std::string& signalName, + const std::string& signalSignature, + DBusProxyConnection::DBusSignalHandler* dbusSignalHandler, + const bool justAddFilter = false); + + COMMONAPI_EXPORT DBusProxyConnection::DBusSignalHandlerToken addSignalMemberHandler( + const std::string &objectPath, + const std::string &interfaceName, + const std::string &signalName, + const std::string &signalSignature, + DBusProxyConnection::DBusSignalHandler *dbusSignalHandler, + const bool justAddFilter = false); + + COMMONAPI_EXPORT bool removeSignalMemberHandler( + const DBusProxyConnection::DBusSignalHandlerToken &_token, + const DBusProxyConnection::DBusSignalHandler *_handler = NULL); + + COMMONAPI_EXPORT virtual void init() = 0; + + private: + COMMONAPI_EXPORT DBusProxyBase(const DBusProxyBase &) = delete; + + DBusAddress dbusAddress_; + std::shared_ptr<DBusProxyConnection> connection_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSPROXYBASE_HPP_ diff --git a/include/CommonAPI/DBus/DBusProxyConnection.hpp b/include/CommonAPI/DBus/DBusProxyConnection.hpp new file mode 100644 index 0000000..cbef54d --- /dev/null +++ b/include/CommonAPI/DBus/DBusProxyConnection.hpp @@ -0,0 +1,127 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSPROXYCONNECTION_HPP_ +#define COMMONAPI_DBUS_DBUSPROXYCONNECTION_HPP_ + +#include <cstdint> +#include <functional> +#include <future> +#include <memory> +#include <set> +#include <tuple> +#include <unordered_map> +#include <utility> +#include <vector> + +#include <CommonAPI/Attribute.hpp> +#include <CommonAPI/Event.hpp> +#include <CommonAPI/Types.hpp> +#include <CommonAPI/DBus/DBusConfig.hpp> +#include <CommonAPI/DBus/DBusError.hpp> +#include <CommonAPI/DBus/DBusMessage.hpp> +#include <CommonAPI/DBus/DBusFunctionalHash.hpp> + +namespace CommonAPI { +namespace DBus { + +typedef std::function<void(const DBusMessage&)> DBusMessageHandler; + +class DBusDaemonProxy; +class DBusServiceRegistry; +class DBusObjectManager; +class DBusProxy; + +class DBusProxyConnection { + public: + class DBusMessageReplyAsyncHandler { + public: + virtual ~DBusMessageReplyAsyncHandler() {} + virtual std::future<CallStatus> getFuture() = 0; + virtual void onDBusMessageReply(const CallStatus&, const DBusMessage&) = 0; + }; + + class DBusSignalHandler { + public: + virtual ~DBusSignalHandler() {} + virtual void onSignalDBusMessage(const DBusMessage&) = 0; + }; + + // objectPath, interfaceName, interfaceMemberName, interfaceMemberSignature + typedef std::tuple<std::string, std::string, std::string, std::string> DBusSignalHandlerPath; + typedef std::unordered_map<DBusSignalHandlerPath, std::pair<std::shared_ptr<std::recursive_mutex>, std::set<DBusSignalHandler* >>> DBusSignalHandlerTable; + typedef DBusSignalHandlerPath DBusSignalHandlerToken; + + typedef Event<AvailabilityStatus> ConnectionStatusEvent; + + virtual ~DBusProxyConnection() {} + + virtual bool isConnected() const = 0; + + virtual ConnectionStatusEvent& getConnectionStatusEvent() = 0; + + virtual bool sendDBusMessage(const DBusMessage& dbusMessage) const = 0; + + virtual std::future<CallStatus> sendDBusMessageWithReplyAsync( + const DBusMessage& dbusMessage, + std::unique_ptr<DBusMessageReplyAsyncHandler> dbusMessageReplyAsyncHandler, + const CommonAPI::CallInfo *_info) const = 0; + + virtual DBusMessage sendDBusMessageWithReplyAndBlock( + const DBusMessage& dbusMessage, + DBusError& dbusError, + const CommonAPI::CallInfo *_info) const = 0; + + virtual DBusSignalHandlerToken addSignalMemberHandler( + const std::string& objectPath, + const std::string& interfaceName, + const std::string& interfaceMemberName, + const std::string& interfaceMemberSignature, + DBusSignalHandler* dbusSignalHandler, + const bool justAddFilter = false) = 0; + + virtual DBusSignalHandlerToken subscribeForSelectiveBroadcast(bool& subscriptionAccepted, + const std::string& objectPath, + const std::string& interfaceName, + const std::string& interfaceMemberName, + const std::string& interfaceMemberSignature, + DBusSignalHandler* dbusSignalHandler, + DBusProxy* callingProxy) = 0; + + virtual void unsubscribeFromSelectiveBroadcast(const std::string& eventName, + DBusProxyConnection::DBusSignalHandlerToken subscription, + DBusProxy* callingProxy, + const DBusSignalHandler* dbusSignalHandler) = 0; + + virtual bool removeSignalMemberHandler(const DBusSignalHandlerToken& dbusSignalHandlerToken, + const DBusSignalHandler* dbusSignalHandler = NULL) = 0; + + virtual bool addObjectManagerSignalMemberHandler(const std::string& dbusBusName, + DBusSignalHandler* dbusSignalHandler) = 0; + virtual bool removeObjectManagerSignalMemberHandler(const std::string& dbusBusName, + DBusSignalHandler* dbusSignalHandler) = 0; + + virtual const std::shared_ptr<DBusObjectManager> getDBusObjectManager() = 0; + + virtual void registerObjectPath(const std::string& objectPath) = 0; + virtual void unregisterObjectPath(const std::string& objectPath) = 0; + + virtual bool requestServiceNameAndBlock(const std::string& serviceName) const = 0; + virtual bool releaseServiceName(const std::string& serviceName) const = 0; + + typedef std::function<bool(const DBusMessage&)> DBusObjectPathMessageHandler; + + virtual void setObjectPathMessageHandler(DBusObjectPathMessageHandler) = 0; + virtual bool isObjectPathMessageHandlerSet() = 0; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSPROXYCONNECTION_HPP_ diff --git a/include/CommonAPI/DBus/DBusProxyHelper.hpp b/include/CommonAPI/DBus/DBusProxyHelper.hpp new file mode 100644 index 0000000..516d923 --- /dev/null +++ b/include/CommonAPI/DBus/DBusProxyHelper.hpp @@ -0,0 +1,258 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSPROXYHELPER_HPP_ +#define COMMONAPI_DBUS_DBUSPROXYHELPER_HPP_ + +#include <functional> +#include <future> +#include <memory> +#include <string> + +#include <CommonAPI/DBus/DBusAddress.hpp> +#include <CommonAPI/DBus/DBusConfig.hpp> +#include <CommonAPI/DBus/DBusMessage.hpp> +#include <CommonAPI/DBus/DBusSerializableArguments.hpp> +#include <CommonAPI/DBus/DBusProxyAsyncCallbackHandler.hpp> +#include <CommonAPI/DBus/DBusProxyConnection.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusProxy; + +template< class, class > +struct DBusProxyHelper; + +template< + template<class ...> class _In, class... _InArgs, + template <class...> class _Out, class... _OutArgs> +struct DBusProxyHelper<_In<DBusInputStream, DBusOutputStream, _InArgs...>, + _Out<DBusInputStream, DBusOutputStream, _OutArgs...>> { + + template <typename _DBusProxy = DBusProxy> + static void callMethod(const _DBusProxy &_proxy, + const std::string &_method, + const std::string &_signature, + const _InArgs&... _in, + CommonAPI::CallStatus &_status) { + + if (_proxy.isAvailableBlocking()) { + DBusMessage message = _proxy.createMethodCall(_method, _signature); + if (sizeof...(_InArgs) > 0) { + DBusOutputStream output(message); + if (DBusSerializableArguments<_InArgs...>::serialize(output, _in...)) { + _status = CallStatus::OUT_OF_MEMORY; + return; + } + output.flush(); + } + + const bool isSent = _proxy.getDBusConnection()->sendDBusMessage(message); + _status = (isSent ? CallStatus::SUCCESS : CallStatus::OUT_OF_MEMORY); + } else { + _status = CallStatus::NOT_AVAILABLE; + } + } + + template <typename _DBusProxy = DBusProxy> + static void callMethodWithReply( + const _DBusProxy &_proxy, + DBusMessage &_message, + const CommonAPI::CallInfo *_info, + const _InArgs&... _in, + CommonAPI::CallStatus &_status, + _OutArgs&... _out) { + + if (sizeof...(_InArgs) > 0) { + DBusOutputStream output(_message); + if (!DBusSerializableArguments<_InArgs...>::serialize(output, _in...)) { + _status = CallStatus::OUT_OF_MEMORY; + return; + } + output.flush(); + } + + DBusError error; + DBusMessage reply = _proxy.getDBusConnection()->sendDBusMessageWithReplyAndBlock(_message, error, _info); + if (error || !reply.isMethodReturnType()) { + _status = CallStatus::REMOTE_ERROR; + return; + } + + if (sizeof...(_OutArgs) > 0) { + DBusInputStream input(reply); + if (!DBusSerializableArguments<_OutArgs...>::deserialize(input, _out...)) { + _status = CallStatus::REMOTE_ERROR; + return; + } + } + _status = CallStatus::SUCCESS; + } + + template <typename _DBusProxy = DBusProxy> + static void callMethodWithReply( + const _DBusProxy &_proxy, + const DBusAddress &_address, + const char *_method, + const char *_signature, + const CommonAPI::CallInfo *_info, + const _InArgs&... _in, + CommonAPI::CallStatus &_status, + _OutArgs&... _out) { + if (_proxy.isAvailableBlocking()) { + DBusMessage message = DBusMessage::createMethodCall(_address, _method, _signature); + callMethodWithReply(_proxy, message, _info, _in..., _status, _out...); + } else { + _status = CallStatus::NOT_AVAILABLE; + } + } + + template <typename _DBusProxy = DBusProxy> + static void callMethodWithReply( + const _DBusProxy &_proxy, + const std::string &_interface, + const std::string &_method, + const std::string &_signature, + const CommonAPI::CallInfo *_info, + const _InArgs&... _in, + CommonAPI::CallStatus &_status, + _OutArgs&... _out) { + DBusAddress itsAddress(_proxy.getDBusAddress()); + itsAddress.setInterface(_interface); + callMethodWithReply( + _proxy, itsAddress, + _method.c_str(), _signature.c_str(), + _info, + _in..., _status, _out...); + } + + template <typename _DBusProxy = DBusProxy> + static void callMethodWithReply( + const _DBusProxy &_proxy, + const std::string &_method, + const std::string &_signature, + const CommonAPI::CallInfo *_info, + const _InArgs&... _in, + CommonAPI::CallStatus &_status, + _OutArgs&... _out) { + if (_proxy.isAvailableBlocking()) { + DBusMessage message = _proxy.createMethodCall(_method, _signature); + callMethodWithReply(_proxy, message, _info, _in..., _status, _out...); + } else { + _status = CallStatus::NOT_AVAILABLE; + } + } + + template <typename _DBusProxy = DBusProxy, typename _AsyncCallback> + static std::future<CallStatus> callMethodAsync( + const _DBusProxy &_proxy, + DBusMessage &_message, + const CommonAPI::CallInfo *_info, + const _InArgs&... _in, + _AsyncCallback _callback, + std::tuple<_OutArgs...> _out) { + if (sizeof...(_InArgs) > 0) { + DBusOutputStream output(_message); + const bool success = DBusSerializableArguments< + _InArgs... + >::serialize(output, _in...); + if (!success) { + std::promise<CallStatus> promise; + promise.set_value(CallStatus::OUT_OF_MEMORY); + return promise.get_future(); + } + output.flush(); + } + + return _proxy.getDBusConnection()->sendDBusMessageWithReplyAsync( + _message, + DBusProxyAsyncCallbackHandler< + _OutArgs... + >::create(std::move(_callback), _out), + _info); + } + + template <typename _DBusProxy = DBusProxy, typename _AsyncCallback> + static std::future<CallStatus> callMethodAsync( + const _DBusProxy &_proxy, + const DBusAddress &_address, + const std::string &_method, + const std::string &_signature, + const CommonAPI::CallInfo *_info, + const _InArgs&... _in, + _AsyncCallback _callback, + std::tuple<_OutArgs...> _out) { + if (_proxy.isAvailable()) { + DBusMessage message = DBusMessage::createMethodCall(_address, _method, _signature); + return callMethodAsync(_proxy, message, _info, _in..., _callback, _out); + } else { + CallStatus status = CallStatus::NOT_AVAILABLE; + callCallbackOnNotAvailable(_callback, typename make_sequence<sizeof...(_OutArgs)>::type(), _out); + + std::promise<CallStatus> promise; + promise.set_value(status); + return promise.get_future(); + } + } + + template <typename _DBusProxy = DBusProxy, typename _AsyncCallback> + static std::future<CallStatus> callMethodAsync( + const _DBusProxy &_proxy, + const std::string &_interface, + const std::string &_method, + const std::string &_signature, + const CommonAPI::CallInfo *_info, + const _InArgs&... _in, + _AsyncCallback _callback, + std::tuple<_OutArgs...> _out) { + DBusAddress itsAddress(_proxy.getDBusAddress()); + itsAddress.setInterface(_interface); + return callMethodAsync( + _proxy, itsAddress, + _method, _signature, + _info, + _in..., _callback, _out); + } + + template <typename _DBusProxy = DBusProxy, typename _AsyncCallback> + static std::future<CallStatus> callMethodAsync( + const _DBusProxy &_proxy, + const std::string &_method, + const std::string &_signature, + const CommonAPI::CallInfo *_info, + const _InArgs&... _in, + _AsyncCallback _callback, + std::tuple<_OutArgs...> _out) { + if (_proxy.isAvailable()) { + DBusMessage message = _proxy.createMethodCall(_method, _signature); + return callMethodAsync(_proxy, message, _info, _in..., _callback, _out); + } else { + callCallbackOnNotAvailable( + _callback, typename make_sequence<sizeof...(_OutArgs)>::type(), _out); + + CallStatus status = CallStatus::NOT_AVAILABLE; + std::promise<CallStatus> promise; + promise.set_value(status); + return promise.get_future(); + } + } + + template <int... _ArgIndices> + static void callCallbackOnNotAvailable(std::function<void(CallStatus, _OutArgs&...)> _callback, + index_sequence<_ArgIndices...>, std::tuple<_OutArgs...> _out) { + const CallStatus status(CallStatus::NOT_AVAILABLE); + _callback(status, std::get<_ArgIndices>(_out)...); + } +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSPROXYHELPER_HPP_ diff --git a/include/CommonAPI/DBus/DBusProxyManager.hpp b/include/CommonAPI/DBus/DBusProxyManager.hpp new file mode 100644 index 0000000..a28527a --- /dev/null +++ b/include/CommonAPI/DBus/DBusProxyManager.hpp @@ -0,0 +1,72 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_PROXYMANAGER_HPP_ +#define COMMONAPI_DBUS_PROXYMANAGER_HPP_ + +#include <functional> +#include <future> +#include <string> +#include <vector> + +#include <CommonAPI/Export.hpp> +#include <CommonAPI/ProxyManager.hpp> +#include <CommonAPI/DBus/DBusProxy.hpp> +#include <CommonAPI/DBus/DBusFactory.hpp> +#include <CommonAPI/DBus/DBusObjectManagerStub.hpp> +#include <CommonAPI/DBus/DBusInstanceAvailabilityStatusChangedEvent.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusProxyManager: public ProxyManager { +public: + COMMONAPI_EXPORT DBusProxyManager(DBusProxy &_proxy, + const std::string &_interfaceName); + + COMMONAPI_EXPORT const std::string &getDomain() const; + COMMONAPI_EXPORT const std::string &getInterface() const; + COMMONAPI_EXPORT const ConnectionId_t &getConnectionId() const; + + COMMONAPI_EXPORT virtual void getAvailableInstances(CommonAPI::CallStatus &, std::vector<std::string> &_instances); + COMMONAPI_EXPORT virtual std::future<CallStatus> getAvailableInstancesAsync(GetAvailableInstancesCallback _callback); + + COMMONAPI_EXPORT virtual void getInstanceAvailabilityStatus(const std::string &_address, + CallStatus &_callStatus, + AvailabilityStatus &_availabilityStatus); + + COMMONAPI_EXPORT virtual std::future<CallStatus> getInstanceAvailabilityStatusAsync( + const std::string&, + GetInstanceAvailabilityStatusCallback callback); + + COMMONAPI_EXPORT virtual InstanceAvailabilityStatusChangedEvent& getInstanceAvailabilityStatusChangedEvent(); + +private: + COMMONAPI_EXPORT void instancesAsyncCallback(const CommonAPI::CallStatus& status, + const DBusObjectManagerStub::DBusObjectPathAndInterfacesDict& dict, + GetAvailableInstancesCallback& call); + + COMMONAPI_EXPORT void instanceAliveAsyncCallback(const AvailabilityStatus &_alive, + GetInstanceAvailabilityStatusCallback &_call, + std::shared_ptr<std::promise<CallStatus>> &_status); + + COMMONAPI_EXPORT void translateCommonApiAddresses(const DBusObjectManagerStub::DBusObjectPathAndInterfacesDict &_dict, + std::vector<std::string> &_instances); + + DBusProxy &proxy_; + DBusInstanceAvailabilityStatusChangedEvent instanceAvailabilityStatusEvent_; + const std::string interfaceId_; + const std::shared_ptr<DBusServiceRegistry> registry_; + ConnectionId_t connectionId_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_PROXYMANAGER_HPP_ diff --git a/include/CommonAPI/DBus/DBusSelectiveEvent.hpp b/include/CommonAPI/DBus/DBusSelectiveEvent.hpp new file mode 100644 index 0000000..65c61f9 --- /dev/null +++ b/include/CommonAPI/DBus/DBusSelectiveEvent.hpp @@ -0,0 +1,56 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSSELECTIVEEVENT_HPP_ +#define COMMONAPI_DBUS_DBUSSELECTIVEEVENT_HPP_ + +#include <CommonAPI/DBus/DBusEvent.hpp> + +namespace CommonAPI { +namespace DBus { + +template<typename _EventType, typename... _Arguments> +class DBusSelectiveEvent: public DBusEvent<_EventType, _Arguments...> { +public: + typedef typename DBusEvent<_EventType, _Arguments...>::Listener Listener; + typedef DBusEvent<_EventType, _Arguments...> DBusEventBase; + + DBusSelectiveEvent(DBusProxy &_proxy, + const char *_name, const char *_signature, + std::tuple<_Arguments...> _arguments) + : DBusEventBase(_proxy, _name, _signature, _arguments) { + } + + DBusSelectiveEvent(DBusProxy &_proxy, + const char *_name, const char *_signature, + const char *_path, const char *_interface, + std::tuple<_Arguments...> _arguments) + : DBusEventBase(_proxy, _name, _signature, _path, _interface, _arguments) { + } + + virtual ~DBusSelectiveEvent() {} + +protected: + void onFirstListenerAdded(const Listener &) { + bool success; + this->subscription_ + = static_cast<DBusProxy&>(this->proxy_).subscribeForSelectiveBroadcastOnConnection( + success, this->path_, this->interface_, this->name_, this->signature_, this); + } + + void onLastListenerRemoved(const Listener &) { + static_cast<DBusProxy&>(this->proxy_).unsubscribeFromSelectiveBroadcast( + this->name_, this->subscription_, this); + } +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSSELECTIVEEVENT_HPP_ diff --git a/include/CommonAPI/DBus/DBusSerializableArguments.hpp b/include/CommonAPI/DBus/DBusSerializableArguments.hpp new file mode 100644 index 0000000..f763ce5 --- /dev/null +++ b/include/CommonAPI/DBus/DBusSerializableArguments.hpp @@ -0,0 +1,30 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUS_SERIALIZABLEARGUMENTS_HPP_ +#define COMMONAPI_DBUS_DBUS_SERIALIZABLEARGUMENTS_HPP_ + +#include <CommonAPI/SerializableArguments.hpp> +#include <CommonAPI/DBus/DBusInputStream.hpp> +#include <CommonAPI/DBus/DBusOutputStream.hpp> + +namespace CommonAPI { +namespace DBus { + +template<typename... _Arguments> +using DBusSerializableArguments = CommonAPI::SerializableArguments< + DBusInputStream, + DBusOutputStream, + _Arguments... + >; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSSERIALIZABLEARGUMENTS_HPP_ diff --git a/include/CommonAPI/DBus/DBusServiceRegistry.hpp b/include/CommonAPI/DBus/DBusServiceRegistry.hpp new file mode 100644 index 0000000..3dbcf1b --- /dev/null +++ b/include/CommonAPI/DBus/DBusServiceRegistry.hpp @@ -0,0 +1,315 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSSERVICEREGISTRY_HPP_ +#define COMMONAPI_DBUS_DBUSSERVICEREGISTRY_HPP_ + +#include <algorithm> +#include <condition_variable> +#include <future> +#include <map> +#include <memory> +#include <mutex> +#include <set> +#include <string> +#include <unordered_map> +#include <unordered_set> +#include <utility> +#include <vector> +#include <list> + +#include <pugixml/pugixml.hpp> + +#include <CommonAPI/Attribute.hpp> +#include <CommonAPI/Proxy.hpp> +#include <CommonAPI/Types.hpp> +#include <CommonAPI/DBus/DBusProxyConnection.hpp> +#include <CommonAPI/DBus/DBusFactory.hpp> + +namespace CommonAPI { +namespace DBus { + +typedef Event<std::string, std::string, std::string> NameOwnerChangedEvent; +typedef Event<std::string, std::string, std::string>::Subscription NameOwnerChangedEventSubscription; + +// Connection name, Object path +typedef std::pair<std::string, std::string> DBusInstanceId; + +class DBusAddress; +class DBusAddressTranslator; +class DBusDaemonProxy; + +class DBusServiceRegistry: public std::enable_shared_from_this<DBusServiceRegistry>, + public DBusProxyConnection::DBusSignalHandler { + public: + enum class DBusRecordState { + UNKNOWN, + AVAILABLE, + RESOLVING, + RESOLVED, + NOT_AVAILABLE + }; + + // template class DBusServiceListener<> { typedef functor; typedef list; typedef subscription } + typedef std::function<void(const AvailabilityStatus& availabilityStatus)> DBusServiceListener; + typedef std::list<DBusServiceListener> DBusServiceListenerList; + typedef DBusServiceListenerList::iterator DBusServiceSubscription; + + typedef std::function<void(const std::vector<std::string>& interfaces, + const AvailabilityStatus& availabilityStatus)> DBusManagedInterfaceListener; + typedef std::list<DBusManagedInterfaceListener> DBusManagedInterfaceListenerList; + typedef DBusManagedInterfaceListenerList::iterator DBusManagedInterfaceSubscription; + + static std::shared_ptr<DBusServiceRegistry> get(std::shared_ptr<DBusProxyConnection> _connection); + + DBusServiceRegistry(std::shared_ptr<DBusProxyConnection> dbusProxyConnection); + + DBusServiceRegistry(const DBusServiceRegistry&) = delete; + DBusServiceRegistry& operator=(const DBusServiceRegistry&) = delete; + + virtual ~DBusServiceRegistry(); + + void init(); + + DBusServiceSubscription subscribeAvailabilityListener(const std::string &_address, + DBusServiceListener _listener); + + void unsubscribeAvailabilityListener(const std::string &_address, + DBusServiceSubscription &_listener); + + + bool isServiceInstanceAlive(const std::string &_dbusInterfaceName, + const std::string &_dbusConnectionName, + const std::string &_dbusObjectPath); + + + virtual std::vector<std::string> getAvailableServiceInstances(const std::string &_interface, + const std::string &_domain = "local"); + + virtual void getAvailableServiceInstancesAsync(CommonAPI::Factory::AvailableInstancesCbk_t _cbk, + const std::string &_interface, + const std::string &_domain = "local"); + + virtual void onSignalDBusMessage(const DBusMessage&); + + private: + struct DBusInterfaceNameListenersRecord { + DBusInterfaceNameListenersRecord() + : state(DBusRecordState::UNKNOWN) { + } + + DBusInterfaceNameListenersRecord(DBusInterfaceNameListenersRecord &&_other) + : state(_other.state), + listenerList(std::move(_other.listenerList)) { + } + + DBusRecordState state; + DBusServiceListenerList listenerList; + }; + + typedef std::unordered_map<std::string, DBusInterfaceNameListenersRecord> DBusInterfaceNameListenersMap; + + struct DBusServiceListenersRecord { + DBusServiceListenersRecord() + : uniqueBusNameState(DBusRecordState::UNKNOWN), + mutexOnResolve() { + } + + DBusServiceListenersRecord(DBusServiceListenersRecord&& other) + : uniqueBusNameState(other.uniqueBusNameState), + uniqueBusName(std::move(other.uniqueBusName)), + promiseOnResolve(std::move(other.promiseOnResolve)), + futureOnResolve(std::move(other.futureOnResolve)), + mutexOnResolve(std::move(other.mutexOnResolve)), + dbusObjectPathListenersMap(std::move(other.dbusObjectPathListenersMap)) { + } + + ~DBusServiceListenersRecord() {}; + + DBusRecordState uniqueBusNameState; + std::string uniqueBusName; + + std::promise<DBusRecordState> promiseOnResolve; + std::shared_future<DBusRecordState> futureOnResolve; + std::unique_lock<std::mutex>* mutexOnResolve; + + std::unordered_map<std::string, DBusInterfaceNameListenersMap> dbusObjectPathListenersMap; + }; + + std::unordered_map<std::string, DBusServiceListenersRecord> dbusServiceListenersMap; + + + struct DBusObjectPathCache { + DBusObjectPathCache() + : referenceCount(0), + state(DBusRecordState::UNKNOWN) { + } + + DBusObjectPathCache(DBusObjectPathCache&& other) + : referenceCount(other.referenceCount), + state(other.state), + promiseOnResolve(std::move(other.promiseOnResolve)), + dbusInterfaceNamesCache(std::move(other.dbusInterfaceNamesCache)) { + } + + ~DBusObjectPathCache() {} + + size_t referenceCount; + DBusRecordState state; + std::promise<DBusRecordState> promiseOnResolve; + + std::unordered_set<std::string> dbusInterfaceNamesCache; + }; + + struct DBusUniqueNameRecord { + DBusUniqueNameRecord() + : objectPathsState(DBusRecordState::UNKNOWN) { + } + + DBusUniqueNameRecord(DBusUniqueNameRecord&& other) + : uniqueName(std::move(other.uniqueName)), + objectPathsState(other.objectPathsState), + ownedBusNames(std::move(other.ownedBusNames)), + dbusObjectPathsCache(std::move(other.dbusObjectPathsCache)) { + } + + std::string uniqueName; + DBusRecordState objectPathsState; + std::unordered_set<std::string> ownedBusNames; + std::unordered_map<std::string, DBusObjectPathCache> dbusObjectPathsCache; + }; + + std::unordered_map<std::string, DBusUniqueNameRecord> dbusUniqueNamesMap_; + typedef std::unordered_map<std::string, DBusUniqueNameRecord>::iterator DBusUniqueNamesMapIterator; + + // mapping service names (well-known names) to service instances + std::unordered_map<std::string, DBusUniqueNameRecord*> dbusServiceNameMap_; + + // protects the dbus service maps + std::mutex dbusServicesMutex_; + + void resolveDBusServiceName(const std::string& dbusServiceName, + DBusServiceListenersRecord& dbusServiceListenersRecord); + + void onGetNameOwnerCallback(const CallStatus& status, + std::string dbusServiceUniqueName, + const std::string& dbusServiceName); + + + DBusRecordState resolveDBusInterfaceNameState(const DBusAddress &_address, + DBusServiceListenersRecord &_record); + + + DBusObjectPathCache& getDBusObjectPathCacheReference(const std::string& dbusObjectPath, + const std::string& dbusServiceUniqueName, + DBusUniqueNameRecord& dbusUniqueNameRecord); + + void releaseDBusObjectPathCacheReference(const std::string& dbusObjectPath, + const DBusServiceListenersRecord& dbusServiceListenersRecord); + + + bool introspectDBusObjectPath(const std::string& dbusServiceUniqueName, const std::string& dbusObjectPath); + + void onIntrospectCallback(const CallStatus& status, + std::string xmlData, + const std::string& dbusServiceName, + const std::string& dbusObjectPath); + + void parseIntrospectionData(const std::string& xmlData, + const std::string& rootObjectPath, + const std::string& dbusServiceUniqueName); + + void parseIntrospectionNode(const pugi::xml_node& node, + const std::string& rootObjectPath, + const std::string& fullObjectPath, + const std::string& dbusServiceUniqueName); + + void processIntrospectionObjectPath(const pugi::xml_node& node, + const std::string& rootObjectPath, + const std::string& dbusServiceUniqueName); + + void processIntrospectionInterface(const pugi::xml_node& node, + const std::string& rootObjectPath, + const std::string& fullObjectPath, + const std::string& dbusServiceUniqueName); + + void onDBusDaemonProxyStatusEvent(const AvailabilityStatus& availabilityStatus); + + void onDBusDaemonProxyNameOwnerChangedEvent(const std::string& name, + const std::string& oldOwner, + const std::string& newOwner); + + std::shared_ptr<DBusDaemonProxy> dbusDaemonProxy_; + bool initialized_; + + ProxyStatusEvent::Subscription dbusDaemonProxyStatusEventSubscription_; + NameOwnerChangedEvent::Subscription dbusDaemonProxyNameOwnerChangedEventSubscription_; + + + void checkDBusServiceWasAvailable(const std::string& dbusServiceName, const std::string& dbusServiceUniqueName); + + void onDBusServiceAvailable(const std::string& dbusServiceName, const std::string& dbusServiceUniqueName); + + void onDBusServiceNotAvailable(DBusServiceListenersRecord& dbusServiceListenersRecord, const std::string &_serviceName = ""); + + + void notifyDBusServiceListeners(const DBusUniqueNameRecord& dbusUniqueNameRecord, + const std::string& dbusObjectPath, + const std::unordered_set<std::string>& dbusInterfaceNames, + const DBusRecordState& dbusInterfaceNamesState); + + void notifyDBusObjectPathResolved(DBusInterfaceNameListenersMap& dbusInterfaceNameListenersMap, + const std::unordered_set<std::string>& dbusInterfaceNames); + + void notifyDBusObjectPathChanged(DBusInterfaceNameListenersMap& dbusInterfaceNameListenersMap, + const std::unordered_set<std::string>& dbusInterfaceNames, + const DBusRecordState& dbusInterfaceNamesState); + + void notifyDBusInterfaceNameListeners(DBusInterfaceNameListenersRecord& dbusInterfaceNameListenersRecord, + const bool& isDBusInterfaceNameAvailable); + + + void removeUniqueName(const DBusUniqueNamesMapIterator &_dbusUniqueName, const std::string &_serviceName); + DBusUniqueNameRecord* insertServiceNameMapping(const std::string& dbusUniqueName, const std::string& dbusServiceName); + bool findCachedDbusService(const std::string& dbusServiceName, DBusUniqueNameRecord** uniqueNameRecord); + bool findCachedObjectPath(const std::string& dbusObjectPathName, const DBusUniqueNameRecord* uniqueNameRecord, DBusObjectPathCache* objectPathCache); + + std::condition_variable monitorResolveAllServices_; + std::mutex mutexServiceResolveCount; + int servicesToResolve; + + std::condition_variable monitorResolveAllObjectPaths_; + std::mutex mutexObjectPathsResolveCount; + int objectPathsToResolve; + + void fetchAllServiceNames(); + + inline const bool isDBusServiceName(const std::string &_name) { + return (_name.length() > 0 && _name[0] != ':'); + }; + + + inline const bool isOrgFreedesktopDBusInterface(const std::string& dbusInterfaceName) { + return dbusInterfaceName.find("org.freedesktop.DBus.") == 0; + } + + std::thread::id notificationThread_; + +private: + typedef std::map<std::shared_ptr<DBusProxyConnection>, std::shared_ptr<DBusServiceRegistry>> RegistryMap_t; + static RegistryMap_t registries_; + static std::mutex registriesMutex_; + std::shared_ptr<DBusAddressTranslator> translator_; +}; + + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSSERVICEREGISTRY_HPP_ diff --git a/include/CommonAPI/DBus/DBusStubAdapter.hpp b/include/CommonAPI/DBus/DBusStubAdapter.hpp new file mode 100644 index 0000000..5d1657b --- /dev/null +++ b/include/CommonAPI/DBus/DBusStubAdapter.hpp @@ -0,0 +1,59 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSSTUBADAPTER_HPP_ +#define COMMONAPI_DBUS_DBUSSTUBADAPTER_HPP_ + +#include <memory> + +#include <CommonAPI/Export.hpp> +#include <CommonAPI/Stub.hpp> +#include <CommonAPI/DBus/DBusAddress.hpp> +#include <CommonAPI/DBus/DBusInterfaceHandler.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusProxyConnection; + +class DBusStubAdapter + : virtual public CommonAPI::StubAdapter, + public DBusInterfaceHandler { + public: + COMMONAPI_EXPORT DBusStubAdapter(const DBusAddress &_dbusAddress, + const std::shared_ptr<DBusProxyConnection> &_connection, + const bool isManagingInterface); + + COMMONAPI_EXPORT virtual ~DBusStubAdapter(); + + COMMONAPI_EXPORT virtual void init(std::shared_ptr<DBusStubAdapter> _instance); + COMMONAPI_EXPORT virtual void deinit(); + + COMMONAPI_EXPORT const DBusAddress &getDBusAddress() const; + COMMONAPI_EXPORT const std::shared_ptr<DBusProxyConnection> &getDBusConnection() const; + + COMMONAPI_EXPORT const bool isManaging() const; + + COMMONAPI_EXPORT virtual const char* getMethodsDBusIntrospectionXmlData() const = 0; + COMMONAPI_EXPORT virtual bool onInterfaceDBusMessage(const DBusMessage &_message) = 0; + + COMMONAPI_EXPORT virtual void deactivateManagedInstances() = 0; + COMMONAPI_EXPORT virtual const bool hasFreedesktopProperties(); + COMMONAPI_EXPORT virtual bool onInterfaceDBusFreedesktopPropertiesMessage(const DBusMessage &_message) = 0; + + protected: + DBusAddress dbusAddress_; + const std::shared_ptr<DBusProxyConnection> connection_; + const bool isManaging_; +}; + +} // namespace dbus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSSTUBADAPTER_HPP_ diff --git a/include/CommonAPI/DBus/DBusStubAdapterHelper.hpp b/include/CommonAPI/DBus/DBusStubAdapterHelper.hpp new file mode 100644 index 0000000..56e5021 --- /dev/null +++ b/include/CommonAPI/DBus/DBusStubAdapterHelper.hpp @@ -0,0 +1,674 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSSTUBADAPTERHELPER_HPP_ +#define COMMONAPI_DBUS_DBUSSTUBADAPTERHELPER_HPP_ + +#include <initializer_list> +#include <memory> +#include <tuple> +#include <unordered_map> +#include <map> + +#include <CommonAPI/Variant.hpp> +#include <CommonAPI/DBus/DBusStubAdapter.hpp> +#include <CommonAPI/DBus/DBusInputStream.hpp> +#include <CommonAPI/DBus/DBusOutputStream.hpp> +#include <CommonAPI/DBus/DBusHelper.hpp> +#include <CommonAPI/DBus/DBusSerializableArguments.hpp> +#include <CommonAPI/DBus/DBusClientId.hpp> + +namespace CommonAPI { +namespace DBus { + +class StubDispatcherBase { +public: + virtual ~StubDispatcherBase() { } +}; + +struct DBusAttributeDispatcherStruct { + StubDispatcherBase* getter; + StubDispatcherBase* setter; + + DBusAttributeDispatcherStruct(StubDispatcherBase* g, StubDispatcherBase* s) { + getter = g; + setter = s; + } +}; + +typedef std::unordered_map<std::string, DBusAttributeDispatcherStruct> StubAttributeTable; + +template <typename _StubClass> +class DBusStubAdapterHelper: public virtual DBusStubAdapter { + public: + typedef typename _StubClass::StubAdapterType StubAdapterType; + typedef typename _StubClass::RemoteEventHandlerType RemoteEventHandlerType; + + class StubDispatcher: public StubDispatcherBase { + public: + virtual ~StubDispatcher() {} + virtual bool dispatchDBusMessage(const DBusMessage& dbusMessage, + const std::shared_ptr<_StubClass>& stub, + DBusStubAdapterHelper<_StubClass>& dbusStubAdapterHelper) = 0; + }; + // interfaceMemberName, interfaceMemberSignature + typedef std::pair<const char*, const char*> DBusInterfaceMemberPath; + typedef std::unordered_map<DBusInterfaceMemberPath, StubDispatcherBase*> StubDispatcherTable; + + DBusStubAdapterHelper(const DBusAddress &_address, + const std::shared_ptr<DBusProxyConnection> &_connection, + const std::shared_ptr<_StubClass> &_stub, + const bool _isManaging): + DBusStubAdapter(_address, _connection, _isManaging), + stub_(_stub), + remoteEventHandler_(nullptr) { + } + + virtual ~DBusStubAdapterHelper() { + DBusStubAdapter::deinit(); + stub_.reset(); + } + + virtual void init(std::shared_ptr<DBusStubAdapter> instance) { + DBusStubAdapter::init(instance); + std::shared_ptr<StubAdapterType> stubAdapter = std::dynamic_pointer_cast<StubAdapterType>(instance); + remoteEventHandler_ = stub_->initStubAdapter(stubAdapter); + } + + virtual void deinit() { + DBusStubAdapter::deinit(); + stub_.reset(); + } + + inline RemoteEventHandlerType* getRemoteEventHandler() { + return remoteEventHandler_; + } + + protected: + + virtual bool onInterfaceDBusMessage(const DBusMessage& dbusMessage) { + const char* interfaceMemberName = dbusMessage.getMember(); + const char* interfaceMemberSignature = dbusMessage.getSignature(); + + assert(interfaceMemberName); + assert(interfaceMemberSignature); + + DBusInterfaceMemberPath dbusInterfaceMemberPath(interfaceMemberName, interfaceMemberSignature); + auto findIterator = getStubDispatcherTable().find(dbusInterfaceMemberPath); + const bool foundInterfaceMemberHandler = (findIterator != getStubDispatcherTable().end()); + bool dbusMessageHandled = false; + if (foundInterfaceMemberHandler) { + StubDispatcher* stubDispatcher = static_cast<StubDispatcher*>(findIterator->second); + dbusMessageHandled = stubDispatcher->dispatchDBusMessage(dbusMessage, stub_, *this); + } + + return dbusMessageHandled; + } + + virtual bool onInterfaceDBusFreedesktopPropertiesMessage(const DBusMessage &_message) { + DBusInputStream input(_message); + + if (_message.hasMemberName("Get")) { + return handleFreedesktopGet(_message, input); + } else if (_message.hasMemberName("Set")) { + return handleFreedesktopSet(_message, input); + } else if (_message.hasMemberName("GetAll")) { + return handleFreedesktopGetAll(_message, input); + } + + return false; + } + + virtual const StubDispatcherTable& getStubDispatcherTable() = 0; + virtual const StubAttributeTable& getStubAttributeTable() = 0; + + std::shared_ptr<_StubClass> stub_; + RemoteEventHandlerType* remoteEventHandler_; + + private: + bool handleFreedesktopGet(const DBusMessage &_message, DBusInputStream &_input) { + std::string interfaceName; + std::string attributeName; + _input >> interfaceName; + _input >> attributeName; + + if (_input.hasError()) { + return false; + } + + auto attributeDispatcherIterator = getStubAttributeTable().find(attributeName); + if (attributeDispatcherIterator == getStubAttributeTable().end()) { + return false; + } + + StubDispatcher* getterDispatcher = static_cast<StubDispatcher*>(attributeDispatcherIterator->second.getter); + assert(getterDispatcher != NULL); // all attributes have at least a getter + return (getterDispatcher->dispatchDBusMessage(_message, stub_, *this)); + } + + bool handleFreedesktopSet(const DBusMessage& dbusMessage, DBusInputStream& dbusInputStream) { + std::string interfaceName; + std::string attributeName; + dbusInputStream >> interfaceName; + dbusInputStream >> attributeName; + + if(dbusInputStream.hasError()) { + return false; + } + + auto attributeDispatcherIterator = getStubAttributeTable().find(attributeName); + if(attributeDispatcherIterator == getStubAttributeTable().end()) { + return false; + } + + StubDispatcher *setterDispatcher = static_cast<StubDispatcher*>(attributeDispatcherIterator->second.setter); + if (setterDispatcher == NULL) { // readonly attributes do not have a setter + return false; + } + + return setterDispatcher->dispatchDBusMessage(dbusMessage, stub_, *this); + } + + bool handleFreedesktopGetAll(const DBusMessage& dbusMessage, DBusInputStream& dbusInputStream) { + std::string interfaceName; + dbusInputStream >> interfaceName; + + if(dbusInputStream.hasError()) { + return false; + } + + DBusMessage dbusMessageReply = dbusMessage.createMethodReturn("a{sv}"); + DBusOutputStream dbusOutputStream(dbusMessageReply); + /* + dbusOutputStream.beginWriteVectorOfSerializableStructs(getStubAttributeTable().size()); + + std::shared_ptr<DBusClientId> clientId = std::make_shared<DBusClientId>(std::string(dbusMessage.getSenderName())); + + for(auto attributeDispatcherIterator = getStubAttributeTable().begin(); attributeDispatcherIterator != getStubAttributeTable().end(); attributeDispatcherIterator++) { + //To prevent the destruction of the stub whilst still handling a message + if (stub_) { + DBusGetFreedesktopAttributeStubDispatcherBase<_StubClass>* const getterDispatcher = dynamic_cast<DBusGetFreedesktopAttributeStubDispatcherBase<_StubClass>*>(attributeDispatcherIterator->second.getter); + + if(getterDispatcher == NULL) { // readonly attributes do not have a setter + return false; + } + + dbusOutputStream << attributeDispatcherIterator->first; + getterDispatcher->dispatchDBusMessageAndAppendReply(dbusMessage, stub_, dbusOutputStream, clientId); + } + } + + dbusOutputStream.endWriteVector(); + */ + dbusOutputStream.flush(); + return getDBusConnection()->sendDBusMessage(dbusMessageReply); + } +}; + +template< class > +struct DBusStubSignalHelper; + +template<template<class ...> class _In, class... _InArgs> +struct DBusStubSignalHelper<_In<DBusInputStream, DBusOutputStream, _InArgs...>> { + + static inline bool sendSignal(const char* objectPath, + const char* interfaceName, + const char* signalName, + const char* signalSignature, + const std::shared_ptr<DBusProxyConnection>& dbusConnection, + const _InArgs&... inArgs) { + DBusMessage dbusMessage = DBusMessage::createSignal( + objectPath, + interfaceName, + signalName, + signalSignature); + + if (sizeof...(_InArgs) > 0) { + DBusOutputStream outputStream(dbusMessage); + const bool success = DBusSerializableArguments<_InArgs...>::serialize(outputStream, inArgs...); + if (!success) { + return false; + } + outputStream.flush(); + } + + const bool dbusMessageSent = dbusConnection->sendDBusMessage(dbusMessage); + return dbusMessageSent; + } + + template <typename _DBusStub = DBusStubAdapter> + static bool sendSignal(const _DBusStub &_stub, + const char *_name, + const char *_signature, + const _InArgs&... inArgs) { + return(sendSignal(_stub.getDBusAddress().getObjectPath().c_str(), + _stub.getDBusAddress().getInterface().c_str(), + _name, + _signature, + _stub.getDBusConnection(), + inArgs...)); + } + + + template <typename _DBusStub = DBusStubAdapter> + static bool sendSignal(const char *_target, + const _DBusStub &_stub, + const char *_name, + const char *_signature, + const _InArgs&... inArgs) { + DBusMessage dbusMessage + = DBusMessage::createSignal( + _stub.getDBusAddress().getObjectPath().c_str(), + _stub.getDBusAddress().getInterface().c_str(), + _name, + _signature); + + dbusMessage.setDestination(_target); + + if (sizeof...(_InArgs) > 0) { + DBusOutputStream outputStream(dbusMessage); + const bool success = DBusSerializableArguments<_InArgs...>::serialize(outputStream, inArgs...); + if (!success) { + return false; + } + outputStream.flush(); + } + + return _stub.getDBusConnection()->sendDBusMessage(dbusMessage); + } +}; + +template< class, class > +class DBusMethodStubDispatcher; + +template < + typename _StubClass, + template <class...> class _In, class... _InArgs> +class DBusMethodStubDispatcher<_StubClass, _In<_InArgs...> >: public DBusStubAdapterHelper<_StubClass>::StubDispatcher { + public: + typedef DBusStubAdapterHelper<_StubClass> DBusStubAdapterHelperType; + typedef void (_StubClass::*_StubFunctor)(std::shared_ptr<CommonAPI::ClientId>, _InArgs...); + + DBusMethodStubDispatcher(_StubFunctor stubFunctor): + stubFunctor_(stubFunctor) { + } + + bool dispatchDBusMessage(const DBusMessage& dbusMessage, const std::shared_ptr<_StubClass>& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper) { + return handleDBusMessage(dbusMessage, stub, dbusStubAdapterHelper, typename make_sequence<sizeof...(_InArgs)>::type()); + } + + private: + template <int... _InArgIndices> + inline bool handleDBusMessage(const DBusMessage& dbusMessage, + const std::shared_ptr<_StubClass>& stub, + DBusStubAdapterHelperType& dbusStubAdapterHelper, + index_sequence<_InArgIndices...>) const { + std::tuple<_InArgs...> argTuple; + + if (sizeof...(_InArgs) > 0) { + DBusInputStream dbusInputStream(dbusMessage); + const bool success = DBusSerializableArguments<_InArgs...>::deserialize(dbusInputStream, std::get<_InArgIndices>(argTuple)...); + if (!success) + return false; + } + + std::shared_ptr<DBusClientId> clientId = std::make_shared<DBusClientId>(std::string(dbusMessage.getSender())); + + (stub.get()->*stubFunctor_)(clientId, std::move(std::get<_InArgIndices>(argTuple))...); + + return true; + } + + _StubFunctor stubFunctor_; +}; + + +template< class, class, class> +class DBusMethodWithReplyStubDispatcher; + +template < + typename _StubClass, + template <class...> class _In, class... _InArgs, + template <class...> class _Out, class... _OutArgs> +class DBusMethodWithReplyStubDispatcher<_StubClass, _In<_InArgs...>, _Out<_OutArgs...> >: + public DBusStubAdapterHelper<_StubClass>::StubDispatcher { + public: + typedef DBusStubAdapterHelper<_StubClass> DBusStubAdapterHelperType; + typedef std::function<void (_OutArgs...)> ReplyType_t; + typedef void (_StubClass::*_StubFunctor)( + std::shared_ptr<CommonAPI::ClientId>, _InArgs..., ReplyType_t); + + DBusMethodWithReplyStubDispatcher(_StubFunctor stubFunctor, const char* dbusReplySignature, std::tuple<_InArgs..., _OutArgs...> _args): + stubFunctor_(stubFunctor), + dbusReplySignature_(dbusReplySignature), + args_(_args), + currentCall_(0) { + } + + bool dispatchDBusMessage(const DBusMessage& dbusMessage, const std::shared_ptr<_StubClass>& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper) { + connection_ = dbusStubAdapterHelper.getDBusConnection(); + return handleDBusMessage( + dbusMessage, + stub, + dbusStubAdapterHelper, + typename make_sequence_range<sizeof...(_InArgs), 0>::type(), + typename make_sequence_range<sizeof...(_OutArgs), sizeof...(_InArgs)>::type(), args_); + } + + bool sendReply(CommonAPI::CallId_t _call, std::tuple<_OutArgs...> args = std::make_tuple()) { + return sendReplyInternal(_call, typename make_sequence_range<sizeof...(_OutArgs), 0>::type(), args); + } + +private: + template <int... _InArgIndices, int... _OutArgIndices> + inline bool handleDBusMessage(const DBusMessage& dbusMessage, + const std::shared_ptr<_StubClass>& stub, + DBusStubAdapterHelperType& dbusStubAdapterHelper, + index_sequence<_InArgIndices...>, + index_sequence<_OutArgIndices...>, + std::tuple<_InArgs..., _OutArgs...> argTuple) { + if (sizeof...(_InArgs) > 0) { + DBusInputStream dbusInputStream(dbusMessage); + const bool success = DBusSerializableArguments<_InArgs...>::deserialize(dbusInputStream, std::get<_InArgIndices>(argTuple)...); + if (!success) + return false; + } + + std::shared_ptr<DBusClientId> clientId + = std::make_shared<DBusClientId>(std::string(dbusMessage.getSender())); + DBusMessage reply = dbusMessage.createMethodReturn(dbusReplySignature_); + + CommonAPI::CallId_t call; + { + std::lock_guard<std::mutex> lock(mutex_); + call = currentCall_++; + pending_[call] = reply; + } + + (stub.get()->*stubFunctor_)( + clientId, + std::move(std::get<_InArgIndices>(argTuple))..., + [call, this](_OutArgs... _args){ + this->sendReply(call, std::make_tuple(_args...)); + } + ); + + return true; + } + + template<int... _OutArgIndices> + bool sendReplyInternal(CommonAPI::CallId_t _call, + index_sequence<_OutArgIndices...>, + std::tuple<_OutArgs...> args) { + std::lock_guard<std::mutex> lock(mutex_); + auto reply = pending_.find(_call); + if (reply != pending_.end()) { + if (sizeof...(_OutArgs) > 0) { + DBusOutputStream output(reply->second); + if (!DBusSerializableArguments<_OutArgs...>::serialize( + output, std::get<_OutArgIndices>(args)...)) { + pending_.erase(_call); + return false; + } + output.flush(); + } + bool isSuccessful = connection_->sendDBusMessage(reply->second); + pending_.erase(_call); + return isSuccessful; + } + return false; + } + + _StubFunctor stubFunctor_; + const char* dbusReplySignature_; + std::tuple<_InArgs..., _OutArgs...> args_; + + CommonAPI::CallId_t currentCall_; + std::map<CommonAPI::CallId_t, DBusMessage> pending_; + std::mutex mutex_; // protects pending_ + + std::shared_ptr<DBusProxyConnection> connection_; +}; + +template< class, class, class, class > +class DBusMethodWithReplyAdapterDispatcher; + +template < + typename _StubClass, + typename _StubAdapterClass, + template <class...> class _In, class... _InArgs, + template <class...> class _Out, class... _OutArgs> +class DBusMethodWithReplyAdapterDispatcher<_StubClass, _StubAdapterClass, _In<_InArgs...>, _Out<_OutArgs...> >: + public DBusStubAdapterHelper<_StubClass>::StubDispatcher { + public: + typedef DBusStubAdapterHelper<_StubClass> DBusStubAdapterHelperType; + typedef void (_StubAdapterClass::*_StubFunctor)(std::shared_ptr<CommonAPI::ClientId>, _InArgs..., _OutArgs&...); + typedef typename CommonAPI::Stub<typename DBusStubAdapterHelperType::StubAdapterType, typename _StubClass::RemoteEventType> StubType; + + DBusMethodWithReplyAdapterDispatcher(_StubFunctor stubFunctor, const char* dbusReplySignature): + stubFunctor_(stubFunctor), + dbusReplySignature_(dbusReplySignature) { + } + + bool dispatchDBusMessage(const DBusMessage& dbusMessage, const std::shared_ptr<_StubClass>& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper) { + std::tuple<_InArgs..., _OutArgs...> argTuple; + return handleDBusMessage( + dbusMessage, + stub, + dbusStubAdapterHelper, + typename make_sequence_range<sizeof...(_InArgs), 0>::type(), + typename make_sequence_range<sizeof...(_OutArgs), sizeof...(_InArgs)>::type(),argTuple); + } + + private: + template <int... _InArgIndices, int... _OutArgIndices> + inline bool handleDBusMessage(const DBusMessage& dbusMessage, + const std::shared_ptr<_StubClass>& stub, + DBusStubAdapterHelperType& dbusStubAdapterHelper, + index_sequence<_InArgIndices...>, + index_sequence<_OutArgIndices...>, + std::tuple<_InArgs..., _OutArgs...> argTuple) const { + + if (sizeof...(_InArgs) > 0) { + DBusInputStream dbusInputStream(dbusMessage); + const bool success = DBusSerializableArguments<_InArgs...>::deserialize(dbusInputStream, std::get<_InArgIndices>(argTuple)...); + if (!success) + return false; + } + + std::shared_ptr<DBusClientId> clientId = std::make_shared<DBusClientId>(std::string(dbusMessage.getSender())); + + (stub->StubType::getStubAdapter().get()->*stubFunctor_)(clientId, std::move(std::get<_InArgIndices>(argTuple))..., std::get<_OutArgIndices>(argTuple)...); + DBusMessage dbusMessageReply = dbusMessage.createMethodReturn(dbusReplySignature_); + + if (sizeof...(_OutArgs) > 0) { + DBusOutputStream dbusOutputStream(dbusMessageReply); + const bool success = DBusSerializableArguments<_OutArgs...>::serialize(dbusOutputStream, std::get<_OutArgIndices>(argTuple)...); + if (!success) + return false; + + dbusOutputStream.flush(); + } + + return dbusStubAdapterHelper.getDBusConnection()->sendDBusMessage(dbusMessageReply); + } + + _StubFunctor stubFunctor_; + const char* dbusReplySignature_; +}; + + +template <typename _StubClass, typename _AttributeType, typename _AttributeDepl = EmptyDeployment> +class DBusGetAttributeStubDispatcher: public virtual DBusStubAdapterHelper<_StubClass>::StubDispatcher { + public: + typedef DBusStubAdapterHelper<_StubClass> DBusStubAdapterHelperType; + typedef const _AttributeType& (_StubClass::*GetStubFunctor)(std::shared_ptr<CommonAPI::ClientId>); + + DBusGetAttributeStubDispatcher(GetStubFunctor _getStubFunctor, const char *_signature, _AttributeDepl *_depl = nullptr): + getStubFunctor_(_getStubFunctor), + signature_(_signature), + depl_(_depl) { + } + + virtual ~DBusGetAttributeStubDispatcher() {}; + + bool dispatchDBusMessage(const DBusMessage& dbusMessage, const std::shared_ptr<_StubClass>& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper) { + return sendAttributeValueReply(dbusMessage, stub, dbusStubAdapterHelper); + } + protected: + virtual bool sendAttributeValueReply(const DBusMessage& dbusMessage, const std::shared_ptr<_StubClass>& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper) { + DBusMessage dbusMessageReply = dbusMessage.createMethodReturn(signature_); + DBusOutputStream dbusOutputStream(dbusMessageReply); + + std::shared_ptr<DBusClientId> clientId = std::make_shared<DBusClientId>(std::string(dbusMessage.getSender())); + + dbusOutputStream << CommonAPI::Deployable<_AttributeType, _AttributeDepl>((stub.get()->*getStubFunctor_)(clientId), depl_); + dbusOutputStream.flush(); + + return dbusStubAdapterHelper.getDBusConnection()->sendDBusMessage(dbusMessageReply); + } + + + GetStubFunctor getStubFunctor_; + const char* signature_; + _AttributeDepl *depl_; +}; + +template <typename _StubClass, typename _AttributeType, typename _AttributeDepl = EmptyDeployment> +class DBusSetAttributeStubDispatcher: public virtual DBusGetAttributeStubDispatcher<_StubClass, _AttributeType, _AttributeDepl> { + public: + typedef typename DBusGetAttributeStubDispatcher<_StubClass, _AttributeType, _AttributeDepl>::DBusStubAdapterHelperType DBusStubAdapterHelperType; + typedef typename DBusStubAdapterHelperType::RemoteEventHandlerType RemoteEventHandlerType; + + typedef typename DBusGetAttributeStubDispatcher<_StubClass, _AttributeType, _AttributeDepl>::GetStubFunctor GetStubFunctor; + typedef bool (RemoteEventHandlerType::*OnRemoteSetFunctor)(std::shared_ptr<CommonAPI::ClientId>, _AttributeType); + typedef void (RemoteEventHandlerType::*OnRemoteChangedFunctor)(); + + DBusSetAttributeStubDispatcher(GetStubFunctor getStubFunctor, + OnRemoteSetFunctor onRemoteSetFunctor, + OnRemoteChangedFunctor onRemoteChangedFunctor, + const char* dbusSignature, + _AttributeDepl *_depl = nullptr) : + DBusGetAttributeStubDispatcher<_StubClass, _AttributeType, _AttributeDepl>(getStubFunctor, dbusSignature, _depl), + onRemoteSetFunctor_(onRemoteSetFunctor), + onRemoteChangedFunctor_(onRemoteChangedFunctor) { + } + + virtual ~DBusSetAttributeStubDispatcher() {}; + + bool dispatchDBusMessage(const DBusMessage& dbusMessage, const std::shared_ptr<_StubClass>& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper) { + bool attributeValueChanged; + + if (!setAttributeValue(dbusMessage, stub, dbusStubAdapterHelper, attributeValueChanged)) + return false; + + if (attributeValueChanged) + notifyOnRemoteChanged(dbusStubAdapterHelper); + + return true; + } + + protected: + virtual _AttributeType retrieveAttributeValue(const DBusMessage& dbusMessage, bool& errorOccured) { + errorOccured = false; + + DBusInputStream dbusInputStream(dbusMessage); + CommonAPI::Deployable<_AttributeType, _AttributeDepl> attributeValue(this->depl_); + dbusInputStream >> attributeValue; + + if (dbusInputStream.hasError()) { + errorOccured = true; + } + + return attributeValue.getValue(); + } + + inline bool setAttributeValue(const DBusMessage& dbusMessage, + const std::shared_ptr<_StubClass>& stub, + DBusStubAdapterHelperType& dbusStubAdapterHelper, + bool& attributeValueChanged) { + bool errorOccured; + CommonAPI::Deployable<_AttributeType, _AttributeDepl> attributeValue( + retrieveAttributeValue(dbusMessage, errorOccured), this->depl_); + + if(errorOccured) { + return false; + } + + std::shared_ptr<DBusClientId> clientId = std::make_shared<DBusClientId>(std::string(dbusMessage.getSender())); + + attributeValueChanged = (dbusStubAdapterHelper.getRemoteEventHandler()->*onRemoteSetFunctor_)(clientId, std::move(attributeValue.getValue())); + + return this->sendAttributeValueReply(dbusMessage, stub, dbusStubAdapterHelper); + } + + inline void notifyOnRemoteChanged(DBusStubAdapterHelperType& dbusStubAdapterHelper) { + (dbusStubAdapterHelper.getRemoteEventHandler()->*onRemoteChangedFunctor_)(); + } + + inline const _AttributeType& getAttributeValue(std::shared_ptr<CommonAPI::ClientId> clientId, const std::shared_ptr<_StubClass>& stub) { + return (stub.get()->*(this->getStubFunctor_))(clientId); + } + + const OnRemoteSetFunctor onRemoteSetFunctor_; + const OnRemoteChangedFunctor onRemoteChangedFunctor_; +}; + +template <typename _StubClass, typename _AttributeType, typename _AttributeDepl = EmptyDeployment> +class DBusSetObservableAttributeStubDispatcher: public virtual DBusSetAttributeStubDispatcher<_StubClass, _AttributeType, _AttributeDepl> { + public: + typedef typename DBusSetAttributeStubDispatcher<_StubClass, _AttributeType, _AttributeDepl>::DBusStubAdapterHelperType DBusStubAdapterHelperType; + typedef typename DBusStubAdapterHelperType::StubAdapterType StubAdapterType; + typedef typename DBusSetAttributeStubDispatcher<_StubClass, _AttributeType, _AttributeDepl>::GetStubFunctor GetStubFunctor; + typedef typename DBusSetAttributeStubDispatcher<_StubClass, _AttributeType, _AttributeDepl>::OnRemoteSetFunctor OnRemoteSetFunctor; + typedef typename DBusSetAttributeStubDispatcher<_StubClass, _AttributeType, _AttributeDepl>::OnRemoteChangedFunctor OnRemoteChangedFunctor; + typedef typename CommonAPI::Stub<StubAdapterType, typename _StubClass::RemoteEventType> StubType; + typedef void (StubAdapterType::*FireChangedFunctor)(const _AttributeType&); + + DBusSetObservableAttributeStubDispatcher(GetStubFunctor getStubFunctor, + OnRemoteSetFunctor onRemoteSetFunctor, + OnRemoteChangedFunctor onRemoteChangedFunctor, + FireChangedFunctor fireChangedFunctor, + const char* dbusSignature, + _AttributeDepl *_depl = nullptr) + : DBusGetAttributeStubDispatcher<_StubClass, _AttributeType, _AttributeDepl>( + getStubFunctor, dbusSignature, _depl), + DBusSetAttributeStubDispatcher<_StubClass, _AttributeType, _AttributeDepl>( + getStubFunctor, onRemoteSetFunctor, onRemoteChangedFunctor, dbusSignature, _depl), + fireChangedFunctor_(fireChangedFunctor) { + } + + virtual ~DBusSetObservableAttributeStubDispatcher() {}; + + bool dispatchDBusMessage(const DBusMessage& dbusMessage, const std::shared_ptr<_StubClass>& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper) { + bool attributeValueChanged; + if (!this->setAttributeValue(dbusMessage, stub, dbusStubAdapterHelper, attributeValueChanged)) + return false; + + if (attributeValueChanged) { + std::shared_ptr<DBusClientId> clientId = std::make_shared<DBusClientId>(std::string(dbusMessage.getSender())); + fireAttributeValueChanged(clientId, dbusStubAdapterHelper, stub); + this->notifyOnRemoteChanged(dbusStubAdapterHelper); + } + return true; + } +protected: + virtual void fireAttributeValueChanged(std::shared_ptr<CommonAPI::ClientId> clientId, + DBusStubAdapterHelperType& dbusStubAdapterHelper, + const std::shared_ptr<_StubClass> stub) { + (stub->StubType::getStubAdapter().get()->*fireChangedFunctor_)(this->getAttributeValue(clientId, stub)); + } + + const FireChangedFunctor fireChangedFunctor_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSSTUBADAPTERHELPER_HPP_ diff --git a/include/CommonAPI/DBus/DBusTypeOutputStream.hpp b/include/CommonAPI/DBus/DBusTypeOutputStream.hpp new file mode 100644 index 0000000..3ba7cf7 --- /dev/null +++ b/include/CommonAPI/DBus/DBusTypeOutputStream.hpp @@ -0,0 +1,153 @@ +// Copyright (C) 2014-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef COMMONAPI_DBUS_DBUSTYPEOUTPUTSTREAM_H_ +#define COMMONAPI_DBUS_DBUSTYPEOUTPUTSTREAM_H_ + +#include <CommonAPI/TypeOutputStream.hpp> + +namespace CommonAPI { +namespace DBus { + +class DBusTypeOutputStream: public TypeOutputStream<DBusTypeOutputStream> { +public: + DBusTypeOutputStream() : signature_("") {} + + TypeOutputStream &writeType(const bool &_type) { + signature_.append("b"); + return (*this); + } + + TypeOutputStream &writeType(const int8_t &) { + signature_.append("y"); + return (*this); + } + + TypeOutputStream &writeType(const int16_t &) { + signature_.append("n"); + return (*this); + } + + TypeOutputStream &writeType(const int32_t &) { + signature_.append("i"); + return (*this); + } + + TypeOutputStream &writeType(const int64_t &) { + signature_.append("x"); + return (*this); + } + + TypeOutputStream &writeType(const uint8_t &) { + signature_.append("y"); + return (*this); + } + + TypeOutputStream &writeType(const uint16_t &) { + signature_.append("q"); + return (*this); + } + + TypeOutputStream &writeType(const uint32_t &) { + signature_.append("u"); + return (*this); + } + + TypeOutputStream &writeType(const uint64_t &) { + signature_.append("t"); + return (*this); + } + + TypeOutputStream &writeType(const float &) { + signature_.append("d"); + return (*this); + } + + TypeOutputStream &writeType(const double &) { + signature_.append("d"); + return (*this); + } + + TypeOutputStream &writeType(const std::string &) { + signature_.append("s"); + return (*this); + } + + TypeOutputStream &writeType() { + signature_.append("ay"); + return (*this); + } + + TypeOutputStream &writeVersionType() { + signature_.append("(uu)"); + return (*this); + } + + template<typename... _Types> + TypeOutputStream &writeType(const Struct<_Types...> &_value) { + signature_.append("("); + const auto itsSize(std::tuple_size<std::tuple<_Types...>>::value); + StructTypeWriter<itsSize-1, DBusTypeOutputStream, Struct<_Types...>>{} + (*this, _value); + signature_.append(")"); + return (*this); + } + + template<class _PolymorphicStruct> + TypeOutputStream &writeType(const std::shared_ptr<_PolymorphicStruct> &_value) { + signature_.append("("); + _value->writeType(*this); + signature_.append(")"); + return (*this); + } + + template<typename... _Types> + TypeOutputStream &writeType(const Variant<_Types...> &_value) { + signature_.append("(yv)"); + return (*this); + } + + template<typename _Deployment, typename... _Types> + TypeOutputStream &writeType(const Variant<_Types...> &_value, const _Deployment *_depl) { + if (_depl != nullptr && _depl->isFreeDesktop_) { + signature_.append("v"); + } else { + signature_.append("(yv)"); + } + TypeOutputStreamWriteVisitor<DBusTypeOutputStream> typeVisitor(*this); + ApplyVoidVisitor<TypeOutputStreamWriteVisitor<DBusTypeOutputStream>, + Variant<_Types...>, _Types...>::visit(typeVisitor, _value); + return (*this); + } + + template<typename _ElementType> + TypeOutputStream &writeType(const std::vector<_ElementType> &_value) { + signature_.append("a"); + return (*this); + } + + template<typename _KeyType, typename _ValueType, typename _HasherType> + TypeOutputStream &writeType(const std::unordered_map<_KeyType, _ValueType, _HasherType> &_value) { + signature_.append("a{"); + _KeyType dummyKey; + writeType(dummyKey); + _ValueType dummyValue; + writeType(dummyValue); + signature_.append("}"); + return (*this); + } + + inline std::string getSignature() { + return std::move(signature_); + } + +private: + std::string signature_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSTYPEOUTPUTSTREAM_HPP_ diff --git a/include/CommonAPI/DBus/DBusTypes.hpp b/include/CommonAPI/DBus/DBusTypes.hpp new file mode 100644 index 0000000..4c723c4 --- /dev/null +++ b/include/CommonAPI/DBus/DBusTypes.hpp @@ -0,0 +1,37 @@ +// Copyright (C) 2014-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSTYPES_HPP_ +#define COMMONAPI_DBUS_DBUSTYPES_HPP_ + +#include <string> +#include <unordered_map> + +#include <dbus/dbus.h> + +namespace CommonAPI { +namespace DBus { + +typedef std::unordered_map<std::string, bool> DBusPropertiesChangedDict; +typedef std::unordered_map<std::string, + DBusPropertiesChangedDict> DBusInterfacesAndPropertiesDict; +typedef std::unordered_map<std::string, + DBusInterfacesAndPropertiesDict> DBusObjectPathAndInterfacesDict; + +enum class DBusType_t { + SESSION = DBUS_BUS_SESSION, + SYSTEM = DBUS_BUS_SYSTEM, + STARTER = DBUS_BUS_STARTER, + WRAPPED +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSTYPES_HPP_ diff --git a/include/CommonAPI/DBus/DBusUtils.hpp b/include/CommonAPI/DBus/DBusUtils.hpp new file mode 100644 index 0000000..ba8826d --- /dev/null +++ b/include/CommonAPI/DBus/DBusUtils.hpp @@ -0,0 +1,37 @@ +// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSUTILS_HPP_ +#define COMMONAPI_DBUS_DBUSUTILS_HPP_ + +#include <future> + +namespace CommonAPI { +namespace DBus { + +//In gcc 4.4.1, the enumeration "std::future_status" is defined, but the return values of some functions +//are bool where the same functions in gcc 4.6. return a value from this enum. This template is a way +//to ensure compatibility for this issue. +template<typename _FutureWaitType> +inline bool checkReady(_FutureWaitType&); + +template<> +inline bool checkReady<bool>(bool& returnedValue) { + return returnedValue; +} + +template<> +inline bool checkReady<std::future_status>(std::future_status& returnedValue) { + return returnedValue == std::future_status::ready; +} + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSUTILS_HPP_ |