diff options
Diffstat (limited to 'binding/telephony-binding.c')
-rw-r--r-- | binding/telephony-binding.c | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/binding/telephony-binding.c b/binding/telephony-binding.c new file mode 100644 index 0000000..f6db061 --- /dev/null +++ b/binding/telephony-binding.c @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2017-2018 Konsulko Group + * + * 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. + */ + +#define _GNU_SOURCE + +#include <glib.h> +#include <json-c/json.h> +#include <string.h> + +#define AFB_BINDING_VERSION 2 +#include <afb/afb-binding.h> + +#include "ofono_manager.h" +#include "ofono_voicecallmanager.h" +#include "ofono_voicecall.h" + +static OrgOfonoVoiceCallManager *vcm; +static OrgOfonoVoiceCall *incoming_call, *voice_call; +static struct afb_event call_state_changed_event; +static struct afb_event dialing_call_event; +static struct afb_event incoming_call_event; +static struct afb_event terminated_call_event; + +static void dial(struct afb_req request) +{ + struct json_object *query, *val; + const char *number; + + query = afb_req_json(request); + json_object_object_get_ex(query, "value", &val); + if (json_object_is_type(val, json_type_string)) { + number = json_object_get_string(val); + if (voice_call) { + AFB_ERROR("dial: cannot dial with active call"); + afb_req_fail(request, "active call", NULL); + } else { + AFB_DEBUG("dial: %s...\n", number); + if (ofono_voicecallmanager_dial(vcm, (gchar *)number, "")) { + afb_req_success(request, NULL, NULL); + } else { + AFB_ERROR("dial: failed to dial number\n"); + afb_req_fail(request, "failed dial", NULL); + } + } + } else { + AFB_ERROR("dial: no phone number parameter\n"); + afb_req_fail(request, "no number", NULL); + } +} + +static void hangup(struct afb_req request) +{ + if (voice_call) { + AFB_DEBUG("Hangup voice call\n"); + ofono_voicecall_hangup(voice_call); + afb_req_success(request, NULL, NULL); + } else if (incoming_call) { + AFB_DEBUG("Reject incoming call\n"); + ofono_voicecall_hangup(incoming_call); + afb_req_success(request, NULL, NULL); + } else { + AFB_ERROR("Hangup: no active call"); + afb_req_fail(request, "failed hangup", NULL); + } +} + +static void answer(struct afb_req request) +{ + if (incoming_call) { + AFB_DEBUG("Answer voice call\n"); + voice_call = incoming_call; + ofono_voicecall_answer(voice_call); + } else { + AFB_ERROR("Answer: no incoming call"); + } +} + +static void subscribe(struct afb_req request) +{ + const char *value = afb_req_value(request, "value"); + if(value) { + if (!strcasecmp(value, "callStateChanged")) { + afb_req_subscribe(request, call_state_changed_event); + } else if (!strcasecmp(value, "dialingCall")) { + afb_req_subscribe(request, dialing_call_event); + } else if (!strcasecmp(value, "incomingCall")) { + afb_req_subscribe(request, incoming_call_event); + } else if (!strcasecmp(value, "terminatedCall")) { + afb_req_subscribe(request, terminated_call_event); + } else { + afb_req_fail(request, "failed", "Invalid event"); + return; + } + } + + afb_req_success(request, NULL, NULL); +} + +static void unsubscribe(struct afb_req request) +{ + const char *value = afb_req_value(request, "value"); + if(value) { + if (!strcasecmp(value, "callStateChanged")) { + afb_req_unsubscribe(request, call_state_changed_event); + } else if (!strcasecmp(value, "dialingCall")) { + afb_req_unsubscribe(request, dialing_call_event); + } else if (!strcasecmp(value, "incomingCall")) { + afb_req_unsubscribe(request, incoming_call_event); + } else if (!strcasecmp(value, "terminatedCall")) { + afb_req_unsubscribe(request, terminated_call_event); + } else { + afb_req_fail(request, "failed", "Invalid event"); + return; + } + } + + afb_req_success(request, NULL, NULL); +} + +static void call_state_changed_cb(OrgOfonoVoiceCall *vc, gchar *state) +{ + struct json_object *call_state; + call_state = json_object_new_object(); + json_object_object_add(call_state, "state", json_object_new_string(state)); + afb_event_push(call_state_changed_event, call_state); +} + +static void incoming_call_cb(OrgOfonoVoiceCallManager *manager, gchar *op, gchar *clip) +{ + struct json_object *call_info; + + call_info = json_object_new_object(); + json_object_object_add(call_info, "clip", json_object_new_string(clip)); + afb_event_push(incoming_call_event, call_info); + incoming_call = ofono_voicecall_new(op, call_state_changed_cb); +} + +static void dialing_call_cb(OrgOfonoVoiceCallManager *manager, gchar *op, gchar *colp) +{ + struct json_object *call_info; + + call_info = json_object_new_object(); + json_object_object_add(call_info, "colp", json_object_new_string(colp)); + afb_event_push(dialing_call_event, call_info); + voice_call = ofono_voicecall_new(op, call_state_changed_cb); +} + +static void terminated_call_cb(OrgOfonoVoiceCallManager *manager, gchar *op) +{ + if (incoming_call) { + ofono_voicecall_free(incoming_call); + incoming_call = NULL; + } else if (voice_call) { + ofono_voicecall_free(voice_call); + } + voice_call = NULL; + afb_event_push(terminated_call_event, NULL); +} + +static void *main_loop_thread(void *unused) +{ + GMainLoop *loop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(loop); + return NULL; +} + +static int ofono_init_default_modem(void) +{ + int ret = 0; + const gchar *modem_path = ofono_manager_get_default_modem_path(); + + if (modem_path) { + vcm = ofono_voicecallmanager_init(modem_path, + incoming_call_cb, + dialing_call_cb, + terminated_call_cb); + if (!vcm) { + AFB_ERROR("failed to initialize voice call manager\n"); + ret = -1; + } + } else { + AFB_ERROR("default modem not set\n"); + ret = -1; + } + + return ret; +} + +static gboolean is_hfp_dev_and_init(struct json_object *dev) +{ + int ret; + gboolean hfp = FALSE; + struct json_object *name, *address, *hfp_connected; + json_object_object_get_ex(dev, "Name", &name); + json_object_object_get_ex(dev, "Address", &address); + json_object_object_get_ex(dev, "HFPConnected", &hfp_connected); + if (!strcmp(json_object_get_string(hfp_connected), "True")) { + ret = ofono_manager_set_default_modem(json_object_get_string(address)); + if (ret == 0) { + ofono_init_default_modem(); + hfp = TRUE; + } + } + + return hfp; +} + +static void discovery_result_cb(void *closure, int status, struct json_object *result) +{ + enum json_type type; + struct json_object *devs, *dev; + int i; + + json_object_object_foreach(result, key, val) { + type = json_object_get_type(val); + switch (type) { + case json_type_array: + json_object_object_get_ex(result, key, &devs); + for (i = 0; i < json_object_array_length(devs); i++) { + dev = json_object_array_get_idx(devs, i); + if (is_hfp_dev_and_init(dev)) + break; + } + break; + case json_type_string: + case json_type_boolean: + case json_type_double: + case json_type_int: + case json_type_object: + case json_type_null: + default: + break; + } + } +} + +static void ofono_hfp_init(void) +{ + struct json_object *args, *response; + + args = json_object_new_object(); + json_object_object_add(args , "value", json_object_new_string("connection")); + afb_service_call_sync("Bluetooth-Manager", "subscribe", args, &response); + + args = json_object_new_object(); + afb_service_call("Bluetooth-Manager", "discovery_result", args, discovery_result_cb, &response); +} + +static int ofono_init(void) +{ + pthread_t tid; + int ret = 0; + + call_state_changed_event = afb_daemon_make_event("callStateChanged"); + dialing_call_event = afb_daemon_make_event("dialingCall"); + incoming_call_event = afb_daemon_make_event("incomingCall"); + terminated_call_event = afb_daemon_make_event("terminatedCall"); + + ret = afb_daemon_require_api("Bluetooth-Manager", 1); + if (ret) { + AFB_ERROR("unable to initialize bluetooth binding"); + return -1; + } + + /* Start the main loop thread */ + pthread_create(&tid, NULL, main_loop_thread, NULL); + + ret = ofono_manager_init(); + if (ret == 0) { + ofono_manager_invalidate_default_modem(); + ofono_hfp_init(); + } else { + AFB_ERROR("failed to initialize ofono manager"); + } + + return ret; +} + +static const struct afb_verb_v2 verbs[]= { + { + .verb = "dial", + .callback = dial, + .auth = NULL, + .session = AFB_SESSION_NONE, + }, + { + .verb = "hangup", + .callback = hangup, + .auth = NULL, + .session = AFB_SESSION_NONE, + }, + { + .verb = "answer", + .callback = answer, + .auth = NULL, + .session = AFB_SESSION_NONE, + }, + { + .verb = "subscribe", + .callback = subscribe, + .auth = NULL, + .session = AFB_SESSION_NONE, + }, + { + .verb = "unsubscribe", + .callback = unsubscribe, + .auth = NULL, + .session = AFB_SESSION_NONE, + }, + {NULL} +}; + +static int init() +{ + AFB_NOTICE("Initializing telephony service"); + + return ofono_init(); +} + +static void process_connection_event(struct json_object *object) +{ + struct json_object *args, *response, *status_obj, *address_obj; + const char *status, *address; + + json_object_object_get_ex(object, "Status", &status_obj); + status = json_object_get_string(status_obj); + + if (!g_strcmp0(status, "connected")) { + args = json_object_new_object(); + afb_service_call("Bluetooth-Manager", "discovery_result", + args, discovery_result_cb, &response); + } else if (!g_strcmp0(status, "disconnected")) { + json_object_object_get_ex(object, "Address", &address_obj); + address = json_object_get_string(address_obj); + if (!g_strcmp0(address, ofono_manager_get_default_modem_address())) { + ofono_manager_invalidate_default_modem(); + ofono_voicecallmanager_free(vcm); + } + } else + AFB_ERROR("Unsupported connection status: %s\n", status); +} + +static void onevent(const char *event, struct json_object *object) +{ + if (!g_strcmp0(event, "Bluetooth-Manager/connection")) + process_connection_event(object); + else + AFB_ERROR("Unsupported event: %s\n", event); +} + +const struct afb_binding_v2 afbBindingV2 = { + .api = "telephony", + .specification = NULL, + .verbs = verbs, + .init = init, + .onevent = onevent, +}; |