aboutsummaryrefslogtreecommitdiff
path: root/src/mm-iface-modem-voice.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mm-iface-modem-voice.c')
-rw-r--r--src/mm-iface-modem-voice.c184
1 files changed, 182 insertions, 2 deletions
diff --git a/src/mm-iface-modem-voice.c b/src/mm-iface-modem-voice.c
index 1e293ba7..22582335 100644
--- a/src/mm-iface-modem-voice.c
+++ b/src/mm-iface-modem-voice.c
@@ -22,11 +22,13 @@
#include "mm-call-list.h"
#include "mm-log.h"
-#define SUPPORT_CHECKED_TAG "voice-support-checked-tag"
-#define SUPPORTED_TAG "voice-supported-tag"
+#define SUPPORT_CHECKED_TAG "voice-support-checked-tag"
+#define SUPPORTED_TAG "voice-supported-tag"
+#define CALL_LIST_POLLING_CONTEXT_TAG "voice-call-list-polling-context-tag"
static GQuark support_checked_quark;
static GQuark supported_quark;
+static GQuark call_list_polling_context_quark;
/*****************************************************************************/
@@ -541,6 +543,159 @@ handle_list (MmGdbusModemVoice *skeleton,
}
/*****************************************************************************/
+/* Call list polling logic
+ *
+ * The call list polling is exclusively used to detect detailed call state
+ * updates while a call is being established. Therefore, if there is no call
+ * being established (i.e. all terminated, unknown or active), then there is
+ * no polling to do.
+ *
+ * Any time we add a new call to the list, we'll setup polling if it's not
+ * already running, and the polling logic itself will decide when the polling
+ * should stop.
+ */
+
+#define CALL_LIST_POLLING_TIMEOUT_SECS 2
+
+typedef struct {
+ guint polling_id;
+ gboolean polling_ongoing;
+} CallListPollingContext;
+
+static void
+call_list_polling_context_free (CallListPollingContext *ctx)
+{
+ if (ctx->polling_id)
+ g_source_remove (ctx->polling_id);
+ g_slice_free (CallListPollingContext, ctx);
+}
+
+static CallListPollingContext *
+get_call_list_polling_context (MMIfaceModemVoice *self)
+{
+ CallListPollingContext *ctx;
+
+ if (G_UNLIKELY (!call_list_polling_context_quark))
+ call_list_polling_context_quark = (g_quark_from_static_string (
+ CALL_LIST_POLLING_CONTEXT_TAG));
+
+ ctx = g_object_get_qdata (G_OBJECT (self), call_list_polling_context_quark);
+ if (!ctx) {
+ /* Create context and keep it as object data */
+ ctx = g_slice_new0 (CallListPollingContext);
+
+ g_object_set_qdata_full (
+ G_OBJECT (self),
+ call_list_polling_context_quark,
+ ctx,
+ (GDestroyNotify)call_list_polling_context_free);
+ }
+
+ return ctx;
+}
+
+static gboolean call_list_poll (MMIfaceModemVoice *self);
+
+static void
+load_call_list_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res)
+{
+ CallListPollingContext *ctx;
+ GList *call_info_list = NULL;
+ GError *error = NULL;
+
+ ctx = get_call_list_polling_context (self);
+ ctx->polling_ongoing = FALSE;
+
+ g_assert (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list_finish);
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list_finish (self, res, &call_info_list, &error)) {
+ mm_warn ("couldn't load call list: %s", error->message);
+ g_error_free (error);
+ }
+
+ /* Always report the list even if NULL (it would mean no ongoing calls) */
+ mm_iface_modem_voice_report_all_calls (self, call_info_list);
+ mm_3gpp_call_info_list_free (call_info_list);
+
+ /* setup the polling again */
+ g_assert (!ctx->polling_id);
+ ctx->polling_id = g_timeout_add_seconds (CALL_LIST_POLLING_TIMEOUT_SECS,
+ (GSourceFunc) call_list_poll,
+ self);
+}
+
+static void
+call_list_foreach_count_establishing (MMBaseCall *call,
+ gpointer user_data)
+{
+ guint *n_calls_establishing = (guint *)user_data;
+
+ switch (mm_base_call_get_state (call)) {
+ case MM_CALL_STATE_DIALING:
+ case MM_CALL_STATE_RINGING_OUT:
+ case MM_CALL_STATE_RINGING_IN:
+ case MM_CALL_STATE_HELD:
+ case MM_CALL_STATE_WAITING:
+ *n_calls_establishing = *n_calls_establishing + 1;
+ break;
+ default:
+ break;
+ }
+}
+
+static gboolean
+call_list_poll (MMIfaceModemVoice *self)
+{
+ CallListPollingContext *ctx;
+ MMCallList *list = NULL;
+ guint n_calls_establishing = 0;
+
+ ctx = get_call_list_polling_context (self);
+ ctx->polling_id = 0;
+
+ g_object_get (MM_BASE_MODEM (self),
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+
+ if (!list) {
+ mm_warn ("Cannot poll call list: missing internal call list");
+ goto out;
+ }
+
+ mm_call_list_foreach (list, (MMCallListForeachFunc) call_list_foreach_count_establishing, &n_calls_establishing);
+
+ /* If there is at least ONE call being established, we need the call list */
+ if (n_calls_establishing > 0) {
+ mm_dbg ("%u calls being established: call list polling required", n_calls_establishing);
+ ctx->polling_ongoing = TRUE;
+ g_assert (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list);
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list (self,
+ (GAsyncReadyCallback)load_call_list_ready,
+ NULL);
+ } else
+ mm_dbg ("no calls being established: call list polling stopped");
+
+out:
+ g_clear_object (&list);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+setup_call_list_polling (MMCallList *call_list,
+ const gchar *call_path_added,
+ MMIfaceModemVoice *self)
+{
+ CallListPollingContext *ctx;
+
+ ctx = get_call_list_polling_context (self);
+
+ if (!ctx->polling_id && !ctx->polling_ongoing)
+ ctx->polling_id = g_timeout_add_seconds (CALL_LIST_POLLING_TIMEOUT_SECS,
+ (GSourceFunc) call_list_poll,
+ self);
+}
+
+/*****************************************************************************/
static void
update_message_list (MmGdbusModemVoice *skeleton,
@@ -843,6 +998,23 @@ interface_enabling_step (GTask *task)
G_CALLBACK (call_deleted),
ctx->skeleton);
+ /* Unless we're told not to, setup call list polling logic */
+ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list &&
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list_finish) {
+ gboolean periodic_call_list_check_disabled = FALSE;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED, &periodic_call_list_check_disabled,
+ NULL);
+ if (!periodic_call_list_check_disabled) {
+ mm_dbg ("periodic call list polling will be used if supported");
+ g_signal_connect (list,
+ MM_CALL_ADDED,
+ G_CALLBACK (setup_call_list_polling),
+ self);
+ }
+ }
+
g_object_unref (list);
/* Fall down to next step */
@@ -1144,6 +1316,14 @@ iface_modem_voice_init (gpointer g_iface)
MM_TYPE_CALL_LIST,
G_PARAM_READWRITE));
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boolean (MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED,
+ "Periodic call list checks disabled",
+ "Whether periodic call list check are disabled.",
+ FALSE,
+ G_PARAM_READWRITE));
+
initialized = TRUE;
}