This extension implements the phone interfaces for IVI using phoned.
It replaces the existing wrt-plugins-ivi-hfp used by Modello home screen.
Original wrt plugin
https://review.tizen.org/git/?p=profile/ivi/wrt-plugins-ivi-hfp.git
It implements the following api:
- selectRemoteDevice()
- unselectRemoteDevice()
- getSelectedRemoteDevice()
- invokeCall()
- answerCall()
- hangupCall()
- activeCall()
- muteCall()
- getContacts()
- getCallHistory()
- addRemoteDeviceSelectedListener()
- removeRemoteDeviceSelectedListener()
- addCallChangedListener()
- removeCallChangedListener()
- addCallHistoryEntryAddedListener()
- removeCallHistoryEntryAddedListener()
- addCallHistoryChangedListener()
- removeCallHistoryChangedListener()
- addContactsChangedListener()
- removeContactsChangedListener()
BUG=XWALK-1661
Signed-off-by: Jimmy Huang <jimmy.huang@intel.com>
--- /dev/null
+{
+ 'includes':[
+ '../common/common.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'tizen_phone',
+ 'type': 'loadable_module',
+ 'variables': {
+ 'packages': [
+ 'gio-2.0',
+ ],
+ },
+ 'includes': [
+ '../common/pkg-config.gypi',
+ ],
+ 'sources': [
+ 'phone_api.js',
+ 'phone_extension.cc',
+ 'phone_extension.h',
+ 'phone_instance.cc',
+ 'phone_instance.h',
+ ],
+ },
+ ],
+}
--- /dev/null
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var _callbacks = {};
+var _nextReplyId = 0;
+
+var _selected_listeners = {};
+var _selected_listener_id = 0;
+var _selected_listeners_count = 0;
+
+var _call_changed_listeners = {};
+var _call_changed_listener_id = 0;
+var _call_changed_listeners_count = 0;
+
+var _call_history_added_listeners = {};
+var _call_history_added_listener_id = 0;
+var _call_history_added_listeners_count = 0;
+
+var _call_history_changed_listeners = {};
+var _call_history_changed_listener_id = 0;
+var _call_history_changed_listeners_count = 0;
+
+var _contacts_changed_listeners = {};
+var _contacts_changed_listener_id = 0;
+var _contacts_changed_listeners_count = 0;
+
+function PhoneError(code, message) {
+ Object.defineProperties(this, {
+ 'code': { writable: false, value: uri, enumerable: true },
+ 'message': { writable: false, value: id, enumerable: true }
+ });
+}
+
+function ActiveCall(json) {
+ for (var field in json) {
+ var val = json[field];
+ Object.defineProperty(this, field, { writable: false, value: val, enumerable: true });
+ }
+}
+
+function getNextReplyId() {
+ return _nextReplyId++;
+}
+
+function postMessage(msg, callback) {
+ var replyId = getNextReplyId();
+ _callbacks[replyId] = callback;
+ msg.replyId = replyId;
+ extension.postMessage(JSON.stringify(msg));
+}
+
+var sendSyncMessage = function(msg) {
+ return JSON.parse(extension.internal.sendSyncMessage(JSON.stringify(msg)));
+};
+
+extension.setMessageListener(function(msg) {
+ var m = JSON.parse(msg);
+ var replyId = m.replyId;
+ var callback = _callbacks[replyId];
+
+ if (m.cmd === 'signal') {
+ if (!m.signal_name) {
+ console.error('Invalid signal from Phone api');
+ return;
+ }
+
+ if (m.signal_name === 'RemoteDeviceSelected') {
+ handleRemoteDeviceSelectedSignal(m);
+ } else if (m.signal_name === 'CallChanged') {
+ handleCallChangedSignal(m);
+ } else if (m.signal_name === 'CallHistoryEntryAdded') {
+ handleCallHistoryEntryAddedSignal(m);
+ } else if (m.signal_name === 'CallHistoryChanged') {
+ handleCallHistoryChangedSignal(m);
+ } else if (m.signal_name === 'ContactsChanged') {
+ handleContactsChangedSignal(m);
+ }
+ } else if (!isNaN(parseInt(replyId)) && (typeof(callback) === 'function')) {
+ callback(m);
+ delete m.replyId;
+ delete _callbacks[replyId];
+ } else {
+ console.error('Invalid replyId from Phone api: ' + replyId);
+ }
+});
+
+function handleRemoteDeviceSelectedSignal(msg) {
+ for (var key in _selected_listeners) {
+ var cb = _selected_listeners[key];
+ if (!cb || typeof(cb) !== 'function') {
+ console.error('No listener object found for id ' + key);
+ throw new tizen.WebAPIException(tizen.WebAPIException.UNKNOWN_ERR);
+ }
+ cb(msg.signal_value);
+ }
+}
+
+function handleCallChangedSignal(msg) {
+ for (var key in _call_changed_listeners) {
+ var cb = _call_changed_listeners[key];
+ if (!cb || typeof(cb) !== 'function') {
+ console.error('No listener object found for id ' + key);
+ throw new tizen.WebAPIException(tizen.WebAPIException.UNKNOWN_ERR);
+ }
+ cb(msg.signal_value);
+ }
+}
+
+function handleCallHistoryEntryAddedSignal(msg) {
+ for (var key in _call_history_added_listeners) {
+ var cb = _call_history_added_listeners[key];
+ if (!cb || typeof(cb) !== 'function') {
+ console.error('No listener object found for id ' + key);
+ throw new tizen.WebAPIException(tizen.WebAPIException.UNKNOWN_ERR);
+ }
+ cb(msg.signal_value);
+ }
+}
+
+function handleCallHistoryChangedSignal(msg) {
+ for (var key in _call_history_changed_listeners) {
+ var cb = _call_history_changed_listeners[key];
+ if (!cb || typeof(cb) !== 'function') {
+ console.error('No listener object found for id ' + key);
+ throw new tizen.WebAPIException(tizen.WebAPIException.UNKNOWN_ERR);
+ }
+ cb();
+ }
+}
+
+function handleContactsChangedSignal(msg) {
+ for (var key in _contacts_changed_listeners) {
+ var cb = _contacts_changed_listeners[key];
+ if (!cb || typeof(cb) !== 'function') {
+ console.error('No listener object found for id ' + key);
+ throw new tizen.WebAPIException(tizen.WebAPIException.UNKNOWN_ERR);
+ }
+ cb();
+ }
+}
+
+exports.selectRemoteDevice = function(address) {
+ if (typeof address !== 'string' && address != undefined)
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+ var msg = { cmd: 'SelectRemoteDevice', address: address };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('SelectedRemoteDevice failed');
+ throw new tizen.WebAPIException(result.errorCode);
+ }
+ });
+};
+
+exports.unselectRemoteDevice = function() {
+ var msg = { cmd: 'UnselectRemoteDevice' };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('UnselectRemoteDevice failed');
+ throw new tizen.WebAPIException(result.errorCode);
+ }
+ });
+};
+
+exports.getSelectedRemoteDevice = function(successCallback) {
+ var msg = { cmd: 'GetSelectedRemoteDevice' };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('GetSelectedRemoteDevice failed');
+ throw new tizen.WebAPIException(result.errorCode);
+ }
+
+ if (successCallback && result.value != undefined) {
+ successCallback(result.value);
+ }
+ });
+};
+
+exports.invokeCall = function(phoneNumber, errorCallback) {
+ if (typeof phoneNumber !== 'string' && phoneNumber != undefined)
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+ var msg = { cmd: 'InvokeCall', phoneNumber: phoneNumber };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('InvokeCall failed');
+ if (errorCallback) {
+ var error = { message: 'InvokeCall failed' };
+ if (result.errorMessage)
+ error.message += ', error: ' + result.errorMessage;
+ errorCallback(error);
+ }
+ }
+ });
+};
+
+exports.answerCall = function(errorCallback) {
+ var msg = { cmd: 'AnswerCall' };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('AnswerCall failed');
+ if (errorCallback) {
+ var error = { message: 'AnswerCall failed' };
+ if (result.errorMessage)
+ error.message += ', error: ' + result.errorMessage;
+ errorCallback(error);
+ }
+ }
+ });
+};
+
+exports.hangupCall = function(errorCallback) {
+ var msg = { cmd: 'HangupCall' };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('HangupCall failed');
+ if (errorCallback) {
+ var error = { message: 'HangupCall failed' };
+ if (result.errorMessage)
+ error.message += ', error: ' + result.errorMessage;
+ errorCallback(error);
+ }
+ }
+ });
+};
+
+exports.activeCall = function() {
+ var result = sendSyncMessage({ cmd: 'ActiveCall' });
+ if (result.isError) {
+ console.error('activeCall failed');
+ throw new tizen.WebAPIException(result.errorCode);
+ }
+
+ if (result.value != undefined) {
+ return new ActiveCall(result.value);
+ }
+
+ return null;
+};
+
+exports.muteCall = function(mute, errorCallback) {
+ if (typeof mute !== 'boolean' && mute != undefined)
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+ var msg = { cmd: 'MuteCall', mute: mute };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('MuteCall failed');
+ if (errorCallback) {
+ var error = { message: 'MuteCall failed' };
+ if (result.errorMessage)
+ error.message += ', error: ' + result.errorMessage;
+ errorCallback(error);
+ }
+ }
+ });
+};
+
+exports.getContacts = function(count, successCallback, errorCallback) {
+ if (typeof count !== 'number' && count != undefined)
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+ var msg = { cmd: 'GetContacts', count: count };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('GetContacts failed');
+ if (errorCallback) {
+ var error = { message: 'GetContacts failed' };
+ if (result.errorMessage)
+ error.message += ', error: ' + result.errorMessage;
+ errorCallback(error);
+ }
+ }
+
+ if (successCallback && result.value != undefined) {
+ successCallback(result.value);
+ }
+ });
+};
+
+exports.getCallHistory = function(count, successCallback, errorCallback) {
+ if (typeof count !== 'number' && count != undefined)
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+ var msg = { cmd: 'GetCallHistory', count: count };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('GetCallHistory failed');
+ if (errorCallback) {
+ var error = { message: 'GetCallHistory failed' };
+ if (result.errorMessage)
+ error.message += ', error: ' + result.errorMessage;
+ errorCallback(error);
+ }
+ }
+
+ if (successCallback && result.value != undefined) {
+ successCallback(result.value);
+ }
+ });
+};
+
+exports.addRemoteDeviceSelectedListener = function(listener) {
+ if (!(listener instanceof Function) && listener != undefined)
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+ for (var key in _selected_listeners) {
+ if (_selected_listeners[key] == listener) {
+ console.log('same listener added');
+ return key;
+ }
+ }
+
+ _selected_listeners[++_selected_listener_id] = listener;
+ _selected_listeners_count++;
+ if (_selected_listeners_count == 1) {
+ var msg = { cmd: 'AddRemoteDeviceSelectedListener' };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('AddRemoteDeviceSelectedListener failed');
+ throw new tizen.WebAPIException(result.errorCode);
+ }
+ });
+ }
+
+ return _selected_listener_id;
+};
+
+exports.removeRemoteDeviceSelectedListener = function(listener_id) {
+ if (typeof listener_id !== 'number' && listener_id != undefined)
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+ var listener = _selected_listeners[listener_id];
+ if (!listener) {
+ throw new tizen.WebAPIException(tizen.WebAPIException.INVALID_VALUES_ERR);
+ }
+
+ delete _selected_listeners[listener_id];
+ _selected_listeners_count--;
+ if (_selected_listeners_count == 0) {
+ var msg = { cmd: 'RemoveRemoteDeviceSelectedListener' };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('RemoveRemoteDeviceSelectedListener failed');
+ throw new tizen.WebAPIException(result.errorCode);
+ }
+ });
+ }
+};
+
+exports.addCallChangedListener = function(listener) {
+ if (!(listener instanceof Function) && listener != undefined)
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+ for (var key in _call_changed_listeners) {
+ if (_call_changed_listeners[key] == listener) {
+ console.log('same listener added');
+ return key;
+ }
+ }
+
+ _call_changed_listeners[++_call_changed_listener_id] = listener;
+ _call_changed_listeners_count++;
+ if (_call_changed_listeners_count == 1) {
+ var msg = { cmd: 'AddCallChangedListener' };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('AddCallChangedListener failed');
+ throw new tizen.WebAPIException(result.errorCode);
+ }
+ });
+ }
+
+ return _call_changed_listener_id;
+};
+
+exports.removeCallChangedListener = function(listener_id) {
+ if (typeof listener_id !== 'number' && listener_id != undefined)
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+ var listener = _call_changed_listeners[listener_id];
+ if (!listener) {
+ throw new tizen.WebAPIException(tizen.WebAPIException.INVALID_VALUES_ERR);
+ }
+
+ delete _call_changed_listeners[listener_id];
+ _call_changed_listeners_count--;
+ if (_call_changed_listeners_count == 0) {
+ var msg = { cmd: 'RemoveCallChangedListener' };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('RemoveCallChangedListener failed');
+ throw new tizen.WebAPIException(result.errorCode);
+ }
+ });
+ }
+};
+
+exports.addCallHistoryEntryAddedListener = function(listener) {
+ if (!(listener instanceof Function) && listener != undefined)
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+ for (var key in _call_history_added_listeners) {
+ if (_call_history_added_listeners[key] == listener) {
+ console.log('same listener added');
+ return key;
+ }
+ }
+
+ _call_history_added_listeners[++_call_history_added_listener_id] = listener;
+ _call_history_added_listeners_count++;
+ if (_call_history_added_listeners_count == 1) {
+ var msg = { cmd: 'AddCallHistoryEntryAddedListener' };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('AddCallHistoryEntryAddedListener failed');
+ throw new tizen.WebAPIException(result.errorCode);
+ }
+ });
+ }
+
+ return _call_history_added_listener_id;
+};
+
+exports.removeCallHistoryEntryAddedListener = function(listener_id) {
+ if (typeof listener_id !== 'number' && listener_id != undefined)
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+ var listener = _call_history_added_listeners[listener_id];
+ if (!listener) {
+ throw new tizen.WebAPIException(tizen.WebAPIException.INVALID_VALUES_ERR);
+ }
+
+ delete _call_history_added_listeners[listener_id];
+ _call_history_added_listeners_count--;
+ if (_call_history_added_listeners_count == 0) {
+ var msg = { cmd: 'RemoveCallHistoryEntryAddedListener' };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('RemoveCallHistoryEntryAddedListener failed');
+ throw new tizen.WebAPIException(result.errorCode);
+ }
+ });
+ }
+};
+
+exports.addCallHistoryChangedListener = function(listener) {
+ if (!(listener instanceof Function) && listener != undefined)
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+ for (var key in _call_history_changed_listeners) {
+ if (_call_history_changed_listeners[key] == listener) {
+ console.log('same listener added');
+ return key;
+ }
+ }
+
+ _call_history_changed_listeners[++_call_history_changed_listener_id] = listener;
+ _call_history_changed_listeners_count++;
+ if (_call_history_changed_listeners_count == 1) {
+ var msg = { cmd: 'AddCallHistoryChangedListener' };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('AddCallHistoryChangedListener failed');
+ throw new tizen.WebAPIException(result.errorCode);
+ }
+ });
+ }
+
+ return _call_history_changed_listener_id;
+};
+
+exports.removeCallHistoryChangedListener = function(listener_id) {
+ if (typeof listener_id !== 'number' && listener_id != undefined)
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+ var listener = _call_history_changed_listeners[listener_id];
+ if (!listener) {
+ throw new tizen.WebAPIException(tizen.WebAPIException.INVALID_VALUES_ERR);
+ }
+
+ delete _call_history_changed_listeners[listener_id];
+ _call_history_changed_listeners_count--;
+ if (_call_history_changed_listeners_count == 0) {
+ var msg = { cmd: 'RemoveCallHistoryChangedListener' };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('RemoveCallHistoryChangedListener failed');
+ throw new tizen.WebAPIException(result.errorCode);
+ }
+ });
+ }
+};
+
+exports.addContactsChangedListener = function(listener) {
+ if (!(listener instanceof Function) && listener != undefined)
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+ for (var key in _contacts_changed_listeners) {
+ if (_contacts_changed_listeners[key] == listener) {
+ console.log('same listener added');
+ return key;
+ }
+ }
+
+ _contacts_changed_listeners[++_contacts_changed_listener_id] = listener;
+ _contacts_changed_listeners_count++;
+ if (_contacts_changed_listeners_count == 1) {
+ var msg = { cmd: 'AddContactsChangedListener' };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('AddContactsChangedListener failed');
+ throw new tizen.WebAPIException(result.errorCode);
+ }
+ });
+ }
+
+ return _contacts_changed_listener_id;
+};
+
+exports.removeContactsChangedListener = function(listener_id) {
+ if (typeof listener_id !== 'number' && listener_id != undefined)
+ throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+
+ var listener = _contacts_changed_listeners[listener_id];
+ if (!listener) {
+ throw new tizen.WebAPIException(tizen.WebAPIException.INVALID_VALUES_ERR);
+ }
+
+ delete _contacts_changed_listeners[listener_id];
+ _contacts_changed_listeners_count--;
+ if (_contacts_changed_listeners_count == 0) {
+ var msg = { cmd: 'RemoveContactsChangedListener' };
+ postMessage(msg, function(result) {
+ if (result.isError) {
+ console.error('RemoveContactsChangedListener failed');
+ throw new tizen.WebAPIException(result.errorCode);
+ }
+ });
+ }
+};
--- /dev/null
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "phone/phone_extension.h"
+#include "phone/phone_instance.h"
+
+common::Extension* CreateExtension() {
+ return new PhoneExtension();
+}
+
+// This will be generated from phone_api.js
+extern const char kSource_phone_api[];
+
+PhoneExtension::PhoneExtension() {
+ SetExtensionName("tizen.phone");
+ SetJavaScriptAPI(kSource_phone_api);
+}
+
+PhoneExtension::~PhoneExtension() {}
+
+common::Instance* PhoneExtension::CreateInstance() {
+ return new PhoneInstance;
+}
--- /dev/null
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PHONE_PHONE_EXTENSION_H_
+#define PHONE_PHONE_EXTENSION_H_
+
+#include "common/extension.h"
+
+class PhoneExtension : public common::Extension {
+ public:
+ PhoneExtension();
+ virtual ~PhoneExtension();
+
+ private:
+ // common::Extension implementation
+ virtual common::Instance* CreateInstance();
+};
+
+#endif // PHONE_PHONE_EXTENSION_H_
--- /dev/null
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "phone/phone_instance.h"
+
+#include <gio/gio.h>
+#include <string>
+
+namespace {
+
+const char kPhoneService[] = "org.tizen.phone";
+const char kPhoneInterface[] = "org.tizen.Phone";
+const char kPhoneObjectPath[] = "/";
+
+} // namespace
+
+guint PhoneInstance::remote_device_selected_listener_id_ = 0;
+guint PhoneInstance::call_changed_listener_id_ = 0;
+guint PhoneInstance::call_history_entry_added_listener_id_ = 0;
+guint PhoneInstance::call_history_changed_listener_id_ = 0;
+guint PhoneInstance::contacts_changed_listener_id_ = 0;
+
+PhoneInstance::PhoneInstance()
+ : main_loop_(g_main_loop_new(0, FALSE)),
+ thread_(PhoneInstance::RunMainloop, this) {
+ thread_.detach();
+}
+
+PhoneInstance::~PhoneInstance() {
+ g_main_loop_quit(main_loop_);
+}
+
+void PhoneInstance::RunMainloop(void* data) {
+ PhoneInstance* self = reinterpret_cast<PhoneInstance*>(data);
+ GMainContext* ctx = g_main_context_default();
+ g_main_context_push_thread_default(ctx);
+ g_main_loop_run(self->main_loop_);
+ g_main_loop_unref(self->main_loop_);
+}
+
+void PhoneInstance::HandleMessage(const char* msg) {
+ picojson::value v;
+
+ std::string err;
+ picojson::parse(v, msg, msg + strlen(msg), &err);
+ if (!err.empty()) {
+ return;
+ }
+
+ const std::string cmd = v.get("cmd").to_str();
+ if (cmd == "SelectRemoteDevice") {
+ HandleSelectRemoteDevice(v);
+ } else if (cmd == "UnselectRemoteDevice") {
+ HandleUnselectRemoteDevice(v);
+ } else if (cmd == "InvokeCall") {
+ HandleInvokeCall(v);
+ } else if (cmd == "AnswerCall") {
+ HandleAnswerCall(v);
+ } else if (cmd == "HangupCall") {
+ HandleHangupCall(v);
+ } else if (cmd == "ActiveCall") {
+ HandleActiveCall(v);
+ } else if (cmd == "MuteCall") {
+ HandleMuteCall(v);
+ } else if (cmd == "GetSelectedRemoteDevice" ||
+ cmd == "GetContacts" ||
+ cmd == "GetCallHistory") {
+ HandleGet(cmd, v);
+ } else if (cmd == "AddRemoteDeviceSelectedListener") {
+ HandleAddListener(remote_device_selected_listener_id_,
+ std::string("RemoteDeviceSelected"), v);
+ } else if (cmd == "RemoveRemoteDeviceSelectedListener") {
+ HandleRemoveListener(remote_device_selected_listener_id_,
+ std::string("RemoteDeviceSelected"), v);
+ } else if (cmd == "AddCallChangedListener") {
+ HandleAddListener(call_changed_listener_id_,
+ std::string("CallChanged"), v);
+ } else if (cmd == "RemoveCallChangedListener") {
+ HandleRemoveListener(call_changed_listener_id_,
+ std::string("CallChanged"), v);
+ } else if (cmd == "AddCallHistoryEntryAddedListener") {
+ HandleAddListener(call_history_entry_added_listener_id_,
+ std::string("CallHistoryEntryAddedChanged"), v);
+ } else if (cmd == "RemoveCallHistoryEntryAddedListener") {
+ HandleRemoveListener(call_history_entry_added_listener_id_,
+ std::string("CallHistoryEntryAddedChanged"), v);
+ } else if (cmd == "AddCallHistoryChangedListener") {
+ HandleAddListener(call_history_changed_listener_id_,
+ std::string("CallHistoryChanged"), v);
+ } else if (cmd == "RemoveCallHistoryChangedListener") {
+ HandleRemoveListener(call_history_changed_listener_id_,
+ std::string("CallHistoryChanged"), v);
+ } else if (cmd == "AddContactsChangedListener") {
+ HandleAddListener(contacts_changed_listener_id_,
+ std::string("ContactsChanged"), v);
+ } else if (cmd == "RemoveContactsChangedListener") {
+ HandleRemoveListener(contacts_changed_listener_id_,
+ std::string("ContactsChanged"), v);
+ } else {
+ std::cerr << "Unknown command: " << cmd << "\n";
+ }
+}
+
+void PhoneInstance::HandleSyncMessage(const char* msg) {
+ picojson::value v;
+ std::string err;
+
+ picojson::parse(v, msg, msg + strlen(msg), &err);
+ if (!err.empty()) {
+ return;
+ }
+
+ const std::string cmd = v.get("cmd").to_str();
+ if (cmd == "ActiveCall") {
+ HandleActiveCall(v);
+ } else {
+ std::cerr << "ASSERT NOT REACHED.\n";
+ }
+}
+
+void PhoneInstance::SendSyncErrorReply(WebApiAPIErrors error_code,
+ const std::string& error_msg = "") {
+ picojson::value::object o;
+ o["isError"] = picojson::value(true);
+ o["errorCode"] = picojson::value(static_cast<double>(error_code));
+ if (!error_msg.empty())
+ o["errorMessage"] = picojson::value(error_msg.c_str());
+ picojson::value v(o);
+ SendSyncReply(v.serialize().c_str());
+}
+
+void PhoneInstance::SendSyncSuccessReply() {
+ picojson::value::object o;
+ o["isError"] = picojson::value(false);
+ picojson::value v(o);
+ SendSyncReply(v.serialize().c_str());
+}
+
+void PhoneInstance::SendSyncSuccessReply(const picojson::value& value) {
+ picojson::value::object o;
+ o["isError"] = picojson::value(false);
+ o["value"] = value;
+ picojson::value v(o);
+ SendSyncReply(v.serialize().c_str());
+}
+
+void PhoneInstance::PostAsyncReply(const picojson::value& msg,
+ picojson::value::object& reply) {
+ reply["replyId"] = picojson::value(msg.get("replyId").get<double>());
+ picojson::value v(reply);
+ PostMessage(v.serialize().c_str());
+}
+
+void PhoneInstance::PostAsyncErrorReply(const picojson::value& msg,
+ WebApiAPIErrors error_code, const std::string& error_msg = "") {
+ picojson::value::object reply;
+ reply["isError"] = picojson::value(true);
+ reply["errorCode"] = picojson::value(static_cast<double>(error_code));
+ if (!error_msg.empty())
+ reply["errorMessage"] = picojson::value(error_msg.c_str());
+ PostAsyncReply(msg, reply);
+}
+
+void PhoneInstance::PostAsyncSuccessReply(const picojson::value& msg,
+ const picojson::value& value) {
+ picojson::value::object reply;
+ reply["isError"] = picojson::value(false);
+ reply["value"] = value;
+ PostAsyncReply(msg, reply);
+}
+
+void PhoneInstance::PostAsyncSuccessReply(const picojson::value& msg) {
+ picojson::value::object reply;
+ reply["isError"] = picojson::value(false);
+ PostAsyncReply(msg, reply);
+}
+
+void PhoneInstance::SendSignal(const picojson::value& signal_name,
+ const picojson::value& signal_value) {
+ picojson::value::object o;
+ o["cmd"] = picojson::value("signal");
+ o["signal_name"] = signal_name;
+ o["signal_value"] = signal_value;
+ picojson::value msg(o);
+ PostMessage(msg.serialize().c_str());
+}
+
+GVariant* PhoneInstance::CallDBus(const gchar* method_name,
+ GVariant* parameters,
+ GError** error) {
+ if (!method_name)
+ return NULL;
+
+ return g_dbus_connection_call_sync(
+ g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL),
+ kPhoneService,
+ kPhoneObjectPath,
+ kPhoneInterface,
+ method_name,
+ parameters,
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ error);
+}
+
+void PhoneInstance::HandleSignal(GDBusConnection* connection,
+ const gchar* sender_name,
+ const gchar* object_path,
+ const gchar* interface_name,
+ const gchar* signal_name,
+ GVariant* parameters,
+ gpointer user_data) {
+ PhoneInstance* instance = static_cast<PhoneInstance*>(user_data);
+ if (!instance) {
+ std::cerr << "Failed to cast to instance..." << "\n";
+ return;
+ }
+
+ if (!strcmp(signal_name, "RemoteDeviceSelected") ||
+ !strcmp(signal_name, "CallHistoryEntryAdded")) {
+ const gchar* result;
+ g_variant_get(parameters, "(s)", &result);
+ if (!result)
+ return;
+
+ picojson::value value;
+ std::string err;
+ picojson::parse(value, result, result + strlen(result), &err);
+ if (!err.empty()) {
+ std::cerr << "cannot parse result.\n";
+ return;
+ }
+
+ instance->SendSignal(picojson::value(signal_name), value);
+ } else if (!strcmp(signal_name, "CallChanged")) {
+ const gchar* key = NULL;
+ const gchar* state = NULL;
+ const gchar* line_id = NULL;
+ const gchar* contact = NULL;
+ GVariantIter* iter;
+ GVariant* value;
+
+ g_variant_get(parameters, "(a{sv})", &iter);
+ while (g_variant_iter_next(iter, "{sv}", &key, &value)) {
+ if (!strcmp(key, "state"))
+ state = g_variant_get_string(value, NULL);
+ else if (!strcmp(key, "line_id"))
+ line_id = g_variant_get_string(value, NULL);
+ else if (!strcmp(key, "contact"))
+ contact = g_variant_get_string(value, NULL);
+ }
+ picojson::value::object o;
+ o["state"] = state ? picojson::value(state) : picojson::value("");
+ o["line_id"] = line_id ? picojson::value(line_id) : picojson::value("");
+ o["contact"] = contact ? picojson::value(contact) : picojson::value("");
+ picojson::value v(o);
+ instance->SendSignal(picojson::value(signal_name), v);
+ } else if (!strcmp(signal_name, "CallHistoryChanged") ||
+ !strcmp(signal_name, "ContactsChanged")) {
+ instance->SendSignal(picojson::value(signal_name), picojson::value(""));
+ } else {
+ std::cerr << "Unknown signal: " << signal_name << "\n";
+ }
+}
+
+void PhoneInstance::HandleSelectRemoteDevice(
+ const picojson::value& msg) {
+ if (!msg.contains("address")) {
+ PostAsyncErrorReply(msg, INVALID_VALUES_ERR);
+ return;
+ }
+
+ GError* error = NULL;
+ CallDBus("SelectRemoteDevice",
+ g_variant_new("(s)", msg.get("address").to_str().c_str()),
+ &error);
+
+ if (error) {
+ // call the error callback to notify client about the error condition
+ std::cerr << "Error occured '" << error->message << "'\n";
+ PostAsyncErrorReply(msg, UNKNOWN_ERR, std::string(error->message));
+ return;
+ }
+
+ PostAsyncSuccessReply(msg);
+}
+
+void PhoneInstance::HandleUnselectRemoteDevice(
+ const picojson::value& msg) {
+ GError* error = NULL;
+ CallDBus("UnselectRemoteDevice", NULL, &error);
+
+ if (error) {
+ // call the error callback to notify client about the error condition
+ std::cerr << "Error occured '" << error->message << "'\n";
+ PostAsyncErrorReply(msg, UNKNOWN_ERR, std::string(error->message));
+ return;
+ }
+
+ PostAsyncSuccessReply(msg);
+}
+
+void PhoneInstance::HandleInvokeCall(const picojson::value& msg) {
+ if (!msg.contains("phoneNumber")) {
+ PostAsyncErrorReply(msg, INVALID_VALUES_ERR);
+ return;
+ }
+
+ picojson::value::object o;
+ GError* error = NULL;
+ CallDBus("Dial",
+ g_variant_new("(s)", msg.get("phoneNumber").to_str().c_str()),
+ &error);
+
+ if (error) {
+ // call the error callback to notify client about the error condition
+ std::cerr << "Error occured '" << error->message << "'\n";
+ PostAsyncErrorReply(msg, UNKNOWN_ERR, std::string(error->message));
+ return;
+ }
+
+ PostAsyncSuccessReply(msg);
+}
+
+void PhoneInstance::HandleAnswerCall(const picojson::value& msg) {
+ GError* error = NULL;
+ CallDBus("Answer", NULL, &error);
+
+ if (error) {
+ // call the error callback to notify client about the error condition
+ std::cerr << "Error occured '" << error->message << "'\n";
+ PostAsyncErrorReply(msg, UNKNOWN_ERR, std::string(error->message));
+ return;
+ }
+
+ PostAsyncSuccessReply(msg);
+}
+
+void PhoneInstance::HandleHangupCall(const picojson::value& msg) {
+ GError* error = NULL;
+ CallDBus("Hangup", NULL, &error);
+
+ if (error) {
+ // call the error callback to notify client about the error condition
+ std::cerr << "Error occured '" << error->message << "'\n";
+ PostAsyncErrorReply(msg, UNKNOWN_ERR, std::string(error->message));
+ return;
+ }
+
+ PostAsyncSuccessReply(msg);
+}
+
+void PhoneInstance::HandleActiveCall(const picojson::value& msg) {
+ GError* error = NULL;
+ GVariant* reply = NULL;
+ reply = CallDBus("ActiveCall", NULL, &error);
+
+ if (error) {
+ // call the error callback to notify client about the error condition
+ std::cerr << "Error occured '" << error->message << "'\n";
+ SendSyncErrorReply(UNKNOWN_ERR, std::string(error->message));
+ } else if (!reply) {
+ std::cerr << "No reply\n";
+ SendSyncErrorReply(UNKNOWN_ERR);
+ } else {
+ picojson::value::object o;
+ const gchar* key = NULL;
+ const gchar* state = NULL;
+ const gchar* line_id = NULL;
+ const gchar* contact = NULL;
+ GVariantIter* iter;
+ GVariant* value;
+
+ g_variant_get(reply, "(a{sv})", &iter);
+ while (g_variant_iter_next(iter, "{sv}", &key, &value)) {
+ if (!strcmp(key, "state"))
+ state = g_variant_get_string(value, NULL);
+ else if (!strcmp(key, "line_id"))
+ line_id = g_variant_get_string(value, NULL);
+ else if (!strcmp(key, "contact"))
+ contact = g_variant_get_string(value, NULL);
+ }
+
+ o["state"] = state ? picojson::value(state) : picojson::value("");
+ o["line_id"] = line_id ? picojson::value(line_id) : picojson::value("");
+ o["contact"] = contact ? picojson::value(contact) : picojson::value("");
+ picojson::value result(o);
+ SendSyncSuccessReply(result);
+ }
+
+ if (reply)
+ g_variant_unref(reply);
+}
+
+void PhoneInstance::HandleMuteCall(const picojson::value& msg) {
+ if (!msg.contains("mute")) {
+ PostAsyncErrorReply(msg, INVALID_VALUES_ERR);
+ return;
+ }
+
+ GError* error = NULL;
+ CallDBus("Mute",
+ g_variant_new("(b)", msg.get("mute").evaluate_as_boolean()),
+ &error);
+
+ if (error) {
+ // call the error callback to notify client about the error condition
+ std::cerr << "Error occured '" << error->message << "'\n";
+ PostAsyncErrorReply(msg, UNKNOWN_ERR, std::string(error->message));
+ return;
+ }
+
+ PostAsyncSuccessReply(msg);
+}
+
+void PhoneInstance::HandleGet(const std::string& cmd,
+ const picojson::value& msg) {
+ GError* error = NULL;
+ GVariant* reply = NULL;
+
+ if (cmd == "GetSelectedRemoteDevice") {
+ reply = CallDBus(cmd.c_str(), NULL, &error);
+ } else if (cmd == "GetContacts" || cmd == "GetCallHistory") {
+ if (!msg.contains("count")) {
+ PostAsyncErrorReply(msg, INVALID_VALUES_ERR);
+ return;
+ }
+
+ reply = CallDBus(cmd.c_str(),
+ g_variant_new("(u)", msg.get("count").get<double>()),
+ &error);
+ }
+
+ if (error) {
+ // call the error callback to notify client about the error condition
+ std::cerr << "Error occured '" << error->message << "'\n";
+ PostAsyncErrorReply(msg, UNKNOWN_ERR, std::string(error->message));
+ } else if (!reply) {
+ std::cerr << "No reply\n";
+ PostAsyncErrorReply(msg, UNKNOWN_ERR);
+ } else {
+ const gchar* result;
+ g_variant_get(reply, "(s)", &result);
+
+ if (!result) {
+ PostAsyncErrorReply(msg, UNKNOWN_ERR);
+ return;
+ }
+
+ if (cmd == "GetSelectedRemoteDevice") {
+ picojson::value value(result);
+ PostAsyncSuccessReply(msg, value);
+ } else if (cmd == "GetContacts" || cmd == "GetCallHistory") {
+ picojson::value value;
+ std::string err;
+ picojson::parse(value, result, result + strlen(result), &err);
+ if (!err.empty()) {
+ std::cerr << "cannot parse result.\n";
+ PostAsyncErrorReply(msg, UNKNOWN_ERR);
+ g_variant_unref(reply);
+ return;
+ }
+ PostAsyncSuccessReply(msg, value);
+ } else {
+ PostAsyncErrorReply(msg, UNKNOWN_ERR);
+ }
+
+ g_variant_unref(reply);
+ }
+}
+
+void PhoneInstance::HandleAddListener(guint& listener_id,
+ const std::string& signal_name,
+ const picojson::value& msg) {
+ listener_id = g_dbus_connection_signal_subscribe(
+ g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL),
+ kPhoneService,
+ kPhoneInterface,
+ signal_name.c_str(),
+ NULL,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ HandleSignal,
+ this,
+ NULL);
+
+ if (listener_id <= 0) {
+ std::cerr << "Failed to subscribe for '" << signal_name << "': "
+ << listener_id << "\n";
+ PostAsyncErrorReply(msg, UNKNOWN_ERR);
+ return;
+ }
+
+ PostAsyncSuccessReply(msg);
+}
+
+void PhoneInstance::HandleRemoveListener(guint& listener_id,
+ const std::string& signal_name,
+ const picojson::value& msg) {
+ if (listener_id == 0) {
+ std::cerr << "Failed to unsubscribe for '" << signal_name << "'\n";
+ PostAsyncErrorReply(msg, UNKNOWN_ERR);
+ return;
+ }
+
+ g_dbus_connection_signal_unsubscribe(
+ g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL),
+ listener_id);
+
+ listener_id = 0;
+ PostAsyncSuccessReply(msg);
+}
--- /dev/null
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PHONE_PHONE_INSTANCE_H_
+#define PHONE_PHONE_INSTANCE_H_
+
+#include <gio/gio.h>
+#include <string>
+#include <thread> // NOLINT
+
+#include "common/extension.h"
+#include "common/picojson.h"
+#include "tizen/tizen.h"
+
+class PhoneInstance : public common::Instance {
+ public:
+ PhoneInstance();
+ ~PhoneInstance();
+
+ private:
+ // common::Instance implementation.
+ virtual void HandleMessage(const char* msg);
+ virtual void HandleSyncMessage(const char* msg);
+
+ // Synchronous messages
+ void HandleActiveCall(const picojson::value& msg);
+
+ // Asynchronous messages
+ void HandleSelectRemoteDevice(const picojson::value& msg);
+ void HandleUnselectRemoteDevice(const picojson::value& msg);
+ void HandleInvokeCall(const picojson::value& msg);
+ void HandleAnswerCall(const picojson::value& msg);
+ void HandleHangupCall(const picojson::value& msg);
+ void HandleMuteCall(const picojson::value& msg);
+ void HandleGet(const std::string& cmd, const picojson::value& msg);
+ void HandleAddListener(guint& listener_id,
+ const std::string& signal_name,
+ const picojson::value& msg);
+ void HandleRemoveListener(guint& listener_id,
+ const std::string& signal_name,
+ const picojson::value& msg);
+
+ // Synchronous message helpers
+ void SendSyncErrorReply(WebApiAPIErrors error_code,
+ const std::string& error_msg);
+ void SendSyncSuccessReply();
+ void SendSyncSuccessReply(const picojson::value& value);
+
+ // Asynchronous message helpers
+ void PostAsyncReply(const picojson::value& msg,
+ picojson::value::object& value);
+ void PostAsyncErrorReply(const picojson::value& msg,
+ WebApiAPIErrors error_code,
+ const std::string& error_msg);
+ void PostAsyncSuccessReply(const picojson::value& msg,
+ const picojson::value& value);
+ void PostAsyncSuccessReply(const picojson::value& msg);
+
+ void SendSignal(const picojson::value& signal_name,
+ const picojson::value& signal_value);
+
+ static GVariant* CallDBus(const gchar* method_name,
+ GVariant* parameters,
+ GError **error);
+
+ 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 RunMainloop(void* data);
+
+ static guint remote_device_selected_listener_id_;
+ static guint call_changed_listener_id_;
+ static guint call_history_entry_added_listener_id_;
+ static guint call_history_changed_listener_id_;
+ static guint contacts_changed_listener_id_;
+
+ GMainLoop* main_loop_;
+ std::thread thread_;
+};
+
+#endif // PHONE_PHONE_INSTANCE_H_
'mediaserver/mediaserver.gyp:*',
'network_bearer_selection/network_bearer_selection.gyp:*',
'notification/notification.gyp:*',
+ 'phone/phone.gyp:*',
'power/power.gyp:*',
'speech/speech.gyp:*',
'system_info/system_info.gyp:*',