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_SUPPORTED,
|
||||||
PROP_MODEM_SIM_HOT_SWAP_CONFIGURED,
|
PROP_MODEM_SIM_HOT_SWAP_CONFIGURED,
|
||||||
PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED,
|
PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED,
|
||||||
|
PROP_MODEM_PERIODIC_CALL_LIST_CHECK_DISABLED,
|
||||||
PROP_MODEM_CARRIER_CONFIG_MAPPING,
|
PROP_MODEM_CARRIER_CONFIG_MAPPING,
|
||||||
PROP_FLOW_CONTROL,
|
PROP_FLOW_CONTROL,
|
||||||
PROP_LAST
|
PROP_LAST
|
||||||
@@ -225,6 +226,7 @@ struct _MMBroadbandModemPrivate {
|
|||||||
/* Properties */
|
/* Properties */
|
||||||
GObject *modem_voice_dbus_skeleton;
|
GObject *modem_voice_dbus_skeleton;
|
||||||
MMCallList *modem_voice_call_list;
|
MMCallList *modem_voice_call_list;
|
||||||
|
gboolean periodic_call_list_check_disabled;
|
||||||
gboolean clcc_supported;
|
gboolean clcc_supported;
|
||||||
|
|
||||||
/*<--- Modem Time interface --->*/
|
/*<--- Modem Time interface --->*/
|
||||||
@@ -11357,6 +11359,9 @@ set_property (GObject *object,
|
|||||||
case PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED:
|
case PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED:
|
||||||
self->priv->periodic_signal_check_disabled = g_value_get_boolean (value);
|
self->priv->periodic_signal_check_disabled = g_value_get_boolean (value);
|
||||||
break;
|
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:
|
case PROP_MODEM_CARRIER_CONFIG_MAPPING:
|
||||||
self->priv->carrier_config_mapping = g_value_dup_string (value);
|
self->priv->carrier_config_mapping = g_value_dup_string (value);
|
||||||
break;
|
break;
|
||||||
@@ -11480,6 +11485,9 @@ get_property (GObject *object,
|
|||||||
case PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED:
|
case PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED:
|
||||||
g_value_set_boolean (value, self->priv->periodic_signal_check_disabled);
|
g_value_set_boolean (value, self->priv->periodic_signal_check_disabled);
|
||||||
break;
|
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:
|
case PROP_MODEM_CARRIER_CONFIG_MAPPING:
|
||||||
g_value_set_string (value, self->priv->carrier_config_mapping);
|
g_value_set_string (value, self->priv->carrier_config_mapping);
|
||||||
break;
|
break;
|
||||||
@@ -11516,6 +11524,7 @@ mm_broadband_modem_init (MMBroadbandModem *self)
|
|||||||
self->priv->current_sms_mem2_storage = MM_SMS_STORAGE_UNKNOWN;
|
self->priv->current_sms_mem2_storage = MM_SMS_STORAGE_UNKNOWN;
|
||||||
self->priv->sim_hot_swap_supported = FALSE;
|
self->priv->sim_hot_swap_supported = FALSE;
|
||||||
self->priv->periodic_signal_check_disabled = 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_enable_mode = MM_3GPP_CMER_MODE_NONE;
|
||||||
self->priv->modem_cmer_disable_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;
|
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,
|
PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED,
|
||||||
MM_IFACE_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,
|
g_object_class_override_property (object_class,
|
||||||
PROP_MODEM_CARRIER_CONFIG_MAPPING,
|
PROP_MODEM_CARRIER_CONFIG_MAPPING,
|
||||||
MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING);
|
MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING);
|
||||||
|
@@ -24,9 +24,11 @@
|
|||||||
|
|
||||||
#define SUPPORT_CHECKED_TAG "voice-support-checked-tag"
|
#define SUPPORT_CHECKED_TAG "voice-support-checked-tag"
|
||||||
#define SUPPORTED_TAG "voice-supported-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 support_checked_quark;
|
||||||
static GQuark supported_quark;
|
static GQuark supported_quark;
|
||||||
|
static GQuark call_list_polling_context_quark;
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
@@ -540,6 +542,159 @@ handle_list (MmGdbusModemVoice *skeleton,
|
|||||||
return TRUE;
|
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
|
static void
|
||||||
@@ -843,6 +998,23 @@ interface_enabling_step (GTask *task)
|
|||||||
G_CALLBACK (call_deleted),
|
G_CALLBACK (call_deleted),
|
||||||
ctx->skeleton);
|
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);
|
g_object_unref (list);
|
||||||
|
|
||||||
/* Fall down to next step */
|
/* Fall down to next step */
|
||||||
@@ -1144,6 +1316,14 @@ iface_modem_voice_init (gpointer g_iface)
|
|||||||
MM_TYPE_CALL_LIST,
|
MM_TYPE_CALL_LIST,
|
||||||
G_PARAM_READWRITE));
|
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;
|
initialized = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#define MM_IFACE_MODEM_VOICE_DBUS_SKELETON "iface-modem-voice-dbus-skeleton"
|
#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_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;
|
typedef struct _MMIfaceModemVoice MMIfaceModemVoice;
|
||||||
|
|
||||||
@@ -77,7 +78,16 @@ struct _MMIfaceModemVoice {
|
|||||||
GAsyncResult *res,
|
GAsyncResult *res,
|
||||||
GError **error);
|
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,
|
MMBaseCall * (* create_call) (MMIfaceModemVoice *self,
|
||||||
MMCallDirection direction,
|
MMCallDirection direction,
|
||||||
const gchar *number);
|
const gchar *number);
|
||||||
|
Reference in New Issue
Block a user