aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@aleksander.es>2019-09-24 13:09:05 +0200
committerAleksander Morgado <aleksander@aleksander.es>2019-10-17 08:31:46 +0000
commit6347a7d783d51ac1ff619bba90eed0675d583c9e (patch)
tree7648f7b880147294bbbefa1cf83057703189fac3
parenteaf665475014878772cec24e042130ae207c7ef7 (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.c42
-rw-r--r--src/mm-base-call.h1
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,