+#include "Phone.h"
+#include <gio/gio.h>
+#include <stdexcept>
+#include <Logger.h>
+
+#include <Commons/ThreadPool.h>
+#include <CommonsJavaScript/Converter.h>
+#include <json-glib/json-gvariant.h>
+
+#define TIZEN_PREFIX "org.tizen"
+#define PHONE_SERVICE TIZEN_PREFIX ".phone"
+#define PHONE_IFACE TIZEN_PREFIX ".Phone"
+#define PHONE_OBJ_PATH "/"
+
+#define DEFAULT_PINCODE "123456"
+#define DEFAULT_PASSKEY 123456
+
+namespace DeviceAPI
+{
+namespace Phone
+{
+
+using namespace WrtDeviceApis::Commons;
+using namespace WrtDeviceApis::CommonsJavaScript;
+
+PhoneMaster::PhoneMaster()
+{
+ LoggerD("entered");
+}
+
+PhoneMaster::~PhoneMaster()
+{
+ LoggerD("entered");
+}
+
+void PhoneMaster::handleSignal( GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ LoggerD("entered");
+ //LoggerD("\tsender_name: " << sender_name);
+ //LoggerD("\tobject_path: " << object_path);
+ //LoggerD("\tinterface_name: " << interface_name);
+ //LoggerD("\tsignal_name: " << signal_name);
+
+ PhoneMaster* ctx = static_cast<PhoneMaster*>(user_data);
+ if(!ctx) {
+ LoggerE("Failed to cast to PhoneMaster ... invalid context");
+ return;
+ }
+
+ if(!strcmp(signal_name, "RemoteDeviceSelected")) {
+ const char *result;
+ g_variant_get(parameters, "(s)", &result);
+ if(result) {
+ if(ctx->mRemoteDeviceSelectedListenersMap.size() > 0) {
+ for(auto it=ctx->mRemoteDeviceSelectedListenersMap.begin(); it!=ctx->mRemoteDeviceSelectedListenersMap.end(); ++it) {
+ PhoneSubscriptionData *data = (*it).second;
+ JSStringRef jsonString = JSStringCreateWithUTF8CString(result);
+ JSValueRef value = JSValueMakeFromJSONString(data->context, jsonString);
+ if(!value)
+ {
+ LoggerE("failed to make value from json");
+ break;
+ }
+ const JSValueRef arguments[1] = { value };
+ JSObjectCallAsFunction(data->context, data->callback, NULL, 1, arguments, NULL);
+ }
+ }
+ }
+ }
+ else if(!strcmp(signal_name, "CallChanged")) {
+ GVariantIter *iter;
+ g_variant_get(parameters, "(a{sv})", &iter);
+ const char *key = NULL, *state = NULL, *line_id = NULL, *contact = NULL;
+ GVariant *value;
+ while(g_variant_iter_next(iter, "{sv}", &key, &value)) {
+ if(!strcmp(key, "state")) {
+ state = g_variant_get_string(value, NULL);
+ LoggerD("\t- state: " << state);
+ }
+ else if(!strcmp(key, "line_id")) {
+ line_id = g_variant_get_string(value, NULL);
+ LoggerD("\t- line_id: " << line_id);
+ }
+ else if(!strcmp(key, "contact")) {
+ contact = g_variant_get_string(value, NULL);
+ LoggerD("\t- contact: " << contact);
+ }
+ }
+
+ if(ctx->mCallChangedListenersMap.size() > 0) {
+ LoggerD("subscribed for 'CallChanged' ... calling callback");
+ std::string json;
+ json += "{\"line_id\" : \"";
+ json += line_id;
+ json += "\", \"state\" : \"";
+ json += state;
+ json += "\", \"contact\" : ";
+ json += contact;
+ json += "}";
+
+ // call all subscribed listeners
+ std::map<int, PhoneMaster::PhoneSubscriptionData*>::iterator iter = ctx->mCallChangedListenersMap.begin();
+ while(iter != ctx->mCallChangedListenersMap.end()) {
+ PhoneSubscriptionData *data = (*iter).second;
+ JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str());
+ JSValueRef value = JSValueMakeFromJSONString(data->context, jsonString);
+ if(!value)
+ {
+ LoggerE("failed to make value from json");
+ break;
+ }
+ const JSValueRef arguments[1] = { value };
+ JSObjectCallAsFunction(data->context, data->callback, NULL, 1, arguments, NULL);
+ iter++;
+ }
+ }
+ }
+ else if(!strcmp(signal_name, "ContactsChanged")) {
+ if(ctx->mContactsChangedListenersMap.size() > 0) {
+ for(auto it=ctx->mContactsChangedListenersMap.begin(); it!=ctx->mContactsChangedListenersMap.end(); ++it) {
+ PhoneSubscriptionData *data = (*it).second;
+ JSObjectCallAsFunction(data->context, data->callback, NULL, 0, NULL, NULL);
+ }
+ }
+ }
+ else if(!strcmp(signal_name, "CallHistoryChanged")) {
+ if(ctx->mCallHistoryChangedListenersMap.size() > 0) {
+ for(auto it=ctx->mCallHistoryChangedListenersMap.begin(); it!=ctx->mCallHistoryChangedListenersMap.end(); ++it) {
+ PhoneSubscriptionData *data = (*it).second;
+ JSObjectCallAsFunction(data->context, data->callback, NULL, 0, NULL, NULL);
+ }
+ }
+ }
+ else if(!strcmp(signal_name, "CallHistoryEntryAdded")) {
+ const char *call;
+ g_variant_get(parameters, "(s)", &call);
+ if(call) {
+ if(ctx->mCallHistoryEntryAddedListenersMap.size() > 0) {
+ for(auto it=ctx->mCallHistoryEntryAddedListenersMap.begin(); it!=ctx->mCallHistoryEntryAddedListenersMap.end(); ++it) {
+ PhoneSubscriptionData *data = (*it).second;
+ JSStringRef jsonString = JSStringCreateWithUTF8CString(call);
+ JSValueRef value = JSValueMakeFromJSONString(data->context, jsonString);
+ if(!value)
+ {
+ LoggerE("failed to make value from json");
+ break;
+ }
+ const JSValueRef arguments[1] = { value };
+ JSObjectCallAsFunction(data->context, data->callback, NULL, 1, arguments, NULL);
+ }
+ }
+ }
+ }
+ else {
+ LoggerD("Unhandled signal: " << signal_name);
+ }
+}
+
+void PhoneMaster::invokeCall(std::string phoneNumber, JSObjectRef errorCallback, JSContextRef context)
+{
+ LoggerD("entered");
+
+ GError *error = NULL;
+ g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL),
+ PHONE_SERVICE,
+ PHONE_OBJ_PATH,
+ PHONE_IFACE,
+ "Dial",
+ g_variant_new("(s)", phoneNumber.c_str()), // floating variants are consumed
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+
+ if(error) {
+ // call the error callback to notify client about the error condition
+ std::string json;
+ json += "{\"code\" : \"";
+ //json += error->code;
+ json += "1";
+ json += "\", \"message\" : \"DBUS: ";
+ json += error->message;
+ json += "\"}";
+
+ g_error_free(error);
+
+ JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str());
+ JSValueRef value = JSValueMakeFromJSONString(context, jsonString);
+ if(!value)
+ {
+ LoggerE("failed to make value from json");
+ return;
+ }
+ const JSValueRef arguments[1] = { value };
+ JSObjectCallAsFunction(context, errorCallback, NULL, 1, arguments, NULL);
+ }
+}
+
+void PhoneMaster::hangupCall(JSObjectRef errorCallback, JSContextRef context)
+{
+ LoggerD("entered");
+
+ GError *error = NULL;
+ g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL),
+ PHONE_SERVICE,
+ PHONE_OBJ_PATH,
+ PHONE_IFACE,
+ "Hangup",
+ NULL,
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+
+ if(error) {
+ LoggerE("Failed to hangup call: " << error->message);
+
+ // call the error callback to notify client about the error condition
+ std::string json;
+ json += "{\"code\" : \"";
+ //json += error->code;
+ json += "1";
+ json += "\", \"message\" : \"DBUS: ";
+ json += error->message;
+ json += "\"}";
+
+ g_error_free(error);
+
+ JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str());
+ JSValueRef value = JSValueMakeFromJSONString(context, jsonString);
+ if(!value)
+ {
+ LoggerE("failed to make value from json");
+ return;
+ }
+ const JSValueRef arguments[1] = { value };
+ JSObjectCallAsFunction(context, errorCallback, NULL, 1, arguments, NULL);
+ }
+}
+
+void PhoneMaster::answerCall(JSObjectRef errorCallback, JSContextRef context)
+{
+ LoggerD("entered");
+
+ GError *error = NULL;
+ g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL),
+ PHONE_SERVICE,
+ PHONE_OBJ_PATH,
+ PHONE_IFACE,
+ "Answer",
+ NULL,
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+
+ if(error) {
+ LoggerE("Failed to answer call: " << error->message);
+
+ // call the error callback to notify client about the error condition
+ std::string json;
+ json += "{\"code\" : \"";
+ //json += error->code;
+ json += "1";
+ json += "\", \"message\" : \"DBUS: ";
+ json += error->message;
+ json += "\"}";
+
+ g_error_free(error);
+
+ JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str());
+ JSValueRef value = JSValueMakeFromJSONString(context, jsonString);
+ if(!value)
+ {
+ LoggerE("failed to make value from json");
+ return;
+ }
+ const JSValueRef arguments[1] = { value };
+ JSObjectCallAsFunction(context, errorCallback, NULL, 1, arguments, NULL);
+ }
+}
+
+void PhoneMaster::muteCall(bool mute, JSObjectRef errorCallback, JSContextRef context)
+{
+ LoggerD("entered");
+
+ GError *error = NULL;
+ g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL),
+ PHONE_SERVICE,
+ PHONE_OBJ_PATH,
+ PHONE_IFACE,
+ "Mute",
+ g_variant_new("(b)", mute), // floating variants are consumed
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+
+ if(error) {
+ LoggerE("Failed to mute call: " << error->message);
+
+ // call the error callback to notify client about the error condition
+ std::string json;
+ json += "{\"code\" : \"";
+ //json += error->code;
+ json += "1";
+ json += "\", \"message\" : \"DBUS: ";
+ json += error->message;
+ json += "\"}";
+
+ g_error_free(error);
+
+ JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str());
+ JSValueRef value = JSValueMakeFromJSONString(context, jsonString);
+ if(!value)
+ {
+ LoggerE("failed to make value from json");
+ return;
+ }
+ const JSValueRef arguments[1] = { value };
+ JSObjectCallAsFunction(context, errorCallback, NULL, 1, arguments, NULL);
+ }
+}
+
+void PhoneMaster::selectRemoteDevice(std::string btAddress) {
+ GError *error = NULL;
+ g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL),
+ PHONE_SERVICE,
+ PHONE_OBJ_PATH,
+ PHONE_IFACE,
+ "SelectRemoteDevice",
+ g_variant_new("(s)", btAddress.c_str()), // floating variants are consumed
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if(error) {
+ LoggerE("Failed to call 'SelectremoteDevice': " << error->message);
+ g_error_free(error);
+ }
+}
+
+void PhoneMaster::unselectRemoteDevice() {
+ GError *error = NULL;
+ g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL),
+ PHONE_SERVICE,
+ PHONE_OBJ_PATH,
+ PHONE_IFACE,
+ "UnselectRemoteDevice",
+ NULL,
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if(error) {
+ LoggerE("Failed to call 'UnselectremoteDevice': " << error->message);
+ g_error_free(error);
+ }
+}
+
+void PhoneMaster::getContacts(unsigned long count, JSObjectRef successCallback, JSObjectRef errorCallback, JSContextRef context)
+{
+ LoggerD("entered");
+
+ GError *error = NULL;
+ GVariant *reply = NULL;
+ reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL),
+ PHONE_SERVICE,
+ PHONE_OBJ_PATH,
+ PHONE_IFACE,
+ "GetContacts",
+ g_variant_new("(u)", count),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+
+ /* for async DBUS call
+ PhoneMaster::PhoneSubscriptionData *data = new PhoneMaster::PhoneSubscriptionData();
+ data->context = context;
+ data->callback = successCallback;
+ data->errorCallback = errorCallback;
+ */
+
+ if(error || !reply) {
+ if(error)
+ LoggerE("Failed to get contacts: " << error->message);
+ if(!reply)
+ LoggerE("reply is null");
+
+ // call the error callback to notify client about the error condition
+ std::string json;
+ json += "{\"code\" : \"";
+ json += error ? 1 : 2;
+ json += "\", \"message\" : \"DBUS: ";
+ json += error ? "Failed to 'GetContacts'" : "Reply from 'GetContacts' is null";
+ json += "\"}";
+
+ if(error)
+ g_error_free(error);
+
+ JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str());
+ JSValueRef value = JSValueMakeFromJSONString(context, jsonString);
+ if(!value)
+ {
+ LoggerE("failed to make value from json");
+ return;
+ }
+ const JSValueRef arguments[1] = { value };
+ JSObjectCallAsFunction(context, errorCallback, NULL, 1, arguments, NULL);
+ }
+
+ char *contacts = NULL;
+ g_variant_get(reply, "(s)", &contacts);
+
+ JSStringRef str = JSStringCreateWithUTF8CString(contacts);
+ JSValueRef value = JSValueMakeFromJSONString(context, str);
+ if(!value)
+ {
+ std::string json;
+ json += "{\"code\" : \"";
+ json += 2;
+ json += "\", \"message\" : \"";
+ json += "Unable to create response value.";
+ json += "\"}";
+
+ JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str());
+ JSValueRef value = JSValueMakeFromJSONString(context, jsonString);
+ if(!value)
+ {
+ LoggerE("Failed to make value from json");
+ // !!! it's bad !!! no callback is called !!!
+ // TODO: should we call error callback with 'undefined' value instead?
+ g_variant_unref(reply);
+ return;
+ }
+ const JSValueRef arguments[1] = { value };
+ JSObjectCallAsFunction(context, errorCallback, NULL, 1, arguments, NULL);
+ }
+ const JSValueRef arguments[1] = { value };
+ JSObjectCallAsFunction(context, successCallback, NULL, 1, arguments, NULL);
+ g_variant_unref(reply);
+}
+
+void PhoneMaster::getCallHistory(unsigned long count, JSObjectRef successCallback, JSObjectRef errorCallback, JSContextRef context)
+{
+ LoggerD("entered");
+
+ GError *error = NULL;
+ GVariant *reply = NULL;
+ reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL),
+ PHONE_SERVICE,
+ PHONE_OBJ_PATH,
+ PHONE_IFACE,
+ "GetCallHistory",
+ g_variant_new("(u)", count),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+
+ /* for async DBUS call
+ PhoneMaster::PhoneSubscriptionData *data = new PhoneMaster::PhoneSubscriptionData();
+ data->context = context;
+ data->callback = successCallback;
+ data->errorCallback = errorCallback;
+ */
+
+ if(error || !reply) {
+ if(error)
+ LoggerE("Failed to get call history: " << error->message);
+ if(!reply)
+ LoggerE("reply is null");
+
+ // call the error callback to notify client about the error condition
+ std::string json;
+ json += "{\"code\" : \"";
+ json += error ? 1 : 2;
+ json += "\", \"message\" : \"DBUS: ";
+ json += error ? "Failed to 'GetCallHistory'" : "Reply from 'GetCallHistory' is null";
+ json += "\"}";
+
+ if(error)
+ g_error_free(error);
+
+ JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str());
+ JSValueRef value = JSValueMakeFromJSONString(context, jsonString);
+ if(!value)
+ {
+ LoggerE("failed to make value from json");
+ return;
+ }
+ const JSValueRef arguments[1] = { value };
+ JSObjectCallAsFunction(context, errorCallback, NULL, 1, arguments, NULL);
+ }
+
+ char *calls = NULL;
+ g_variant_get(reply, "(s)", &calls);
+
+ JSStringRef str = JSStringCreateWithUTF8CString(calls);
+ JSValueRef value = JSValueMakeFromJSONString(context, str);
+ if(!value)
+ {
+ std::string json;
+ json += "{\"code\" : \"";
+ json += 2;
+ json += "\", \"message\" : \"";
+ json += "Unable to create response value.";
+ json += "\"}";
+
+ JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str());
+ JSValueRef value = JSValueMakeFromJSONString(context, jsonString);
+ if(!value)
+ {
+ LoggerE("Failed to make value from json");
+ // !!! it's bad !!! no callback is called !!!
+ // TODO: should we call error callback with 'undefined' value instead?
+ g_variant_unref(reply);
+ return;
+ }
+ const JSValueRef arguments[1] = { value };
+ JSObjectCallAsFunction(context, errorCallback, NULL, 1, arguments, NULL);
+ }
+ const JSValueRef arguments[1] = { value };
+ JSObjectCallAsFunction(context, successCallback, NULL, 1, arguments, NULL);
+ g_variant_unref(reply);
+}
+
+int PhoneMaster::addRemoteDeviceSelectedListener(JSObjectRef callback, JSContextRef context)
+{
+ LoggerD("entered");
+
+ guint id = 0;
+ if(mRemoteDeviceSelectedListenersMap.size() == 0) {
+ // we are not yet subscribed to the signal
+ id = g_dbus_connection_signal_subscribe( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL),
+ PHONE_SERVICE,
+ PHONE_IFACE,
+ "RemoteDeviceSelected",
+ NULL,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ handleSignal,
+ this,
+ NULL);
+ if(id <= 0) {
+ LoggerE("Failed to subscribe for 'RemoteDeviceSelected': " << id);
+ return -1;
+ }
+ }
+
+ PhoneMaster::PhoneSubscriptionData *data = new PhoneMaster::PhoneSubscriptionData();
+ if(!data) {
+ // unsubscribe the signal, othewise we will have 'unhandled' signal
+ LoggerE("Unable to create subscription data ... removing subscription with id: " << id);
+ g_dbus_connection_signal_unsubscribe(g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), id);
+ return -2;
+ }
+ data->context = context;
+ data->callback = callback;
+
+ mRemoteDeviceSelectedListenersMap[id] = data;
+
+ return id;
+}
+
+void PhoneMaster::removeRemoteDeviceSelectedListener(int id, JSContextRef context)
+{
+ LoggerD("removing listener id=" << id);
+
+ mRemoteDeviceSelectedListenersMap.erase(id);
+}
+
+int PhoneMaster::addCallChangedListener(JSObjectRef callback, JSContextRef context)
+{
+ LoggerD("adding listener for CallChanged");
+
+ // we are not yet subscribed to ofono's CallChanged signal
+ guint id = 0;
+ if(mCallChangedListenersMap.size() == 0) {
+ id = g_dbus_connection_signal_subscribe( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL),
+ PHONE_SERVICE,
+ PHONE_IFACE,
+ "CallChanged",
+ NULL,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ handleSignal,
+ this,
+ NULL);
+ if(id <= 0) {
+ LoggerE("Failed to subscribe for 'CallChanged': " << id);
+ return -1;
+ }
+ }
+
+ PhoneMaster::PhoneSubscriptionData *data = new PhoneMaster::PhoneSubscriptionData();
+ if(!data) {
+ // unsubscribe the signal, othewise we will have 'unhandled' signal
+ LoggerE("Unable to create subscription data ... removing subscription with id: " << id);
+ g_dbus_connection_signal_unsubscribe(g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), id);
+ return -2;
+ }
+ data->context = context;
+ data->callback = callback;
+
+ mCallChangedListenersMap[id] = data;
+
+ return id;
+}
+
+void PhoneMaster::removeCallChangedListener(int id, JSContextRef context)
+{
+ LoggerD("removing listener id=" << id);
+
+ mCallChangedListenersMap.erase(id);
+}
+
+int PhoneMaster::addCallHistoryEntryAddedListener(JSObjectRef callback, JSContextRef context)
+{
+ LoggerD("adding listener for CallHistoryEntryAdded");
+
+ guint id = 0;
+ if(mCallHistoryEntryAddedListenersMap.size() == 0) {
+ // we are not yet subscribed to the signal
+ id = g_dbus_connection_signal_subscribe( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL),
+ PHONE_SERVICE,
+ PHONE_IFACE,
+ "CallHistoryEntryAdded",
+ NULL,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ handleSignal,
+ this,
+ NULL);
+ if(id <= 0) {
+ LoggerE("Failed to subscribe for 'CallHistoryEntryAdded': " << id);
+ return -1;
+ }
+ }
+
+ PhoneMaster::PhoneSubscriptionData *data = new PhoneMaster::PhoneSubscriptionData();
+ if(!data) {
+ // unsubscribe the signal, othewise we will have 'unhandled' signal
+ LoggerE("Unable to create subscription data ... removing subscription with id: " << id);
+ g_dbus_connection_signal_unsubscribe(g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), id);
+ return -2;
+ }
+ data->context = context;
+ data->callback = callback;
+
+ mCallHistoryEntryAddedListenersMap[id] = data;
+
+ return id;
+}
+
+void PhoneMaster::removeCallHistoryEntryAddedListener(int id, JSContextRef context)
+{
+ LoggerD("removing listener id=" << id);
+
+ mCallHistoryEntryAddedListenersMap.erase(id);
+}
+
+int PhoneMaster::addContactsChangedListener(JSObjectRef callback, JSContextRef context)
+{
+ LoggerD("entered");
+
+ guint id = 0;
+ if(mContactsChangedListenersMap.size() == 0) {
+ // we are not yet subscribed to the signal
+ id = g_dbus_connection_signal_subscribe( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL),
+ PHONE_SERVICE,
+ PHONE_IFACE,
+ "ContactsChanged",
+ NULL,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ handleSignal,
+ this,
+ NULL);
+ if(id <= 0) {
+ LoggerE("Failed to subscribe for 'ContactsChanged': " << id);
+ return -1;
+ }
+ }
+
+ PhoneMaster::PhoneSubscriptionData *data = new PhoneMaster::PhoneSubscriptionData();
+ if(!data) {
+ // unsubscribe the signal, othewise we will have 'unhandled' signal
+ LoggerE("Unable to create subscription data ... removing subscription with id: " << id);
+ g_dbus_connection_signal_unsubscribe(g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), id);
+ return -2;
+ }
+ data->context = context;
+ data->callback = callback;
+
+ mContactsChangedListenersMap[id] = data;
+
+ return id;
+}
+
+void PhoneMaster::removeContactsChangedListener(int id, JSContextRef context)
+{
+ LoggerD("removing listener id=" << id);
+
+ mContactsChangedListenersMap.erase(id);
+}
+
+int PhoneMaster::addCallHistoryChangedListener(JSObjectRef callback, JSContextRef context)
+{
+ LoggerD("entered");
+
+ guint id = 0;
+ if(mCallHistoryChangedListenersMap.size() == 0) {
+ // we are not yet subscribed to the signal
+ id = g_dbus_connection_signal_subscribe( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL),
+ PHONE_SERVICE,
+ PHONE_IFACE,
+ "CallHistoryChanged",
+ NULL,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ handleSignal,
+ this,
+ NULL);
+ if(id <= 0) {
+ LoggerE("Failed to subscribe for 'CallHistoryChanged': " << id);
+ return -1;
+ }
+ }
+
+ PhoneMaster::PhoneSubscriptionData *data = new PhoneMaster::PhoneSubscriptionData();
+ if(!data) {
+ // unsubscribe the signal, othewise we will have 'unhandled' signal
+ LoggerE("Unable to create subscription data ... removing subscription with id: " << id);
+ g_dbus_connection_signal_unsubscribe(g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), id);
+ return -2;
+ }
+ data->context = context;
+ data->callback = callback;
+
+ mCallHistoryChangedListenersMap[id] = data;
+
+ return id;
+}
+
+void PhoneMaster::removeCallHistoryChangedListener(int id, JSContextRef context)
+{
+ LoggerD("removing listener id=" << id);
+
+ mCallHistoryChangedListenersMap.erase(id);
+}
+
+std::string PhoneMaster::activeCall(void) {
+ LoggerD("entered");
+
+ GError *error = NULL;
+ GVariant *reply = NULL;
+ reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL),
+ PHONE_SERVICE,
+ PHONE_OBJ_PATH,
+ PHONE_IFACE,
+ "ActiveCall",
+ NULL,
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+
+ std::string json;
+ if(error || !reply) {
+ if(error) {
+ LoggerE("Failed to get 'ActiveCall': " << error->message);
+ g_error_free(error);
+ }
+ else if(!reply)
+ LoggerE("reply is null");
+
+ json += "{\"line_id\" : \"";
+ json += "";
+ json += "\", \"state\" : \"";
+ json += "disconnected";
+ json += "\", \"contact\" : \"";
+ json += "{}";
+ json += "\"}";
+ }
+ else {
+ GVariantIter *iter;
+ g_variant_get(reply, "(a{sv})", &iter);
+ const char *key = NULL, *state = NULL, *line_id = NULL, *contact = NULL;
+ GVariant *value;
+ while(g_variant_iter_next(iter, "{sv}", &key, &value)) {
+ if(!strcmp(key, "state")) {
+ state = g_variant_get_string(value, NULL);
+ LoggerD("\t- state: " << state);
+ }
+ else if(!strcmp(key, "line_id")) {
+ line_id = g_variant_get_string(value, NULL);
+ LoggerD("\t- line_id: " << line_id);
+ }
+ else if(!strcmp(key, "contact")) {
+ contact = g_variant_get_string(value, NULL);
+ LoggerD("\t- contact: " << contact);
+ }
+ }
+
+ json += "{\"line_id\" : \"";
+ json += line_id;
+ json += "\", \"state\" : \"";
+ json += state;
+ json += "\", \"contact\" : ";
+ json += contact;
+ json += "}";
+
+ g_variant_unref(reply);
+ }
+
+ return json;
+}
+
+void PhoneMaster::getSelectedRemoteDevice(JSObjectRef callback, JSContextRef context) {
+ LoggerD("entered");
+
+ PhoneMaster::PhoneSubscriptionData *data = new PhoneMaster::PhoneSubscriptionData();
+ data->context = context;
+ data->callback = callback;
+ data->errorCallback = NULL;
+
+ g_dbus_connection_call( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL),
+ PHONE_SERVICE,
+ PHONE_OBJ_PATH,
+ PHONE_IFACE,
+ "GetSelectedRemoteDevice",
+ NULL,
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ PhoneMaster::asyncGetSelectedRemoteDeviceCB,
+ data);
+}
+
+void PhoneMaster::asyncGetSelectedRemoteDeviceCB(GObject *source, GAsyncResult *result, gpointer user_data) {
+ PhoneMaster::PhoneSubscriptionData *data = static_cast<PhoneMaster::PhoneSubscriptionData*>(user_data);
+ if(!data) {
+ LoggerE("Failed to cast object: PhoneSubscriptionData");
+ return;
+ }
+
+ GError *err = NULL;
+ GVariant *reply;
+ reply = g_dbus_connection_call_finish(g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL), result, &err);
+ JSStringRef strRef = JSStringCreateWithUTF8CString("");
+ if(err || !reply) {
+ if(err) {
+ LoggerE("Failed to \"GetSelectedRemoteDevice\": " << err->message);
+ g_error_free(err);
+ }
+ else if(!reply)
+ LoggerE("reply is null");
+ }
+ else {
+ const char *str;
+ g_variant_get(reply, "(s)", &str);
+ if(str)
+ strRef = JSStringCreateWithUTF8CString(str);
+ }
+
+ JSValueRef value = JSValueMakeString(data->context, strRef);
+ const JSValueRef arguments[1] = { value };
+ JSObjectCallAsFunction(data->context, data->callback, NULL, 1, arguments, NULL);
+ delete data;
+ if(reply)
+ g_variant_unref(reply);
+}
+
+} // Phone
+} // DeviceAPI
+