/* * Copyright (C) 2017-2019 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 #include #include #define AFB_BINDING_VERSION 3 #include #include "ofono_manager.h" #include "ofono_voicecallmanager.h" #include "ofono_voicecall.h" #define OFONO_MODEM_INTERFACE "org.ofono.Modem" static OrgOfonoVoiceCallManager *vcm; static OrgOfonoVoiceCall *incoming_call, *voice_call; static afb_event_t call_state_changed_event; static afb_event_t dialing_call_event; static afb_event_t incoming_call_event; static afb_event_t terminated_call_event; static void dial(afb_req_t 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(afb_req_t 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(afb_req_t 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(afb_req_t 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(afb_req_t 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 void ofono_modem_signal_callback( GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { GVariant *var = NULL; const gchar *key = NULL; gchar *address, *tmp; gboolean state; /* Only support hands-free profile for now */ if (strncasecmp(object_path, "/hfp/org/bluez", 14)) return; if (g_strcmp0(signal_name, "PropertyChanged")) return; g_variant_get(parameters, "(sv)", &key, &var); if (g_strcmp0(key, "Online")) return; state = g_variant_get_boolean(var); address = g_strdup(strstr(object_path, "dev_") + 4); tmp = address; for (; *tmp; tmp++) { if (*tmp == '_') *tmp = ':'; } if (state) { ofono_manager_set_default_modem((const char *) address); if (ofono_manager_get_default_modem_valid()) { ofono_init_default_modem(); } } else if (!g_strcmp0(ofono_manager_get_default_modem_address(), address)) { AFB_NOTICE("Removing modem: (%s)", address); ofono_manager_invalidate_default_modem(); } g_free(address); } static void ofono_event_init(void) { GError *error = NULL; GDBusConnection *conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); g_dbus_connection_signal_subscribe(conn, NULL, /* sender */ OFONO_MODEM_INTERFACE, NULL, /* member */ NULL, /* object path */ NULL, /* arg0 */ G_DBUS_SIGNAL_FLAGS_NONE, ofono_modem_signal_callback, NULL, /* user ptr */ NULL); } static void ofono_hfp_init(void) { ofono_manager_set_default_modem(NULL); if (ofono_manager_get_default_modem_valid()) { ofono_init_default_modem(); } } static int ofono_init(afb_api_t api) { 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"); /* Start the main loop thread */ pthread_create(&tid, NULL, main_loop_thread, NULL); ret = ofono_manager_init(); if (ret == 0) { ofono_event_init(); ofono_hfp_init(); } else { AFB_ERROR("failed to initialize ofono manager"); } return ret; } static const afb_verb_t verbs[]= { { .verb = "dial", .callback = dial, }, { .verb = "hangup", .callback = hangup, }, { .verb = "answer", .callback = answer, }, { .verb = "subscribe", .callback = subscribe, }, { .verb = "unsubscribe", .callback = unsubscribe, }, { } }; static int init(afb_api_t api) { AFB_NOTICE("Initializing telephony service"); return ofono_init(api); } const afb_binding_t afbBindingV3 = { .api = "telephony", .verbs = verbs, .init = init, };