iface-modem-voice: setup full call list polling logic
If the modem supports call list polling, we'll setup a timeout to poll for the full call list periodically, in order to get detailed call states. The timeout is setup as soon as a new call is created, and it will be kept enabled as long as there is a call being established (i.e. not unknown, not terminated, not active).
This commit is contained in:
@@ -120,6 +120,7 @@ enum {
|
||||
PROP_MODEM_SIM_HOT_SWAP_SUPPORTED,
|
||||
PROP_MODEM_SIM_HOT_SWAP_CONFIGURED,
|
||||
PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED,
|
||||
PROP_MODEM_PERIODIC_CALL_LIST_CHECK_DISABLED,
|
||||
PROP_MODEM_CARRIER_CONFIG_MAPPING,
|
||||
PROP_FLOW_CONTROL,
|
||||
PROP_LAST
|
||||
@@ -225,6 +226,7 @@ struct _MMBroadbandModemPrivate {
|
||||
/* Properties */
|
||||
GObject *modem_voice_dbus_skeleton;
|
||||
MMCallList *modem_voice_call_list;
|
||||
gboolean periodic_call_list_check_disabled;
|
||||
gboolean clcc_supported;
|
||||
|
||||
/*<--- Modem Time interface --->*/
|
||||
@@ -11357,6 +11359,9 @@ set_property (GObject *object,
|
||||
case PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED:
|
||||
self->priv->periodic_signal_check_disabled = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_MODEM_PERIODIC_CALL_LIST_CHECK_DISABLED:
|
||||
self->priv->periodic_call_list_check_disabled = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_MODEM_CARRIER_CONFIG_MAPPING:
|
||||
self->priv->carrier_config_mapping = g_value_dup_string (value);
|
||||
break;
|
||||
@@ -11480,6 +11485,9 @@ get_property (GObject *object,
|
||||
case PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED:
|
||||
g_value_set_boolean (value, self->priv->periodic_signal_check_disabled);
|
||||
break;
|
||||
case PROP_MODEM_PERIODIC_CALL_LIST_CHECK_DISABLED:
|
||||
g_value_set_boolean (value, self->priv->periodic_call_list_check_disabled);
|
||||
break;
|
||||
case PROP_MODEM_CARRIER_CONFIG_MAPPING:
|
||||
g_value_set_string (value, self->priv->carrier_config_mapping);
|
||||
break;
|
||||
@@ -11516,6 +11524,7 @@ mm_broadband_modem_init (MMBroadbandModem *self)
|
||||
self->priv->current_sms_mem2_storage = MM_SMS_STORAGE_UNKNOWN;
|
||||
self->priv->sim_hot_swap_supported = FALSE;
|
||||
self->priv->periodic_signal_check_disabled = FALSE;
|
||||
self->priv->periodic_call_list_check_disabled = FALSE;
|
||||
self->priv->modem_cmer_enable_mode = MM_3GPP_CMER_MODE_NONE;
|
||||
self->priv->modem_cmer_disable_mode = MM_3GPP_CMER_MODE_NONE;
|
||||
self->priv->modem_cmer_ind = MM_3GPP_CMER_IND_NONE;
|
||||
@@ -12019,6 +12028,10 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass)
|
||||
PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED,
|
||||
MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED);
|
||||
|
||||
g_object_class_override_property (object_class,
|
||||
PROP_MODEM_PERIODIC_CALL_LIST_CHECK_DISABLED,
|
||||
MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED);
|
||||
|
||||
g_object_class_override_property (object_class,
|
||||
PROP_MODEM_CARRIER_CONFIG_MAPPING,
|
||||
MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING);
|
||||
|
@@ -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;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
@@ -540,6 +542,159 @@ handle_list (MmGdbusModemVoice *skeleton,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -29,8 +29,9 @@
|
||||
#define MM_IS_IFACE_MODEM_VOICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_VOICE))
|
||||
#define MM_IFACE_MODEM_VOICE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_VOICE, MMIfaceModemVoice))
|
||||
|
||||
#define MM_IFACE_MODEM_VOICE_DBUS_SKELETON "iface-modem-voice-dbus-skeleton"
|
||||
#define MM_IFACE_MODEM_VOICE_CALL_LIST "iface-modem-voice-call-list"
|
||||
#define MM_IFACE_MODEM_VOICE_DBUS_SKELETON "iface-modem-voice-dbus-skeleton"
|
||||
#define MM_IFACE_MODEM_VOICE_CALL_LIST "iface-modem-voice-call-list"
|
||||
#define MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED "iface-modem-voice-periodic-call-list-check-disabled"
|
||||
|
||||
typedef struct _MMIfaceModemVoice MMIfaceModemVoice;
|
||||
|
||||
@@ -77,7 +78,16 @@ struct _MMIfaceModemVoice {
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
/* Create CALL objects */
|
||||
/* Load full list of calls (MMCallInfo list) */
|
||||
void (* load_call_list) (MMIfaceModemVoice *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean (* load_call_list_finish) (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GList **call_info_list,
|
||||
GError **error);
|
||||
|
||||
/* Create call objects */
|
||||
MMBaseCall * (* create_call) (MMIfaceModemVoice *self,
|
||||
MMCallDirection direction,
|
||||
const gchar *number);
|
||||
|
Reference in New Issue
Block a user