bearer: defer forced disconnection when modem unregistered while connected

If the bearer is connected and we get a notification of being unregistered, wait
up to 15s to force the disconnection, in case we can recover the registration
in the meantime.

https://bugzilla.gnome.org/show_bug.cgi?id=699803
This commit is contained in:
Aleksander Morgado
2013-05-09 10:50:31 +02:00
parent e18e168682
commit 2a93ca9281

View File

@@ -40,6 +40,8 @@
/* We require up to 20s to get a proper IP when using PPP */ /* We require up to 20s to get a proper IP when using PPP */
#define MM_BEARER_IP_TIMEOUT_DEFAULT 20 #define MM_BEARER_IP_TIMEOUT_DEFAULT 20
#define MM_BEARER_DEFERRED_UNREGISTRATION_TIMEOUT 15
G_DEFINE_TYPE (MMBearer, mm_bearer, MM_GDBUS_TYPE_BEARER_SKELETON); G_DEFINE_TYPE (MMBearer, mm_bearer, MM_GDBUS_TYPE_BEARER_SKELETON);
@@ -83,12 +85,14 @@ struct _MMBearerPrivate {
gulong disconnect_signal_handler; gulong disconnect_signal_handler;
/*-- 3GPP specific --*/ /*-- 3GPP specific --*/
guint deferred_3gpp_unregistration_id;
/* Reason if 3GPP connection is forbidden */ /* Reason if 3GPP connection is forbidden */
ConnectionForbiddenReason reason_3gpp; ConnectionForbiddenReason reason_3gpp;
/* Handler ID for the registration state change signals */ /* Handler ID for the registration state change signals */
guint id_3gpp_registration_change; guint id_3gpp_registration_change;
/*-- CDMA specific --*/ /*-- CDMA specific --*/
guint deferred_cdma_unregistration_id;
/* Reason if CDMA connection is forbidden */ /* Reason if CDMA connection is forbidden */
ConnectionForbiddenReason reason_cdma; ConnectionForbiddenReason reason_cdma;
/* Handler IDs for the registration state change signals */ /* Handler IDs for the registration state change signals */
@@ -175,6 +179,31 @@ bearer_update_status_connected (MMBearer *self,
/*****************************************************************************/ /*****************************************************************************/
static void
reset_deferred_unregistration (MMBearer *self)
{
if (self->priv->deferred_cdma_unregistration_id) {
g_source_remove (self->priv->deferred_cdma_unregistration_id);
self->priv->deferred_cdma_unregistration_id = 0;
}
if (self->priv->deferred_3gpp_unregistration_id) {
g_source_remove (self->priv->deferred_3gpp_unregistration_id);
self->priv->deferred_3gpp_unregistration_id = 0;
}
}
static gboolean
deferred_3gpp_unregistration_cb (MMBearer *self)
{
g_warn_if_fail (self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_UNREGISTERED);
self->priv->deferred_3gpp_unregistration_id = 0;
mm_dbg ("Forcing bearer disconnection, not registered in 3GPP network");
mm_bearer_disconnect_force (MM_BEARER (self));
return FALSE;
}
static void static void
modem_3gpp_registration_state_changed (MMIfaceModem3gpp *modem, modem_3gpp_registration_state_changed (MMIfaceModem3gpp *modem,
GParamSpec *pspec, GParamSpec *pspec,
@@ -204,29 +233,57 @@ modem_3gpp_registration_state_changed (MMIfaceModem3gpp *modem,
break; break;
} }
/* close connection if some reason found */ /* If no reason to disconnect, or if it's a mixed CDMA+LTE modem without a CDMA reason,
* just don't do anything. */
if (self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_NONE) if (self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_NONE ||
(mm_iface_modem_is_cdma (MM_IFACE_MODEM (modem)) &&
self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_NONE)) {
reset_deferred_unregistration (self);
return; return;
/* For mixed CDMA+LTE modems, don't close the connection if there is no
* CDMA reason to do so. */
if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (modem)) &&
self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_NONE)
return;
switch (self->priv->reason_3gpp) {
case CONNECTION_FORBIDDEN_REASON_UNREGISTERED:
mm_dbg ("Bearer not allowed to connect, not registered in 3GPP network");
break;
case CONNECTION_FORBIDDEN_REASON_ROAMING:
mm_dbg ("Bearer not allowed to connect, registered in roaming 3GPP network");
break;
default:
break;
} }
/* Modem is roaming and roaming not allowed, report right away */
if (self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_ROAMING) {
mm_dbg ("Bearer not allowed to connect, registered in roaming 3GPP network");
reset_deferred_unregistration (self);
mm_bearer_disconnect_force (MM_BEARER (self)); mm_bearer_disconnect_force (MM_BEARER (self));
return;
}
/* Modem reports being unregistered */
if (self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_UNREGISTERED) {
/* If there is already a notification pending, just return */
if (self->priv->deferred_3gpp_unregistration_id)
return;
/* If the bearer is not connected, report right away */
if (self->priv->status != MM_BEARER_STATUS_CONNECTED) {
mm_dbg ("Bearer not allowed to connect, not registered in 3GPP network");
mm_bearer_disconnect_force (MM_BEARER (self));
return;
}
/* Otherwise, setup the new timeout */
mm_dbg ("Connected bearer not registered in 3GPP network");
self->priv->deferred_3gpp_unregistration_id =
g_timeout_add_seconds (MM_BEARER_DEFERRED_UNREGISTRATION_TIMEOUT,
(GSourceFunc) deferred_3gpp_unregistration_cb,
self);
return;
}
g_assert_not_reached ();
}
static gboolean
deferred_cdma_unregistration_cb (MMBearer *self)
{
g_warn_if_fail (self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_UNREGISTERED);
self->priv->deferred_cdma_unregistration_id = 0;
mm_dbg ("Forcing bearer disconnection, not registered in CDMA network");
mm_bearer_disconnect_force (MM_BEARER (self));
return FALSE;
} }
static void static void
@@ -255,29 +312,46 @@ modem_cdma_registration_state_changed (MMIfaceModemCdma *modem,
self->priv->reason_cdma = CONNECTION_FORBIDDEN_REASON_UNREGISTERED; self->priv->reason_cdma = CONNECTION_FORBIDDEN_REASON_UNREGISTERED;
} }
/* close connection if some reason found */ /* If no reason to disconnect, or if it's a mixed CDMA+LTE modem without a 3GPP reason,
* just don't do anything. */
if (self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_NONE) if (self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_NONE ||
(mm_iface_modem_is_3gpp (MM_IFACE_MODEM (modem)) &&
self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_NONE)) {
reset_deferred_unregistration (self);
return; return;
/* For mixed CDMA+LTE modems, don't close the connection if there is no
* 3GPP reason to do so. */
if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (modem)) &&
self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_NONE)
return;
switch (self->priv->reason_cdma) {
case CONNECTION_FORBIDDEN_REASON_UNREGISTERED:
mm_dbg ("Bearer not allowed to connect, not registered in CDMA network");
break;
case CONNECTION_FORBIDDEN_REASON_ROAMING:
mm_dbg ("Bearer not allowed to connect, registered in roaming CDMA network");
break;
default:
break;
} }
/* Modem is roaming and roaming not allowed, report right away */
if (self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_ROAMING) {
mm_dbg ("Bearer not allowed to connect, registered in roaming CDMA network");
reset_deferred_unregistration (self);
mm_bearer_disconnect_force (MM_BEARER (self)); mm_bearer_disconnect_force (MM_BEARER (self));
return;
}
/* Modem reports being unregistered */
if (self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_UNREGISTERED) {
/* If there is already a notification pending, just return */
if (self->priv->deferred_cdma_unregistration_id)
return;
/* If the bearer is not connected, report right away */
if (self->priv->status != MM_BEARER_STATUS_CONNECTED) {
mm_dbg ("Bearer not allowed to connect, not registered in CDMA network");
mm_bearer_disconnect_force (MM_BEARER (self));
return;
}
/* Otherwise, setup the new timeout */
mm_dbg ("Connected bearer not registered in CDMA network");
self->priv->deferred_cdma_unregistration_id =
g_timeout_add_seconds (MM_BEARER_DEFERRED_UNREGISTRATION_TIMEOUT,
(GSourceFunc) deferred_cdma_unregistration_cb,
self);
return;
}
g_assert_not_reached ();
} }
static void static void
@@ -1065,6 +1139,7 @@ dispose (GObject *object)
} }
reset_signal_handlers (self); reset_signal_handlers (self);
reset_deferred_unregistration (self);
g_clear_object (&self->priv->modem); g_clear_object (&self->priv->modem);
g_clear_object (&self->priv->config); g_clear_object (&self->priv->config);