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:
@@ -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, ¤t,
|
MM_IFACE_MODEM_STATE, ¤t,
|
||||||
@@ -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);
|
||||||
|
Reference in New Issue
Block a user