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.
This commit is contained in:
Alexander Couzens 2017-07-25 12:42:29 +02:00 committed by Pau Espin Pedrol
parent d03af77d94
commit a09aa1c6ea
5 changed files with 411 additions and 3 deletions

1
.gitignore vendored
View File

@ -47,6 +47,7 @@ unit/test-rilmodem-cs
unit/test-rilmodem-gprs
unit/test-rilmodem-sms
unit/test-mbim
unit/test-call-list
unit/test-*.log
unit/test-*.trs
test-driver

View File

@ -79,7 +79,7 @@ pkginclude_HEADERS = include/log.h include/plugin.h include/history.h \
include/cdma-provision.h include/handsfree.h \
include/handsfree-audio.h include/siri.h \
include/netmon.h include/lte.h include/ims.h \
include/storage.h
include/storage.h include/call-list.h
nodist_pkginclude_HEADERS = include/version.h
@ -724,7 +724,7 @@ src_ofonod_SOURCES = $(builtin_sources) $(gatchat_sources) \
src/handsfree-audio.c src/bluetooth.h \
src/hfp.h src/siri.c \
src/netmon.c src/lte.c src/ims.c \
src/netmonagent.c src/netmonagent.h
src/netmonagent.c src/netmonagent.h src/call-list.c
src_ofonod_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) $(ell_ldadd) \
@GLIB_LIBS@ @DBUS_LIBS@ -ldl
@ -906,7 +906,8 @@ unit_tests = unit/test-common unit/test-util \
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
@ -944,6 +945,12 @@ unit_test_sms_root_SOURCES = unit/test-sms-root.c \
unit_test_sms_root_LDADD = @GLIB_LIBS@ $(ell_ldadd)
unit_objects += $(unit_test_sms_root_OBJECTS)
unit_test_call_list_SOURCES = \
src/common.c src/util.c \
src/call-list.c unit/test-call-list.c
unit_test_call_list_LDADD = @GLIB_LIBS@ $(ell_ldadd)
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)

38
include/call-list.h Normal file
View File

@ -0,0 +1,38 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 Alexander Couzens <lynxis@fe80.eu>
*
* 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 <glib.h>
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);

110
src/call-list.c Normal file
View File

@ -0,0 +1,110 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 Alexander Couzens <lynxis@fe80.eu>
*
* 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 <glib.h>
#include "common.h"
#include <ofono/types.h>
#include <ofono/log.h>
#include <ofono/voicecall.h>
#include <ofono/call-list.h>
#include <string.h>
void ofono_call_list_dial_callback(struct ofono_voicecall *vc,
GSList **call_list,
const struct ofono_phone_number *ph,
int call_id)
{
struct ofono_call *call;
GSList *list;
/* check if call_id already present */
list = g_slist_find_custom(*call_list,
GINT_TO_POINTER(call_id),
ofono_call_compare_by_id);
if (list) {
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;
}

252
unit/test-call-list.c Normal file
View File

@ -0,0 +1,252 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 Alexander Couzens <lynxis@fe80.eu>
*
* 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 <glib.h>
#include <string.h>
#include "../src/common.h"
#include <ofono/types.h>
#include <ofono/call-list.h>
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(&notified_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;
/* reset test */
reset_notified();
call_list = NULL;
strcpy(ph.number, "0099301234567890");
ph.type = 0;
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);
}
static void test_dial_callback_race(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;
/* outgoing call */
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);
g_assert(call_list->next == NULL);
/* check how many items in the variable */
g_slist_free_full(call_list, g_free);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/test-call-list/dial_callback", test_dial_callback);
g_test_add_func("/test-call-list/dial_callback_race", test_dial_callback_race);
g_test_add_func("/test-call-list/test_notify", test_notify);
g_test_add_func("/test-call-list/test_notify_disconnected",
test_notify_disconnected);
return g_test_run();
}