diff options
Diffstat (limited to 'src/mm-base-bearer.c')
-rw-r--r-- | src/mm-base-bearer.c | 1021 |
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; } |