Initial checkin of the wrt-plugins-ivi-hfp for the HTML5 UI 13/17713/1 accepted/tizen_ivi accepted/tizen_ivi_panda accepted/tizen_ivi_release tizen tizen_ivi_panda tizen_ivi_release accepted/tizen/ivi/20140308.072139 accepted/tizen/ivi/panda/20140403.015656 accepted/tizen/ivi/release/20140315.041624 submit/tizen/20140307.230430 submit/tizen_ivi_panda/20140403.012243 submit/tizen_ivi_release/20140315.041347
authorbrianjjones <brian.j.jones@intel.com>
Fri, 7 Mar 2014 22:57:02 +0000 (14:57 -0800)
committerbrianjjones <brian.j.jones@intel.com>
Fri, 7 Mar 2014 22:58:18 +0000 (14:58 -0800)
Change-Id: Id4241ae56a4ed897518953a55d6ce63f27c09efa

13 files changed:
CMakeLists.txt [new file with mode: 0644]
config.dtd [new file with mode: 0644]
packaging/wrt-plugins-ivi-phone.changes [new file with mode: 0644]
packaging/wrt-plugins-ivi-phone.spec [new file with mode: 0644]
pkgconfigs/wrt-plugins-tizen-phone.pc.in [new file with mode: 0644]
src/JSPhone.cpp [new file with mode: 0644]
src/JSPhone.h [new file with mode: 0644]
src/Phone.cpp [new file with mode: 0644]
src/Phone.h [new file with mode: 0644]
src/Phone.idl [new file with mode: 0644]
src/config.xml [new file with mode: 0644]
src/plugin_initializer.cpp [new file with mode: 0644]
wrt-plugins-ivi-phone.manifest [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..4fb84eb
--- /dev/null
@@ -0,0 +1,152 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
+PROJECT(wrt-plugin-ivi)
+
+SET(CMAKE_INSTALL_PREFIX "/usr")
+
+# -----------------------------------------------------------------------------
+# Required platform modules
+# -----------------------------------------------------------------------------
+INCLUDE(FindPkgConfig)
+
+PKG_CHECK_MODULES(dpl REQUIRED dpl-efl)
+PKG_CHECK_MODULES(wrt-plugins-types REQUIRED wrt-plugins-types)
+PKG_CHECK_MODULES(wrt-deviceapis-commons-javascript REQUIRED wrt-plugins-commons-javascript)
+PKG_CHECK_MODULES(wrt-plugins-tizen-common REQUIRED wrt-plugins-tizen-common)
+PKG_CHECK_MODULES(json-glib REQUIRED json-glib-1.0)
+
+INCLUDE_DIRECTORIES(
+  ${dpl_INCLUDE_DIRS}
+  ${wrt-plugins-types_INCLUDE_DIRS}
+  ${wrt-deviceapis-commons-javascript_INCLUDE_DIRS}
+  ${wrt-plugins-tizen-common_INCLUDE_DIRS}
+  ${json-glib_INCLUDE_DIRS}
+)
+
+# -----------------------------------------------------------------------------
+# Determine the log option
+# -----------------------------------------------------------------------------
+
+OPTION(DPL_LOG "DPL logs status" ON)
+
+IF(DPL_LOG)
+    MESSAGE(STATUS "Logging enabled for DPL")
+    ADD_DEFINITIONS("-DDPL_LOGS_ENABLED")
+ELSE(DPL_LOG)
+    MESSAGE(STATUS "Logging disabled for DPL")
+ENDIF(DPL_LOG)
+
+# -----------------------------------------------------------------------------
+# Determine the time tracing option
+# -----------------------------------------------------------------------------
+
+OPTION(ENABLE_TIME_TRACER "TIME TRACING" OFF)
+
+IF(ENABLE_TIME_TRACER)
+    MESSAGE(STATUS "Time tracer enabled")
+    ADD_DEFINITIONS("-DENABLE_TIME_TRACER")
+ELSE(ENABLE_TIME_TRACER)
+    MESSAGE(STATUS "Time tracer disabled")
+ENDIF(ENABLE_TIME_TRACER)
+
+# -----------------------------------------------------------------------------
+# Set build type (Release by default)
+# -----------------------------------------------------------------------------
+IF("${CMAKE_BUILD_TYPE}" STREQUAL "")
+     SET(CMAKE_BUILD_TYPE Release)
+ENDIF("${CMAKE_BUILD_TYPE}" STREQUAL "")
+
+MESSAGE("Build type: ${CMAKE_BUILD_TYPE}")
+
+# -----------------------------------------------------------------------------
+# CFlags
+# -----------------------------------------------------------------------------
+SET(CMAKE_C_FLAGS_PROFILING    "-O0 -g -pg")
+SET(CMAKE_CXX_FLAGS_PROFILING  "-O0 -std=c++0x -g -pg")
+SET(CMAKE_C_FLAGS_DEBUG        "-O0 -g")
+SET(CMAKE_CXX_FLAGS_DEBUG      "-O0 -std=c++0x -g")
+SET(CMAKE_C_FLAGS_RELEASE      "-O2 -g")
+SET(CMAKE_CXX_FLAGS_RELEASE    "-O2 -std=c++0x -g")
+ADD_DEFINITIONS("-DCLIENT_IPC_THREAD")
+ADD_DEFINITIONS("-DEXPORT_API=")
+ADD_DEFINITIONS("-Wall")
+ADD_DEFINITIONS("-D_FILE_OFFSET_BITS=64")
+#ADD_DEFINITIONS("-Werror")
+#ADD_DEFINITIONS("-Wextra")
+ADD_DEFINITIONS("-DAPPLICATION_API_BACKWARD_COMPATIBILITY")
+ADD_DEFINITIONS("-std=c++11")
+
+
+# -----------------------------------------------------------------------------
+# Schema of plugin's configuration file
+# -----------------------------------------------------------------------------
+SET(COMMON_CONFIG_DTD ${CMAKE_CURRENT_SOURCE_DIR}/config.dtd)
+SET(COMMON_CONFIG_DTD_DST /usr/etc/tizen-apis)
+INSTALL(FILES ${COMMON_CONFIG_DTD} DESTINATION ${COMMON_CONFIG_DTD_DST})
+
+# -----------------------------------------------------------------------------
+# Global variables
+# -----------------------------------------------------------------------------
+
+SET(DESTINATION_LIB_PREFIX lib/wrt-plugins)
+
+SET(DESTINATION_HEADER_PREFIX include/${PROJECT_NAME})
+
+SET(LIBS_WIDGETDB ${wrt-plugins-widgetdb_LIBRARIES})
+
+# -----------------------------------------------------------------------------
+# Macros for pkgconfig
+# -----------------------------------------------------------------------------
+SET(PKGCONFIG_DIR ${CMAKE_SOURCE_DIR}/pkgconfigs)
+#SET(PKGCONFIG_DIR ${CMAKE_SOURCE_DIR})
+
+MACRO(configure_and_install_pkg PKG_FILE)
+    CONFIGURE_FILE(${PKGCONFIG_DIR}/${PKG_FILE}.in
+               ${PKGCONFIG_DIR}/${PKG_FILE} @ONLY)
+    INSTALL(FILES ${PKGCONFIG_DIR}/${PKG_FILE} DESTINATION lib/pkgconfig)
+ENDMACRO(configure_and_install_pkg)
+
+SET(TARGET_NAME wrt-plugins-tizen-phone)
+SET(DESTINATION_NAME tizen-phone)
+SET(TARGET_IMPL_NAME wrt-plugins-tizen-phone-impl)
+
+configure_and_install_pkg(wrt-plugins-tizen-phone.pc)
+
+SET(CMAKE_INSTALL_RPATH
+       ${CMAKE_INSTALL_RPATH}
+       ${CMAKE_INSTALL_PREFIX}/${DESTINATION_LIB_PREFIX}/${tizen_dest}
+       ${CMAKE_INSTALL_PREFIX}/${DESTINATION_LIB_PREFIX}/${DESTINATION_NAME}
+)
+
+SET(SRCS_IMPL
+       src/Phone.cpp
+       src/JSPhone.cpp
+)
+
+ADD_LIBRARY(${TARGET_IMPL_NAME} SHARED ${SRCS_IMPL})
+
+TARGET_LINK_LIBRARIES(${TARGET_IMPL_NAME}
+        ${dpl_LDFLAGS}
+        ${dpl-event_LDFLAGS}
+        ${wrt-deviceapis-commons-javascript_LDFLAGS}
+       ${wrt-plugins-tizen-common_LDFLAGS}
+        ${json-glib_LDFLAGS}
+       -L/usr/lib/wrt-plugins/tizen-tizen/
+)
+
+SET(SRCS
+       src/plugin_initializer.cpp
+)
+
+ADD_LIBRARY(${TARGET_NAME} SHARED ${SRCS})
+
+TARGET_LINK_LIBRARIES(${TARGET_NAME}
+       ${TARGET_IMPL_NAME}
+)
+
+INSTALL(TARGETS ${TARGET_NAME} ${TARGET_IMPL_NAME} LIBRARY DESTINATION ${DESTINATION_LIB_PREFIX}/${DESTINATION_NAME})
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/config.xml DESTINATION ${DESTINATION_LIB_PREFIX}/${DESTINATION_NAME})
+INSTALL(
+       DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/ DESTINATION ${DESTINATION_HEADER_PREFIX}/phone
+       FILES_MATCHING PATTERN "*.h" PATTERN "CMakeFiles" EXCLUDE
+)
+
diff --git a/config.dtd b/config.dtd
new file mode 100644 (file)
index 0000000..f1a1de4
--- /dev/null
@@ -0,0 +1,32 @@
+<!ELEMENT plugin-properties (library-name, feature-install-uri?,
+  feature-key-cn?, feature-root-cn?,
+  feature-root-fingerprint?, feature-set*, api-feature+) >
+
+<!ELEMENT library-name (#PCDATA)>
+
+<!ELEMENT feature-set (api-feature-ref+)>
+<!ATTLIST feature-set name CDATA #REQUIRED >
+
+<!ELEMENT api-feature-ref EMPTY>
+<!ATTLIST api-feature-ref name CDATA #REQUIRED >
+
+<!ELEMENT api-feature (name, device-capability*, Object*, Function*, Property* )>
+
+<!ELEMENT name (#PCDATA) >
+<!ELEMENT device-capability (#PCDATA )>
+
+<!ELEMENT Object (Function*, Property*)>
+<!ATTLIST Object name CDATA #REQUIRED >
+
+<!ELEMENT Function (#PCDATA )>
+<!ATTLIST Function name CDATA #REQUIRED >
+<!ATTLIST Function restricted CDATA #IMPLIED >
+
+<!ELEMENT Property (#PCDATA )>
+
+
+<!ELEMENT feature-install-uri (#PCDATA)>
+<!ELEMENT feature-key-cn (#PCDATA)>
+<!ELEMENT feature-root-cn (#PCDATA)>
+<!ELEMENT feature-root-fingerprint (#PCDATA)>
+
diff --git a/packaging/wrt-plugins-ivi-phone.changes b/packaging/wrt-plugins-ivi-phone.changes
new file mode 100644 (file)
index 0000000..ea708c6
--- /dev/null
@@ -0,0 +1,3 @@
+* Fri Mar 07 2014 brianjjones <brian.j.jones@intel.com> 54b2161
+- Initial checkin of the wrt-plugins-ivi-hfp for the HTML5 UI
+
diff --git a/packaging/wrt-plugins-ivi-phone.spec b/packaging/wrt-plugins-ivi-phone.spec
new file mode 100644 (file)
index 0000000..e322feb
--- /dev/null
@@ -0,0 +1,60 @@
+Name:       wrt-plugins-ivi-phone
+Summary:    JavaScript plugin to access Phone for WebRuntime
+Version:    0.8.5
+Release:    1
+Group:      Development/Libraries
+License:    Apache-2.0
+Source0:    %{name}-%{version}.tar.gz
+
+BuildRequires:  pkgconfig(ewebkit2)
+BuildRequires:  pkgconfig(dpl-efl)
+BuildRequires:  pkgconfig(dpl-event-efl)
+BuildRequires:  pkgconfig(wrt-plugins-commons)
+BuildRequires:  pkgconfig(wrt-plugins-commons-javascript)
+BuildRequires:  wrt-plugins-tizen-devel
+BuildRequires:  expat-devel
+BuildRequires:  cmake
+BuildRequires:  gettext-devel
+BuildRequires:  pkgconfig(json-glib-1.0)
+
+%description
+JavaScript plugin to access Phone for WebRuntime
+
+%package devel
+Summary:    Wrt-plugins-ivi-phone development headers
+Group:      Development/Libraries
+Requires:   %{name} = %{version}
+
+%description devel
+Wrt-plugins-ivi-phone development headers
+
+%prep
+%setup -q
+
+%build
+
+%define PREFIX "%{_libdir}/wrt-plugins"
+
+export LDFLAGS+="-Wl,--rpath=%{PREFIX} -Wl,--as-needed"
+
+cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} -DDPL_LOG="ON" -DENABLE_TIME_TRACER="OFF"
+
+make %{?jobs:-j%jobs} VERBOSE=1
+
+%install
+rm -rf %{buildroot}
+%make_install
+
+%post
+wrt-installer -p
+
+%postun
+
+%files
+%manifest wrt-plugins-ivi-phone.manifest
+%{_libdir}/wrt-plugins/*
+/usr/etc/tizen-apis/*
+
+%files devel
+%{_includedir}/*
+%{_libdir}/pkgconfig/*
diff --git a/pkgconfigs/wrt-plugins-tizen-phone.pc.in b/pkgconfigs/wrt-plugins-tizen-phone.pc.in
new file mode 100644 (file)
index 0000000..285d15d
--- /dev/null
@@ -0,0 +1,13 @@
+prefix=/usr
+project_name=@CMAKE_PROJECT_NAME@
+module_name=phone
+exec_prefix=${prefix}
+libdir=${prefix}/lib/wrt-plugins/tizen-${module_name}
+includedir=${prefix}/include/${project_name}
+
+Name: wrt-plugins-tizen-${module_name}
+Description: wrt-plugins-tizen-${module_name}
+Version: @CMAKE_PROJECT_VERSION@
+Requires: icu-i18n icu-io icu-le icu-lx icu-uc
+Libs: -L${libdir} -lwrt-plugins-tizen-${module_name}-impl
+Cflags: -I${includedir}/${module_name}
diff --git a/src/JSPhone.cpp b/src/JSPhone.cpp
new file mode 100644 (file)
index 0000000..515846f
--- /dev/null
@@ -0,0 +1,734 @@
+#include "JSPhone.h"
+#include "Phone.h"
+
+#include <Logger.h>
+#include <Commons/Exception.h>
+#include <CommonsJavaScript/Utils.h>
+#include <CommonsJavaScript/JSCallbackManager.h>
+#include <JSWebAPIErrorFactory.h>
+#include <ArgumentValidator.h>
+#include <CommonsJavaScript/Converter.h>
+#include <dpl/scoped_ptr.h>
+#include <sstream>
+#include <map>
+
+#include <json-glib/json-gvariant.h>
+
+namespace DeviceAPI {
+namespace Phone {
+
+using namespace DPL;
+using namespace DeviceAPI::Common;
+using namespace WrtDeviceApis::Commons;
+using namespace WrtDeviceApis::CommonsJavaScript;
+
+#define PHONE_ACTIVECALL_PROP "activeCall"
+
+JSClassDefinition JSPhone::m_classInfo = {
+    0,
+    kJSClassAttributeNone,
+    "Phone",
+    0,
+    m_property,
+    m_function,
+    initialize,
+    finalize,
+    NULL, //HasProperty,
+    NULL, //GetProperty,
+    NULL, //SetProperty,
+    NULL, //DeleteProperty,
+    NULL, //GetPropertyNames,
+    NULL, //CallAsFunction,
+    NULL, //CallAsConstructor,
+    hasInstance,
+    NULL, //ConvertToType
+};
+
+JSStaticFunction JSPhone::m_function[] = {
+    { "invokeCall", JSPhone::invokeCall, kJSPropertyAttributeNone },
+    { "answerCall", JSPhone::answerCall, kJSPropertyAttributeNone },
+    { "hangupCall", JSPhone::hangupCall, kJSPropertyAttributeNone },
+    { "muteCall", JSPhone::muteCall, kJSPropertyAttributeNone },
+    { "selectRemoteDevice", JSPhone::selectRemoteDevice, kJSPropertyAttributeNone },
+    { "unselectRemoteDevice", JSPhone::unselectRemoteDevice, kJSPropertyAttributeNone },
+    { "addRemoteDeviceSelectedListener", JSPhone::addRemoteDeviceSelectedListener, kJSPropertyAttributeNone },
+    { "removeRemoteDeviceSelectedListener", JSPhone::removeRemoteDeviceSelectedListener, kJSPropertyAttributeNone },
+    { "getSelectedRemoteDevice", JSPhone::getSelectedRemoteDevice, kJSPropertyAttributeNone },
+    { "getContacts", JSPhone::getContacts, kJSPropertyAttributeNone },
+    { "addContactsChangedListener", JSPhone::addContactsChangedListener, kJSPropertyAttributeNone },
+    { "removeContactsChangedListener", JSPhone::removeContactsChangedListener, kJSPropertyAttributeNone },
+    { "getCallHistory", JSPhone::getCallHistory, kJSPropertyAttributeNone },
+    { "addCallHistoryChangedListener", JSPhone::addCallHistoryChangedListener, kJSPropertyAttributeNone },
+    { "removeCallHistoryChangedListener", JSPhone::removeCallHistoryChangedListener, kJSPropertyAttributeNone },
+    { "addCallChangedListener", JSPhone::addCallChangedListener, kJSPropertyAttributeNone },
+    { "removeCallChangedListener", JSPhone::removeCallChangedListener, kJSPropertyAttributeNone },
+    { "addCallHistoryEntryAddedListener", JSPhone::addCallHistoryEntryAddedListener, kJSPropertyAttributeNone },
+    { "removeCallHistoryEntryAddedListener", JSPhone::removeCallHistoryEntryAddedListener, kJSPropertyAttributeNone },
+    { 0, 0, 0 }
+};
+
+JSStaticValue JSPhone::m_property[] = {
+    {PHONE_ACTIVECALL_PROP,  getProperty, NULL, kJSPropertyAttributeNone},
+    { 0, 0, 0, 0 }
+};
+
+const JSClassRef JSPhone::getClassRef()
+{
+    if (!m_jsClassRef)
+    {
+        m_jsClassRef = JSClassCreate(&m_classInfo);
+    }
+    return m_jsClassRef;
+}
+
+const JSClassDefinition* JSPhone::getClassInfo()
+{
+    return &m_classInfo;
+}
+
+JSValueRef JSPhone::getProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
+{
+    LoggerD("Enter");
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(object));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    Try {
+        if (JSStringIsEqualToUTF8CString(propertyName, PHONE_ACTIVECALL_PROP)) {
+            PhonePtr phone(privateObject->getObject());
+            JSStringRef state = JSStringCreateWithUTF8CString(phone->activeCall().c_str());
+            JSValueRef result = JSValueMakeFromJSONString(context, state);
+            return result;
+        }
+    } catch (...) {
+        return JSValueMakeUndefined(context);
+    }
+
+    return JSValueMakeUndefined(context);
+}
+
+JSClassRef JSPhone::m_jsClassRef = JSClassCreate(JSPhone::getClassInfo());
+
+void JSPhone::initialize(JSContextRef context, JSObjectRef object)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* priv = static_cast<PhonePrivObject*>(JSObjectGetPrivate(object));
+    if (!priv)
+    {
+        PhonePtr phone(new PhoneMaster());
+        priv = new PhonePrivObject( context, phone);
+        if(!JSObjectSetPrivate(object, static_cast<void*>(priv)))
+        {
+            LoggerE("Object can't store private data.");
+            delete priv;
+        }
+    }
+
+    LoggerD("JSPhone::initialize ");
+}
+
+void JSPhone::finalize(JSObjectRef object)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* priv = static_cast<PhonePrivObject*>(JSObjectGetPrivate(object));
+    JSObjectSetPrivate(object, NULL);
+    LoggerD("Deleting timeutil");
+    delete priv;
+}
+
+bool JSPhone::hasInstance(JSContextRef context,
+                          JSObjectRef constructor,
+                          JSValueRef possibleInstance,
+                          JSValueRef* exception)
+{
+    return JSValueIsObjectOfClass(context, possibleInstance, getClassRef());
+}
+
+JSValueRef JSPhone::invokeCall(JSContextRef context,
+                               JSObjectRef object,
+                               JSObjectRef thisObject,
+                               size_t argumentCount,
+                               const JSValueRef arguments[],
+                               JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    JSContextRef gContext = privateObject->getContext();
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+    std::string phoneNumber = validator.toString(0);
+    LoggerD("Dialing phone number: " << phoneNumber);
+
+    JSObjectRef errorCallback = validator.toFunction(1, false);
+
+    JSValueProtect(context, errorCallback);
+
+    phone->invokeCall(phoneNumber, errorCallback, gContext);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::hangupCall(JSContextRef context,
+                               JSObjectRef object,
+                               JSObjectRef thisObject,
+                               size_t argumentCount,
+                               const JSValueRef arguments[],
+                               JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    JSContextRef gContext = privateObject->getContext();
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+
+    JSObjectRef errorCallback = validator.toFunction(0, false);
+
+    JSValueProtect(context, errorCallback);
+
+    phone->hangupCall(errorCallback, gContext);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::answerCall(JSContextRef context,
+                               JSObjectRef object,
+                               JSObjectRef thisObject,
+                               size_t argumentCount,
+                               const JSValueRef arguments[],
+                               JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    JSContextRef gContext = privateObject->getContext();
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+
+    JSObjectRef errorCallback = validator.toFunction(0, false);
+
+    JSValueProtect(context, errorCallback);
+
+    phone->answerCall(errorCallback, gContext);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::muteCall(JSContextRef context,
+                             JSObjectRef object,
+                             JSObjectRef thisObject,
+                             size_t argumentCount,
+                             const JSValueRef arguments[],
+                             JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    JSContextRef gContext = privateObject->getContext();
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+    bool mute = validator.toBool(0);
+    JSObjectRef errorCallback = validator.toFunction(1, false);
+    JSValueProtect(context, errorCallback);
+
+    phone->muteCall(mute, errorCallback, gContext);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::getContacts(JSContextRef context,
+                                JSObjectRef object,
+                                JSObjectRef thisObject,
+                                size_t argumentCount,
+                                const JSValueRef arguments[],
+                                JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    JSContextRef gContext = privateObject->getContext();
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+
+    unsigned long count = validator.toULong(0);
+    LoggerD("Retrieving contacts - the latest " << count << " contacts");
+    JSObjectRef successCallback = validator.toFunction(1, false);
+    JSObjectRef errorCallback = validator.toFunction(2, false);
+
+    JSValueProtect(context, successCallback);
+    JSValueProtect(context, errorCallback);
+
+    phone->getContacts(count, successCallback, errorCallback, gContext);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::getCallHistory(JSContextRef context,
+                                   JSObjectRef object,
+                                   JSObjectRef thisObject,
+                                   size_t argumentCount,
+                                   const JSValueRef arguments[],
+                                   JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    JSContextRef gContext = privateObject->getContext();
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+
+    unsigned long count = validator.toULong(0);
+    LoggerD("Retrieving call history - the latest " << count << " calls");
+    JSObjectRef successCallback = validator.toFunction(1, false);
+    JSObjectRef errorCallback = validator.toFunction(2, false);
+
+    JSValueProtect(context, successCallback);
+    JSValueProtect(context, errorCallback);
+
+    phone->getCallHistory(count, successCallback, errorCallback, gContext);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::addCallChangedListener(JSContextRef context,
+                                           JSObjectRef object,
+                                           JSObjectRef thisObject,
+                                           size_t argumentCount,
+                                           const JSValueRef arguments[],
+                                           JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    JSContextRef gContext = privateObject->getContext();
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+
+    JSObjectRef callback = validator.toFunction(0, false);
+
+    JSValueProtect(gContext, callback);
+
+    int id = phone->addCallChangedListener(callback, gContext);
+    if(id>0)
+        return JSValueMakeNumber(context, id);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::selectRemoteDevice(JSContextRef context,
+                                       JSObjectRef object,
+                                       JSObjectRef thisObject,
+                                       size_t argumentCount,
+                                       const JSValueRef arguments[],
+                                       JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+    std::string btAddress = validator.toString(0);
+    LoggerD("Selecting device: " << btAddress);
+
+    phone->selectRemoteDevice(btAddress);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::unselectRemoteDevice(JSContextRef context,
+                                         JSObjectRef object,
+                                         JSObjectRef thisObject,
+                                         size_t argumentCount,
+                                         const JSValueRef arguments[],
+                                         JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    LoggerD("Un-selecting selected device");
+
+    phone->unselectRemoteDevice();
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::addRemoteDeviceSelectedListener(JSContextRef context,
+                                                    JSObjectRef object,
+                                                    JSObjectRef thisObject,
+                                                    size_t argumentCount,
+                                                    const JSValueRef arguments[],
+                                                    JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+    JSContextRef gContext = privateObject->getContext();
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+    JSObjectRef callback = validator.toFunction(0, false);
+    JSValueProtect(gContext, callback);
+
+    int id = phone->addRemoteDeviceSelectedListener(callback, gContext);
+    if(id>0)
+        return JSValueMakeNumber(context, id);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::removeRemoteDeviceSelectedListener(JSContextRef context,
+                                                       JSObjectRef object,
+                                                       JSObjectRef thisObject,
+                                                       size_t argumentCount,
+                                                       const JSValueRef arguments[],
+                                                       JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    JSContextRef gContext = privateObject->getContext();
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+
+    int id = validator.toNumber(0);
+
+    phone->removeRemoteDeviceSelectedListener(id, gContext);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::getSelectedRemoteDevice(JSContextRef context,
+                                            JSObjectRef object,
+                                            JSObjectRef thisObject,
+                                            size_t argumentCount,
+                                            const JSValueRef arguments[],
+                                            JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+    JSContextRef gContext = privateObject->getContext();
+    ArgumentValidator validator(context, argumentCount, arguments);
+    JSObjectRef callback = validator.toFunction(0, false);
+    JSValueProtect(gContext, callback);
+    phone->getSelectedRemoteDevice(callback, gContext);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::removeCallChangedListener(JSContextRef context,
+                                              JSObjectRef object,
+                                              JSObjectRef thisObject,
+                                              size_t argumentCount,
+                                              const JSValueRef arguments[],
+                                              JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    JSContextRef gContext = privateObject->getContext();
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+
+    int id = validator.toNumber(0);
+
+    phone->removeCallChangedListener(id, gContext);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::addCallHistoryEntryAddedListener(JSContextRef context,
+                                                     JSObjectRef object,
+                                                     JSObjectRef thisObject,
+                                                     size_t argumentCount,
+                                                     const JSValueRef arguments[],
+                                                     JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    JSContextRef gContext = privateObject->getContext();
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+
+    JSObjectRef callback = validator.toFunction(0, false);
+
+    JSValueProtect(gContext, callback);
+
+    int id = phone->addCallHistoryEntryAddedListener(callback, gContext);
+    if(id>0)
+        return JSValueMakeNumber(context, id);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::removeCallHistoryEntryAddedListener(JSContextRef context,
+                                                        JSObjectRef object,
+                                                        JSObjectRef thisObject,
+                                                        size_t argumentCount,
+                                                        const JSValueRef arguments[],
+                                                        JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    JSContextRef gContext = privateObject->getContext();
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+
+    int id = validator.toNumber(0);
+
+    phone->removeCallHistoryEntryAddedListener(id, gContext);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::addContactsChangedListener(JSContextRef context,
+                                               JSObjectRef object,
+                                               JSObjectRef thisObject,
+                                               size_t argumentCount,
+                                               const JSValueRef arguments[],
+                                               JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    JSContextRef gContext = privateObject->getContext();
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+
+    JSObjectRef callback = validator.toFunction(0, false);
+
+    JSValueProtect(gContext, callback);
+
+    int id = phone->addContactsChangedListener(callback, gContext);
+    if(id>0)
+        return JSValueMakeNumber(context, id);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::removeContactsChangedListener(JSContextRef context,
+                                                  JSObjectRef object,
+                                                  JSObjectRef thisObject,
+                                                  size_t argumentCount,
+                                                  const JSValueRef arguments[],
+                                                  JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    JSContextRef gContext = privateObject->getContext();
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+
+    int id = validator.toNumber(0);
+
+    phone->removeContactsChangedListener(id, gContext);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::addCallHistoryChangedListener(JSContextRef context,
+                                                  JSObjectRef object,
+                                                  JSObjectRef thisObject,
+                                                  size_t argumentCount,
+                                                  const JSValueRef arguments[],
+                                                  JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    JSContextRef gContext = privateObject->getContext();
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+
+    JSObjectRef callback = validator.toFunction(0, false);
+
+    JSValueProtect(gContext, callback);
+
+    int id = phone->addCallHistoryChangedListener(callback, gContext);
+    if(id>0)
+        return JSValueMakeNumber(context, id);
+
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSPhone::removeCallHistoryChangedListener(JSContextRef context,
+                                                     JSObjectRef object,
+                                                     JSObjectRef thisObject,
+                                                     size_t argumentCount,
+                                                     const JSValueRef arguments[],
+                                                     JSValueRef* exception)
+{
+    LoggerD("Entered");
+
+    PhonePrivObject* privateObject = static_cast<PhonePrivObject*>(JSObjectGetPrivate(thisObject));
+    if (NULL == privateObject)
+    {
+        LoggerE("private object is null");
+        return JSValueMakeUndefined(context);
+    }
+
+    PhonePtr phone(privateObject->getObject());
+
+    JSContextRef gContext = privateObject->getContext();
+
+    ArgumentValidator validator(context, argumentCount, arguments);
+
+    int id = validator.toNumber(0);
+
+    phone->removeCallHistoryChangedListener(id, gContext);
+
+    return JSValueMakeUndefined(context);
+}
+
+} // Phone
+} // DeviceAPI
+
diff --git a/src/JSPhone.h b/src/JSPhone.h
new file mode 100644 (file)
index 0000000..1edd394
--- /dev/null
@@ -0,0 +1,477 @@
+#ifndef JSPHONE_H
+#define JSPHONE_H
+
+#include "Phone.h"
+#include <dpl/shared_ptr.h>
+#include <JavaScriptCore/JavaScript.h>
+#include <CommonsJavaScript/JSPendingOperationPrivateObject.h>
+#include <CommonsJavaScript/PrivateObject.h>
+
+namespace DeviceAPI {
+namespace Phone {
+
+typedef WrtDeviceApis::CommonsJavaScript::PrivateObject<PhonePtr, WrtDeviceApis::CommonsJavaScript::NoOwnership> PhonePrivObject;
+
+/**
+ * @defgroup wrt-plugins-ivi-phone wrt-plugins-ivi-phone shared library
+ * \brief WebAPI to provide access to phone's functionality.
+ *
+ * WebAPI to provide access to phone's functionality, such as invoke phone call, accept/decline incoming call, hangup active call, retrieve a list of contacts stored in internal memory of a phone device, as well as its call history.
+ * @{
+ */
+
+/*! \class DeviceAPI::Phone::JSPhone
+ * \brief A Class which implements \b tizen.phone object to access phone's functionality from within Web app.
+ *
+ * A Class which implements \b tizen.phone object to access phone's functionality from within Web app.
+ *
+ * It defines the following methods on \b tizen.phone:
+ * <ul>
+       <li> \b selectRemoteDevice ( \a \b address ) Selects remote BT device for phone's operations. Once remote device gets selected, a listener registered by addRemoteDeviceSelectedListener() is called, passing a MAC address of selected remote device to indicate the success of the method call. </li>
+       <ul>
+           <li> \a \b address A MAC address of remote device to be selected. </li>
+       </ul>
+
+       <li> \b unselectRemoteDevice () Unselects remote BT device selected by selectRemoteDevice(). Once remote device gets unselected, a listener registered by addRemoteDeviceSelectedListener() is called, passing \b "" to indicate that there is no currently selected remote device for phone's operations. </li>
+
+       <li> \b getSelectedRemoteDevice ( \a \b callback ) Method to get currently selected remote device. </li>
+       <ul>
+           <li> \a \b callback ( \a \b address ) A callback to invoke when the remote device is retrieved. </li>
+           <ul>
+               <li>\a \b address A MAC address of selected remote device. If there is no currently selected remote device, \b "" is returned. </li>
+           </ul>
+       </ul>
+
+       <li> \b addRemoteDeviceSelectedListener ( \a \b listener ) Adds a listener to get notifified when selected remote device has changed. </li>
+       <ul>
+           <li> \a \b listener ( \a \b address ) A listener to call when the remote device has changed. </li>
+           <ul>
+               <li>\a \b address A MAC address of selected remote device. Empty string \b "" means no currently selected remote device. </li>
+           </ul>
+           <li>\a \b returns Id of registered listener. </li>
+       </ul>
+
+       <li> \b removeRemoteDeviceSelectedListener ( \a \b id ) Removes a listener previously registered by addRemoteDeviceSelectedListener(). </li>
+       <ul>
+           <li> \a \b id An id of registered listener to be removed. </li>
+       </ul>
+
+       <li> \b invokeCall ( \a \b number, \a \b errorCallback ) Dials a given phone number. </li>
+       <ul>
+           <li> \a \b number A phone number to dial. </li>
+           <li> \a \b errorCallback ( \a \b error ) A callback to invoke when dialing has failed.
+           <ul>
+               <li> \a \b error A JSON object describing the reason of failure. It contains a \a \b message attribute holding a string representation of failure. </li>
+           </ul>
+       </ul>
+
+       <li> \b answerCall ( \a \b errorCallback ) Answers incoming phone call. </li>
+       <ul>
+           <li> \a \b errorCallback ( \a \b error ) A callback to invoke when answering the call has failed. </li>
+           <ul>
+               <li> \a \b error A JSON object describing the reason of failure. It contains a \a \b message attribute holding a string representation of failure. </li>
+           </ul>
+       </ul>
+
+       <li> \b hangupCall ( \a \b errorCallback ) Declines incoming phone call, or hangs-up active one. </li>
+       <ul>
+           <li> \a \b errorCallback ( \a \b error ) A callback to invoke when hanging-up the call has failed. </li>
+           <ul>
+               <li> \a \b error A JSON object describing the reason of failure. It contains a \a \b message attribute holding a string representation of failure. </li>
+           </ul>
+       </ul>
+
+       <li> \b muteCall ( \a \b mute, \a \b errorCallback ) Mutes/unmutes the active phone call. </li>
+       <ul>
+           <li> \a \b mute Whether to mute, or unmute the active phone call. </li>
+           <li> \a \b errorCallback ( \a \b error ) A callback to invoke when muting the call has failed. </li>
+           <ul>
+               <li> \a \b error A JSON object describing the reason of failure. It contains a \a \b message attribute holding a string representation of failure. </li>
+           </ul>
+       </ul>
+
+       <li> \b activeCall () Returns active phone call. </li>
+       <ul>
+           <li> \a \b returns Returns active phone call. The active call object contains attributes \b line_id specifying phone number of caller, or calling person respectively, \b state which specify the state of the call and \b contact which matches the \b line_id. If there is no active call, \b line_id is set to \b "" and \b state is set to \b "disconnected".  </li>
+       </ul>
+
+       <li> \b addCallChangedListener ( \a \b listener ) Adds a listener to get notifified when there is a change on active call, ie. to get notified about incoming call and all other states that the call may go through, up to \b "disconnected" state. </li>
+       <ul>
+           <li> \a \b listener ( \a \b call ) A listener to call when there is a change on the active call. </li>
+           <ul>
+               <li>\a \b call The call object contains attributes \b line_id specifying phone number of caller, or calling person respectively, \b state which specify the state of the call and \b contact which matches the \b line_id. If there is no active call, \b line_id is set to \b "" and \b state is set to \b "disconnected". </li>
+           </ul>
+           <li>\a \b returns Id of registered listener. </li>
+       </ul>
+
+       <li> \b removeCallChangedListener ( \a \b id ) Removes a listener previously registered by addCallChangedListener(). </li>
+       <ul>
+           <li> \a \b id An id of registered listener to be removed. </li>
+       </ul>
+
+       <li> \b getContacts ( \a \b count, \a \b successCallback, \a \b errorCallback ) Retrieves an array of \a \b count synchronized contacts in JSON \b tizen.Contact format. </li>
+       <ul>
+           <li> \a \b count Specifies \a \b count the frist contacts to retrieve. \b 0 means to retrieve all contacts.  </li>
+           <li> \a \b successCallback ( \a \b contacts ) A callback to call when contacts are successfuly retrieved. </li>
+           <ul>
+               <li>\a \b contacts An array of retrieved contacts in \b tizen.Contact JSON format. </li>
+           </ul>
+           <li> \a \b errorCallback ( \a \b error ) A callback to invoke when retrieving the contacts has failed. </li>
+           <ul>
+               <li> \a \b error A JSON object describing the reason of failure. It contains a \a \b message attribute holding a string representation of failure. </li>
+           </ul>
+       </ul>
+
+       <li> \b getCallHistory ( \a \b count, \a \b successCallback, \a \b errorCallback ) Retrieves an array of \a \b count synchronized call history entries in JSON \b tizen.CallHistoryEntry format. </li>
+       <ul>
+           <li> \a \b count Specifies \a \b count the frist call history entries to retrieve. \b 0 means to retrieve all calls. </li>
+           <li> \a \b successCallback ( \a \b contacts ) A callback to call when contacts are successfuly retrieved. </li>
+           <ul>
+               <li>\a \b contacts An array of retrieved call history entries in \b tizen.CallHistoryEntry JSON format. </li>
+           </ul>
+           <li> \a \b errorCallback ( \a \b error ) A callback to invoke when retrieving the call history entries has failed. </li>
+           <ul>
+               <li> \a \b error A JSON object describing the reason of failure. It contains a \a \b message attribute holding a string representation of failure. </li>
+           </ul>
+       </ul>
+
+       <li> \b addCallHistoryEntryAddedListener ( \a \b listener ) Adds a listener to get notifified when a call entry has been added to the history list, due to incoming, or dialed call. </li>
+       <ul>
+           <li> \a \b listener ( \a \b call ) A listener to call when the call history entry was added to the list. </li>
+           <ul>
+               <li>\a \b call Call history entry added to the history list. </li>
+           </ul>
+           <li>\a \b returns Id of registered listener. </li>
+       </ul>
+
+       <li> \b removeCallHistoryEntryAddedListener ( \a \b id ) Removes a listener previously registered by addCallHistoryEntryAddedListener(). </li>
+       <ul>
+           <li> \a \b id An id of registered listener to be removed. </li>
+       </ul>
+
+       <li> \b addContactsChangedListener ( \a \b listener ) Adds a listener to get notifified when contacts list has changed, ie. when a new contact was added, or if the list was cleared. </li>
+       <ul>
+           <li> \a \b listener () A listener to call when the contacts list has changed. </li>
+           <li>\a \b returns Id of registered listener. </li>
+       </ul>
+
+       <li> \b removeContactsChangedListener ( \a \b id ) Removes a listener previously registered by addContactsChangedListener(). </li>
+       <ul>
+           <li> \a \b id An id of registered listener to be removed. </li>
+       </ul>
+
+       <li> \b addCallHistoryChangedListener ( \a \b listener ) Adds a listener to get notifified when call history list has changed, ie. when a new call history entry was added, or if the list was cleared. </li>
+       <ul>
+           <li> \a \b listener () A listener to call when the call history list has changed. </li>
+           <li>\a \b returns Id of registered listener. </li>
+       </ul>
+
+       <li> \b removeCallHistoryChangedListener ( \a \b id ) Removes a listener previously registered by addCallHistoryChangedListener(). </li>
+       <ul>
+           <li> \a \b id An id of registered listener to be removed. </li>
+       </ul>
+
+       <li> \b createBonding ( \a \b address, \a \b successCallback, \a \b errorCallback ) Initiates a pairing sequence with the remote BT device, specified by it's MAC address. </li>
+       <ul>
+           <li> \a \b address A MAC address of remote device to pair with. </li>
+           <li> \a \b successCallback () A callback to invoke when the pairing has successfuly finished. </li>
+           <li> \a \b errorCallback ( \a \b error ) A callback to invoke when pairing with remote BT address has failed. </li>
+           <ul>
+               <li> \a \b error A JSON object describing the reason of failure. It contains a \a \b message attribute holding a string representation of failure. </li>
+           </ul>
+       </ul>
+
+       <li> \b destroyBonding ( \a \b address, \a \b successCallback, \a \b errorCallback ) Unpairs with the remote BT device, specified by it's MAC address. </li>
+       <ul>
+           <li> \a \b address A MAC address of remote device to unpair with. </li>
+           <li> \a \b successCallback () A callback to invoke when the pairing has successfuly finished. </li>
+           <li> \a \b errorCallback ( \a \b error ) A callback to invoke when unpairing with remote BT address has failed. </li>
+           <ul>
+               <li> \a \b error A JSON object describing the reason of failure. It contains a \a \b message attribute holding a string representation of failure. </li>
+           </ul>
+       </ul>
+ * </ul>
+ */
+class JSPhone
+{
+public:
+        /**
+         * Gets the Call Definition.
+         * @see getClassRef()
+         * @return JS class definition.
+         */
+        static const JSClassDefinition* getClassInfo();
+
+        /**
+         * Method to create (if it's not yet created) and return this class reference. Used by the plugin loader to load this plugin.
+         * @see getClassInfo()
+         * @return JS class reference.
+         */
+        static const JSClassRef getClassRef();
+
+private:
+        /**
+         * The callback invoked when an object is first created.
+         */
+        static void initialize(JSContextRef context,
+                               JSObjectRef object);
+
+        /**
+         * The callback invoked when an object is finalized.
+         */
+        static void finalize(JSObjectRef object);
+
+        /**
+         * Getters for properties
+         */
+        static JSValueRef getProperty(JSContextRef context,
+                                      JSObjectRef object,
+                                      JSStringRef propertyName,
+                                      JSValueRef* exception);
+
+        /**
+         * The callback invoked when an object is used as the target of an 'instanceof' expression.
+         */
+        static bool hasInstance(JSContextRef ctx,
+                                JSObjectRef constructor,
+                                JSValueRef possibleInstance,
+                                JSValueRef* exception);
+
+        /**
+         * Invoke phone call on BT paired device
+         */
+        static JSValueRef invokeCall(JSContextRef context,
+                                     JSObjectRef object,
+                                     JSObjectRef thisObject,
+                                     size_t argumentCount,
+                                     const JSValueRef arguments[],
+                                     JSValueRef* exception);
+
+        /**
+         * Hangup (active/incoming, dialing) phone call.
+         */
+        static JSValueRef hangupCall(JSContextRef context,
+                                     JSObjectRef object,
+                                     JSObjectRef thisObject,
+                                     size_t argumentCount,
+                                     const JSValueRef arguments[],
+                                     JSValueRef* exception);
+
+        /**
+         * Answer (incoming) phone call.
+         */
+        static JSValueRef answerCall(JSContextRef context,
+                                     JSObjectRef object,
+                                     JSObjectRef thisObject,
+                                     size_t argumentCount,
+                                     const JSValueRef arguments[],
+                                     JSValueRef* exception);
+
+        /**
+         * Mute/UnMute phone call.
+         */
+        static JSValueRef muteCall(JSContextRef context,
+                                   JSObjectRef object,
+                                   JSObjectRef thisObject,
+                                   size_t argumentCount,
+                                   const JSValueRef arguments[],
+                                   JSValueRef* exception);
+
+        /**
+         * Selects a remote BT device for Phone operations
+         * btAddress: address of paired and trusted BT device
+         * the client app should subscribe for RemoteDeviceSelected signal, ie. should call addRemoteDeviceSelectedListener()
+         * see addRemoteDeviceSelectedListener for more information
+         */
+        static JSValueRef selectRemoteDevice(JSContextRef context,
+                                             JSObjectRef object,
+                                             JSObjectRef thisObject,
+                                             size_t argumentCount,
+                                             const JSValueRef arguments[],
+                                             JSValueRef* exception);
+
+        /**
+         * Un-selects selected remote BT device for Phone operations
+         * the client app should subscribe for RemoteDeviceSelected signal, ie. should call addRemoteDeviceSelectedListener()
+         * see addRemoteDeviceSelectedListener for more information
+         */
+        static JSValueRef unselectRemoteDevice(JSContextRef context,
+                                               JSObjectRef object,
+                                               JSObjectRef thisObject,
+                                               size_t argumentCount,
+                                               const JSValueRef arguments[],
+                                               JSValueRef* exception);
+
+        /**
+         * Adds listener to get notified about the result of selecting remote device
+         * parameter to the callback defines result of operation:
+         *     if result contains 'error' atribute, it contains error meessage of the failure
+         *     if resutl contains 'value' and if value is "<SOME_MAC_ADDRESS>" - it holds a MAC address of selected device
+         *     if resutl contains 'value' and if value is "" - it indicates that the remote device is unselected
+         * services OFono/Obex are up and running
+         * returns id of registered listener, to be used for removing
+         */
+        static JSValueRef addRemoteDeviceSelectedListener(JSContextRef context,
+                                                          JSObjectRef object,
+                                                          JSObjectRef thisObject,
+                                                          size_t argumentCount,
+                                                          const JSValueRef arguments[],
+                                                          JSValueRef* exception);
+
+        /**
+         * Removes listener for notification about selected remote device.
+         * parameter is id of listener registered via addRemoteDeviceSelectedListener()
+         */
+        static JSValueRef removeRemoteDeviceSelectedListener(JSContextRef context,
+                                                             JSObjectRef object,
+                                                             JSObjectRef thisObject,
+                                                             size_t argumentCount,
+                                                             const JSValueRef arguments[],
+                                                             JSValueRef* exception);
+
+        /**
+         * Get selected remote BT device
+         * returns: MAC address of selected remote BT device
+         */
+        static JSValueRef getSelectedRemoteDevice(JSContextRef context,
+                                                  JSObjectRef object,
+                                                  JSObjectRef thisObject,
+                                                  size_t argumentCount,
+                                                  const JSValueRef arguments[],
+                                                  JSValueRef* exception);
+
+        /**
+         * Retrieve contacts from BT paired device: the latest 'count' entries
+         */
+        static JSValueRef getContacts(JSContextRef context,
+                                      JSObjectRef object,
+                                      JSObjectRef thisObject,
+                                      size_t argumentCount,
+                                      const JSValueRef arguments[],
+                                      JSValueRef* exception);
+
+        /**
+         * Retrieve call history: the latest 'count' entries
+         */
+        static JSValueRef getCallHistory(JSContextRef context,
+                                         JSObjectRef object,
+                                         JSObjectRef thisObject,
+                                         size_t argumentCount,
+                                         const JSValueRef arguments[],
+                                         JSValueRef* exception);
+
+        /**
+         * Adds listener for property changed on active call
+         * returns id of registered listener, to be used for removing
+         */
+        static JSValueRef addCallChangedListener(JSContextRef context,
+                                                 JSObjectRef object,
+                                                 JSObjectRef thisObject,
+                                                 size_t argumentCount,
+                                                 const JSValueRef arguments[],
+                                                 JSValueRef* exception);
+
+        /**
+         * Removes listener for property changed on active call.
+         * parameter is id of listener registered via addCallCangedListener()
+         */
+        static JSValueRef removeCallChangedListener(JSContextRef context,
+                                                    JSObjectRef object,
+                                                    JSObjectRef thisObject,
+                                                    size_t argumentCount,
+                                                    const JSValueRef arguments[],
+                                                    JSValueRef* exception);
+
+        /**
+         * Adds listener to get notified about an Call entry being added to the Call History
+         * returns id of registered listener, to be used for removing
+         */
+        static JSValueRef addCallHistoryEntryAddedListener(JSContextRef context,
+                                                           JSObjectRef object,
+                                                           JSObjectRef thisObject,
+                                                           size_t argumentCount,
+                                                           const JSValueRef arguments[],
+                                                           JSValueRef* exception);
+
+        /**
+         * Removes listener for notification about call entry being added to the Call History
+         * parameter is id of listener registered via addCallHistoryEntryAddedListener()
+         */
+        static JSValueRef removeCallHistoryEntryAddedListener(JSContextRef context,
+                                                              JSObjectRef object,
+                                                              JSObjectRef thisObject,
+                                                              size_t argumentCount,
+                                                              const JSValueRef arguments[],
+                                                              JSValueRef* exception);
+
+        /**
+         * Adds listener to get notified about an Contacts being changed - as a result of selectRemoteDevice
+         * returns id of registered listener, to be used for removing
+         */
+        static JSValueRef addContactsChangedListener(JSContextRef context,
+                                                     JSObjectRef object,
+                                                     JSObjectRef thisObject,
+                                                     size_t argumentCount,
+                                                     const JSValueRef arguments[],
+                                                     JSValueRef* exception);
+
+        /**
+         * Removes listener for notification about contacts being changed (as a result of selectRemoteDevice)
+         * parameter is id of listener registered via addContactsChangedListener()
+         */
+        static JSValueRef removeContactsChangedListener(JSContextRef context,
+                                                        JSObjectRef object,
+                                                        JSObjectRef thisObject,
+                                                        size_t argumentCount,
+                                                        const JSValueRef arguments[],
+                                                        JSValueRef* exception);
+
+        /**
+         * Adds listener to get notified about an CallHistory being changed - as a result of selectRemoteDevice
+         * returns id of registered listener, to be used for removing
+         */
+        static JSValueRef addCallHistoryChangedListener(JSContextRef context,
+                                                        JSObjectRef object,
+                                                        JSObjectRef thisObject,
+                                                        size_t argumentCount,
+                                                        const JSValueRef arguments[],
+                                                        JSValueRef* exception);
+
+        /**
+         * Removes listener for notification about contacts being changed (as a result of selectRemoteDevice)
+         * parameter is id of listener registered via addCallHistoryChangedListener()
+         */
+        static JSValueRef removeCallHistoryChangedListener(JSContextRef context,
+                                                           JSObjectRef object,
+                                                           JSObjectRef thisObject,
+                                                           size_t argumentCount,
+                                                           const JSValueRef arguments[],
+                                                           JSValueRef* exception);
+
+        /**
+         * This structure contains properties and callbacks that define a type of object.
+         */
+        static JSClassDefinition m_classInfo;
+
+        /**
+         * This structure describes a statically declared function.
+         */
+        static JSStaticFunction m_function[];
+
+        /**
+         * This member variable contains the initialization values for the
+         * properties of this class. The values are given according to the
+         * data structure JSPropertySpec.
+         */
+        static JSStaticValue m_property[];
+
+        static JSClassRef m_jsClassRef;
+};
+
+} // Phone
+} // DeviceAPI
+
+#endif // JSPHONE_H
+
+/** @} */
+
diff --git a/src/Phone.cpp b/src/Phone.cpp
new file mode 100644 (file)
index 0000000..e53eb40
--- /dev/null
@@ -0,0 +1,894 @@
+#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
+
diff --git a/src/Phone.h b/src/Phone.h
new file mode 100644 (file)
index 0000000..6b688ec
--- /dev/null
@@ -0,0 +1,232 @@
+#ifndef PHONE_H
+#define PHONE_H
+
+#include <string>
+#include <dpl/mutex.h>
+#include <dpl/shared_ptr.h>
+#include <JavaScriptCore/JavaScript.h>
+#include <gio/gio.h>
+#include <map>
+
+class AbstractPropertyType;
+
+namespace DeviceAPI {
+namespace Phone {
+
+/**
+ * @addtogroup wrt-plugins-ivi-phone
+ *
+ * @{
+ */
+
+/*! \class DeviceAPI::Phone::PhoneMaster
+ *  \brief Class which is utilizing Phone D-Bus service.
+ *
+ * A Class which is utilizing Phone D-Bus service. It connects to \b org.tizen.phone D-Bus service, subscribes to it's signals and implements methods to allow JSPhone to access phone's functionality.
+ */
+class PhoneMaster
+{
+    /**
+     * A helper class to store context/callback for asynchronous operations.
+     */
+    class PhoneSubscriptionData
+    {
+    public:
+        JSContextRef context;       /*!< A variable to store context. */
+        JSObjectRef  callback;      /*!< A variable to store callback. */
+        JSObjectRef  errorCallback; /*!< A variable to store error callback. */
+    };
+public:
+
+    /**
+     * A default constructor. Constructs the object.
+     */
+    PhoneMaster();
+
+    /**
+     * A destructor. Destructs the object.
+     */
+    ~PhoneMaster();
+
+    /**
+     * Selects remote BT device for phone's operations.
+     * @param[in] btAddress A MAC address of the remote device to get selected. Will call a listener registered by addRemoteDeviceSelectedListener().
+     */
+    void selectRemoteDevice(std::string btAddress);
+
+    /**
+     * Unselects remote BT device selected by selectRemoteDevice(). Will call a listener registered by addRemoteDeviceSelectedListener().
+     */
+    void unselectRemoteDevice();
+
+    /**
+     * Method to get currently selected remote device.
+     * @param[in] callback A callback to invoke when the remote device is retrieved, passing a MAC address of selected remote device, or \b "" if there is no currently selected remote device.
+     * @param[in] context A context on which the method is invoked.
+     */
+    void getSelectedRemoteDevice(JSObjectRef callback, JSContextRef context);
+
+    /**
+     * Adds a listener to get notifified when selected remote device has changed.
+     * @param[in] callback A listener to call when the remote device has changed.
+     * @param[in] context A context on which the method is invoked.
+     * @return Id of registered listener.
+     */
+    int  addRemoteDeviceSelectedListener(JSObjectRef callback, JSContextRef context);
+
+    /**
+     * Removes a listener previously registered by addRemoteDeviceSelectedListener().
+     * @param[in] id An id of registered listener to be removed.
+     * @param[in] context A context on which the method is invoked.
+     */
+    void removeRemoteDeviceSelectedListener(int id, JSContextRef context);
+
+    /**
+     * Dials a given phone number.
+     * @param[in] phoneNumber A phone number to dial.
+     * @param[in] errorCallback A callback to invoke when dialing has failed.
+     * @param[in] context A context on which the method is invoked.
+     */
+    void invokeCall(std::string phoneNumber, JSObjectRef errorCallback, JSContextRef context);
+
+    /**
+     * Answers incoming phone call.
+     * @param[in] errorCallback A callback to invoke when answering the call has failed.
+     * @param[in] context A context on which the method is invoked.
+     */
+    void answerCall(JSObjectRef errorCallback, JSContextRef context);
+
+    /**
+     * Declines incoming phone call, or hangs-up active one.
+     * @param[in] errorCallback A callback to invoke when hanging-up the call has failed.
+     * @param[in] context A context on which the method is invoked.
+     */
+    void hangupCall(JSObjectRef errorCallback, JSContextRef context);
+
+    /**
+     * Mutes/unmutes the active phone call.
+     * @param[in] mute Whether to mute, or unmute the active phone call.
+     * @param[in] errorCallback A callback to invoke when muting the call has failed.
+     * @param[in] context A context on which the method is invoked.
+     */
+    void muteCall(bool mute, JSObjectRef errorCallback, JSContextRef context);
+
+    /**
+     * Returns active phone call.
+     * @return Active phone call.
+     */
+    std::string activeCall(void);
+
+    /**
+     * Adds a listener to get notifified when there is a change on active call, ie. to get notified about incoming call and all other states that the call may go through, up to \b "disconnected" state.
+     * @param[in] callback A listener to call when there is a change on active call.
+     * @param[in] context A context on which the method is invoked.
+     * @return Id of registered listener.
+     */
+    int  addCallChangedListener(JSObjectRef callback, JSContextRef context); // returns id of registered listener
+
+    /**
+     * Removes a listener previously registered by addCallChangedListener().
+     * @param[in] id An id of registered listener to be removed.
+     * @param[in] context A context on which the method is invoked.
+     */
+    void removeCallChangedListener(int id, JSContextRef context);
+
+    // Obex specific
+
+    /**
+     * Retrieves an array of \a \b count synchronized contacts in JSON \b tizen.Contact format.
+     * @param[in] count Specifies \a \b count the frist contacts to retrieve. \b 0 means to retrieve all contacts.
+     * @param[in] successCallback A callback to invoke when contacts are successfuly retrieved.
+     * @param[in] errorCallback A callback to invoke when retrieving the contacts has failed.
+     * @param[in] context A context on which the method is invoked.
+     */
+    void getContacts(unsigned long count, JSObjectRef successCallback, JSObjectRef errorCallback, JSContextRef context);
+
+    /**
+     * Retrieves an array of \a \b count synchronized call history entries in JSON \b tizen.CallHistoryEntry format.
+     * @param[in] count Specifies \a \b count the frist call history entries to retrieve.
+     * @param[in] successCallback A callback to invoke when call history entries are successfuly retrieved.
+     * @param[in] errorCallback A callback to invoke when retrieving the call history entries has failed.
+     * @param[in] context A context on which the method is invoked.
+     */
+    void getCallHistory(unsigned long count, JSObjectRef successCallback, JSObjectRef errorCallback, JSContextRef context);
+
+    /**
+     * Adds a listener to get notifified when a call entry has been added to the history list, due to incoming, or dialed call.
+     * @param[in] callback A listener to call when the call history entry was added to the list.
+     * @param[in] context A context on which the method is invoked.
+     * @return Id of registered listener.
+     */
+    int  addCallHistoryEntryAddedListener(JSObjectRef callback, JSContextRef context);
+
+    /**
+     * Removes a listener previously registered by addCallHistoryEntryAddedListener().
+     * @param[in] id An id of registered listener to be removed.
+     * @param[in] context A context on which the method is invoked.
+     */
+    void removeCallHistoryEntryAddedListener(int id, JSContextRef context);
+
+    /**
+     * Adds a listener to get notifified when contacts list has changed, ie. when a new contact was added, or the list was cleared.
+     * @param[in] callback A listener to call when the contacts list has changed.
+     * @param[in] context A context on which the method is invoked.
+     * @return Id of registered listener.
+     */
+    int  addContactsChangedListener(JSObjectRef callback, JSContextRef context);
+
+    /**
+     * Removes a listener previously registered by addContactsChangedListener().
+     * @param[in] id An id of registered listener to be removed.
+     * @param[in] context A context on which the method is invoked.
+     */
+    void removeContactsChangedListener(int id, JSContextRef context);
+
+    /**
+     * Adds a listener to get notifified when call history list has changed, ie. when a new call history entry was added, or the list was cleared.
+     * @param[in] callback A listener to call when the call history list has changed.
+     * @param[in] context A context on which the method is invoked.
+     * @return Id of registered listener.
+     */
+    int  addCallHistoryChangedListener(JSObjectRef callback, JSContextRef context);
+
+    /**
+     * Removes a listener previously registered by addCallHistoryChangedListener().
+     * @param[in] id An id of registered listener to be removed.
+     * @param[in] context A context on which the method is invoked.
+     */
+    void removeCallHistoryChangedListener(int id, JSContextRef context);
+
+private:
+    // OFono specific
+    static void handleSignal( GDBusConnection *connection,
+                              const gchar *sender_name,
+                              const gchar *object_path,
+                              const gchar *interface_name,
+                              const gchar *signal_name,
+                              GVariant *parameters,
+                              gpointer user_data);
+
+    static void asyncGetSelectedRemoteDeviceCB(GObject *source, GAsyncResult *result, gpointer user_data);
+
+private:
+    std::map<int, PhoneMaster::PhoneSubscriptionData*>  mCallChangedListenersMap;
+    std::map<int, PhoneMaster::PhoneSubscriptionData*>  mCallHistoryEntryAddedListenersMap;
+    std::map<int, PhoneMaster::PhoneSubscriptionData*>  mContactsChangedListenersMap;
+    std::map<int, PhoneMaster::PhoneSubscriptionData*>  mCallHistoryChangedListenersMap;
+    std::map<int, PhoneMaster::PhoneSubscriptionData*>  mRemoteDeviceSelectedListenersMap;
+};
+
+/**
+ * A typedef for shared pointer to PhoneMaster.
+ */
+typedef DPL::SharedPtr<PhoneMaster> PhonePtr;
+
+
+} // Phone
+} // DeviceAPI
+
+#endif // PHONE_H
+
+/** @} */
+
diff --git a/src/Phone.idl b/src/Phone.idl
new file mode 100644 (file)
index 0000000..ac9d366
--- /dev/null
@@ -0,0 +1,172 @@
+/**
+Details.
+\brief Allows access to the phone API
+*/
+
+module Phone {
+
+[NoInterfaceObject]
+interface PhoneError {
+       /**  code
+        *   MUST return error code.
+        **/
+       attribute unsigned short code;
+
+       /**  message
+        *   MUST return error message
+        **/
+       attribute DOMString message;
+};
+
+typedef DOMString PersonId;
+typedef DOMString ContactId;
+typedef DOMString ContactGroupId;
+
+[NoInterfaceObject]
+interface ContactName {
+    attribute DOMString? prefix;
+    attribute DOMString? suffix;
+    attribute DOMString? firstName;
+    attribute DOMString? middleName;
+    attribute DOMString? lastName;
+    attribute DOMString[] nicknames;
+    attribute DOMString? phoneticFirstName;
+    attribute DOMString? phoneticLastName;
+    readonly attribute DOMString? displayName;
+};
+
+[NoInterfaceObject]
+interface ContactAddress {
+    attribute DOMString? country;
+    attribute DOMString? region;
+    attribute DOMString? city;
+    attribute DOMString? streetAddress;
+    attribute DOMString? additionalInformation;
+    attribute DOMString? postalCode;
+    attribute boolean isDefault;
+    attribute DOMString[] types;
+};
+
+[NoInterfaceObject]
+interface ContactPhoneNumber {
+    attribute DOMString number;
+    attribute boolean isDefault;
+    attribute DOMString[] types;
+};
+
+[NoInterfaceObject]
+interface ContactEmailAddress {
+    attribute DOMString email;
+    attribute boolean isDefault;
+    attribute DOMString[] types;
+};
+
+[NoInterfaceObject]
+interface ContactAnniversary {
+    attribute Date date;
+    attribute DOMString? label;
+};
+
+[NoInterfaceObject]
+interface ContactOrganization {
+    attribute DOMString? name;
+    attribute DOMString? department;
+    attribute DOMString? title;
+    attribute DOMString? role;
+    attribute DOMString? logoURI;
+};
+
+[NoInterfaceObject]
+interface ContactWebSite {
+    attribute DOMString url;
+    attribute DOMString type;
+};
+
+[NoInterfaceObject]
+interface Contact {
+    readonly attribute ContactId? id;
+    readonly attribute PersonId? personId;
+    readonly attribute AddressBookId? addressBookId;
+    readonly attribute Date? lastUpdated;
+    readonly attribute boolean isFavorite;
+    attribute DOMString? photoURI;
+    attribute ContactPhoneNumber[] phoneNumbers;
+    attribute ContactEmailAddress[] emails;
+    attribute Date? birthday;
+    attribute ContactAnniversary[] anniversaries;
+    attribute ContactOrganization[] organizations;
+    attribute DOMString[] notes;
+    attribute ContactWebSite[] urls;
+    attribute DOMString? ringtoneURI;
+    attribute ContactGroupId[] groupIds;
+    //DOMString convertToString(optional ContactTextFormat? format);
+    //Contact clone();
+};
+
+[NoInterfaceObject]
+interface RemoteParty {
+    readonly attribute DOMString? remoteParty;
+    readonly attribute PersonId? personId;
+};
+
+[NoInterfaceObject]
+interface CallHistoryEntry {
+    readonly attribute DOMString uid;
+    readonly attribute DOMString type;
+    readonly attribute DOMString[]? features;
+    readonly attribute RemoteParty[] remoteParties;
+    readonly attribute Date startTime;
+    readonly attribute unsigned long duration;
+    readonly attribute DOMString direction;
+};
+
+callback PhoneErrorCallback = void (PhoneError error);
+callback GetContactsCallback = void (Contact[] contacts);
+callback GetCallHistoryCallback = void (CallHistoryEntry[] entries);
+
+[NoInterfaceObject]
+interface Phone  {
+
+       /**
+        *  \brief Invoke phone call on BT paired device
+        *  \arg DOMString phoneNumber Phone number to invoke call to
+        *  \arg PhoneErrorCallback errorCallback. Callback if an error has occurred
+       **/
+        invokeCall(DOMString phoneNumber, PhoneErrorCallback errorCallback);
+
+       /**
+        *  \brief Answer incoming phone call.
+        *  \arg PhoneErrorCallback errorCallback. Callback if an error has occurred
+       **/
+        answerCall(PhoneErrorCallback errorCallback);
+
+       /**
+        *  \brief Hangup/End incoming/active phone call
+        *  \arg PhoneErrorCallback errorCallback. Callback if an error has occurred
+       **/
+        hangupCall(PhoneErrorCallback errorCallback);
+
+       /**
+        *  \brief Mute/UnMute active phone call on BT paired device
+        *  \arg boolean mute Specifies whether the phone call should be muted, or unmuted.
+       **/
+        muteCall(boolean mute);
+
+       /**
+        *  \brief Retrieve contacts from BT paired device
+        *  \arg GetContactsCallback successCallback. Callback with the result of the method call
+        *  \arg PhoneErrorCallback errorCallback. Callback if an error has occurred
+       **/
+        getContacts(GetContactsCallback successCallback, PhoneErrorCallback errorCallback);
+
+       /**
+        *  \brief Get call history of given direction (DIALED/MISSED/RECEIVED)
+        *  \arg DOMString direction An attribute to indicate the type of CallHistory to retrieve ("DIALED", "MISSED", "RECEIVED")
+        *  \arg GetCallHistoryCallback successCallback. Callback with the result of the method call
+        *  \arg PhoneErrorCallback errorCallback. Callback if an error has occurred
+       **/
+        getCallHistory(DOMString direction, GetCallHistoryCallback successCallback, PhoneErrorCallback errorCallback);
+};
+
+};
+
diff --git a/src/config.xml b/src/config.xml
new file mode 100644 (file)
index 0000000..d561d4e
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" ?>
+<!DOCTYPE plugin-properties SYSTEM "/usr/etc/tizen-apis/config.dtd">
+<plugin-properties>
+    <library-name>libwrt-plugins-tizen-phone.so</library-name>
+    <feature-install-uri>phone.install.uri</feature-install-uri>
+    <feature-key-cn>INTEL plugin group</feature-key-cn>
+    <feature-root-cn>INTEL certificate authority</feature-root-cn>
+    <feature-root-fingerprint>AAAABBBBCCCCDDDEEEE0000</feature-root-fingerprint>
+
+    <api-feature>
+        <name>http://tizen.org/privilege/phone</name>
+        <device-capability>phone</device-capability>
+    </api-feature>
+
+
+</plugin-properties>
diff --git a/src/plugin_initializer.cpp b/src/plugin_initializer.cpp
new file mode 100644 (file)
index 0000000..1dc3189
--- /dev/null
@@ -0,0 +1,77 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2013 Intel Corporation
+//
+// 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 <Commons/plugin_initializer_def.h>
+#include <Commons/WrtAccess/WrtAccess.h>
+#include "JSPhone.h"
+#include <Logger.h>
+
+namespace DeviceAPI {
+namespace Phone {
+
+using namespace WrtDeviceApis;
+using namespace WrtDeviceApis::Commons;
+
+class_definition_options_t ConstructorClassOptions =
+{
+       JS_INTERFACE,
+       CREATE_INSTANCE,
+       NONE_NOTICE,
+       USE_OVERLAYED,
+       NULL,
+       NULL,
+       NULL
+};
+
+void on_widget_start_callback(int widgetId) {
+       LoggerD("[TIZEN\\Phone] on_widget_start_callback ("<<widgetId<<")");
+       Try
+       {
+               WrtAccessSingleton::Instance().initialize(widgetId);
+       }
+       Catch(Commons::Exception)
+       {
+               LoggerE("WrtAccess initialization failed");
+       }
+}
+
+void on_widget_stop_callback(int widgetId) {
+       LoggerD("[TIZEN\\Phone] on_widget_stop_callback ("<<widgetId<<")");
+       Try
+       {
+               WrtAccessSingleton::Instance().deinitialize(widgetId);
+       }
+       Catch(Commons::Exception)
+       {
+               LoggerE("WrtAccess deinitialization failed");
+       }
+}
+
+PLUGIN_ON_WIDGET_START(on_widget_start_callback)
+PLUGIN_ON_WIDGET_STOP(on_widget_stop_callback)
+
+PLUGIN_CLASS_MAP_BEGIN
+PLUGIN_CLASS_MAP_ADD_CLASS(WRT_JS_EXTENSION_OBJECT_TIZEN,
+               "phone",
+               (js_class_template_getter)JSPhone::getClassRef,
+               NULL)
+
+PLUGIN_CLASS_MAP_END
+
+} // Phone
+} // DeviceAPI
+
diff --git a/wrt-plugins-ivi-phone.manifest b/wrt-plugins-ivi-phone.manifest
new file mode 100644 (file)
index 0000000..81ace0c
--- /dev/null
@@ -0,0 +1,6 @@
+<manifest>
+       <request>
+               <domain name="_"/>
+       </request>
+</manifest>
+