summaryrefslogtreecommitdiff
path: root/src/mm-base-bearer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mm-base-bearer.c')
-rw-r--r--src/mm-base-bearer.c1021
1 files changed, 868 insertions, 153 deletions
diff --git a/src/mm-base-bearer.c b/src/mm-base-bearer.c
index 1b1c7b31..e5990b96 100644
--- a/src/mm-base-bearer.c
+++ b/src/mm-base-bearer.c
@@ -13,7 +13,8 @@
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2011 Red Hat, Inc.
* Copyright (C) 2011 Google, Inc.
- * Copyright (C) 2011 - 2014 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2015 Azimut Electronics
+ * Copyright (C) 2011 - 2016 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <config.h>
@@ -34,20 +35,32 @@
#include "mm-base-bearer.h"
#include "mm-base-modem-at.h"
#include "mm-base-modem.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
+#include "mm-error-helpers.h"
+#include "mm-bearer-stats.h"
/* We require up to 20s to get a proper IP when using PPP */
#define BEARER_IP_TIMEOUT_DEFAULT 20
#define BEARER_DEFERRED_UNREGISTRATION_TIMEOUT 15
-G_DEFINE_TYPE (MMBaseBearer, mm_base_bearer, MM_GDBUS_TYPE_BEARER_SKELETON);
+#define BEARER_STATS_UPDATE_TIMEOUT 30
+
+/* Initial connectivity check after 30s, then each 5s */
+#define BEARER_CONNECTION_MONITOR_INITIAL_TIMEOUT 30
+#define BEARER_CONNECTION_MONITOR_TIMEOUT 5
+
+static void log_object_iface_init (MMLogObjectInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMBaseBearer, mm_base_bearer, MM_GDBUS_TYPE_BEARER_SKELETON, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
typedef enum {
CONNECTION_FORBIDDEN_REASON_NONE,
CONNECTION_FORBIDDEN_REASON_UNREGISTERED,
CONNECTION_FORBIDDEN_REASON_ROAMING,
+ CONNECTION_FORBIDDEN_REASON_EMERGENCY_ONLY,
CONNECTION_FORBIDDEN_REASON_LAST
} ConnectionForbiddenReason;
@@ -58,7 +71,6 @@ enum {
PROP_MODEM,
PROP_STATUS,
PROP_CONFIG,
- PROP_DEFAULT_IP_FAMILY,
PROP_LAST
};
@@ -67,22 +79,30 @@ static GParamSpec *properties[PROP_LAST];
struct _MMBaseBearerPrivate {
/* The connection to the system bus */
GDBusConnection *connection;
+ guint dbus_id;
+
/* The modem which owns this BEARER */
MMBaseModem *modem;
/* The path where the BEARER object is exported */
gchar *path;
/* Status of this bearer */
MMBearerStatus status;
+ /* Whether we must ignore all disconnection updates if they're
+ * detected by ModemManager itself. */
+ gboolean ignore_disconnection_reports;
/* Configuration of the bearer */
MMBearerProperties *config;
- /* Default IP family of this bearer */
- MMBearerIpFamily default_ip_family;
/* Cancellable for connect() */
GCancellable *connect_cancellable;
/* handler id for the disconnect + cancel connect request */
gulong disconnect_signal_handler;
+ /* Connection status monitoring */
+ guint connection_monitor_id;
+ /* Flag to specify whether connection monitoring is supported or not */
+ gboolean load_connection_status_unsupported;
+
/*-- 3GPP specific --*/
guint deferred_3gpp_unregistration_id;
/* Reason if 3GPP connection is forbidden */
@@ -97,6 +117,15 @@ struct _MMBaseBearerPrivate {
/* Handler IDs for the registration state change signals */
guint id_cdma1x_registration_change;
guint id_evdo_registration_change;
+
+ /* The stats object to expose */
+ MMBearerStats *stats;
+ /* Handler id for the stats update timeout */
+ guint stats_update_id;
+ /* Timer to measure the duration of the connection */
+ GTimer *duration_timer;
+ /* Flag to specify whether reloading stats is supported or not */
+ gboolean reload_stats_unsupported;
};
/*****************************************************************************/
@@ -104,7 +133,8 @@ struct _MMBaseBearerPrivate {
static const gchar *connection_forbidden_reason_str [CONNECTION_FORBIDDEN_REASON_LAST] = {
"none",
"Not registered in the network",
- "Registered in roaming network, and roaming not allowed"
+ "Registered in roaming network, and roaming not allowed",
+ "Emergency services only",
};
/*****************************************************************************/
@@ -112,10 +142,9 @@ static const gchar *connection_forbidden_reason_str [CONNECTION_FORBIDDEN_REASON
void
mm_base_bearer_export (MMBaseBearer *self)
{
- static guint id = 0;
gchar *path;
- path = g_strdup_printf (MM_DBUS_BEARER_PREFIX "/%d", id++);
+ path = g_strdup_printf (MM_DBUS_BEARER_PREFIX "/%d", self->priv->dbus_id);
g_object_set (self,
MM_BASE_BEARER_PATH, path,
NULL);
@@ -125,8 +154,345 @@ mm_base_bearer_export (MMBaseBearer *self)
/*****************************************************************************/
static void
+connection_monitor_stop (MMBaseBearer *self)
+{
+ if (self->priv->connection_monitor_id) {
+ g_source_remove (self->priv->connection_monitor_id);
+ self->priv->connection_monitor_id = 0;
+ }
+}
+
+static void
+load_connection_status_ready (MMBaseBearer *self,
+ GAsyncResult *res)
+{
+ GError *error = NULL;
+ MMBearerConnectionStatus status;
+
+ status = MM_BASE_BEARER_GET_CLASS (self)->load_connection_status_finish (self, res, &error);
+ if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) {
+ /* Only warn if not reporting an "unsupported" error */
+ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
+ mm_obj_warn (self, "checking if connected failed: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ /* If we're being told that connection monitoring is unsupported, just
+ * ignore the error and remove the timeout. */
+ mm_obj_dbg (self, "connection monitoring is unsupported by the device");
+ self->priv->load_connection_status_unsupported = TRUE;
+ connection_monitor_stop (self);
+ g_error_free (error);
+ return;
+ }
+
+ /* Report connection or disconnection */
+ g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED || status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+ mm_obj_dbg (self, "connection status loaded: %s", mm_bearer_connection_status_get_string (status));
+ mm_base_bearer_report_connection_status (self, status);
+}
+
+static gboolean
+connection_monitor_cb (MMBaseBearer *self)
+{
+ /* If the implementation knows how to load connection status, run it */
+ if (self->priv->status == MM_BEARER_STATUS_CONNECTED)
+ MM_BASE_BEARER_GET_CLASS (self)->load_connection_status (
+ self,
+ (GAsyncReadyCallback)load_connection_status_ready,
+ NULL);
+ return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+initial_connection_monitor_cb (MMBaseBearer *self)
+{
+ if (self->priv->status == MM_BEARER_STATUS_CONNECTED)
+ MM_BASE_BEARER_GET_CLASS (self)->load_connection_status (
+ self,
+ (GAsyncReadyCallback)load_connection_status_ready,
+ NULL);
+
+ /* Add new monitor timeout at a higher rate */
+ self->priv->connection_monitor_id = g_timeout_add_seconds (BEARER_CONNECTION_MONITOR_TIMEOUT,
+ (GSourceFunc) connection_monitor_cb,
+ self);
+
+ /* Remove the initial connection monitor timeout as we added a new one */
+ return G_SOURCE_REMOVE;
+}
+
+static void
+connection_monitor_start (MMBaseBearer *self)
+{
+ /* If not implemented, don't schedule anything */
+ if (!MM_BASE_BEARER_GET_CLASS (self)->load_connection_status ||
+ !MM_BASE_BEARER_GET_CLASS (self)->load_connection_status_finish)
+ return;
+
+ if (self->priv->load_connection_status_unsupported)
+ return;
+
+ /* Schedule initial check */
+ g_assert (!self->priv->connection_monitor_id);
+ self->priv->connection_monitor_id = g_timeout_add_seconds (BEARER_CONNECTION_MONITOR_INITIAL_TIMEOUT,
+ (GSourceFunc) initial_connection_monitor_cb,
+ self);
+}
+
+/*****************************************************************************/
+
+static void
+bearer_update_connection_error (MMBaseBearer *self,
+ const GError *connection_error)
+{
+ g_autoptr(GVariant) tuple = NULL;
+
+ if (connection_error) {
+ /* Never overwrite a connection error if it's already set */
+ tuple = mm_gdbus_bearer_dup_connection_error (MM_GDBUS_BEARER (self));
+ if (tuple)
+ return;
+
+ /*
+ * Limit the type of errors we can expose in the interface;
+ * e.g. we don't want QMI or MBIM specific errors reported.
+ *
+ * G_IO_ERROR_CANCELLED is an exception, because we map it to
+ * MM_CORE_ERROR_CANCELLED implicitly when building the DBus error name.
+ */
+ if ((connection_error->domain != MM_CORE_ERROR) &&
+ (connection_error->domain != MM_MOBILE_EQUIPMENT_ERROR) &&
+ (connection_error->domain != MM_CONNECTION_ERROR) &&
+ (connection_error->domain != MM_SERIAL_ERROR) &&
+ (connection_error->domain != MM_CDMA_ACTIVATION_ERROR) &&
+ (!g_error_matches (connection_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))) {
+ g_autoptr(GError) default_connection_error = NULL;
+
+#if defined WITH_QMI
+ if (connection_error->domain == QMI_CORE_ERROR)
+ mm_obj_dbg (self, "cannot set QMI core error as connection error: %s", connection_error->message);
+ else if (connection_error->domain == QMI_PROTOCOL_ERROR)
+ mm_obj_dbg (self, "cannot set QMI protocol error as connection error: %s", connection_error->message);
+ else
+#endif
+#if defined WITH_MBIM
+ if (connection_error->domain == MBIM_CORE_ERROR)
+ mm_obj_dbg (self, "cannot set MBIM core error as connection error: %s", connection_error->message);
+ else if (connection_error->domain == MBIM_PROTOCOL_ERROR)
+ mm_obj_dbg (self, "cannot set MBIM protocol error as connection error: %s", connection_error->message);
+ else if (connection_error->domain == MBIM_STATUS_ERROR)
+ mm_obj_dbg (self, "cannot set MBIM status error as connection error: %s", connection_error->message);
+ else
+#endif
+ mm_obj_dbg (self, "cannot set unhandled domain error as connection error: %s", connection_error->message);
+
+ default_connection_error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN,
+ "%s", connection_error->message);
+ tuple = mm_common_error_to_tuple (default_connection_error);
+ } else
+ tuple = mm_common_error_to_tuple (connection_error);
+ }
+ mm_gdbus_bearer_set_connection_error (MM_GDBUS_BEARER (self), tuple);
+}
+
+/*****************************************************************************/
+
+static void
+bearer_update_interface_stats (MMBaseBearer *self)
+{
+ mm_gdbus_bearer_set_stats (
+ MM_GDBUS_BEARER (self),
+ mm_bearer_stats_get_dictionary (self->priv->stats));
+}
+
+static void
+bearer_reset_ongoing_interface_stats (MMBaseBearer *self)
+{
+ mm_bearer_stats_set_duration (self->priv->stats, 0);
+ mm_bearer_stats_set_tx_bytes (self->priv->stats, 0);
+ mm_bearer_stats_set_rx_bytes (self->priv->stats, 0);
+ mm_bearer_stats_set_start_date (self->priv->stats, 0);
+ mm_bearer_stats_set_uplink_speed (self->priv->stats, 0);
+ mm_bearer_stats_set_downlink_speed (self->priv->stats, 0);
+ bearer_update_interface_stats (self);
+}
+
+static void
+bearer_set_ongoing_interface_stats (MMBaseBearer *self,
+ guint duration,
+ guint64 rx_bytes,
+ guint64 tx_bytes)
+{
+ guint n_updates = 0;
+
+ /* Make sure we don't reset to 0 these values if we had ever set them
+ * before. Just ignore the update if we're reported 0 */
+
+ if (duration) {
+ gint delta_duration;
+
+ delta_duration = duration - mm_bearer_stats_get_duration (self->priv->stats);
+ if (delta_duration > 0) {
+ mm_bearer_stats_set_duration (self->priv->stats, duration);
+ mm_bearer_stats_set_total_duration (self->priv->stats,
+ mm_bearer_stats_get_total_duration (self->priv->stats) + delta_duration);
+ n_updates++;
+ }
+ }
+
+ if (rx_bytes) {
+ gint64 delta_rx_bytes;
+
+ delta_rx_bytes = rx_bytes - mm_bearer_stats_get_rx_bytes (self->priv->stats);
+ if (delta_rx_bytes > 0) {
+ mm_bearer_stats_set_rx_bytes (self->priv->stats, rx_bytes);
+ mm_bearer_stats_set_total_rx_bytes (self->priv->stats,
+ mm_bearer_stats_get_total_rx_bytes (self->priv->stats) + delta_rx_bytes);
+ n_updates++;
+ }
+ }
+
+ if (tx_bytes) {
+ gint64 delta_tx_bytes;
+
+ delta_tx_bytes = tx_bytes - mm_bearer_stats_get_tx_bytes (self->priv->stats);
+ if (delta_tx_bytes > 0) {
+ mm_bearer_stats_set_tx_bytes (self->priv->stats, tx_bytes);
+ mm_bearer_stats_set_total_tx_bytes (self->priv->stats,
+ mm_bearer_stats_get_total_tx_bytes (self->priv->stats) + delta_tx_bytes);
+ n_updates++;
+ }
+ }
+
+ if (n_updates)
+ bearer_update_interface_stats (self);
+}
+
+static void
+bearer_stats_stop (MMBaseBearer *self)
+{
+ if (self->priv->duration_timer) {
+ bearer_set_ongoing_interface_stats (self,
+ (guint64) g_timer_elapsed (self->priv->duration_timer, NULL),
+ 0,
+ 0);
+ g_timer_destroy (self->priv->duration_timer);
+ self->priv->duration_timer = NULL;
+ }
+
+ if (self->priv->stats_update_id) {
+ g_source_remove (self->priv->stats_update_id);
+ self->priv->stats_update_id = 0;
+ }
+}
+
+static void
+reload_stats_ready (MMBaseBearer *self,
+ GAsyncResult *res)
+{
+ GError *error = NULL;
+ guint64 rx_bytes = 0;
+ guint64 tx_bytes = 0;
+
+ if (!MM_BASE_BEARER_GET_CLASS (self)->reload_stats_finish (self, &rx_bytes, &tx_bytes, res, &error)) {
+ /* If reloading stats fails, warn about it and don't update anything */
+ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
+ mm_obj_warn (self, "reloading stats failed: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ /* If we're being told that reloading stats is unsupported, just ignore
+ * the error and update oly the duration timer. */
+ mm_obj_dbg (self, "reloading stats is unsupported by the device");
+ self->priv->reload_stats_unsupported = TRUE;
+ rx_bytes = 0;
+ tx_bytes = 0;
+ g_error_free (error);
+ }
+
+ /* We only update stats if they were retrieved properly */
+ bearer_set_ongoing_interface_stats (self,
+ (guint32) g_timer_elapsed (self->priv->duration_timer, NULL),
+ rx_bytes,
+ tx_bytes);
+}
+
+static gboolean
+stats_update_cb (MMBaseBearer *self)
+{
+ /* Ignore stats update if we're not connected */
+ if (self->priv->status != MM_BEARER_STATUS_CONNECTED)
+ return G_SOURCE_CONTINUE;
+
+ /* If the implementation knows how to update stat values, run it */
+ if (!self->priv->reload_stats_unsupported &&
+ MM_BASE_BEARER_GET_CLASS (self)->reload_stats &&
+ MM_BASE_BEARER_GET_CLASS (self)->reload_stats_finish) {
+ MM_BASE_BEARER_GET_CLASS (self)->reload_stats (
+ self,
+ (GAsyncReadyCallback)reload_stats_ready,
+ NULL);
+ return G_SOURCE_CONTINUE;
+ }
+
+ /* Otherwise, just update duration and we're done */
+ bearer_set_ongoing_interface_stats (self,
+ (guint32) g_timer_elapsed (self->priv->duration_timer, NULL),
+ 0,
+ 0);
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+bearer_stats_start (MMBaseBearer *self,
+ guint64 uplink_speed,
+ guint64 downlink_speed)
+{
+ /* Start duration timer */
+ g_assert (!self->priv->duration_timer);
+ self->priv->duration_timer = g_timer_new ();
+
+ /* Schedule */
+ g_assert (!self->priv->stats_update_id);
+ self->priv->stats_update_id = g_timeout_add_seconds (BEARER_STATS_UPDATE_TIMEOUT,
+ (GSourceFunc) stats_update_cb,
+ self);
+
+ mm_bearer_stats_set_start_date (self->priv->stats, (guint64)(g_get_real_time() / G_USEC_PER_SEC));
+ mm_bearer_stats_set_uplink_speed (self->priv->stats, uplink_speed);
+ mm_bearer_stats_set_downlink_speed (self->priv->stats, downlink_speed);
+ bearer_update_interface_stats (self);
+
+ /* Load initial values */
+ stats_update_cb (self);
+}
+
+/*****************************************************************************/
+
+void
+mm_base_bearer_report_speeds (MMBaseBearer *self,
+ guint64 uplink_speed,
+ guint64 downlink_speed)
+{
+ /* Ignore speeds update if we're not connected */
+ if (self->priv->status != MM_BEARER_STATUS_CONNECTED)
+ return;
+ mm_bearer_stats_set_uplink_speed (self->priv->stats, uplink_speed);
+ mm_bearer_stats_set_downlink_speed (self->priv->stats, downlink_speed);
+ bearer_update_interface_stats (self);
+}
+
+/*****************************************************************************/
+
+static void
bearer_reset_interface_status (MMBaseBearer *self)
{
+ mm_gdbus_bearer_set_profile_id (MM_GDBUS_BEARER (self), MM_3GPP_PROFILE_ID_UNKNOWN);
+ mm_gdbus_bearer_set_multiplexed (MM_GDBUS_BEARER (self), FALSE);
mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), FALSE);
mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE);
mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), NULL);
@@ -145,22 +511,54 @@ bearer_update_status (MMBaseBearer *self,
/* NOTE: we do allow status 'CONNECTED' here; it may happen if we go into
* DISCONNECTING and we cannot disconnect */
+ /* Do nothing if the status is the same */
+ if (self->priv->status == status)
+ return;
+
/* Update the property value */
self->priv->status = status;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATUS]);
/* Ensure that we don't expose any connection related data in the
* interface when going into disconnected state. */
- if (self->priv->status == MM_BEARER_STATUS_DISCONNECTED)
+ if (self->priv->status == MM_BEARER_STATUS_DISCONNECTED) {
+ g_autoptr(GString) report = NULL;
+
bearer_reset_interface_status (self);
+ /* Cleanup flag to ignore disconnection reports */
+ self->priv->ignore_disconnection_reports = FALSE;
+ /* Stop statistics */
+ bearer_stats_stop (self);
+ /* Stop connection monitoring */
+ connection_monitor_stop (self);
+
+ /* Build and log report */
+ report = g_string_new (NULL);
+ g_string_append_printf (report,
+ "connection #%u finished: duration %us",
+ mm_bearer_stats_get_attempts (self->priv->stats),
+ mm_bearer_stats_get_duration (self->priv->stats));
+ if (!self->priv->reload_stats_unsupported)
+ g_string_append_printf (report,
+ ", tx: %" G_GUINT64_FORMAT " bytes, rx: %" G_GUINT64_FORMAT " bytes",
+ mm_bearer_stats_get_tx_bytes (self->priv->stats),
+ mm_bearer_stats_get_rx_bytes (self->priv->stats));
+ mm_obj_info (self, "%s", report->str);
+ }
}
static void
-bearer_update_status_connected (MMBaseBearer *self,
- const gchar *interface,
+bearer_update_status_connected (MMBaseBearer *self,
+ const gchar *interface,
+ gboolean multiplexed,
+ gint profile_id,
MMBearerIpConfig *ipv4_config,
- MMBearerIpConfig *ipv6_config)
+ MMBearerIpConfig *ipv6_config,
+ guint64 uplink_speed,
+ guint64 downlink_speed)
{
+ mm_gdbus_bearer_set_profile_id (MM_GDBUS_BEARER (self), profile_id);
+ mm_gdbus_bearer_set_multiplexed (MM_GDBUS_BEARER (self), multiplexed);
mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), TRUE);
mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE);
mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), interface);
@@ -171,9 +569,25 @@ bearer_update_status_connected (MMBaseBearer *self,
MM_GDBUS_BEARER (self),
mm_bearer_ip_config_get_dictionary (ipv6_config));
+ /* If PPP is involved in the requested IP config, we must ignore
+ * all disconnection reports found via CGACT? polling or CGEV URCs.
+ * In this case, upper layers should always explicitly disconnect
+ * the bearer when ownership of the TTY is given back to MM. */
+ if ((ipv4_config && mm_bearer_ip_config_get_method (ipv4_config) == MM_BEARER_IP_METHOD_PPP) ||
+ (ipv6_config && mm_bearer_ip_config_get_method (ipv6_config) == MM_BEARER_IP_METHOD_PPP)) {
+ mm_obj_dbg (self, "PPP is required for connection, will ignore disconnection reports");
+ self->priv->ignore_disconnection_reports = TRUE;
+ }
+
/* Update the property value */
self->priv->status = MM_BEARER_STATUS_CONNECTED;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATUS]);
+
+ /* Start statistics */
+ bearer_stats_start (self, uplink_speed, downlink_speed);
+
+ /* Start connection monitor, if supported */
+ connection_monitor_start (self);
}
/*****************************************************************************/
@@ -198,9 +612,9 @@ deferred_3gpp_unregistration_cb (MMBaseBearer *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_obj_dbg (self, "forcing bearer disconnection, not registered in 3GPP network");
mm_base_bearer_disconnect_force (self);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
@@ -221,15 +635,25 @@ modem_3gpp_registration_state_changed (MMIfaceModem3gpp *modem,
self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_UNREGISTERED;
break;
case MM_MODEM_3GPP_REGISTRATION_STATE_HOME:
+ case MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY:
+ case MM_MODEM_3GPP_REGISTRATION_STATE_HOME_CSFB_NOT_PREFERRED:
case MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING:
+ case MM_MODEM_3GPP_REGISTRATION_STATE_ATTACHED_RLOS:
self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_NONE;
break;
case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING:
+ case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_SMS_ONLY:
+ case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_CSFB_NOT_PREFERRED:
if (mm_bearer_properties_get_allow_roaming (mm_base_bearer_peek_config (self)))
self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_NONE;
else
self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_ROAMING;
break;
+ case MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY:
+ self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_EMERGENCY_ONLY;
+ break;
+ default:
+ g_assert_not_reached ();
}
/* If no reason to disconnect, or if it's a mixed CDMA+LTE modem without a CDMA reason,
@@ -243,7 +667,15 @@ modem_3gpp_registration_state_changed (MMIfaceModem3gpp *modem,
/* 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");
+ mm_obj_dbg (self, "bearer not allowed to connect, registered in roaming 3GPP network");
+ reset_deferred_unregistration (self);
+ mm_base_bearer_disconnect_force (self);
+ return;
+ }
+
+ /* Modem is registered under emergency services only? */
+ if (self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_EMERGENCY_ONLY) {
+ mm_obj_dbg (self, "bearer not allowed to connect, emergency services only");
reset_deferred_unregistration (self);
mm_base_bearer_disconnect_force (self);
return;
@@ -257,13 +689,13 @@ modem_3gpp_registration_state_changed (MMIfaceModem3gpp *modem,
/* 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_obj_dbg (self, "bearer not allowed to connect, not registered in 3GPP network");
mm_base_bearer_disconnect_force (self);
return;
}
/* Otherwise, setup the new timeout */
- mm_dbg ("Connected bearer not registered in 3GPP network");
+ mm_obj_dbg (self, "connected bearer not registered in 3GPP network");
self->priv->deferred_3gpp_unregistration_id =
g_timeout_add_seconds (BEARER_DEFERRED_UNREGISTRATION_TIMEOUT,
(GSourceFunc) deferred_3gpp_unregistration_cb,
@@ -280,9 +712,9 @@ deferred_cdma_unregistration_cb (MMBaseBearer *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_obj_dbg (self, "forcing bearer disconnection, not registered in CDMA network");
mm_base_bearer_disconnect_force (self);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
@@ -322,7 +754,7 @@ modem_cdma_registration_state_changed (MMIfaceModemCdma *modem,
/* 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");
+ mm_obj_dbg (self, "bearer not allowed to connect, registered in roaming CDMA network");
reset_deferred_unregistration (self);
mm_base_bearer_disconnect_force (self);
return;
@@ -336,13 +768,13 @@ modem_cdma_registration_state_changed (MMIfaceModemCdma *modem,
/* 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_obj_dbg (self, "bearer not allowed to connect, not registered in CDMA network");
mm_base_bearer_disconnect_force (self);
return;
}
/* Otherwise, setup the new timeout */
- mm_dbg ("Connected bearer not registered in CDMA network");
+ mm_obj_dbg (self, "connected bearer not registered in CDMA network");
self->priv->deferred_cdma_unregistration_id =
g_timeout_add_seconds (BEARER_DEFERRED_UNREGISTRATION_TIMEOUT,
(GSourceFunc) deferred_cdma_unregistration_cb,
@@ -421,7 +853,7 @@ mm_base_bearer_connect_finish (MMBaseBearer *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
@@ -431,14 +863,11 @@ disconnect_after_cancel_ready (MMBaseBearer *self,
GError *error = NULL;
if (!MM_BASE_BEARER_GET_CLASS (self)->disconnect_finish (self, res, &error)) {
- mm_warn ("Error disconnecting bearer '%s': '%s'. "
- "Will assume disconnected anyway.",
- self->priv->path,
- error->message);
+ mm_obj_warn (self, "error disconnecting: %s; will assume disconnected anyway", error->message);
g_error_free (error);
}
else
- mm_dbg ("Disconnected bearer '%s'", self->priv->path);
+ mm_obj_dbg (self, "disconnected bearer '%s'", self->priv->path);
/* Report disconnection to the bearer object using class method
* mm_bearer_report_connection_status. This gives subclass implementations a
@@ -451,7 +880,7 @@ disconnect_after_cancel_ready (MMBaseBearer *self,
static void
connect_ready (MMBaseBearer *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
gboolean launch_disconnect = FALSE;
@@ -460,44 +889,51 @@ connect_ready (MMBaseBearer *self,
/* NOTE: connect() implementations *MUST* handle cancellations themselves */
result = MM_BASE_BEARER_GET_CLASS (self)->connect_finish (self, res, &error);
if (!result) {
- mm_dbg ("Couldn't connect bearer '%s': '%s'",
- self->priv->path,
- error->message);
- if (g_error_matches (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED)) {
+ mm_obj_warn (self, "connection attempt #%u failed: %s",
+ mm_bearer_stats_get_attempts (self->priv->stats),
+ error->message);
+
+ /* Update failed attempts */
+ mm_bearer_stats_set_failed_attempts (self->priv->stats,
+ mm_bearer_stats_get_failed_attempts (self->priv->stats) + 1);
+ bearer_update_interface_stats (self);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
/* Will launch disconnection */
launch_disconnect = TRUE;
- } else
+ } else {
+ /* Update reported connection error before the status update */
+ bearer_update_connection_error (self, error);
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
-
- g_simple_async_result_take_error (simple, error);
+ }
}
/* Handle cancellations detected after successful connection */
else if (g_cancellable_is_cancelled (self->priv->connect_cancellable)) {
- mm_dbg ("Connected bearer '%s', but need to disconnect", self->priv->path);
+ mm_obj_dbg (self, "connected, but need to disconnect");
mm_bearer_connect_result_unref (result);
- g_simple_async_result_set_error (
- simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Bearer got connected, but had to disconnect after cancellation request");
- launch_disconnect = TRUE;
+ error = g_error_new (G_IO_ERROR, G_IO_ERROR_CANCELLED,
+ "Bearer got connected, but had to disconnect after cancellation request");
+ launch_disconnect = TRUE;
}
else {
- mm_dbg ("Connected bearer '%s'", self->priv->path);
+ mm_obj_dbg (self, "connected");
/* Update bearer and interface status */
bearer_update_status_connected (
self,
mm_port_get_device (mm_bearer_connect_result_peek_data (result)),
+ mm_bearer_connect_result_get_multiplexed (result),
+ mm_bearer_connect_result_get_profile_id (result),
mm_bearer_connect_result_peek_ipv4_config (result),
- mm_bearer_connect_result_peek_ipv6_config (result));
+ mm_bearer_connect_result_peek_ipv6_config (result),
+ mm_bearer_connect_result_get_uplink_speed (result),
+ mm_bearer_connect_result_get_downlink_speed (result));
mm_bearer_connect_result_unref (result);
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
}
if (launch_disconnect) {
+ /* Update reported connection error before the status update */
+ bearer_update_connection_error (self, error);
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTING);
MM_BASE_BEARER_GET_CLASS (self)->disconnect (
self,
@@ -506,8 +942,13 @@ connect_ready (MMBaseBearer *self,
}
g_clear_object (&self->priv->connect_cancellable);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
void
@@ -515,17 +956,28 @@ mm_base_bearer_connect (MMBaseBearer *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- g_assert (MM_BASE_BEARER_GET_CLASS (self)->connect != NULL);
- g_assert (MM_BASE_BEARER_GET_CLASS (self)->connect_finish != NULL);
+ if (!MM_BASE_BEARER_GET_CLASS (self)->connect) {
+ g_assert (!MM_BASE_BEARER_GET_CLASS (self)->connect_finish);
+ g_task_report_new_error (
+ self,
+ callback,
+ user_data,
+ mm_base_bearer_connect,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Bearer doesn't allow explicit connection requests");
+ return;
+ }
/* If already connecting, return error, don't allow a second request. */
if (self->priv->status == MM_BEARER_STATUS_CONNECTING) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
+ g_task_report_new_error (
+ self,
callback,
user_data,
+ mm_base_bearer_connect,
MM_CORE_ERROR,
MM_CORE_ERROR_IN_PROGRESS,
"Bearer already being connected");
@@ -535,10 +987,11 @@ mm_base_bearer_connect (MMBaseBearer *self,
/* If currently disconnecting, return error, previous operation should
* finish before allowing to connect again. */
if (self->priv->status == MM_BEARER_STATUS_DISCONNECTING) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
+ g_task_report_new_error (
+ self,
callback,
user_data,
+ mm_base_bearer_connect,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Bearer currently being disconnected");
@@ -548,10 +1001,11 @@ mm_base_bearer_connect (MMBaseBearer *self,
/* Check 3GPP roaming allowance, *only* roaming related here */
if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self->priv->modem)) &&
self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_ROAMING) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
+ g_task_report_new_error (
+ self,
callback,
user_data,
+ mm_base_bearer_connect,
MM_CORE_ERROR,
MM_CORE_ERROR_UNAUTHORIZED,
"Not allowed to connect bearer in 3GPP network: '%s'",
@@ -562,10 +1016,11 @@ mm_base_bearer_connect (MMBaseBearer *self,
/* Check CDMA roaming allowance, *only* roaming related here */
if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self->priv->modem)) &&
self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_ROAMING) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
+ g_task_report_new_error (
+ self,
callback,
user_data,
+ mm_base_bearer_connect,
MM_CORE_ERROR,
MM_CORE_ERROR_UNAUTHORIZED,
"Not allowed to connect bearer in CDMA network: '%s'",
@@ -573,28 +1028,32 @@ mm_base_bearer_connect (MMBaseBearer *self,
return;
}
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_base_bearer_connect);
+ task = g_task_new (self, NULL, callback, user_data);
/* If already connected, done */
if (self->priv->status == MM_BEARER_STATUS_CONNECTED) {
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
+ /* Update total attempts */
+ mm_bearer_stats_set_attempts (self->priv->stats,
+ mm_bearer_stats_get_attempts (self->priv->stats) + 1);
+ bearer_reset_ongoing_interface_stats (self);
+
+ /* Clear previous connection error, if any */
+ bearer_update_connection_error (self, NULL);
+
/* Connecting! */
- mm_dbg ("Connecting bearer '%s'", self->priv->path);
+ mm_obj_dbg (self, "connecting...");
self->priv->connect_cancellable = g_cancellable_new ();
bearer_update_status (self, MM_BEARER_STATUS_CONNECTING);
MM_BASE_BEARER_GET_CLASS (self)->connect (
self,
self->priv->connect_cancellable,
(GAsyncReadyCallback)connect_ready,
- result);
+ task);
}
typedef struct {
@@ -658,6 +1117,8 @@ handle_connect (MMBaseBearer *self,
MM_BASE_BEARER_MODEM, &ctx->modem,
NULL);
+ mm_obj_dbg (self, "user request to connect");
+
mm_base_modem_authorize (ctx->modem,
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
@@ -674,52 +1135,49 @@ mm_base_bearer_disconnect_finish (MMBaseBearer *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
disconnect_ready (MMBaseBearer *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!MM_BASE_BEARER_GET_CLASS (self)->disconnect_finish (self, res, &error)) {
- mm_dbg ("Couldn't disconnect bearer '%s'", self->priv->path);
+ mm_obj_dbg (self, "couldn't disconnect: %s", error->message);
bearer_update_status (self, MM_BEARER_STATUS_CONNECTED);
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
}
else {
- mm_dbg ("Disconnected bearer '%s'", self->priv->path);
+ mm_obj_dbg (self, "disconnected");
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_task_return_boolean (task, TRUE);
}
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
status_changed_complete_disconnect (MMBaseBearer *self,
GParamSpec *pspec,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
/* We may get other states here before DISCONNECTED, like DISCONNECTING or
* even CONNECTED. */
if (self->priv->status != MM_BEARER_STATUS_DISCONNECTED)
return;
- mm_dbg ("Disconnected bearer '%s' after cancelling previous connect request",
- self->priv->path);
+ mm_obj_dbg (self, "disconnected after cancelling previous connect request");
g_signal_handler_disconnect (self,
self->priv->disconnect_signal_handler);
self->priv->disconnect_signal_handler = 0;
/* Note: interface state is updated when the DISCONNECTED state is set */
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
void
@@ -727,37 +1185,40 @@ mm_base_bearer_disconnect (MMBaseBearer *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *simple;
+ GTask *task;
- g_assert (MM_BASE_BEARER_GET_CLASS (self)->disconnect != NULL);
- g_assert (MM_BASE_BEARER_GET_CLASS (self)->disconnect_finish != NULL);
+ task = g_task_new (self, NULL, callback, user_data);
- simple = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_base_bearer_disconnect);
+ if (!MM_BASE_BEARER_GET_CLASS (self)->disconnect) {
+ g_assert (!MM_BASE_BEARER_GET_CLASS (self)->disconnect_finish);
+ g_task_return_new_error (
+ task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Bearer doesn't allow explicit disconnection requests");
+ g_object_unref (task);
+ return;
+ }
/* If already disconnected, done */
if (self->priv->status == MM_BEARER_STATUS_DISCONNECTED) {
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
/* If already disconnecting, return error, don't allow a second request. */
if (self->priv->status == MM_BEARER_STATUS_DISCONNECTING) {
- g_simple_async_result_set_error (
- simple,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_IN_PROGRESS,
"Bearer already being disconnected");
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
+ g_object_unref (task);
return;
}
- mm_dbg ("Disconnecting bearer '%s'", self->priv->path);
+ mm_obj_dbg (self, "disconnecting...");
/* If currently connecting, try to cancel that operation, and wait to get
* disconnected. */
@@ -774,7 +1235,7 @@ mm_base_bearer_disconnect (MMBaseBearer *self,
g_signal_connect (self,
"notify::" MM_BASE_BEARER_STATUS,
(GCallback)status_changed_complete_disconnect,
- simple); /* takes ownership */
+ task); /* takes ownership */
return;
}
@@ -784,7 +1245,7 @@ mm_base_bearer_disconnect (MMBaseBearer *self,
MM_BASE_BEARER_GET_CLASS (self)->disconnect (
self,
(GAsyncReadyCallback)disconnect_ready,
- simple); /* takes ownership */
+ task); /* takes ownership */
}
typedef struct {
@@ -848,6 +1309,8 @@ handle_disconnect (MMBaseBearer *self,
MM_BASE_BEARER_MODEM, &ctx->modem,
NULL);
+ mm_obj_dbg (self, "user request to disconnect");
+
mm_base_modem_authorize (ctx->modem,
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
@@ -877,9 +1340,7 @@ base_bearer_dbus_export (MMBaseBearer *self)
self->priv->connection,
self->priv->path,
&error)) {
- mm_warn ("couldn't export BEARER at '%s': '%s'",
- self->priv->path,
- error->message);
+ mm_obj_warn (self, "couldn't export to bus: %s", error->message);
g_error_free (error);
}
}
@@ -892,7 +1353,7 @@ base_bearer_dbus_unexport (MMBaseBearer *self)
path = g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (self));
/* Only unexport if currently exported */
if (path) {
- mm_dbg ("Removing from DBus bearer at '%s'", path);
+ mm_obj_dbg (self, "removing from bus");
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self));
}
}
@@ -925,10 +1386,10 @@ mm_base_bearer_get_config (MMBaseBearer *self)
NULL);
}
-MMBearerIpFamily
-mm_base_bearer_get_default_ip_family (MMBaseBearer *self)
+gint
+mm_base_bearer_get_profile_id (MMBaseBearer *self)
{
- return self->priv->default_ip_family;
+ return mm_gdbus_bearer_get_profile_id (MM_GDBUS_BEARER (self));
}
/*****************************************************************************/
@@ -940,14 +1401,11 @@ disconnect_force_ready (MMBaseBearer *self,
GError *error = NULL;
if (!MM_BASE_BEARER_GET_CLASS (self)->disconnect_finish (self, res, &error)) {
- mm_warn ("Error disconnecting bearer '%s': '%s'. "
- "Will assume disconnected anyway.",
- self->priv->path,
- error->message);
+ mm_obj_warn (self, "error disconnecting: %s; will assume disconnected anyway", error->message);
g_error_free (error);
}
else
- mm_dbg ("Disconnected bearer '%s'", self->priv->path);
+ mm_obj_dbg (self, "disconnected");
/* Report disconnection to the bearer object using class method
* mm_bearer_report_connection_status. This gives subclass implementations a
@@ -964,7 +1422,12 @@ mm_base_bearer_disconnect_force (MMBaseBearer *self)
self->priv->status == MM_BEARER_STATUS_DISCONNECTED)
return;
- mm_dbg ("Forcing disconnection of bearer '%s'", self->priv->path);
+ if (self->priv->ignore_disconnection_reports) {
+ mm_obj_dbg (self, "disconnection should be forced but it's explicitly ignored");
+ return;
+ }
+
+ mm_obj_dbg (self, "forcing disconnection");
/* If currently connecting, try to cancel that operation. */
if (self->priv->status == MM_BEARER_STATUS_CONNECTING) {
@@ -983,28 +1446,215 @@ mm_base_bearer_disconnect_force (MMBaseBearer *self)
/*****************************************************************************/
static void
-report_connection_status (MMBaseBearer *self,
- MMBearerConnectionStatus status)
+report_connection_status (MMBaseBearer *self,
+ MMBearerConnectionStatus status,
+ const GError *connection_error)
{
- /* The only status expected at this point is DISCONNECTED.
- * No other status should have been given to the generic implementation
- * of report_connection_status (it would be an error).
+ /* The only status expected at this point is DISCONNECTED or CONNECTED,
+ * although here we just process the DISCONNECTED one.
*/
- g_assert (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+ g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED || status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
/* In the generic bearer implementation we just need to reset the
* interface status */
- bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
+ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) {
+ bearer_update_connection_error (self, connection_error);
+ bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
+ }
}
+/*
+ * This method is used exclusively in two different scenarios:
+ * a) to report disconnections detected by ModemManager itself (e.g. based on
+ * CGACT polling or CGEV URCs), applicable to bearers using both NET and
+ * PPP data ports.
+ * b) to report failed or successful connection attempts by plugins using NET
+ * data ports that rely on vendor-specific URCs (e.g. Icera, MBM, Option
+ * HSO).
+ *
+ * The method is also subclass-able because plugins may require specific
+ * cleanup operations to be done when a bearer is reported as disconnected.
+ * (e.g. the QMI or MBIM implementations require removing signal handlers).
+ *
+ * For all the scenarios involving a) the plugins are required to call the
+ * parent report_connection_status() implementation to report the
+ * DISCONNECTED state. For scenarios involving b) the parent reporting is not
+ * expected at all. In other words, the parent report_connection_status()
+ * is exclusively used in processing disconnections detected by ModemManager
+ * itself.
+ *
+ * If the bearer has been connected and it has required PPP method, we will
+ * ignore all disconnection reports because we cannot disconnect a PPP-based
+ * bearer before the upper layers have stopped using the TTY. In this case,
+ * we must wait for upper layers to detect the disconnection themselves (e.g.
+ * pppd should detect it) and disconnect the bearer through DBus.
+ */
void
-mm_base_bearer_report_connection_status (MMBaseBearer *self,
- MMBearerConnectionStatus status)
+mm_base_bearer_report_connection_status_detailed (MMBaseBearer *self,
+ MMBearerConnectionStatus status,
+ const GError *connection_error)
{
- return MM_BASE_BEARER_GET_CLASS (self)->report_connection_status (self, status);
+ /* Reporting disconnection? */
+ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED || status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED) {
+ if (self->priv->ignore_disconnection_reports) {
+ mm_obj_dbg (self, "ignoring disconnection report");
+ return;
+ }
+
+ /* Setup a generic default error if none explicitly given when reporting
+ * bearer disconnections. */
+ if (!connection_error) {
+ g_autoptr(GError) default_connection_error = NULL;
+
+ default_connection_error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, self);
+ return MM_BASE_BEARER_GET_CLASS (self)->report_connection_status (self, status, default_connection_error);
+ }
+ }
+
+ return MM_BASE_BEARER_GET_CLASS (self)->report_connection_status (self, status, connection_error);
+}
+
+/*****************************************************************************/
+
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+typedef struct _SyncingContext SyncingContext;
+static void interface_syncing_step (GTask *task);
+
+typedef enum {
+ SYNCING_STEP_FIRST,
+ SYNCING_STEP_REFRESH_CONNECTION,
+ SYNCING_STEP_LAST
+} SyncingStep;
+
+struct _SyncingContext {
+ SyncingStep step;
+ MMBearerStatus status;
+};
+
+gboolean
+mm_base_bearer_sync_finish (MMBaseBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
+reload_connection_status_ready (MMBaseBearer *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SyncingContext *ctx;
+ MMBearerConnectionStatus reloaded_status;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ /* The only update we're really interested in is the connected->disconnected
+ * one, because any other would be extremely strange and it's probably not
+ * worth trying to support those; e.g. a disconnected->connected change here
+ * would be impossible to be handled correctly. We'll also ignore intermediate
+ * states (connecting/disconnecting), as we can rely on the reports of the final
+ * state at some point soon.
+ *
+ * So, just handle DISCONNECTED at this point.
+ */
+ reloaded_status = MM_BASE_BEARER_GET_CLASS (self)->reload_connection_status_finish (self, res, &error);
+ if (reloaded_status == MM_BEARER_CONNECTION_STATUS_UNKNOWN)
+ mm_obj_warn (self, "reloading connection status failed: %s", error->message);
+ else if ((ctx->status == MM_BEARER_STATUS_CONNECTED) &&
+ (reloaded_status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED)) {
+ mm_obj_dbg (self, "disconnection detected during status synchronization");
+ mm_base_bearer_report_connection_status (self, reloaded_status);
+ }
+
+ /* Go on to the next step */
+ ctx->step++;
+ interface_syncing_step (task);
+}
+
+static void
+interface_syncing_step (GTask *task)
+{
+ MMBaseBearer *self;
+ SyncingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case SYNCING_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case SYNCING_STEP_REFRESH_CONNECTION:
+ /*
+ * AT+PPP based connections should not be synced.
+ * When a AT+PPP connection bearer is connected, the 'ignore_disconnection_reports' flag is set.
+ */
+ if (!self->priv->ignore_disconnection_reports) {
+ if (!MM_BASE_BEARER_GET_CLASS (self)->reload_connection_status)
+ mm_obj_warn (self, "unable to reload connection status, method not implemented");
+ else {
+ mm_obj_dbg (self, "refreshing connection status");
+ MM_BASE_BEARER_GET_CLASS (self)->reload_connection_status (self,
+ (GAsyncReadyCallback) reload_connection_status_ready,
+ task);
+ return;
+ }
+ }
+ ctx->step++;
+ /* fall through */
+
+ case SYNCING_STEP_LAST:
+ /* We are done without errors! */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_base_bearer_sync (MMBaseBearer *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SyncingContext *ctx;
+ GTask *task;
+
+ /* Create SyncingContext and store the original bearer status */
+ ctx = g_new0 (SyncingContext, 1);
+ ctx->step = SYNCING_STEP_FIRST;
+ ctx->status = self->priv->status;
+
+ /* Create sync steps task and execute it */
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)g_free);
+ interface_syncing_step (task);
+}
+
+#endif
+
+/*****************************************************************************/
+
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ MMBaseBearer *self;
+
+ self = MM_BASE_BEARER (_self);
+ return g_strdup_printf ("bearer%u", self->priv->dbus_id);
+}
+
+/*****************************************************************************/
+
+static void
set_property (GObject *object,
guint prop_id,
const GValue *value,
@@ -1036,6 +1686,8 @@ set_property (GObject *object,
g_clear_object (&self->priv->modem);
self->priv->modem = g_value_dup_object (value);
if (self->priv->modem) {
+ /* Set owner ID */
+ mm_log_object_set_owner_id (MM_LOG_OBJECT (self), mm_log_object_get_id (MM_LOG_OBJECT (self->priv->modem)));
/* Bind the modem's connection (which is set when it is exported,
* and unset when unexported) to the BEARER's connection */
g_object_bind_property (self->priv->modem, MM_BASE_MODEM_CONNECTION,
@@ -1069,9 +1721,6 @@ set_property (GObject *object,
g_variant_unref (dictionary);
break;
}
- case PROP_DEFAULT_IP_FAMILY:
- self->priv->default_ip_family = g_value_get_flags (value);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1102,9 +1751,6 @@ get_property (GObject *object,
case PROP_CONFIG:
g_value_set_object (value, self->priv->config);
break;
- case PROP_DEFAULT_IP_FAMILY:
- g_value_set_flags (value, self->priv->default_ip_family);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1114,25 +1760,35 @@ get_property (GObject *object,
static void
mm_base_bearer_init (MMBaseBearer *self)
{
+ static guint id = 0;
+
/* Initialize private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MM_TYPE_BASE_BEARER,
MMBaseBearerPrivate);
+
+ /* Each bearer is given a unique id to build its own DBus path */
+ self->priv->dbus_id = id++;
+
self->priv->status = MM_BEARER_STATUS_DISCONNECTED;
self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_NONE;
self->priv->reason_cdma = CONNECTION_FORBIDDEN_REASON_NONE;
- self->priv->default_ip_family = MM_BEARER_IP_FAMILY_IPV4;
+ self->priv->stats = mm_bearer_stats_new ();
/* Set defaults */
- mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), NULL);
- mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), FALSE);
- mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE);
- mm_gdbus_bearer_set_properties (MM_GDBUS_BEARER (self), NULL);
- mm_gdbus_bearer_set_ip_timeout (MM_GDBUS_BEARER (self), BEARER_IP_TIMEOUT_DEFAULT);
- mm_gdbus_bearer_set_ip4_config (MM_GDBUS_BEARER (self),
- mm_bearer_ip_config_get_dictionary (NULL));
- mm_gdbus_bearer_set_ip6_config (MM_GDBUS_BEARER (self),
- mm_bearer_ip_config_get_dictionary (NULL));
+ mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), NULL);
+ mm_gdbus_bearer_set_multiplexed (MM_GDBUS_BEARER (self), FALSE);
+ mm_gdbus_bearer_set_profile_id (MM_GDBUS_BEARER (self), MM_3GPP_PROFILE_ID_UNKNOWN);
+ mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), FALSE);
+ mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE);
+ mm_gdbus_bearer_set_properties (MM_GDBUS_BEARER (self), NULL);
+ mm_gdbus_bearer_set_ip_timeout (MM_GDBUS_BEARER (self), BEARER_IP_TIMEOUT_DEFAULT);
+ mm_gdbus_bearer_set_bearer_type (MM_GDBUS_BEARER (self), MM_BEARER_TYPE_DEFAULT);
+ mm_gdbus_bearer_set_ip4_config (MM_GDBUS_BEARER (self),
+ mm_bearer_ip_config_get_dictionary (NULL));
+ mm_gdbus_bearer_set_ip6_config (MM_GDBUS_BEARER (self),
+ mm_bearer_ip_config_get_dictionary (NULL));
+ bearer_update_interface_stats (self);
}
static void
@@ -1150,6 +1806,10 @@ dispose (GObject *object)
{
MMBaseBearer *self = MM_BASE_BEARER (object);
+ connection_monitor_stop (self);
+ bearer_stats_stop (self);
+ g_clear_object (&self->priv->stats);
+
if (self->priv->connection) {
base_bearer_dbus_unexport (self);
g_clear_object (&self->priv->connection);
@@ -1165,6 +1825,12 @@ dispose (GObject *object)
}
static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
mm_base_bearer_class_init (MMBaseBearerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -1219,25 +1885,20 @@ mm_base_bearer_class_init (MMBaseBearerClass *klass)
MM_TYPE_BEARER_PROPERTIES,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_CONFIG, properties[PROP_CONFIG]);
-
- properties[PROP_DEFAULT_IP_FAMILY] =
- g_param_spec_flags (MM_BASE_BEARER_DEFAULT_IP_FAMILY,
- "Bearer default IP family",
- "IP family to use for this bearer when no IP family is specified",
- MM_TYPE_BEARER_IP_FAMILY,
- MM_BEARER_IP_FAMILY_IPV4,
- G_PARAM_READWRITE);
- g_object_class_install_property (object_class, PROP_DEFAULT_IP_FAMILY, properties[PROP_DEFAULT_IP_FAMILY]);
}
/*****************************************************************************/
/* Helpers to implement connect() */
struct _MMBearerConnectResult {
- volatile gint ref_count;
- MMPort *data;
+ volatile gint ref_count;
+ MMPort *data;
MMBearerIpConfig *ipv4_config;
MMBearerIpConfig *ipv6_config;
+ gboolean multiplexed;
+ gint profile_id;
+ guint64 uplink_speed;
+ guint64 downlink_speed;
};
MMBearerConnectResult *
@@ -1279,8 +1940,60 @@ mm_bearer_connect_result_peek_ipv6_config (MMBearerConnectResult *result)
return result->ipv6_config;
}
+void
+mm_bearer_connect_result_set_multiplexed (MMBearerConnectResult *result,
+ gboolean multiplexed)
+{
+ result->multiplexed = multiplexed;
+}
+
+gboolean
+mm_bearer_connect_result_get_multiplexed (MMBearerConnectResult *result)
+{
+ return result->multiplexed;
+}
+
+void
+mm_bearer_connect_result_set_profile_id (MMBearerConnectResult *result,
+ gint profile_id)
+{
+ result->profile_id = profile_id;
+}
+
+gint
+mm_bearer_connect_result_get_profile_id (MMBearerConnectResult *result)
+{
+ return result->profile_id;
+}
+
+void
+mm_bearer_connect_result_set_uplink_speed (MMBearerConnectResult *result,
+ guint64 speed)
+{
+ result->uplink_speed = speed;
+}
+
+guint64
+mm_bearer_connect_result_get_uplink_speed (MMBearerConnectResult *result)
+{
+ return result->uplink_speed;
+}
+
+void
+mm_bearer_connect_result_set_downlink_speed (MMBearerConnectResult *result,
+ guint64 speed)
+{
+ result->downlink_speed = speed;
+}
+
+guint64
+mm_bearer_connect_result_get_downlink_speed (MMBearerConnectResult *result)
+{
+ return result->downlink_speed;
+}
+
MMBearerConnectResult *
-mm_bearer_connect_result_new (MMPort *data,
+mm_bearer_connect_result_new (MMPort *data,
MMBearerIpConfig *ipv4_config,
MMBearerIpConfig *ipv6_config)
{
@@ -1296,5 +2009,7 @@ mm_bearer_connect_result_new (MMPort *data,
result->ipv4_config = g_object_ref (ipv4_config);
if (ipv6_config)
result->ipv6_config = g_object_ref (ipv6_config);
+ result->multiplexed = FALSE; /* default */
+ result->profile_id = MM_3GPP_PROFILE_ID_UNKNOWN;
return result;
}