base-call: implement generic audio channel setup/cleanup handlers

Modems that require specific commands to setup or cleanup the audio
channel as soon as a call is ongoing can subclass these two new
methods.

The setup() method is considered part of the call start/accept
process, and so if it fails, the whole operation will fail.

The failures in the cleanup() method will be reported in the log, but
otherwise ignored, as this operation may be executed without any
user intervention (e.g. if the remote party hangs up).
This commit is contained in:
Aleksander Morgado
2018-08-10 13:52:06 +02:00
committed by Dan Williams
parent 17c511ec69
commit 329caff84f
2 changed files with 175 additions and 42 deletions

View File

@@ -60,6 +60,9 @@ struct _MMBaseCallPrivate {
guint incoming_timeout; guint incoming_timeout;
GRegex *in_call_events; GRegex *in_call_events;
/* The port used for audio while call is ongoing, if known */
MMPort *audio_port;
}; };
/*****************************************************************************/ /*****************************************************************************/
@@ -163,6 +166,27 @@ mm_base_call_incoming_refresh (MMBaseCall *self)
self->priv->incoming_timeout = g_timeout_add_seconds (INCOMING_TIMEOUT_SECS, (GSourceFunc)incoming_timeout_cb, self); self->priv->incoming_timeout = g_timeout_add_seconds (INCOMING_TIMEOUT_SECS, (GSourceFunc)incoming_timeout_cb, self);
} }
/*****************************************************************************/
/* Update audio settings */
static void
update_audio_settings (MMBaseCall *self,
MMPort *audio_port,
MMCallAudioFormat *audio_format)
{
if (!audio_port && self->priv->audio_port && mm_port_get_connected (self->priv->audio_port))
mm_port_set_connected (self->priv->audio_port, FALSE);
g_clear_object (&self->priv->audio_port);
if (audio_port) {
self->priv->audio_port = g_object_ref (audio_port);
mm_port_set_connected (self->priv->audio_port, TRUE);
}
mm_gdbus_call_set_audio_port (MM_GDBUS_CALL (self), audio_port ? mm_port_get_device (audio_port) : NULL);
mm_gdbus_call_set_audio_format (MM_GDBUS_CALL (self), mm_call_audio_format_get_dictionary (audio_format));
}
/*****************************************************************************/ /*****************************************************************************/
/* Start call (DBus call handling) */ /* Start call (DBus call handling) */
@@ -182,8 +206,52 @@ handle_start_context_free (HandleStartContext *ctx)
} }
static void static void
handle_start_ready (MMBaseCall *self, call_started (HandleStartContext *ctx)
GAsyncResult *res, {
mm_info ("call is started");
/* If dialing to ringing supported, leave it dialing */
if (!ctx->self->priv->supports_dialing_to_ringing) {
/* If ringing to active supported, set it ringing */
if (ctx->self->priv->supports_ringing_to_active)
mm_base_call_change_state (ctx->self, MM_CALL_STATE_RINGING_OUT, MM_CALL_STATE_REASON_OUTGOING_STARTED);
else
/* Otherwise, active right away */
mm_base_call_change_state (ctx->self, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_OUTGOING_STARTED);
}
mm_gdbus_call_complete_start (MM_GDBUS_CALL (ctx->self), ctx->invocation);
handle_start_context_free (ctx);
}
static void
start_setup_audio_channel_ready (MMBaseCall *self,
GAsyncResult *res,
HandleStartContext *ctx)
{
MMPort *audio_port = NULL;
MMCallAudioFormat *audio_format = NULL;
GError *error = NULL;
if (!MM_BASE_CALL_GET_CLASS (self)->setup_audio_channel_finish (self, res, &audio_port, &audio_format, &error)) {
mm_warn ("Couldn't setup audio channel: '%s'", error->message);
mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_AUDIO_SETUP_FAILED);
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_start_context_free (ctx);
return;
}
if (audio_port || audio_format) {
update_audio_settings (self, audio_port, audio_format);
g_clear_object (&audio_port);
g_clear_object (&audio_format);
}
call_started (ctx);
}
static void
handle_start_ready (MMBaseCall *self,
GAsyncResult *res,
HandleStartContext *ctx) HandleStartContext *ctx)
{ {
GError *error = NULL; GError *error = NULL;
@@ -200,19 +268,21 @@ handle_start_ready (MMBaseCall *self,
else else
mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_UNKNOWN); mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_UNKNOWN);
g_dbus_method_invocation_take_error (ctx->invocation, error); g_dbus_method_invocation_take_error (ctx->invocation, error);
} else { handle_start_context_free (ctx);
/* If dialing to ringing supported, leave it dialing */ return;
if (!self->priv->supports_dialing_to_ringing) {
/* If ringing to active supported, set it ringing */
if (self->priv->supports_ringing_to_active)
mm_base_call_change_state (self, MM_CALL_STATE_RINGING_OUT, MM_CALL_STATE_REASON_OUTGOING_STARTED);
else
/* Otherwise, active right away */
mm_base_call_change_state (self, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_OUTGOING_STARTED);
}
mm_gdbus_call_complete_start (MM_GDBUS_CALL (ctx->self), ctx->invocation);
} }
handle_start_context_free (ctx);
/* If there is an audio setup method, run it now */
if (MM_BASE_CALL_GET_CLASS (self)->setup_audio_channel) {
mm_info ("setting up audio channel...");
MM_BASE_CALL_GET_CLASS (self)->setup_audio_channel (self,
(GAsyncReadyCallback) start_setup_audio_channel_ready,
ctx);
return;
}
/* Otherwise, we're done */
call_started (ctx);
} }
static void static void
@@ -300,6 +370,46 @@ handle_accept_context_free (HandleAcceptContext *ctx)
g_free (ctx); g_free (ctx);
} }
static void
call_accepted (HandleAcceptContext *ctx)
{
mm_info ("call is accepted");
if (ctx->self->priv->incoming_timeout) {
g_source_remove (ctx->self->priv->incoming_timeout);
ctx->self->priv->incoming_timeout = 0;
}
mm_base_call_change_state (ctx->self, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_ACCEPTED);
mm_gdbus_call_complete_accept (MM_GDBUS_CALL (ctx->self), ctx->invocation);
handle_accept_context_free (ctx);
}
static void
accept_setup_audio_channel_ready (MMBaseCall *self,
GAsyncResult *res,
HandleAcceptContext *ctx)
{
MMPort *audio_port = NULL;
MMCallAudioFormat *audio_format = NULL;
GError *error = NULL;
if (!MM_BASE_CALL_GET_CLASS (self)->setup_audio_channel_finish (self, res, &audio_port, &audio_format, &error)) {
mm_warn ("Couldn't setup audio channel: '%s'", error->message);
mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_AUDIO_SETUP_FAILED);
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_accept_context_free (ctx);
return;
}
if (audio_port || audio_format) {
update_audio_settings (self, audio_port, audio_format);
g_clear_object (&audio_port);
g_clear_object (&audio_format);
}
call_accepted (ctx);
}
static void static void
handle_accept_ready (MMBaseCall *self, handle_accept_ready (MMBaseCall *self,
GAsyncResult *res, GAsyncResult *res,
@@ -310,15 +420,21 @@ handle_accept_ready (MMBaseCall *self,
if (!MM_BASE_CALL_GET_CLASS (self)->accept_finish (self, res, &error)) { if (!MM_BASE_CALL_GET_CLASS (self)->accept_finish (self, res, &error)) {
mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_ERROR); mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_ERROR);
g_dbus_method_invocation_take_error (ctx->invocation, error); g_dbus_method_invocation_take_error (ctx->invocation, error);
} else { handle_accept_context_free (ctx);
if (ctx->self->priv->incoming_timeout) { return;
g_source_remove (ctx->self->priv->incoming_timeout);
ctx->self->priv->incoming_timeout = 0;
}
mm_base_call_change_state (self, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_ACCEPTED);
mm_gdbus_call_complete_accept (MM_GDBUS_CALL (ctx->self), ctx->invocation);
} }
handle_accept_context_free (ctx);
/* If there is an audio setup method, run it now */
if (MM_BASE_CALL_GET_CLASS (self)->setup_audio_channel) {
mm_info ("setting up audio channel...");
MM_BASE_CALL_GET_CLASS (self)->setup_audio_channel (self,
(GAsyncReadyCallback) accept_setup_audio_channel_ready,
ctx);
return;
}
/* Otherwise, we're done */
call_accepted (ctx);
} }
static void static void
@@ -677,6 +793,18 @@ mm_base_call_get_path (MMBaseCall *self)
state == MM_CALL_STATE_RINGING_OUT || \ state == MM_CALL_STATE_RINGING_OUT || \
state == MM_CALL_STATE_ACTIVE) state == MM_CALL_STATE_ACTIVE)
static void
cleanup_audio_channel_ready (MMBaseCall *self,
GAsyncResult *res)
{
GError *error = NULL;
if (!MM_BASE_CALL_GET_CLASS (self)->cleanup_audio_channel_finish (self, res, &error)) {
mm_warn ("audio channel cleanup failed: %s", error->message);
g_error_free (error);
}
}
void void
mm_base_call_change_state (MMBaseCall *self, mm_base_call_change_state (MMBaseCall *self,
MMCallState new_state, MMCallState new_state,
@@ -710,6 +838,13 @@ mm_base_call_change_state (MMBaseCall *self,
mm_warn ("Couldn't cleanup in-call unsolicited events: %s", error->message); mm_warn ("Couldn't cleanup in-call unsolicited events: %s", error->message);
g_error_free (error); g_error_free (error);
} }
if (MM_BASE_CALL_GET_CLASS (self)->cleanup_audio_channel) {
mm_info ("cleaning up audio channel...");
update_audio_settings (self, NULL, NULL);
MM_BASE_CALL_GET_CLASS (self)->cleanup_audio_channel (self,
(GAsyncReadyCallback) cleanup_audio_channel_ready,
NULL);
}
} }
mm_gdbus_call_set_state (MM_GDBUS_CALL (self), new_state); mm_gdbus_call_set_state (MM_GDBUS_CALL (self), new_state);
@@ -724,21 +859,6 @@ mm_base_call_received_dtmf (MMBaseCall *self,
mm_gdbus_call_emit_dtmf_received (MM_GDBUS_CALL (self), dtmf); mm_gdbus_call_emit_dtmf_received (MM_GDBUS_CALL (self), dtmf);
} }
void
mm_base_call_set_audio_port (MMBaseCall *self, const gchar *port)
{
mm_gdbus_call_set_audio_port (MM_GDBUS_CALL (self), port);
}
void
mm_base_call_set_audio_format (MMBaseCall *self,
MMCallAudioFormat *audio_format)
{
mm_gdbus_call_set_audio_format (
MM_GDBUS_CALL (self),
mm_call_audio_format_get_dictionary (audio_format));
}
/*****************************************************************************/ /*****************************************************************************/
/* Start the CALL */ /* Start the CALL */
@@ -1067,6 +1187,8 @@ dispose (GObject *object)
{ {
MMBaseCall *self = MM_BASE_CALL (object); MMBaseCall *self = MM_BASE_CALL (object);
g_clear_object (&self->priv->audio_port);
if (self->priv->incoming_timeout) { if (self->priv->incoming_timeout) {
g_source_remove (self->priv->incoming_timeout); g_source_remove (self->priv->incoming_timeout);
self->priv->incoming_timeout = 0; self->priv->incoming_timeout = 0;

View File

@@ -88,6 +88,22 @@ struct _MMBaseCallClass {
GError **error); GError **error);
gboolean (* cleanup_unsolicited_events) (MMBaseCall *self, gboolean (* cleanup_unsolicited_events) (MMBaseCall *self,
GError **error); GError **error);
/* Setup/cleanup audio channel */
void (* setup_audio_channel) (MMBaseCall *self,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean (* setup_audio_channel_finish) (MMBaseCall *self,
GAsyncResult *res,
MMPort **audio_port,
MMCallAudioFormat **audio_format,
GError **error);
void (* cleanup_audio_channel) (MMBaseCall *self,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean (* cleanup_audio_channel_finish) (MMBaseCall *self,
GAsyncResult *res,
GError **error);
}; };
GType mm_base_call_get_type (void); GType mm_base_call_get_type (void);
@@ -105,11 +121,6 @@ void mm_base_call_change_state (MMBaseCall *self,
MMCallState new_state, MMCallState new_state,
MMCallStateReason reason); MMCallStateReason reason);
void mm_base_call_set_audio_port (MMBaseCall *self,
const gchar *port);
void mm_base_call_set_audio_format (MMBaseCall *self,
MMCallAudioFormat *audio_format);
void mm_base_call_received_dtmf (MMBaseCall *self, void mm_base_call_received_dtmf (MMBaseCall *self,
const gchar *dtmf); const gchar *dtmf);