iface-modem-simple: allow single ongoing Connect() attempt

We only allow one single Simple.Connect() attempt at a time. If more
than one arrives, the second and next ones will be forbidden.

The ongoing connection attempt can now always be cancelled via
Simple.Disconnect(), not only *after* the bearer object connection has
started. i.e. if Simple.Disconnect() is called e.g. while waiting to
be registered, we would also be cancelled properly.
This commit is contained in:
Aleksander Morgado
2019-10-08 15:41:04 +02:00
parent f228d0dd6f
commit d555c4c45c

View File

@@ -28,6 +28,40 @@
#include "mm-iface-modem-simple.h" #include "mm-iface-modem-simple.h"
#include "mm-log.h" #include "mm-log.h"
/*****************************************************************************/
/* Private data context */
#define PRIVATE_TAG "iface-modem-simple-private-tag"
static GQuark private_quark;
typedef struct {
GCancellable *ongoing_connect;
} Private;
static void
private_free (Private *priv)
{
g_assert (!priv->ongoing_connect);
g_slice_free (Private, priv);
}
static Private *
get_private (MMIfaceModemSimple *self)
{
Private *priv;
if (G_UNLIKELY (!private_quark))
private_quark = g_quark_from_static_string (PRIVATE_TAG);
priv = g_object_get_qdata (G_OBJECT (self), private_quark);
if (!priv) {
priv = g_slice_new0 (Private);
g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
}
return priv;
}
/*****************************************************************************/ /*****************************************************************************/
/* Register in either a CDMA or a 3GPP network (or both) */ /* Register in either a CDMA or a 3GPP network (or both) */
@@ -198,16 +232,18 @@ typedef struct {
/* Results to set */ /* Results to set */
MMBaseBearer *bearer; MMBaseBearer *bearer;
/* Cancellation */
GCancellable *cancellable;
} ConnectionContext; } ConnectionContext;
static void static void
connection_context_free (ConnectionContext *ctx) connection_context_free (ConnectionContext *ctx)
{ {
g_clear_object (&ctx->cancellable);
g_clear_object (&ctx->properties);
g_clear_object (&ctx->bearer);
g_variant_unref (ctx->dictionary); g_variant_unref (ctx->dictionary);
if (ctx->properties)
g_object_unref (ctx->properties);
if (ctx->bearer)
g_object_unref (ctx->bearer);
g_object_unref (ctx->skeleton); g_object_unref (ctx->skeleton);
g_object_unref (ctx->invocation); g_object_unref (ctx->invocation);
g_object_unref (ctx->self); g_object_unref (ctx->self);
@@ -436,9 +472,58 @@ bearer_list_find_disconnected (MMBaseBearer *bearer,
ctx->found = g_object_ref (bearer); ctx->found = g_object_ref (bearer);
} }
static gboolean
completed_if_cancelled (ConnectionContext *ctx)
{
/* Do nothing if not cancelled */
if (!g_cancellable_is_cancelled (ctx->cancellable))
return FALSE;
/* Otherwise cancellable is valid and it's cancelled */
g_dbus_method_invocation_return_error (
ctx->invocation, G_IO_ERROR, G_IO_ERROR_CANCELLED,
"Connection attempt cancelled");
connection_context_free (ctx);
return TRUE;
}
static void
cleanup_cancellation (ConnectionContext *ctx)
{
Private *priv;
priv = get_private (ctx->self);
g_clear_object (&priv->ongoing_connect);
g_clear_object (&ctx->cancellable);
}
static gboolean
setup_cancellation (ConnectionContext *ctx,
GError **error)
{
Private *priv;
/* Only one connection attempt by Simple.Connect() may be handled at a time */
priv = get_private (ctx->self);
if (priv->ongoing_connect) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS,
"Connection request forbidden: operation already in progress");
return FALSE;
}
g_assert (!ctx->cancellable);
ctx->cancellable = g_cancellable_new ();
priv->ongoing_connect = g_object_ref (ctx->cancellable);
return TRUE;
}
static void static void
connection_step (ConnectionContext *ctx) connection_step (ConnectionContext *ctx)
{ {
/* Early abort if operation is cancelled */
if (completed_if_cancelled (ctx))
return;
switch (ctx->step) { switch (ctx->step) {
case CONNECTION_STEP_FIRST: case CONNECTION_STEP_FIRST:
/* Fall down to next step */ /* Fall down to next step */
@@ -588,6 +673,10 @@ connection_step (ConnectionContext *ctx)
mm_info ("Simple connect state (%d/%d): Connect", mm_info ("Simple connect state (%d/%d): Connect",
ctx->step, CONNECTION_STEP_LAST); ctx->step, CONNECTION_STEP_LAST);
/* At this point, we can cleanup the cancellation point in the Simple interface,
* because the bearer connection has its own cancellation setup. */
cleanup_cancellation (ctx);
/* Wait... if we're already using an existing bearer, we need to check if it is /* Wait... if we're already using an existing bearer, we need to check if it is
* already connected; and if so, just don't do anything else */ * already connected; and if so, just don't do anything else */
if (mm_base_bearer_get_status (ctx->bearer) != MM_BEARER_STATUS_CONNECTED) { if (mm_base_bearer_get_status (ctx->bearer) != MM_BEARER_STATUS_CONNECTED) {
@@ -639,6 +728,12 @@ connect_auth_ready (MMBaseModem *self,
return; return;
} }
if (!setup_cancellation (ctx, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
connection_context_free (ctx);
return;
}
/* We may be able to skip some steps, so check that before doing anything */ /* We may be able to skip some steps, so check that before doing anything */
g_object_get (self, g_object_get (self,
MM_IFACE_MODEM_STATE, &current, MM_IFACE_MODEM_STATE, &current,
@@ -826,6 +921,7 @@ disconnect_auth_ready (MMBaseModem *self,
{ {
GError *error = NULL; GError *error = NULL;
MMBearerList *list = NULL; MMBearerList *list = NULL;
Private *priv;
if (!mm_base_modem_authorize_finish (self, res, &error)) { if (!mm_base_modem_authorize_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error); g_dbus_method_invocation_take_error (ctx->invocation, error);
@@ -833,6 +929,14 @@ disconnect_auth_ready (MMBaseModem *self,
return; return;
} }
/* If not disconnecting a specific bearer, also cancel any ongoing
* connection attempt. */
priv = get_private (MM_IFACE_MODEM_SIMPLE (self));
if (!ctx->bearer_path && priv->ongoing_connect) {
g_cancellable_cancel (priv->ongoing_connect);
g_clear_object (&priv->ongoing_connect);
}
g_object_get (self, g_object_get (self,
MM_IFACE_MODEM_BEARER_LIST, &list, MM_IFACE_MODEM_BEARER_LIST, &list,
NULL); NULL);