--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <types_internal.h>
+#include <ScopeMutex.h>
+#include "DBusClient.h"
+
+using namespace ctx;
+
+static const gchar __introspection[] =
+ "<node>"
+ " <interface name='" DBUS_IFACE "'>"
+ " <method name='" METHOD_RESPOND "'>"
+ " <arg type='i' name='" ARG_REQID "' direction='in'/>"
+ " <arg type='s' name='" ARG_SUBJECT "' direction='in'/>"
+ " <arg type='i' name='" ARG_RESULT_ERR "' direction='in'/>"
+ " <arg type='s' name='" ARG_OUTPUT "' direction='in'/>"
+ " </method>"
+ " </interface>"
+ "</node>";
+
+GDBusConnection *DBusClient::__connection = NULL;
+GDBusNodeInfo *DBusClient::__nodeInfo = NULL;
+std::atomic_int DBusClient::__instanceCount(0);
+std::map<std::string, IDBusClientListener*> DBusClient::__listenerMap;
+
+DBusClient::DBusClient()
+{
+ ++__instanceCount;
+}
+
+DBusClient::~DBusClient()
+{
+ if (--__instanceCount == 0)
+ __release();
+}
+
+void DBusClient::__onMethodCalled(GDBusConnection *conn, const gchar *sender,
+ const gchar *path, const gchar *iface, const gchar *name,
+ GVariant *param, GDBusMethodInvocation *invocation, gpointer userData)
+{
+ IF_FAIL_VOID_TAG(STR_EQ(name, METHOD_RESPOND), _W, "Invalid method: %s", name);
+
+ gint reqId = 0;
+ const gchar *subject = NULL;
+ gint error = 0;
+ const gchar *data = NULL;
+
+ g_variant_get(param, "(i&si&s)", &reqId, &subject, &error, &data);
+ _D("[Response] ReqId: %d, Subject: %s, Error: %d", reqId, subject, error);
+ IF_FAIL_VOID_TAG(subject && data, _W, "Invalid parameter");
+
+ auto it = __listenerMap.find(subject);
+ it->second->onPublish(subject, reqId, error, data);
+
+ g_dbus_method_invocation_return_value(invocation, NULL);
+}
+
+bool DBusClient::__init()
+{
+ static GMutex mutex;
+ ScopeMutex sm(&mutex);
+
+ if (__connection)
+ return true;
+
+ GError *gerr = NULL;
+ gchar *addr = NULL;
+ GDBusInterfaceVTable vtable;
+ guint regId;
+
+ __nodeInfo = g_dbus_node_info_new_for_xml(__introspection, NULL);
+ IF_FAIL_RETURN_TAG(__nodeInfo != NULL, false, _E, "Initialization failed");
+
+ addr = g_dbus_address_get_for_bus_sync(G_BUS_TYPE_SESSION, NULL, &gerr);
+ HANDLE_GERROR(gerr);
+ IF_FAIL_CATCH_TAG(addr != NULL, _E, "Getting address failed");
+ _SD("Address: %s", addr);
+
+ __connection = g_dbus_connection_new_for_address_sync(addr,
+ (GDBusConnectionFlags)(G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION),
+ NULL, NULL, &gerr);
+ g_free(addr);
+ HANDLE_GERROR(gerr);
+ IF_FAIL_CATCH_TAG(__connection != NULL, _E, "Connection failed");
+
+ vtable.method_call = __onMethodCalled;
+ vtable.get_property = NULL;
+ vtable.set_property = NULL;
+
+ regId = g_dbus_connection_register_object(__connection, DBUS_PATH,
+ __nodeInfo->interfaces[0], &vtable, NULL, NULL, &gerr);
+ HANDLE_GERROR(gerr);
+ IF_FAIL_CATCH_TAG(regId>0, _E, "Object registration failed");
+
+ _I("DBus connection established");
+ _D("DBus name: %s", g_dbus_connection_get_unique_name(__connection));
+ return true;
+
+CATCH:
+ __release();
+ return false;
+}
+
+void DBusClient::__release()
+{
+ if (__connection) {
+ g_dbus_connection_flush_sync(__connection, NULL, NULL);
+ g_dbus_connection_close_sync(__connection, NULL, NULL);
+ g_object_unref(__connection);
+ __connection = NULL;
+ }
+
+ if (__nodeInfo) {
+ g_dbus_node_info_unref(__nodeInfo);
+ __nodeInfo = NULL;
+ }
+
+ _I("DBus connection released");
+}
+
+int DBusClient::__request(int type, int reqId, const char *subject, const char *input,
+ std::string *result, std::string *outputData)
+{
+ _D("Requesting: %d, %d, %s", type, reqId, subject);
+
+ if (input == NULL)
+ input = EMPTY_JSON_OBJECT;
+
+ /* FIXME: the second param is the security cookie, which is deprected in 3.0.
+ * We need to completely REMOVE this parameter from the dbus protocol. */
+ GVariant *param = g_variant_new("(isiss)", type, "", reqId, subject, input);
+ IF_FAIL_RETURN_TAG(param, ERR_OUT_OF_MEMORY, _E, "Memory allocation failed");
+
+ GError *err = NULL;
+ GVariant *response = g_dbus_connection_call_sync(__connection, DBUS_DEST, DBUS_PATH, DBUS_IFACE,
+ METHOD_REQUEST, param, NULL, G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, &err);
+ HANDLE_GERROR(err);
+ IF_FAIL_RETURN_TAG(response, ERR_OPERATION_FAILED, _E, "Method call failed");
+
+ gint error = ERR_OPERATION_FAILED;
+ const gchar *resultStr = NULL;
+ const gchar *dataStr = NULL;
+
+ g_variant_get(response, "(i&s&s)", &error, &resultStr, &dataStr);
+
+ if (result && resultStr)
+ *result = resultStr;
+
+ if (outputData && dataStr)
+ *outputData = dataStr;
+
+ g_variant_unref(response);
+
+ return error;
+}
+
+int DBusClient::__request(int type, int reqId, const char* subject, const char* input)
+{
+ _D("Requesting: %d, %d, %s", type, reqId, subject);
+
+ if (input == NULL)
+ input = EMPTY_JSON_OBJECT;
+
+ /* FIXME: the second param is the security cookie, which is deprected in 3.0.
+ * We need to completely REMOVE this parameter from the dbus protocol. */
+ GVariant *param = g_variant_new("(isiss)", type, "", reqId, subject, input);
+ IF_FAIL_RETURN_TAG(param, ERR_OUT_OF_MEMORY, _E, "Memory allocation failed");
+
+ GError *err = NULL;
+ g_dbus_connection_call(__connection, DBUS_DEST, DBUS_PATH, DBUS_IFACE,
+ METHOD_REQUEST, param, NULL, G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, NULL, &err);
+
+ if (err) {
+ HANDLE_GERROR(err);
+ return ERR_OPERATION_FAILED;
+ }
+
+ return ERR_NONE;
+}
+
+int DBusClient::__generateReqId()
+{
+ static GMutex mutex;
+ static int reqId = 0;
+
+ ctx::ScopeMutex sm(&mutex);
+
+ /* Overflow handling */
+ if (++reqId < 0)
+ reqId = 1;
+
+ return reqId;
+}
+
+void DBusClient::addListener(std::string subject, IDBusClientListener *listener)
+{
+ _D("Registering the listener for '%s'", subject.c_str());
+
+ static GMutex mutex;
+ ctx::ScopeMutex sm(&mutex);
+
+ __listenerMap[subject] = listener;
+}
+
+int DBusClient::isSupported(std::string subject)
+{
+ IF_FAIL_RETURN(__init(), ERR_OPERATION_FAILED);
+
+ return __request(REQ_SUPPORT, __generateReqId(), subject.c_str(), NULL, NULL, NULL);
+}
+
+int DBusClient::subscribe(std::string subject, json option, int *reqId, json *result)
+{
+ IF_FAIL_RETURN(__init(), ERR_OPERATION_FAILED);
+ ASSERT_NOT_NULL(reqId);
+
+ *reqId = __generateReqId();
+
+ _I("[Subscribe] ReqId: %d, Subject: %s", *reqId, subject.c_str());
+
+ std::string resultStr;
+ int error = __request(REQ_SUBSCRIBE, *reqId, subject.c_str(), option.str().c_str(), &resultStr, NULL);
+
+ if (result)
+ *result = resultStr;
+
+ _D("Error: %#x", error);
+ _SD("Result: %s", resultStr.c_str());
+
+ return error;
+}
+
+int DBusClient::unsubscribe(std::string subject, int reqId)
+{
+ IF_FAIL_RETURN(__init(), ERR_OPERATION_FAILED);
+
+ _I("[Unsubscribe] ReqId: %d, Subject: %s", reqId, subject.c_str());
+
+ return __request(REQ_UNSUBSCRIBE, reqId, subject.c_str(), NULL, NULL, NULL);
+}
+
+int DBusClient::read(std::string subject, json option, int *reqId, json *result)
+{
+ IF_FAIL_RETURN(__init(), ERR_OPERATION_FAILED);
+ ASSERT_NOT_NULL(reqId);
+
+ *reqId = __generateReqId();
+
+ _I("[Read] ReqId: %d, Subject: %s", *reqId, subject.c_str());
+
+ std::string resultStr;
+ int error = __request(REQ_READ, *reqId, subject.c_str(), option.str().c_str(), &resultStr, NULL);
+
+ if (result)
+ *result = resultStr;
+
+ _D("Error: %#x", error);
+ _SD("Result: %s", resultStr.c_str());
+
+ return error;
+}
+
+int DBusClient::readSync(std::string subject, json option, int *reqId, json *outputData)
+{
+ IF_FAIL_RETURN(__init(), ERR_OPERATION_FAILED);
+ ASSERT_NOT_NULL(reqId);
+ ASSERT_NOT_NULL(outputData);
+
+ *reqId = __generateReqId();
+
+ _I("[ReadSync] ReqId: %d, Subject: %s", *reqId, subject.c_str());
+
+ std::string output;
+ int error = __request(REQ_READ_SYNC, *reqId, subject.c_str(), option.str().c_str(), NULL, &output);
+
+ *outputData = output;
+
+ _D("Error: %#x", error);
+ _SD("Data: %s", output.c_str());
+
+ return error;
+}
+
+int DBusClient::write(std::string subject, json inputData)
+{
+ IF_FAIL_RETURN(__init(), ERR_OPERATION_FAILED);
+
+ int reqId = __generateReqId();
+
+ _I("[Write] ReqId: %d, Subject: %s", reqId, subject.c_str());
+ _SD("Data: %s", inputData.str().c_str());
+
+ int error = __request(REQ_WRITE, reqId, subject.c_str(), inputData.str().c_str());
+ _D("Error: %#x", error);
+
+ return error;
+}
+
+int DBusClient::write(std::string subject, json inputData, json *result)
+{
+ IF_FAIL_RETURN(__init(), ERR_OPERATION_FAILED);
+
+ int reqId = __generateReqId();
+
+ _I("[Write with reply] ReqId: %d, Subject: %s", reqId, subject.c_str());
+ _SD("Data: %s", inputData.str().c_str());
+
+ std::string resultStr;
+ int error = __request(REQ_WRITE, reqId, subject.c_str(), inputData.str().c_str(), &resultStr, NULL);
+
+ if (result)
+ *result = resultStr;
+
+ _D("Error: %#x", error);
+ _SD("Result: %s", resultStr.c_str());
+
+ return error;
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CONTEXT_DBUS_CLIENT_H_
+#define _CONTEXT_DBUS_CLIENT_H_
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <string>
+#include <map>
+#include <atomic>
+#include "IDBusClientListener.h"
+
+namespace ctx {
+
+ class DBusClient {
+ public:
+ DBusClient();
+ ~DBusClient();
+
+ void addListener(std::string subject, IDBusClientListener *listener);
+
+ int isSupported(std::string subject);
+
+ int subscribe(std::string subject, json option, int *reqId, json *result);
+ int unsubscribe(std::string subject, int reqId);
+ int read(std::string subject, json option, int *reqId, json *result);
+ int readSync(std::string subject, json option, int *reqId, json *outputData);
+ int write(std::string subject, json inputData);
+ int write(std::string subject, json inputData, json *result);
+
+ private:
+ static void __onMethodCalled(GDBusConnection *conn, const gchar *sender,
+ const gchar *path, const gchar *iface, const gchar *name,
+ GVariant *param, GDBusMethodInvocation *invocation, gpointer userData);
+
+ bool __init();
+ void __release();
+ int __request(int type, int reqId, const char *subject, const char *input,
+ std::string *result, std::string *outputData);
+ int __request(int type, int reqId, const char *subject, const char *input);
+ int __generateReqId();
+
+
+ static GDBusConnection *__connection;
+ static GDBusNodeInfo *__nodeInfo;
+ static std::atomic_int __instanceCount;
+ static std::map<std::string, IDBusClientListener*> __listenerMap;
+
+ }; /* class ctx::DBusClient */
+
+} /* namespace ctx */
+
+#endif // _CONTEXT_DBUS_CLIENT_H_
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* TODO: Remove this request_handler. All APIs and the testsuite should use DBusClient directly. */
+
+#include <string>
+#include <map>
+
+#include <types_internal.h>
+#include <ScopeMutex.h>
+#include "DBusClient.h"
+#include "request_handler.h"
+
+using namespace ctx;
+
+class DBusClientListenerImpl : public IDBusClientListener {
+public:
+ DBusClientListenerImpl() {}
+ ~DBusClientListenerImpl() {}
+ void setCb(std::string subject, request_handler::subject_response_cb cb);
+ void onPublish(std::string subject, int reqId, int error, json event);
+private:
+ std::map<std::string, request_handler::subject_response_cb> __callbackMap;
+};
+
+void DBusClientListenerImpl::setCb(std::string subject, request_handler::subject_response_cb cb)
+{
+ __callbackMap[subject] = cb;
+}
+
+void DBusClientListenerImpl::onPublish(std::string subject, int reqId, int error, json event)
+{
+ auto it = __callbackMap.find(subject);
+ IF_FAIL_VOID_TAG(it != __callbackMap.end(), _W, "Unregistered subject");
+ it->second(subject.c_str(), reqId, error, event);
+}
+
+static DBusClientListenerImpl __dbusListener;
+static DBusClient __dbusClient;
+
+EXTAPI int ctx::request_handler::subscribe(const char* subject, ctx::json* option, int* req_id, ctx::json* request_result)
+{
+ return __dbusClient.subscribe(subject, option ? *option : NULL, req_id, request_result);
+}
+
+EXTAPI int ctx::request_handler::unsubscribe(const char* subject, int req_id)
+{
+ return __dbusClient.unsubscribe(subject, req_id);
+}
+
+EXTAPI int ctx::request_handler::read(const char* subject, ctx::json* option, int* req_id, ctx::json* request_result)
+{
+ return __dbusClient.read(subject, option ? *option : NULL, req_id, request_result);
+}
+
+EXTAPI int ctx::request_handler::read_sync(const char* subject, ctx::json* option, int* req_id, ctx::json* data_read)
+{
+ return __dbusClient.readSync(subject, option ? *option : NULL, req_id, data_read);
+}
+
+EXTAPI int ctx::request_handler::write(const char* subject, ctx::json* data)
+{
+ return __dbusClient.write(subject, *data);
+}
+
+EXTAPI int ctx::request_handler::write_with_reply(const char* subject, ctx::json* data, ctx::json* request_result)
+{
+ return __dbusClient.write(subject, *data, request_result);
+}
+
+EXTAPI int ctx::request_handler::is_supported(const char* subject)
+{
+ return __dbusClient.isSupported(subject);
+}
+
+EXTAPI bool ctx::request_handler::register_callback(const char* subject, subject_response_cb callback)
+{
+ __dbusListener.setCb(subject, callback);
+ __dbusClient.addListener(subject, &__dbusListener);
+ return true;
+}