diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2019-09-24 13:09:05 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2019-10-17 08:31:46 +0000 |
commit | 6347a7d783d51ac1ff619bba90eed0675d583c9e (patch) | |
tree | 7648f7b880147294bbbefa1cf83057703189fac3 | |
parent | eaf665475014878772cec24e042130ae207c7ef7 (diff) |
base-call: catch terminated errors before ATD replies
If we get one of the in-call termination errors before ATD replies OK,
we need to cancel the start() operation right away and return an
error to the caller.
Otherwise, the Call will be reported as "terminated" right away, but
the caller would not get an error back until 90s later:
$ sudo mmcli --call 0 --start
error: couldn't start the call: 'GDBus.Error:org.freedesktop.ModemManager1.Error.Serial.ResponseTimeout: Serial command timed out'
-rw-r--r-- | src/mm-base-call.c | 42 | ||||
-rw-r--r-- | src/mm-base-call.h | 1 |
2 files changed, 37 insertions, 6 deletions
diff --git a/src/mm-base-call.c b/src/mm-base-call.c index 0d09a09d..ce41112e 100644 --- a/src/mm-base-call.c +++ b/src/mm-base-call.c @@ -34,6 +34,7 @@ #include "mm-base-modem.h" #include "mm-log.h" #include "mm-modem-helpers.h" +#include "mm-error-helpers.h" G_DEFINE_TYPE (MMBaseCall, mm_base_call, MM_GDBUS_TYPE_CALL_SKELETON) @@ -69,6 +70,11 @@ struct _MMBaseCallPrivate { /* Ongoing call index */ guint index; + + /* Start cancellable, used when the call state transition to + * 'terminated' is coming asynchronously (e.g. via in-call state + * update notifications) */ + GCancellable *start_cancellable; }; /*****************************************************************************/ @@ -148,8 +154,19 @@ handle_start_ready (MMBaseCall *self, { GError *error = NULL; + g_clear_object (&ctx->self->priv->start_cancellable); + if (!MM_BASE_CALL_GET_CLASS (self)->start_finish (self, res, &error)) { mm_warn ("Couldn't start call : '%s'", error->message); + + /* When cancelled via the start cancellable, it's because we got an early in-call error + * before the call attempt was reported as started. */ + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) || + g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED)) { + g_clear_error (&error); + error = mm_connection_error_for_code (MM_CONNECTION_ERROR_NO_DIALTONE); + } + /* Convert errors into call state updates */ if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_DIALTONE)) mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_ERROR); @@ -159,6 +176,7 @@ handle_start_ready (MMBaseCall *self, mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_REFUSED_OR_BUSY); else mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_UNKNOWN); + g_dbus_method_invocation_take_error (ctx->invocation, error); handle_start_context_free (ctx); return; @@ -220,7 +238,12 @@ handle_start_auth_ready (MMBaseModem *modem, mm_base_call_change_state (ctx->self, MM_CALL_STATE_DIALING, MM_CALL_STATE_REASON_OUTGOING_STARTED); + /* Setup start cancellable to get notified of termination asynchronously */ + g_assert (!ctx->self->priv->start_cancellable); + ctx->self->priv->start_cancellable = g_cancellable_new (); + MM_BASE_CALL_GET_CLASS (ctx->self)->start (ctx->self, + ctx->self->priv->start_cancellable, (GAsyncReadyCallback)handle_start_ready, ctx); } @@ -970,6 +993,8 @@ mm_base_call_change_state (MMBaseCall *self, g_source_remove (self->priv->incoming_timeout); self->priv->incoming_timeout = 0; } + /* cancel start if ongoing */ + g_cancellable_cancel (self->priv->start_cancellable); } mm_gdbus_call_set_state (MM_GDBUS_CALL (self), new_state); @@ -1021,6 +1046,7 @@ call_start_ready (MMBaseModem *modem, static void call_start (MMBaseCall *self, + GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { @@ -1030,12 +1056,15 @@ call_start (MMBaseCall *self, task = g_task_new (self, NULL, callback, user_data); cmd = g_strdup_printf ("ATD%s;", mm_gdbus_call_get_number (MM_GDBUS_CALL (self))); - mm_base_modem_at_command (self->priv->modem, - cmd, - 90, - FALSE, - (GAsyncReadyCallback)call_start_ready, - task); + mm_base_modem_at_command_full (self->priv->modem, + mm_base_modem_peek_port_primary (self->priv->modem), + cmd, + 90, + FALSE, /* no cached */ + FALSE, /* no raw */ + cancellable, + (GAsyncReadyCallback)call_start_ready, + task); g_free (cmd); } @@ -1400,6 +1429,7 @@ finalize (GObject *object) { MMBaseCall *self = MM_BASE_CALL (object); + g_assert (!self->priv->start_cancellable); g_free (self->priv->path); G_OBJECT_CLASS (mm_base_call_parent_class)->finalize (object); diff --git a/src/mm-base-call.h b/src/mm-base-call.h index 973bcafa..8d6944f5 100644 --- a/src/mm-base-call.h +++ b/src/mm-base-call.h @@ -54,6 +54,7 @@ struct _MMBaseCallClass { /* Start the call */ void (* start) (MMBaseCall *self, + GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean (* start_finish) (MMBaseCall *self, |