#include <string>
#include <functional>
+#include <map>
#include <klay/dbus/variant.h>
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,
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
class Variant {
public:
+ Variant();
Variant(GVariant* var);
Variant(Variant&& var);
- Variant();
+ Variant(const std::string& format, ...);
~Variant();
Variant& operator=(GVariant* var);
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>
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);
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);
}
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);
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
{
}
+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)
return *this;
}
+GVariant* Variant::operator & ()
+{
+ return variant;
+}
+
Variant::operator bool () const
{
return variant != nullptr;
* 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 {
} 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());
+ }
+}