diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-broadband-modem.c | 65 | ||||
-rw-r--r-- | src/mm-iface-modem-voice.c | 220 | ||||
-rw-r--r-- | src/mm-iface-modem-voice.h | 7 |
3 files changed, 202 insertions, 90 deletions
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c index 15194f42..696f31a0 100644 --- a/src/mm-broadband-modem.c +++ b/src/mm-broadband-modem.c @@ -7331,53 +7331,76 @@ modem_voice_setup_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self, } static void -ccwa_received (MMPortSerialAt *port, - GMatchInfo *info, +ccwa_received (MMPortSerialAt *port, + GMatchInfo *info, MMBroadbandModem *self) { - gchar *str; + MMCallInfo call_info; - str = mm_get_string_unquoted_from_match_info (info, 1); - mm_dbg ("Call waiting (%s)", str); - mm_iface_modem_voice_report_incoming_call (MM_IFACE_MODEM_VOICE (self), str, MM_CALL_STATE_WAITING); - g_free (str); + call_info.index = 0; + call_info.direction = MM_CALL_DIRECTION_INCOMING; + call_info.state = MM_CALL_STATE_WAITING; + call_info.number = mm_get_string_unquoted_from_match_info (info, 1); + + mm_dbg ("Call waiting (%s)", call_info.number); + mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info); + + g_free (call_info.number); } static void -ring_received (MMPortSerialAt *port, - GMatchInfo *info, +ring_received (MMPortSerialAt *port, + GMatchInfo *info, MMBroadbandModem *self) { + MMCallInfo call_info; + + call_info.index = 0; + call_info.direction = MM_CALL_DIRECTION_INCOMING; + call_info.state = MM_CALL_STATE_RINGING_IN; + call_info.number = NULL; + mm_dbg ("Ringing"); - mm_iface_modem_voice_report_incoming_call (MM_IFACE_MODEM_VOICE (self), NULL, MM_CALL_STATE_RINGING_IN); + mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info); } static void -cring_received (MMPortSerialAt *port, - GMatchInfo *info, +cring_received (MMPortSerialAt *port, + GMatchInfo *info, MMBroadbandModem *self) { - gchar *str; + MMCallInfo call_info; + gchar *str; /* We could have "VOICE" or "DATA". Now consider only "VOICE" */ - str = mm_get_string_unquoted_from_match_info (info, 1); mm_dbg ("Ringing (%s)", str); g_free (str); - mm_iface_modem_voice_report_incoming_call (MM_IFACE_MODEM_VOICE (self), NULL, MM_CALL_STATE_RINGING_IN); + call_info.index = 0; + call_info.direction = MM_CALL_DIRECTION_INCOMING; + call_info.state = MM_CALL_STATE_RINGING_IN; + call_info.number = NULL; + + mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info); } static void -clip_received (MMPortSerialAt *port, - GMatchInfo *info, +clip_received (MMPortSerialAt *port, + GMatchInfo *info, MMBroadbandModem *self) { - gchar *str; + MMCallInfo call_info; - str = mm_get_string_unquoted_from_match_info (info, 1); - mm_iface_modem_voice_report_incoming_call (MM_IFACE_MODEM_VOICE (self), str, MM_CALL_STATE_RINGING_IN); - g_free (str); + call_info.index = 0; + call_info.direction = MM_CALL_DIRECTION_INCOMING; + call_info.state = MM_CALL_STATE_RINGING_IN; + call_info.number = mm_get_string_unquoted_from_match_info (info, 1); + + mm_dbg ("Ringing (%s)", call_info.number); + mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info); + + g_free (call_info.number); } static void diff --git a/src/mm-iface-modem-voice.c b/src/mm-iface-modem-voice.c index 7e91fbd8..a051aad7 100644 --- a/src/mm-iface-modem-voice.c +++ b/src/mm-iface-modem-voice.c @@ -85,42 +85,169 @@ create_outgoing_call_from_properties (MMIfaceModemVoice *self, } /*****************************************************************************/ +/* Common helper to match call info against a known call object */ + +static gboolean +match_single_call_info (const MMCallInfo *call_info, + MMBaseCall *call) +{ + MMCallState state; + MMCallDirection direction; + const gchar *number; + guint idx; + gboolean match_direction_and_state = FALSE; + gboolean match_index = FALSE; + gboolean match_terminated = FALSE; + + /* try to look for a matching call by direction/number/index */ + state = mm_base_call_get_state (call); + direction = mm_base_call_get_direction (call); + number = mm_base_call_get_number (call); + idx = mm_base_call_get_index (call); + + /* Match index */ + if (call_info->index && (call_info->index == idx)) + match_index = TRUE; + + /* Match direction and state. + * We cannot apply this match if both call info and call have an index set + * and they're different already. */ + if ((call_info->direction == direction) && + (call_info->state == state) && + (!call_info->index || !idx || match_index)) + match_direction_and_state = TRUE; + + /* Match special terminated event */ + if ((call_info->state == MM_CALL_STATE_TERMINATED) && + (call_info->direction == MM_CALL_DIRECTION_UNKNOWN) && + !call_info->index && + !call_info->number) + match_terminated = TRUE; + + /* If no clear match, nothing to do */ + if (!match_index && !match_direction_and_state && !match_terminated) + return FALSE; + + mm_dbg ("call info matched (matched direction/state %s, matched index %s, matched terminated %s) with call at '%s'", + match_direction_and_state ? "yes" : "no", + match_index ? "yes" : "no", + match_terminated ? "yes" : "no", + mm_base_call_get_path (call)); + + /* Early detect if a known incoming call that was created + * from a plain CRING URC (i.e. without caller number) + * needs to have the number provided. + */ + if (call_info->number && !number) { + mm_dbg (" number set: %s", call_info->number); + mm_base_call_set_number (call, call_info->number); + } + + /* Early detect if a known incoming/outgoing call does + * not have a known call index yet. + */ + if (call_info->index && !idx) { + mm_dbg (" index set: %u", call_info->index); + mm_base_call_set_index (call, call_info->index); + } + + /* Update state if it changed */ + if (call_info->state != state) { + mm_dbg (" state updated: %s", mm_call_state_get_string (call_info->state)); + mm_base_call_change_state (call, call_info->state, MM_CALL_STATE_REASON_UNKNOWN); + } + + /* refresh if incoming and new state is not terminated */ + if ((call_info->state != MM_CALL_STATE_TERMINATED) && + (direction == MM_CALL_DIRECTION_INCOMING)) { + mm_dbg (" incoming refreshed"); + mm_base_call_incoming_refresh (call); + } + + return TRUE; +} + +/*****************************************************************************/ + +typedef struct { + const MMCallInfo *call_info; +} ReportCallForeachContext; + +static void +report_call_foreach (MMBaseCall *call, + ReportCallForeachContext *ctx) +{ + /* Do nothing if already matched */ + if (!ctx->call_info) + return; + + /* fully ignore already terminated calls */ + if (mm_base_call_get_state (call) == MM_CALL_STATE_TERMINATED) + return; + + /* Reset call info in context if the call info matches an existing call */ + if (match_single_call_info (ctx->call_info, call)) + ctx->call_info = NULL; +} void -mm_iface_modem_voice_report_incoming_call (MMIfaceModemVoice *self, - const gchar *number, - MMCallState state) +mm_iface_modem_voice_report_call (MMIfaceModemVoice *self, + const MMCallInfo *call_info) { - MMBaseCall *call = NULL; - MMCallList *list = NULL; + ReportCallForeachContext ctx = { 0 }; + MMBaseCall *call = NULL; + MMCallList *list = NULL; - g_assert (state == MM_CALL_STATE_RINGING_IN || state == MM_CALL_STATE_WAITING); + /* When reporting single call, the only mandatory parameter is the state: + * - index is optional (e.g. unavailable when receiving +CLIP URCs) + * - number is optional (e.g. unavailable when receiving +CRING URCs) + * - direction is optional (e.g. unavailable when receiving some vendor-specific URCs) + */ + g_assert (call_info->state != MM_CALL_STATE_UNKNOWN); + + /* Early debugging of the call state update */ + mm_dbg ("call at index %u: direction %s, state %s, number %s", + call_info->index, + mm_call_direction_get_string (call_info->direction), + mm_call_state_get_string (call_info->state), + call_info->number ? call_info->number : "n/a"); g_object_get (MM_BASE_MODEM (self), MM_IFACE_MODEM_VOICE_CALL_LIST, &list, NULL); if (!list) { - mm_warn ("Cannot create incoming call: missing call list"); + mm_warn ("Cannot process call state update: missing call list"); return; } - call = mm_call_list_get_first_incoming_call (list, state); + /* Iterate over all known calls and try to match a known one */ + ctx.call_info = call_info; + mm_call_list_foreach (list, (MMCallListForeachFunc)report_call_foreach, &ctx); - /* If call exists already, refresh its validity and set number if it wasn't set */ - if (call) { - if (number && !mm_base_call_get_number (call)) - mm_base_call_set_number (call, number); - mm_base_call_incoming_refresh (call); - g_object_unref (list); - return; + /* If call info matched with an existing one, the context call info would have been reseted */ + if (!ctx.call_info) + goto out; + + /* If call info didn't match with any known call, it may be because we're being + * reported a NEW incoming call. If that's not the case, we'll ignore the report. */ + if ((call_info->direction != MM_CALL_DIRECTION_INCOMING) || + ((call_info->state != MM_CALL_STATE_WAITING) && (call_info->state != MM_CALL_STATE_RINGING_IN))) { + mm_dbg ("unhandled call state update reported: direction: %s, state %s", + mm_call_direction_get_string (call_info->direction), + mm_call_state_get_string (call_info->state)); + goto out; } mm_dbg ("Creating new incoming call..."); - call = create_incoming_call (self, number); + call = create_incoming_call (self, call_info->number); /* Set the state */ - mm_base_call_change_state (call, state, MM_CALL_STATE_REASON_INCOMING_NEW); + mm_base_call_change_state (call, call_info->state, MM_CALL_STATE_REASON_INCOMING_NEW); + + /* Set the index, if known */ + if (call_info->index) + mm_base_call_set_index (call, call_info->index); /* Start its validity timeout */ mm_base_call_incoming_refresh (call); @@ -129,6 +256,8 @@ mm_iface_modem_voice_report_incoming_call (MMIfaceModemVoice *self, mm_base_call_export (call); mm_call_list_add_call (list, call); g_object_unref (call); + + out: g_object_unref (list); } @@ -155,61 +284,18 @@ static void report_all_calls_foreach (MMBaseCall *call, ReportAllCallsForeachContext *ctx) { - GList *l; - MMCallState state; - MMCallDirection direction; - const gchar *number; - guint idx; + GList *l; /* fully ignore already terminated calls */ - state = mm_base_call_get_state (call); - if (state == MM_CALL_STATE_TERMINATED) + if (mm_base_call_get_state (call) == MM_CALL_STATE_TERMINATED) return; - /* try to look for a matching call by direction/number/index */ - direction = mm_base_call_get_direction (call); - number = mm_base_call_get_number (call); - idx = mm_base_call_get_index (call); + /* Iterate over the call info list */ for (l = ctx->call_info_list; l; l = g_list_next (l)) { MMCallInfo *call_info = (MMCallInfo *)(l->data); - /* Early detect if a known incoming call that was created - * from a plain CRING URC (i.e. without caller number) - * needs to have the number provided. We match by state - * as well as there may be different types of incoming - * calls reported here (e.g. ringing, waiting). - */ - if ((direction == MM_CALL_DIRECTION_INCOMING) && - (call_info->direction == direction) && - (call_info->state == state) && - (call_info->number && !number)) - mm_base_call_set_number (call, call_info->number); - - /* Early detect if a known incoming/outgoing call does - * not have a known call index yet. - */ - if ((call_info->direction == direction) && - (call_info->state == state) && - (call_info->index && !idx)) { - mm_base_call_set_index (call, call_info->index); - idx = call_info->index; /* so that we match next properly */ - } - - /* Exact match? note that if both numbers are NULL, it will - * also match (e.g. if network doesn't report the caller number). - */ - if ((call_info->direction == direction) && - (g_strcmp0 (call_info->number, number) == 0) && - (call_info->index == idx)) { - /* Update state if it changed */ - if (state != call_info->state) - mm_base_call_change_state (call, call_info->state, MM_CALL_STATE_REASON_UNKNOWN); - /* refresh if incoming and new state is not terminated */ - if ((call_info->state != MM_CALL_STATE_TERMINATED) && - (direction == MM_CALL_DIRECTION_INCOMING)) { - mm_base_call_incoming_refresh (call); - } - /* delete item from list and halt iteration right away */ + /* if match found, delete item from list and halt iteration right away */ + if (match_single_call_info (call_info, call)) { ctx->call_info_list = g_list_delete_link (ctx->call_info_list, l); return; } @@ -232,6 +318,10 @@ mm_iface_modem_voice_report_all_calls (MMIfaceModemVoice *self, for (l = call_info_list; l; l = g_list_next (l)) { MMCallInfo *call_info = (MMCallInfo *)(l->data); + /* When reporting full list of calls, index and state are mandatory */ + g_assert (call_info->index != 0); + g_assert (call_info->state != MM_CALL_STATE_UNKNOWN); + mm_dbg ("call at index %u: direction %s, state %s, number %s", call_info->index, mm_call_direction_get_string (call_info->direction), diff --git a/src/mm-iface-modem-voice.h b/src/mm-iface-modem-voice.h index 7cc82601..0e91d7c7 100644 --- a/src/mm-iface-modem-voice.h +++ b/src/mm-iface-modem-voice.h @@ -194,10 +194,9 @@ void mm_iface_modem_voice_shutdown (MMIfaceModemVoice *self); void mm_iface_modem_voice_bind_simple_status (MMIfaceModemVoice *self, MMSimpleStatus *status); -/* Incoming call reporting */ -void mm_iface_modem_voice_report_incoming_call (MMIfaceModemVoice *self, - const gchar *number, - MMCallState state); +/* Single call info reporting */ +void mm_iface_modem_voice_report_call (MMIfaceModemVoice *self, + const MMCallInfo *call_info); /* Full current call list reporting (MMCallInfo list) */ void mm_iface_modem_voice_report_all_calls (MMIfaceModemVoice *self, |