From 2ae2366c262a15e4f3269afd9c80ddf06c1b9b46 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 25 Jul 2017 12:42:29 +0200 Subject: [PATCH 06/17] add call-list helper to manage voice call lists Many drivers asks the modem for a complete call list of current calls. These list of calls can be feeded into call-list which parse the list and notify ofono for new calls. --- Makefile.am | 13 ++- include/call-list.h | 38 ++++++++ src/call-list.c | 114 ++++++++++++++++++++++++ unit/test-call-list.c | 237 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 399 insertions(+), 3 deletions(-) create mode 100644 include/call-list.h create mode 100644 src/call-list.c create mode 100644 unit/test-call-list.c Index: ofono-1.21/Makefile.am =================================================================== --- ofono-1.21.orig/Makefile.am +++ ofono-1.21/Makefile.am @@ -22,7 +22,7 @@ pkginclude_HEADERS = include/log.h inclu include/private-network.h include/cdma-netreg.h \ include/cdma-provision.h include/handsfree.h \ include/handsfree-audio.h include/siri.h \ - include/netmon.h include/lte.h + include/netmon.h include/lte.h include/call-list.h nodist_pkginclude_HEADERS = include/version.h @@ -630,7 +630,7 @@ src_ofonod_SOURCES = $(builtin_sources) src/cdma-provision.c src/handsfree.c \ src/handsfree-audio.c src/bluetooth.h \ src/hfp.h src/siri.c \ - src/netmon.c src/lte.c \ + src/netmon.c src/lte.c src/call-list.c \ src/netmonagent.c src/netmonagent.h src_ofonod_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \ @@ -807,7 +807,8 @@ unit_tests = unit/test-common unit/test- unit/test-rilmodem-cs \ unit/test-rilmodem-sms \ unit/test-rilmodem-cb \ - unit/test-rilmodem-gprs + unit/test-rilmodem-gprs \ + unit/test-call-list noinst_PROGRAMS = $(unit_tests) \ unit/test-sms-root unit/test-mux unit/test-caif @@ -849,6 +850,12 @@ unit_test_sms_root_SOURCES = unit/test-s unit_test_sms_root_LDADD = @GLIB_LIBS@ unit_objects += $(unit_test_sms_root_OBJECTS) +unit_test_call_list_SOURCES = \ + src/common.c src/util.c src/log.c \ + src/call-list.c unit/test-call-list.c +unit_test_call_list_LDADD = @GLIB_LIBS@ -ldl +unit_objects += $(unit_test_call_list_OBJECTS) + unit_test_mux_SOURCES = unit/test-mux.c $(gatchat_sources) unit_test_mux_LDADD = @GLIB_LIBS@ unit_objects += $(unit_test_mux_OBJECTS) Index: ofono-1.21/include/call-list.h =================================================================== --- /dev/null +++ ofono-1.21/include/call-list.h @@ -0,0 +1,38 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2017 Alexander Couzens + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include + +struct ofono_voicecall; +struct ofono_phone_number; + +/* + * Can be called by the driver in the dialing callback, + * when the new call id already known + */ +void ofono_call_list_dial_callback(struct ofono_voicecall *vc, + GSList **call_list, + const struct ofono_phone_number *ph, + int call_id); + +/* + * Called with a list of known calls e.g. clcc. + * Call list will take ownership of all ofono call within the calls. + */ +void ofono_call_list_notify(struct ofono_voicecall *vc, + GSList **call_list, + GSList *calls); Index: ofono-1.21/src/call-list.c =================================================================== --- /dev/null +++ ofono-1.21/src/call-list.c @@ -0,0 +1,114 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2017 Alexander Couzens + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include + +#include "common.h" + +#include +#include +#include +#include + +#include + +void ofono_call_list_dial_callback(struct ofono_voicecall *vc, + GSList **call_list, + const struct ofono_phone_number *ph, + int call_id) +{ + GSList *list; + struct ofono_call *call; + + /* list_notify could be triggered before this call back is handled */ + list = g_slist_find_custom(*call_list, + GINT_TO_POINTER(call_id), + ofono_call_compare_by_id); + + if (list && list->data) { + call = list->data; + DBG("Call id %d already known. In state %s(%d)", + call_id, ofono_call_status_to_string(call->status), + call->status); + return; + } + + call = g_new0(struct ofono_call, 1); + call->id = call_id; + + memcpy(&call->called_number, ph, sizeof(*ph)); + call->direction = CALL_DIRECTION_MOBILE_ORIGINATED; + call->status = CALL_STATUS_DIALING; + call->type = 0; /* voice */ + + *call_list = g_slist_insert_sorted(*call_list, + call, + ofono_call_compare); + ofono_voicecall_notify(vc, call); +} + +void ofono_call_list_notify(struct ofono_voicecall *vc, + GSList **call_list, + GSList *calls) +{ + GSList *old_calls = *call_list; + GSList *new_calls = calls; + struct ofono_call *new_call, *old_call; + + while (old_calls || new_calls) { + old_call = old_calls ? old_calls->data : NULL; + new_call = new_calls ? new_calls->data : NULL; + + /* we drop disconnected calls and treat them as not existent */ + if (new_call && new_call->status == CALL_STATUS_DISCONNECTED) { + new_calls = new_calls->next; + calls = g_slist_remove(calls, new_call); + g_free(new_call); + continue; + } + + if (old_call && + (new_call == NULL || + (new_call->id > old_call->id))) { + ofono_voicecall_disconnected( + vc, + old_call->id, + OFONO_DISCONNECT_REASON_UNKNOWN, + NULL); + old_calls = old_calls->next; + } else if (new_call && + (old_call == NULL || + (new_call->id < old_call->id))) { + + /* new call, signal it */ + if (new_call->type == 0) + ofono_voicecall_notify(vc, new_call); + + new_calls = new_calls->next; + } else { + if (memcmp(new_call, old_call, sizeof(*new_call)) + && new_call->type == 0) + ofono_voicecall_notify(vc, new_call); + + new_calls = new_calls->next; + old_calls = old_calls->next; + } + } + + g_slist_free_full(*call_list, g_free); + *call_list = calls; +} Index: ofono-1.21/unit/test-call-list.c =================================================================== --- /dev/null +++ ofono-1.21/unit/test-call-list.c @@ -0,0 +1,237 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2017 Alexander Couzens + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + + +#include +#include + + +#include "../src/common.h" +#include +#include + +struct voicecall { +}; + +struct notified { + unsigned int id; + enum call_status status; +}; + +static struct notified notified_list[32]; +static int notified_idx; +static int notified_check; + +void reset_notified(void) +{ + notified_idx = 0; + notified_check = 0; + memset(¬ified_list, 0, sizeof(notified_list)); +} + +void ofono_voicecall_notify(struct ofono_voicecall *vc, + struct ofono_call *call) +{ + notified_list[notified_idx].id = call->id; + notified_list[notified_idx].status = call->status; + notified_idx++; +} + +void ofono_voicecall_disconnected(struct ofono_voicecall *vc, int id, + enum ofono_disconnect_reason reason, + const struct ofono_error *error) +{ + notified_list[notified_idx].id = id; + notified_list[notified_idx].status = CALL_STATUS_DISCONNECTED; + notified_idx++; +} + +static GSList *create_call( + GSList *calls, + unsigned int id, + enum call_status status, + enum call_direction direction) +{ + struct ofono_call *call = g_new0(struct ofono_call, 1); + + call->id = id; + call->status = status; + call->direction = direction; + + calls = g_slist_insert_sorted(calls, call, ofono_call_compare); + + return calls; +} + +static void assert_notified(unsigned int call_id, int call_status) +{ + g_assert(notified_idx >= notified_check); + g_assert(notified_list[notified_check].id == call_id); + g_assert(notified_list[notified_check].status == call_status); + + notified_check++; +} + +static void test_notify_disconnected(void) +{ + struct ofono_voicecall *vc = NULL; + struct ofono_phone_number ph; + GSList *call_list; + GSList *calls; + + strcpy(ph.number, "004888123456"); + ph.type = 0; + + /* reset test */ + reset_notified(); + call_list = NULL; + + /* fill disconnected call*/ + calls = create_call(NULL, 1, CALL_STATUS_DISCONNECTED, + CALL_DIRECTION_MOBILE_TERMINATED); + ofono_call_list_notify(vc, &call_list, calls); + + /* incoming call */ + calls = create_call(NULL, 1, CALL_STATUS_DISCONNECTED, + CALL_DIRECTION_MOBILE_TERMINATED); + calls = create_call(calls, 1, CALL_STATUS_ALERTING, + CALL_DIRECTION_MOBILE_TERMINATED); + ofono_call_list_notify(vc, &call_list, calls); + + /* answer call */ + calls = create_call(NULL, 1, CALL_STATUS_ACTIVE, + CALL_DIRECTION_MOBILE_TERMINATED); + calls = create_call(calls, 1, CALL_STATUS_DISCONNECTED, + CALL_DIRECTION_MOBILE_TERMINATED); + ofono_call_list_notify(vc, &call_list, calls); + + /* another call waiting */ + calls = create_call(NULL, 1, CALL_STATUS_DISCONNECTED, + CALL_DIRECTION_MOBILE_TERMINATED); + calls = create_call(calls, 1, CALL_STATUS_ACTIVE, + CALL_DIRECTION_MOBILE_TERMINATED); + calls = create_call(calls, 2, CALL_STATUS_DISCONNECTED, + CALL_DIRECTION_MOBILE_TERMINATED); + calls = create_call(calls, 2, CALL_STATUS_WAITING, + CALL_DIRECTION_MOBILE_TERMINATED); + calls = create_call(calls, 2, CALL_STATUS_DISCONNECTED, + CALL_DIRECTION_MOBILE_TERMINATED); + ofono_call_list_notify(vc, &call_list, calls); + + /* end all calls */ + ofono_call_list_notify(vc, &call_list, NULL); + + /* verify call history */ + assert_notified(1, CALL_STATUS_ALERTING); + assert_notified(1, CALL_STATUS_ACTIVE); + assert_notified(2, CALL_STATUS_WAITING); + assert_notified(1, CALL_STATUS_DISCONNECTED); + assert_notified(2, CALL_STATUS_DISCONNECTED); + + g_assert(notified_check == notified_idx); + g_slist_free_full(call_list, g_free); +} + +static void test_notify(void) +{ + struct ofono_voicecall *vc = NULL; + struct ofono_phone_number ph; + GSList *call_list; + GSList *calls; + + strcpy(ph.number, "004888123456"); + ph.type = 0; + + /* reset test */ + reset_notified(); + call_list = NULL; + + /* incoming call */ + calls = create_call(NULL, 1, CALL_STATUS_ALERTING, + CALL_DIRECTION_MOBILE_TERMINATED); + ofono_call_list_notify(vc, &call_list, calls); + + /* answer call */ + calls = create_call(NULL, 1, CALL_STATUS_ACTIVE, + CALL_DIRECTION_MOBILE_TERMINATED); + ofono_call_list_notify(vc, &call_list, calls); + + /* another call waiting */ + calls = create_call(NULL, 1, CALL_STATUS_ACTIVE, + CALL_DIRECTION_MOBILE_TERMINATED); + calls = create_call(calls, 2, CALL_STATUS_WAITING, + CALL_DIRECTION_MOBILE_TERMINATED); + ofono_call_list_notify(vc, &call_list, calls); + + /* end all calls */ + ofono_call_list_notify(vc, &call_list, NULL); + + /* verify call history */ + assert_notified(1, CALL_STATUS_ALERTING); + assert_notified(1, CALL_STATUS_ACTIVE); + assert_notified(2, CALL_STATUS_WAITING); + assert_notified(1, CALL_STATUS_DISCONNECTED); + assert_notified(2, CALL_STATUS_DISCONNECTED); + + g_assert(notified_check == notified_idx); + g_slist_free_full(call_list, g_free); +} + +static void test_dial_callback(void) +{ + struct ofono_voicecall *vc = NULL; + struct ofono_phone_number ph; + struct ofono_call *call; + GSList *call_list, *calls; + + /* reset test */ + reset_notified(); + call_list = NULL; + + strcpy(ph.number, "0099301234567890"); + ph.type = 0; + + /* check if a call gets added to the call_list */ + ofono_call_list_dial_callback(vc, &call_list, &ph, 33); + + call = call_list->data; + g_assert(strcmp(call->called_number.number, ph.number) == 0); + g_slist_free_full(call_list, g_free); + + /* check when notify is faster than dial_callback */ + call_list = NULL; + calls = create_call(NULL, 1, CALL_STATUS_DIALING, + CALL_DIRECTION_MOBILE_ORIGINATED); + ofono_call_list_notify(vc, &call_list, calls); + ofono_call_list_dial_callback(vc, &call_list, &ph, 1); + call = call_list->data; + g_assert(call_list->next == NULL); + g_slist_free_full(call_list, g_free); + + call_list = NULL; +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/test-call-list/test_notify", test_notify); + g_test_add_func("/test-call-list/test_notify_disconnected", + test_notify_disconnected); + g_test_add_func("/test-call-list/dial_callback", test_dial_callback); + return g_test_run(); +}