/* * 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; 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 waiting_call_event; static afb_event_t terminated_call_event; static afb_event_t online_event; static afb_event_t battery_event; static afb_event_t signal_event; static GList *voice_calls = NULL; static void remover(gpointer data, gchar *path) { if (!g_strcmp0(path, g_dbus_proxy_get_object_path(G_DBUS_PROXY((OrgOfonoVoiceCall*)data)))) { ofono_voicecall_free((OrgOfonoVoiceCall*)data); voice_calls = g_list_remove(voice_calls, (gconstpointer)data); } } 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_calls) { 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 last_dial(afb_req_t request) { if (voice_calls) { AFB_ERROR("dial: cannot dial with active call"); afb_req_fail(request, "active call", NULL); } else { if (ofono_voicecallmanager_last_dial(vcm)) { afb_req_success(request, NULL, NULL); } else { AFB_ERROR("dial: failed to dial number\n"); afb_req_fail(request, "failed dial", NULL); } } } static void send_tones(afb_req_t request) { const char *value = afb_req_value(request, "value"); if (!voice_calls) { AFB_ERROR("send_tones: cannot send tone without active call"); afb_req_fail(request, "there is no active call", NULL); } else { if (ofono_voicecallmanager_send_tones(vcm, value)) { afb_req_success(request, NULL, NULL); } else { AFB_ERROR("send_tones: failed to send tone\n"); afb_req_fail(request, "failed send tone", NULL); } } } static void hangup_all (afb_req_t request) { if (!voice_calls) { AFB_ERROR("hangup_all: cannot hang up without active call"); afb_req_fail(request, "there is no active call", NULL); } else { ofono_voicecallmanager_hangup_all(vcm); afb_req_success(request, NULL, NULL); } } static void hangup(afb_req_t request) { const char* value = afb_req_value(request, "id"); const gchar *modem = ofono_manager_get_default_modem_path(); gchar *op = g_strconcat(modem, "/" , value, NULL); if (g_list_length(voice_calls) > 0) { GList *list; for (list = voice_calls; list != NULL; list = list->next) { if (!g_strcmp0(op, g_dbus_proxy_get_object_path(G_DBUS_PROXY((OrgOfonoVoiceCall*)list->data)))) { AFB_ERROR("object path = %s", g_dbus_proxy_get_object_path(G_DBUS_PROXY((OrgOfonoVoiceCall*)list->data))); ofono_voicecall_hangup((OrgOfonoVoiceCall*)list->data); afb_req_success(request, NULL, NULL); g_free(op); return; } } AFB_ERROR("Hangup: Invalid ID"); afb_req_fail(request, "Invalid ID", NULL); g_free(op); return; } else { AFB_ERROR("Hangup: no active call"); afb_req_fail(request, "failed hangup", NULL); return; } } static void answer(afb_req_t request) { if (incoming_call) { AFB_DEBUG("Answer voice call\n"); voice_calls = g_list_append(voice_calls, (gpointer)incoming_call); ofono_voicecall_answer(incoming_call); ofono_voicecall_free(incoming_call); incoming_call = NULL; } else { AFB_ERROR("Answer: no incoming call"); } } static void hangup_multiparty(afb_req_t request) { if (g_list_length(voice_calls) < 2) { afb_req_fail(request, "failed hangup multiparty", NULL); } else { ofono_voicecallmanager_hangup_multiparty(vcm); afb_req_success(request, NULL, NULL); } } static void create_multiparty(afb_req_t request) { gchar **reply = NULL; if (g_list_length(voice_calls) > 1) { reply = ofono_voicecallmanager_create_multiparty(vcm); if (reply) afb_req_success(request, NULL, NULL); else afb_req_fail(request, "failed create multiparty", NULL); } else { AFB_ERROR("Create MultiParty: It should be greater than 1 active call"); afb_req_fail(request, "failed create multiparty", NULL); } } static void hold_and_answer(afb_req_t request) { if (g_list_length(voice_calls) > 1) { if (ofono_voicecallmanager_hold_and_answer(vcm)) { afb_req_success(request, NULL, NULL); } else { AFB_ERROR("hold_and_answer: failed"); afb_req_fail(request, "failed hold and answer", NULL); } } else { AFB_ERROR("hold_and_answer: cannot hold and answer without waiting call"); afb_req_fail(request, "no waiting call", NULL); } } static void release_and_answer(afb_req_t request) { if (g_list_length(voice_calls) > 1) { if (ofono_voicecallmanager_release_and_answer(vcm)) { afb_req_success(request, NULL, NULL); } else { AFB_ERROR("release_and_answer: failed to release and answer\n"); afb_req_fail(request, "failed release and answer", NULL); } } else { AFB_ERROR("release_and_answer: cannot release and answer without waiting call"); afb_req_fail(request, "does not exist waiting call", NULL); } } static void swap_calls(afb_req_t request) { if (g_list_length(voice_calls) > 1) { if (ofono_voicecallmanager_swap_calls(vcm)) { afb_req_success(request, NULL, NULL); } else { AFB_ERROR("dial: failed to swap calls\n"); afb_req_fail(request, "failed swap_calls", NULL); } } else { AFB_ERROR("swap: there is no waiting call"); afb_req_fail(request, "swap calls", NULL); } } static void get_calls(afb_req_t request) { json_object *result = ofono_voicecallmanager_get_calls(vcm); if (!result) afb_req_fail(request, "failed", "Can not find any active voice calls"); else afb_req_success(request, result, NULL); } static void get_battery_level(afb_req_t request) { const gchar *device; device = ofono_manager_get_default_modem_path(); if (!device) { afb_req_fail(request, "failed", "No path find"); return; } json_object *result = ofono_get_property(device, OFONO_HANDSFREE_INTERFACE, "BatteryChargeLevel", NULL); if (!result) afb_req_fail(request, "failed", "Can not find Battery object"); else afb_req_success(request, result, "OK"); return; } static void get_network_registration(afb_req_t request) { const gchar *device; device = ofono_manager_get_default_modem_path(); if (!device) { afb_req_fail(request, "failed", "No path find"); return; } json_object *result = ofono_get_property(device, OFONO_NetworkRegistration_INTERFACE, NULL, NULL); if (!result) afb_req_fail(request, "failed", "Can not find Network Registration object"); else afb_req_success(request, result, "OK"); return; } 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, "waitingCall")) { afb_req_subscribe(request, waiting_call_event); } else if (!strcasecmp(value, "terminatedCall")) { afb_req_subscribe(request, terminated_call_event); } else if (!strcasecmp(value, "battery")) { afb_req_subscribe(request, battery_event); } else if (!strcasecmp(value, "signal")) { afb_req_subscribe(request, signal_event); } else if (!strcasecmp(value, "online")) { json_object *jresp = json_object_new_object(); afb_req_subscribe(request, online_event); json_object_object_add(jresp, "connected", json_object_new_boolean(ofono_manager_get_default_modem_online())); afb_event_push(online_event, jresp); } 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, "waitingCall")) { afb_req_unsubscribe(request, waiting_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); voice_calls = g_list_append(voice_calls, (gpointer)incoming_call); } 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_calls = g_list_append(voice_calls, (gpointer)ofono_voicecall_new(op, call_state_changed_cb)); } static void waiting_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(waiting_call_event, call_info); voice_calls = g_list_append(voice_calls, (gpointer)ofono_voicecall_new(op, call_state_changed_cb)); } static void terminated_call_cb(OrgOfonoVoiceCallManager *manager, gchar *op) { if (g_list_length(voice_calls) > 0) g_list_foreach(voice_calls, (GFunc)remover, op); 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, waiting_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()) { json_object *jresp = json_object_new_object(); ofono_init_default_modem(); json_object_object_add(jresp, "connected", json_object_new_boolean(TRUE)); afb_event_push(online_event, jresp); } } else if (!g_strcmp0(ofono_manager_get_default_modem_address(), address)) { json_object *jresp = json_object_new_object(); AFB_NOTICE("Removing modem: (%s)", address); ofono_manager_invalidate_default_modem(); json_object_object_add(jresp, "connected", json_object_new_boolean(FALSE)); afb_event_push(online_event, jresp); } g_free(address); } static void ofono_handsfree_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; 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, "BatteryChargeLevel")) return; g_variant_get(parameters, "(sv)", &key, &var); json_object *jresp = json_object_new_object(); json_object_object_add(jresp, "battery level", json_object_new_int((int)g_variant_get_byte(var))); afb_event_push(battery_event, jresp); } static void ofono_networkregistration_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; 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, "Strength")) return; json_object *jresp = json_object_new_object(); json_object_object_add(jresp, "signal level", json_object_new_int((int)g_variant_get_byte(var))); afb_event_push(signal_event, jresp); } 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); g_dbus_connection_signal_subscribe(conn, NULL, /* sender */ OFONO_HANDSFREE_INTERFACE, NULL, /* member */ NULL, /* object path */ NULL, /* arg0 */ G_DBUS_SIGNAL_FLAGS_NONE, ofono_handsfree_signal_callback, NULL, /* user ptr */ NULL); g_dbus_connection_signal_subscribe(conn, NULL, /* sender */ OFONO_NetworkRegistration_INTERFACE, NULL, /* member */ NULL, /* object path */ NULL, /* arg0 */ G_DBUS_SIGNAL_FLAGS_NONE, ofono_networkregistration_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"); waiting_call_event = afb_daemon_make_event("waitingCall"); terminated_call_event = afb_daemon_make_event("terminatedCall"); online_event = afb_daemon_make_event("online"); battery_event = afb_daemon_make_event("battery"); signal_event = afb_daemon_make_event("signal"); /* 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 = "last_dial", .callback = last_dial, }, { .verb = "send_tones", .callback = send_tones, }, { .verb = "hangup", .callback = hangup, }, { .verb = "hangup_all", .callback = hangup_all, }, { .verb = "answer", .callback = answer, }, { .verb = "hangup_multiparty", .callback = hangup_multiparty, }, { .verb = "create_multiparty", .callback = create_multiparty, }, { .verb = "swap_calls", .callback = swap_calls, }, { .verb = "hold_and_answer", .callback = hold_and_answer, }, { .verb = "release_and_answer", .callback = release_and_answer, }, { .verb = "get_calls", .callback = get_calls, }, { .verb = "get_battery_level", .callback = get_battery_level, }, { .verb = "get_network_registration", .callback = get_network_registration, }, { .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, };