Add support of object registration to dbus 40/103440/7
authorSungbae Yoo <sungbae.yoo@samsung.com>
Thu, 8 Dec 2016 09:09:32 +0000 (18:09 +0900)
committerJaemin Ryu <jm77.ryu@samsung.com>
Sun, 18 Dec 2016 03:44:54 +0000 (12:44 +0900)
Signed-off-by: Sungbae Yoo <sungbae.yoo@samsung.com>
Change-Id: Ifc2c1260d5b7fe2cdccab4a41701fc03cc6328ba
Signed-off-by: Jaemin Ryu <jm77.ryu@samsung.com>
include/klay/dbus/connection.h
include/klay/dbus/variant.h
src/dbus/connection.cpp
src/dbus/variant.cpp
test/dbus.cpp

index dfa776a4cfc6fd2c22c59ce596fedee5d9db017a..b53af520c3f33bc0a705ab155a5c8d72c6fa8faf 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <string>
 #include <functional>
+#include <map>
 
 #include <klay/dbus/variant.h>
 
@@ -28,25 +29,42 @@ namespace dbus {
 
 class Connection {
 public:
-       typedef unsigned int subscriptionId;
-       typedef std::function<void(Variant)> signalCallback;
+       typedef unsigned int NameId;
+       typedef unsigned int ObjectId;
+       typedef unsigned int SubscriptionId;
+
+       typedef std::function<void()> VoidCallback;
+
+       typedef std::function<void(Variant parameters)> SignalCallback;
+
+       typedef std::function<void(const std::string& name)> ClientVanishedCallback;
+
+       typedef std::function<Variant(const std::string& objectPath,
+                                                                 const std::string& interface,
+                                                                 const std::string& method,
+                                                                 Variant parameters)> MethodCallCallback;
+
+       Connection(const std::string& address);
 
        Connection() = delete;
        Connection(const Connection&) = delete;
        ~Connection();
 
        Connection& operator=(const Connection&) = delete;
+       Connection& operator=(Connection&&) = delete;
 
        static Connection& getSystem();
 
-       subscriptionId subscribeSignal(const std::string& sender,
+       void setName(const std::string& name, const VoidCallback& nameAcquireCallback,
+                                                                                 const VoidCallback& nameLostCallback);
+
+       SubscriptionId subscribeSignal(const std::string& sender,
                                                                   const std::string& interface,
                                                                   const std::string& object,
                                                                   const std::string& member,
-                                                                  const signalCallback callback);
+                                                                  const SignalCallback& callback);
 
-
-       void unsubscribeSignal(subscriptionId id);
+       void unsubscribeSignal(SubscriptionId id);
 
        const Variant methodcall(const std::string& busName,
                                                         const std::string& object,
@@ -57,10 +75,80 @@ public:
                                                         const std::string& paramType,
                                                         ...);
 
+       ObjectId registerObject(const std::string& interface,
+                                                       const std::string& manifest,
+                                                       const MethodCallCallback& methodcall,
+                                                       const ClientVanishedCallback& vanished);
+
+       void unregisterObject(ObjectId id);
+
 private:
-       Connection(const std::string& address);
 
+       static void onNameAcquired(GDBusConnection* connection,
+                                                          const gchar* name, gpointer userData);
+
+       static void onNameLost(GDBusConnection* connection,
+                                                  const gchar* name, gpointer userData);
+
+       static void onClientVanish(GDBusConnection* connection,
+                                                          const gchar* name, gpointer userData);
+
+       static void onSignal(GDBusConnection* connection,
+                                                const gchar *sender,
+                                                const gchar *objectPath,
+                                                const gchar *interface,
+                                                const gchar *signal,
+                                                GVariant *parameters,
+                                                gpointer userData);
+
+       static void onMethodCall(GDBusConnection* connection,
+                                                        const gchar* sender,
+                                                        const gchar* objectPath,
+                                                        const gchar* interface,
+                                                        const gchar* method,
+                                                        GVariant* parameters,
+                                                        GDBusMethodInvocation* invocation,
+                                                        gpointer userData);
+
+private:
+       typedef std::map<std::string, guint> ClientMap;
+
+       struct NameCallback {
+               VoidCallback nameAcquired;
+               VoidCallback nameLost;
+
+               NameCallback(const VoidCallback& acquired, const VoidCallback& lost) :
+                       nameAcquired(acquired), nameLost(lost)
+               {
+               }
+       };
+
+       struct VanishedCallback {
+               ClientVanishedCallback clientVanished;
+               ClientMap &watchedClients;
+
+               VanishedCallback(const ClientVanishedCallback& vanished, ClientMap& clients) :
+                       clientVanished(vanished), watchedClients(clients)
+               {
+               }
+       };
+
+       struct MethodCallback {
+               MethodCallCallback methodcall;
+               ClientVanishedCallback clientVanished;
+               Connection* connection;
+
+               MethodCallback(const MethodCallCallback& method,
+                                          const ClientVanishedCallback& vanished,
+                                          Connection* conn) :
+                       methodcall(method), clientVanished(vanished), connection(conn)
+               {
+               }
+       };
+
+       ClientMap watchedClients;
        GDBusConnection* connection;
+       NameId ownedNameId;
 };
 
 } // namespace dbus
index 39354540d527bf55feb03ef9d6d21627cfb7a39e..5d0b193efc208a6d891d73e94f74e9b752540fac 100644 (file)
@@ -25,9 +25,10 @@ namespace dbus {
 
 class Variant {
 public:
+       Variant();
        Variant(GVariant* var);
        Variant(Variant&& var);
-       Variant();
+       Variant(const std::string& format, ...);
        ~Variant();
 
        Variant& operator=(GVariant* var);
index 3bb15440cd3ea57c25229347337be2143c0e040b..1517272bdcca79f05c1d82cf205e7f999df19526 100644 (file)
@@ -25,22 +25,22 @@ namespace {
 
 const std::string DBUS_SYSTEM_BUS_ADDRESS = "kernel:path=/sys/fs/kdbus/0-system/bus;unix:path=/var/run/dbus/system_bus_socket";
 
-void defaultCallback(GDBusConnection* connection,
-                                        const gchar *sender_name,
-                                        const gchar *object_path,
-                                        const gchar *interface_name,
-                                        const gchar *signal_name,
-                                        GVariant *parameters,
-                                        gpointer user_data) {
-       Connection::signalCallback* func = reinterpret_cast<Connection::signalCallback*> (user_data);
-       (*func)(Variant(parameters));
-       delete func;
+template <typename T>
+void freeUserData(void *userData)
+{
+       delete reinterpret_cast<T *>(userData);
+}
+
+template<typename T>
+const T& userDataCast(void *userData)
+{
+       return *reinterpret_cast<T *>(userData);
 }
 
 } // namespace
 
 Connection::Connection(const std::string& address) :
-        connection(nullptr)
+        connection(nullptr), ownedNameId(0)
 {
        Error error;
        const GDBusConnectionFlags flags = static_cast<GDBusConnectionFlags>
@@ -55,6 +55,14 @@ Connection::Connection(const std::string& address) :
 
 Connection::~Connection()
 {
+       for (auto client : watchedClients) {
+               g_bus_unwatch_name(client.second);
+       }
+
+       if (ownedNameId) {
+               g_bus_unown_name(ownedNameId);
+       }
+
        if (connection) {
                g_dbus_connection_close_sync(connection, NULL, NULL);
                g_object_unref(connection);
@@ -67,27 +75,25 @@ Connection& Connection::getSystem()
        return __instance__;
 }
 
-Connection::subscriptionId Connection::subscribeSignal(const std::string& sender,
+Connection::SubscriptionId Connection::subscribeSignal(const std::string& sender,
                                                                                                           const std::string& interface,
                                                                                                           const std::string& object,
                                                                                                           const std::string& member,
-                                                                                                          const signalCallback callback)
-{
-       subscriptionId id;
-       id = g_dbus_connection_signal_subscribe(connection,
-                                                                                       sender.empty()    ? NULL : sender.c_str(),
-                                                                                       interface.empty() ? NULL : interface.c_str(),
-                                                                                       object.empty()    ? NULL : object.c_str(),
-                                                                                       member.empty()    ? NULL : member.c_str(),
-                                                                                       NULL,
-                                                                                       G_DBUS_SIGNAL_FLAGS_NONE,
-                                                                                       defaultCallback,
-                                                                                       new signalCallback(callback),
-                                                                                       NULL);
-       return id;
+                                                                                                          const SignalCallback& callback)
+{
+       return g_dbus_connection_signal_subscribe(connection,
+                                                                                         sender.empty()    ? NULL : sender.c_str(),
+                                                                                         interface.empty() ? NULL : interface.c_str(),
+                                                                                         object.empty()    ? NULL : object.c_str(),
+                                                                                         member.empty()    ? NULL : member.c_str(),
+                                                                                         NULL,
+                                                                                         G_DBUS_SIGNAL_FLAGS_NONE,
+                                                                                         &onSignal,
+                                                                                         new SignalCallback(callback),
+                                                                                         &freeUserData<SignalCallback>);
 }
 
-void Connection::unsubscribeSignal(Connection::subscriptionId id)
+void Connection::unsubscribeSignal(Connection::SubscriptionId id)
 {
        g_dbus_connection_signal_unsubscribe(connection, id);
 }
@@ -106,21 +112,20 @@ const Variant Connection::methodcall(const std::string& busName,
        va_list ap;
 
        va_start(ap, paramType);
+       GVariant* variant = g_variant_new_va(paramType.c_str(), NULL, &ap);
+       va_end(ap);
+
        result = g_dbus_connection_call_sync(connection,
-                                                                                busName.empty() ? NULL :
-                                                                                busName.c_str(),
+                                                                                busName.empty() ? NULL : busName.c_str(),
                                                                                 object.c_str(),
                                                                                 interface.c_str(),
                                                                                 method.c_str(),
-                                                                                paramType.empty() ? NULL :
-                                                                                g_variant_new_va(paramType.c_str(), NULL, &ap),
-                                                                                replyType.empty() ? NULL :
-                                                                                G_VARIANT_TYPE(replyType.c_str()),
+                                                                                paramType.empty() ? NULL : variant,
+                                                                                replyType.empty() ? NULL : G_VARIANT_TYPE(replyType.c_str()),
                                                                                 G_DBUS_CALL_FLAGS_NONE,
                                                                                 timeout,
                                                                                 NULL,
                                                                                 &error);
-       va_end(ap);
 
        if (error) {
                ERROR(error->message);
@@ -130,4 +135,147 @@ const Variant Connection::methodcall(const std::string& busName,
        return result;
 }
 
+void Connection::setName(const std::string& name,
+                                                const VoidCallback& nameAcquiredCallback,
+                                                const VoidCallback& nameLostCallback)
+{
+       ownedNameId = g_bus_own_name_on_connection(connection,
+                                                                                          name.c_str(),
+                                                                                          G_BUS_NAME_OWNER_FLAGS_NONE,
+                                                                                          &onNameAcquired,
+                                                                                          &onNameLost,
+                                                                                          new NameCallback(nameAcquiredCallback, nameLostCallback),
+                                                                                          &freeUserData<NameCallback>);
+}
+
+Connection::ObjectId Connection::registerObject(const std::string& object,
+                                                                                           const std::string& manifest,
+                                                                                               const MethodCallCallback& methodcall,
+                                                                                               const ClientVanishedCallback& vanished)
+{
+       Error error;
+       GDBusNodeInfo* node = g_dbus_node_info_new_for_xml(manifest.c_str(), &error);
+       if (node != NULL && (node->interfaces == NULL ||
+                                                node->interfaces[0] == NULL ||
+                                                node->interfaces[1] != NULL)) {
+               g_dbus_node_info_unref(node);
+               g_set_error(&error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                                                       "Unexpected interface");
+       }
+
+       if (error) {
+               throw runtime::Exception(error->message);
+       }
+
+       GDBusInterfaceInfo* inf = node->interfaces[0];
+       GDBusInterfaceVTable vtable;
+       vtable.method_call = &Connection::onMethodCall;
+       vtable.get_property = NULL;
+       vtable.set_property = NULL;
+
+       ObjectId id = g_dbus_connection_register_object(connection,
+                                                                                                   object.c_str(),
+                                                                                                   inf,
+                                                                                                   &vtable,
+                                                                                                   new MethodCallback(methodcall, vanished, this),
+                                                                                                   &freeUserData<MethodCallback>,
+                                                                                                   &error);
+       g_dbus_node_info_unref(node);
+    if (error) {
+        ERROR(error->message);
+        throw runtime::Exception(error->message);
+    }
+
+       return id;
+}
+
+void Connection::unregisterObject(ObjectId id)
+{
+       g_dbus_connection_unregister_object(connection, id);
+}
+
+void Connection::onClientVanish(GDBusConnection* connection, const gchar* name, gpointer userData)
+{
+       DEBUG("Client Vanished:" << name);
+       const VanishedCallback& callback = userDataCast<VanishedCallback>(userData);
+
+       guint id = callback.watchedClients[name];
+       callback.watchedClients.erase(name);
+
+       if (callback.clientVanished) {
+               callback.clientVanished(name);
+       }
+
+       g_bus_unwatch_name(id);
+}
+
+void Connection::onNameAcquired(GDBusConnection* connection, const gchar* name, gpointer userData)
+{
+       DEBUG("Name Acquired: " << name);
+       const NameCallback& callbacks = userDataCast<NameCallback>(userData);
+       if (callbacks.nameAcquired) {
+               callbacks.nameAcquired();
+       }
+}
+
+void Connection::onNameLost(GDBusConnection* connection, const gchar* name, gpointer userData)
+{
+       DEBUG("Name Lost" << name);
+       const NameCallback& callbacks = userDataCast<NameCallback>(userData);
+       if (callbacks.nameLost) {
+               callbacks.nameLost();
+       }
+}
+
+void Connection::onSignal(GDBusConnection* connection,
+                                                 const gchar *sender,
+                                                 const gchar *objectPath,
+                                                 const gchar *interface,
+                                                 const gchar *signal,
+                                                 GVariant *parameters,
+                                                 gpointer userData)
+{
+       DEBUG("Signal : " << sender << " : " << objectPath << " : "
+                                         << interface << " : " << signal);
+       const SignalCallback& callback = userDataCast<SignalCallback>(userData);
+       if (callback) {
+               callback(Variant(parameters));
+       }
+}
+
+void Connection::onMethodCall(GDBusConnection* connection,
+                                                         const gchar* sender,
+                                                         const gchar* objectPath,
+                                                         const gchar* interface,
+                                                         const gchar* method,
+                                                         GVariant* parameters,
+                                                         GDBusMethodInvocation* invocation,
+                                                         gpointer userData)
+{
+       DEBUG("MethodCall: " << sender << " : " << objectPath << " : "
+                                                << interface << " : " << method);
+       const MethodCallback callbackSet = userDataCast<MethodCallback>(userData);
+
+       ClientMap &watchMap = callbackSet.connection->watchedClients;
+
+       if (watchMap.find(sender) == watchMap.end()) {
+               guint id = g_bus_watch_name_on_connection(connection,
+                                                                                                 sender,
+                                                                                                 G_BUS_NAME_WATCHER_FLAGS_NONE,
+                                                                                                 NULL,
+                                                                                                 &onClientVanish,
+                                                                                                 new VanishedCallback(callbackSet.clientVanished, watchMap),
+                                                                                                 &freeUserData<VanishedCallback>);
+               watchMap[sender] = id;
+       }
+
+       try {
+               Variant variant = callbackSet.methodcall(objectPath, interface, method, Variant(parameters));
+               g_dbus_method_invocation_return_value(invocation, &variant);
+       } catch (runtime::Exception& e) {
+               ERROR("Error on metod handling");
+               g_dbus_method_invocation_return_dbus_error(invocation, (interface + std::string(".Error")).c_str(), e.what());
+       }
+}
+
 } // namespace dbus
index 94a0142667d0a688e3920672a7ba862143acce13..423941d3e559545456e396a97a09eea85b22bd2f 100644 (file)
@@ -33,11 +33,23 @@ Variant::Variant() :
 {
 }
 
+Variant::Variant(const std::string& format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+       variant = g_variant_new_va(format.c_str(), NULL, &ap);
+       va_end(ap);
+}
+
 Variant::~Variant()
 {
-       if (variant) {
-               g_variant_unref(variant);
-       }
+       /*
+        * variant is foating
+        * if (variant) {
+        *     g_variant_unref(variant);
+        * }
+        */
 }
 
 Variant& Variant::operator=(GVariant* var)
@@ -46,6 +58,11 @@ Variant& Variant::operator=(GVariant* var)
        return *this;
 }
 
+GVariant* Variant::operator & ()
+{
+       return variant;
+}
+
 Variant::operator bool () const
 {
        return variant != nullptr;
index bec1ed2825f95c23c843a8643cb3c1a44c3a87ff..3d6cd449169b4e5346ab8db0dd2c71ba388ef92a 100644 (file)
  *  See the License for the specific language governing permissions and
  *  limitations under the License
  */
+#include <thread>
+#include <memory>
+#include <glib.h>
+
 #include <klay/exception.h>
 #include <klay/audit/logger.h>
 #include <klay/dbus/variant.h>
 #include <klay/dbus/connection.h>
+#include <klay/latch.h>
 
 #include <klay/testbench.h>
 
+const std::string TESTSVC_BUS_NAME       = "org.tizen.klay";
+const std::string TESTSVC_OBJECT_PATH    = "/org/tizen/klay";
+const std::string TESTSVC_INTERFACE      = "test.api";
+const std::string TESTSVC_METHOD_NOOP    = "Noop";
+const std::string TESTSVC_METHOD_PROCESS = "Process";
+const std::string TESTSVC_METHOD_THROW   = "Throw";
+const std::string TESTSVC_SIGNAL_NOTIFY  = "Notify";
+
+const std::string manifest =
+       "<node>"
+       "       <interface name='" + TESTSVC_INTERFACE + "'>"
+       "               <method name='" + TESTSVC_METHOD_NOOP + "'/>"
+       "               <method name='" + TESTSVC_METHOD_PROCESS + "'>"
+       "                       <arg type='s' name='argument' direction='in'/>"
+       "                       <arg type='s' name='response' direction='out'/>"
+       "               </method>"
+       "               <method name='" + TESTSVC_METHOD_THROW + "'>"
+       "                       <arg type='i' name='argument' direction='in'/>"
+       "               </method>"
+       "               <signal name='" + TESTSVC_SIGNAL_NOTIFY + "'>"
+       "                       <arg type='s' name='arument'/>"
+       "               </signal>"
+       "       </interface>"
+       "</node>";
+
 TESTCASE(DbusNegativeTest)
 {
        try {
@@ -28,3 +58,94 @@ TESTCASE(DbusNegativeTest)
        } catch (std::exception& e) {
        }
 }
+
+class ScopedGMainLoop {
+public:
+       ScopedGMainLoop() :
+               mainloop(g_main_loop_new(NULL, FALSE), g_main_loop_unref)
+       {
+               handle = std::thread(g_main_loop_run, mainloop.get());
+       }
+
+       ~ScopedGMainLoop()
+       {
+               while (!g_main_loop_is_running(mainloop.get())) {
+                       std::this_thread::yield();
+               }
+
+               g_main_loop_quit(mainloop.get());
+               handle.join();
+       }
+
+private:
+       std::unique_ptr<GMainLoop, void(*)(GMainLoop*)> mainloop;
+       std::thread handle;
+};
+
+TESTCASE(DbusRegisterObjectTest)
+{
+       runtime::Latch nameAcquired;
+       ScopedGMainLoop mainloop;
+
+    auto handler = [](const std::string& objectPath,
+                      const std::string& interface,
+                      const std::string& methodName,
+                      dbus::Variant parameters) {
+        if (objectPath != TESTSVC_OBJECT_PATH || interface != TESTSVC_INTERFACE) {
+                       throw runtime::Exception("Unknown Method");
+        }
+        if (methodName == TESTSVC_METHOD_NOOP) {
+            return dbus::Variant();
+        } else if (methodName == TESTSVC_METHOD_PROCESS) {
+                       const gchar *arg = NULL;
+                       parameters.get("(&s)", &arg);
+                       return dbus::Variant("(s)", "result form process method");
+        } else if (methodName == TESTSVC_METHOD_THROW) {
+                       int arg = 0;
+                       parameters.get("(i)", &arg);
+                       return dbus::Variant();
+        }
+
+               return dbus::Variant();
+    };
+
+       try {
+               dbus::Connection& svc = dbus::Connection::getSystem();
+               svc.setName(TESTSVC_BUS_NAME, [&]{ nameAcquired.set(); },
+                                                                         []{});
+
+               nameAcquired.wait();
+
+               svc.registerObject(TESTSVC_OBJECT_PATH, manifest, handler, nullptr);
+
+               std::cout << "Method Call Test" << std::endl;
+               dbus::Connection &client = dbus::Connection::getSystem();
+               client.methodcall(TESTSVC_BUS_NAME,
+                                                 TESTSVC_OBJECT_PATH,
+                                                 TESTSVC_INTERFACE,
+                                                 TESTSVC_METHOD_NOOP,
+                                                 -1,
+                                                 "()",
+                                                 "()");
+
+               const dbus::Variant& result = client.methodcall(TESTSVC_BUS_NAME,
+                                                                                                               TESTSVC_OBJECT_PATH,
+                                                                                                               TESTSVC_INTERFACE,
+                                                                                                               TESTSVC_METHOD_PROCESS,
+                                                                                                               -1,
+                                                                                                               "(s)",
+                                                                                                               "(s)", "arg");
+               char *ret = NULL;
+               result.get("(s)", &ret);
+               std::cout << ">>> Result: " << ret << std::endl;
+               client.methodcall(TESTSVC_BUS_NAME,
+                                                 TESTSVC_OBJECT_PATH,
+                                                 TESTSVC_INTERFACE,
+                                                 TESTSVC_METHOD_THROW,
+                                                 -1,
+                                                 "()",
+                                                 "(i)", 7);
+       } catch (std::exception& e) {
+               ERROR(e.what());
+       }
+}