summaryrefslogtreecommitdiffstats
path: root/panels/wwan/cc-wwan-data.c
diff options
context:
space:
mode:
Diffstat (limited to 'panels/wwan/cc-wwan-data.c')
-rw-r--r--panels/wwan/cc-wwan-data.c1502
1 files changed, 1502 insertions, 0 deletions
diff --git a/panels/wwan/cc-wwan-data.c b/panels/wwan/cc-wwan-data.c
new file mode 100644
index 0000000..2608ab2
--- /dev/null
+++ b/panels/wwan/cc-wwan-data.c
@@ -0,0 +1,1502 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* cc-wwan-data.c
+ *
+ * Copyright 2019 Purism SPC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author(s):
+ * Mohammed Sadiq <sadiq@sadiqpk.org>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "cc-wwan-data"
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <glib/gi18n.h>
+#include <nma-mobile-providers.h>
+
+#include "cc-wwan-data.h"
+
+/**
+ * @short_description: Device Internet Data Object
+ * @include: "cc-wwan-device-data.h"
+ *
+ * #CcWwanData represents the data object of the given
+ * #CcWwanDevice. Please note that while #CcWWanDevice
+ * is bound to the hardware device, #CcWwanData may also
+ * depend on the inserted SIM (if supported). So the state
+ * of #CcWwanData changes when SIM is changed.
+ */
+
+/*
+ * Priority for connections. The larger the number, the lower the priority
+ * https://developer.gnome.org/NetworkManager/stable/nm-settings.html:
+ *
+ * A lower value is better (higher priority). Zero selects a globally
+ * configured default value. If the latter is missing or zero too, it
+ * defaults to 50 for VPNs and 100 for other connections.
+ *
+ * Since WiFi and other network connections will likely get the default
+ * setting of 100, set WWAN DNS priorities higher than the default, with
+ * room to allow multiple modems to set priority above/below each other.
+ */
+#define CC_WWAN_DNS_PRIORITY_LOW (120)
+#define CC_WWAN_DNS_PRIORITY_HIGH (115)
+
+/* These are to be set as route metric */
+#define CC_WWAN_ROUTE_PRIORITY_LOW (1050)
+#define CC_WWAN_ROUTE_PRIORITY_HIGH (1040)
+
+struct _CcWwanData
+{
+ GObject parent_instance;
+
+ MMObject *mm_object;
+ MMModem *modem;
+ MMSim *sim;
+ gchar *sim_id;
+
+ gchar *operator_code; /* MCCMNC */
+ GError *error;
+
+ NMClient *nm_client;
+ NMDevice *nm_device;
+ NMAMobileProvidersDatabase *apn_db;
+ NMAMobileProvider *apn_provider;
+ CcWwanDataApn *default_apn;
+ CcWwanDataApn *old_default_apn;
+ GListStore *apn_list;
+ NMActiveConnection *active_connection;
+
+ gint priority;
+ gboolean data_enabled; /* autoconnect enabled */
+ gboolean home_only; /* Data roaming */
+ gboolean apn_list_updated; /* APN list updated from mobile-provider-info */
+};
+
+G_DEFINE_TYPE (CcWwanData, cc_wwan_data, G_TYPE_OBJECT)
+
+/*
+ * Default Access Point Settings Logic:
+ * For a provided SIM, all the APNs available from NetworkManager
+ * that matches the given SIM identifier (ICCID, available via
+ * mm_sim_get_identifier() or similar gdbus API) is loaded for
+ * the Device (In NetworkManager, it is saved as ‘sim-id’, if
+ * present). At a time, only one connection will be bound to
+ * a device. If there are more than one match, the item with
+ * the highest ‘route-metric’ is taken. If more matches are
+ * still available, the first item is chosen.
+ *
+ * Populating All available APNs:
+ * All Possible APNs for the given sim are populated the following
+ * way (A list of all the following avoiding duplicates)
+ * 1. The above mentioned “Default Access Point Settings Logic”
+ * 2. Get All saved Network Manager connections with the
+ * provided MCCMNC of the given SIM
+ * 3. Get All possible APNs for the MCCMNC from mobile-provider-info
+ *
+ * Testing if data is enabled:
+ * Check if any of the items from step 1 have ‘autoconnect’ set
+ *
+ * Checking/Setting current SIM for data (in case of multiple SIM):
+ * Since other networks (like wifi, ethernet) should have higher
+ * priorities we use a negative number for priority.
+ * 1. All APNs by default have priority CC_WWAN_APN_PRIORITY_LOW
+ * 2. APN of selected SIM for active data have priority of
+ * CC_WWAN_APN_PRIORITY_HIGH
+ *
+ * XXX: Since users may create custom APNs via nmtui or like tools
+ * we may have to check if there are some inconsistencies with APNs
+ * available in NetworkManager, and ask user if they have to reset
+ * the APNs that have invalid settings (basically, we care only APNs
+ * that are set to have ‘autoconnect’ enabled, and all we need is to
+ * disable autoconnect). We won’t interfere CDMA/EVDO networks.
+ */
+struct _CcWwanDataApn {
+ GObject parent_instance;
+
+ /* Set if the APN is from the mobile-provider-info database */
+ NMAMobileAccessMethod *access_method;
+
+ /* Set if the APN is saved in NetworkManager */
+ NMConnection *nm_connection;
+ NMRemoteConnection *remote_connection;
+
+ gboolean modified;
+};
+
+G_DEFINE_TYPE (CcWwanDataApn, cc_wwan_data_apn, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_ERROR,
+ PROP_ENABLED,
+ N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS];
+
+static void
+wwan_data_apn_reset (CcWwanDataApn *apn)
+{
+ if (!apn)
+ return;
+
+ g_clear_object (&apn->nm_connection);
+ g_clear_object (&apn->remote_connection);
+}
+
+static NMConnection *
+wwan_data_get_nm_connection (CcWwanDataApn *apn)
+{
+ NMConnection *connection;
+ NMSetting *setting;
+ g_autofree gchar *uuid = NULL;
+
+ if (apn->nm_connection)
+ return apn->nm_connection;
+
+ if (apn->remote_connection)
+ return NM_CONNECTION (apn->remote_connection);
+
+ connection = nm_simple_connection_new ();
+ apn->nm_connection = connection;
+
+ setting = nm_setting_connection_new ();
+ uuid = nm_utils_uuid_generate ();
+ g_object_set (setting,
+ NM_SETTING_CONNECTION_UUID, uuid,
+ NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME,
+ NULL);
+ nm_connection_add_setting (connection, setting);
+
+ setting = nm_setting_serial_new ();
+ nm_connection_add_setting (connection, setting);
+
+ setting = nm_setting_ip4_config_new ();
+ g_object_set (setting, NM_SETTING_IP_CONFIG_METHOD, "auto", NULL);
+ nm_connection_add_setting (connection, setting);
+
+ nm_connection_add_setting (connection, nm_setting_gsm_new ());
+ nm_connection_add_setting (connection, nm_setting_ppp_new ());
+
+ return apn->nm_connection;
+}
+
+static gboolean
+wwan_data_apn_are_same (CcWwanDataApn *apn,
+ NMAMobileAccessMethod *access_method)
+{
+ NMConnection *connection;
+ NMSetting *setting;
+
+ if (!apn->remote_connection)
+ return FALSE;
+
+ connection = NM_CONNECTION (apn->remote_connection);
+ setting = NM_SETTING (nm_connection_get_setting_gsm (connection));
+
+ if (g_strcmp0 (nma_mobile_access_method_get_3gpp_apn (access_method),
+ nm_setting_gsm_get_apn (NM_SETTING_GSM (setting))) != 0)
+ return FALSE;
+
+ if (g_strcmp0 (nma_mobile_access_method_get_username (access_method),
+ nm_setting_gsm_get_username (NM_SETTING_GSM (setting))) != 0)
+ return FALSE;
+
+ if (g_strcmp0 (nma_mobile_access_method_get_password (access_method),
+ cc_wwan_data_apn_get_password (apn)) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static CcWwanDataApn *
+wwan_data_find_matching_apn (CcWwanData *self,
+ NMAMobileAccessMethod *access_method)
+{
+ CcWwanDataApn *apn;
+ guint i, n_items;
+
+ n_items = g_list_model_get_n_items (G_LIST_MODEL (self->apn_list));
+
+ for (i = 0; i < n_items; i++)
+ {
+ apn = g_list_model_get_item (G_LIST_MODEL (self->apn_list), i);
+
+ if (apn->access_method == access_method)
+ return apn;
+
+ if (wwan_data_apn_are_same (apn, access_method))
+ return apn;
+
+ g_object_unref (apn);
+ }
+
+ return NULL;
+}
+
+static gboolean
+wwan_data_nma_method_is_mms (NMAMobileAccessMethod *method)
+{
+ const char *str;
+
+ str = nma_mobile_access_method_get_3gpp_apn (method);
+ if (str && strcasestr (str, "mms"))
+ return TRUE;
+
+ str = nma_mobile_access_method_get_name (method);
+ if (str && strcasestr (str, "mms"))
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+wwan_data_update_apn_list_db (CcWwanData *self)
+{
+ GSList *apn_methods = NULL, *l;
+ g_autoptr(GError) error = NULL;
+ guint i = 0;
+
+ if (!self->sim || !self->operator_code || self->apn_list_updated)
+ return;
+
+ if (!self->apn_list)
+ return;
+
+ if (!self->apn_db)
+ self->apn_db = nma_mobile_providers_database_new_sync (NULL, NULL, NULL, &error);
+
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ return;
+ }
+
+ if (!self->apn_provider)
+ self->apn_provider = nma_mobile_providers_database_lookup_3gpp_mcc_mnc (self->apn_db,
+ self->operator_code);
+
+ if (self->apn_provider)
+ apn_methods = nma_mobile_provider_get_methods (self->apn_provider);
+
+ self->apn_list_updated = TRUE;
+
+ for (l = apn_methods; l; l = l->next, i++)
+ {
+ g_autoptr(CcWwanDataApn) apn = NULL;
+
+ /* We don’t list MMS APNs */
+ if (wwan_data_nma_method_is_mms (l->data))
+ continue;
+
+ apn = wwan_data_find_matching_apn (self, l->data);
+
+ /* Prepend the item in order */
+ if (!apn)
+ {
+ apn = cc_wwan_data_apn_new ();
+ apn->access_method = l->data;
+ g_list_store_insert (self->apn_list, i, apn);
+ }
+
+ apn->access_method = l->data;
+ }
+}
+
+static void
+wwan_data_update_apn_list (CcWwanData *self)
+{
+ const GPtrArray *nm_connections;
+ guint i;
+
+ if (self->apn_list || !self->sim || !self->nm_device ||
+ nm_device_get_state (self->nm_device) <= NM_DEVICE_STATE_UNAVAILABLE)
+ return;
+
+ if (!self->apn_list)
+ self->apn_list = g_list_store_new (CC_TYPE_WWAN_DATA_APN);
+
+ if (self->nm_device)
+ {
+ nm_connections = nm_device_get_available_connections (self->nm_device);
+
+ for (i = 0; i < nm_connections->len; i++)
+ {
+ g_autoptr(CcWwanDataApn) apn = NULL;
+
+ apn = cc_wwan_data_apn_new ();
+ apn->remote_connection = g_object_ref (nm_connections->pdata[i]);
+ g_list_store_append (self->apn_list, apn);
+
+ /* Load the default APN */
+ if (!self->default_apn && self->sim_id)
+ {
+ NMSettingConnection *connection_setting;
+ NMSettingIPConfig *ip_setting;
+ NMSettingGsm *setting;
+ NMConnection *connection;
+ const gchar *sim_id;
+
+ connection = NM_CONNECTION (apn->remote_connection);
+ setting = nm_connection_get_setting_gsm (connection);
+ connection_setting = nm_connection_get_setting_connection (connection);
+ sim_id = nm_setting_gsm_get_sim_id (setting);
+
+ if (sim_id && *sim_id && g_str_equal (sim_id, self->sim_id))
+ {
+ self->default_apn = apn;
+ self->home_only = nm_setting_gsm_get_home_only (setting);
+ self->data_enabled = nm_setting_connection_get_autoconnect (connection_setting);
+
+ /* If any of the APN has a high priority, the device have high priority */
+ ip_setting = nm_connection_get_setting_ip4_config (connection);
+ if (nm_setting_ip_config_get_route_metric (ip_setting) == CC_WWAN_ROUTE_PRIORITY_HIGH)
+ self->priority = CC_WWAN_APN_PRIORITY_HIGH;
+ }
+ }
+ }
+ }
+}
+
+static void
+wwan_device_state_changed_cb (CcWwanData *self)
+{
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ENABLED]);
+}
+
+static void
+wwan_device_3gpp_operator_code_changd_cb (CcWwanData *self)
+{
+ MMModem3gpp *modem_3gpp;
+
+ modem_3gpp = mm_object_peek_modem_3gpp (self->mm_object);
+
+ if (!self->operator_code)
+ {
+ self->operator_code = mm_modem_3gpp_dup_operator_code (modem_3gpp);
+
+ if (self->operator_code)
+ {
+ wwan_data_update_apn_list (self);
+ wwan_data_update_apn_list_db (self);
+ }
+ }
+}
+
+static void
+cc_wwan_data_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CcWwanData *self = (CcWwanData *)object;
+
+ switch (prop_id)
+ {
+ case PROP_ERROR:
+ g_value_set_boolean (value, self->error != NULL);
+ break;
+
+ case PROP_ENABLED:
+ g_value_set_boolean (value, cc_wwan_data_get_enabled (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+cc_wwan_data_dispose (GObject *object)
+{
+ CcWwanData *self = (CcWwanData *)object;
+
+ g_clear_pointer (&self->sim_id, g_free);
+ g_clear_pointer (&self->operator_code, g_free);
+ g_clear_error (&self->error);
+ g_clear_object (&self->apn_list);
+ g_clear_object (&self->modem);
+ g_clear_object (&self->mm_object);
+ g_clear_object (&self->nm_client);
+ g_clear_object (&self->active_connection);
+ g_clear_object (&self->apn_db);
+
+ G_OBJECT_CLASS (cc_wwan_data_parent_class)->dispose (object);
+}
+
+static void
+cc_wwan_data_class_init (CcWwanDataClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = cc_wwan_data_get_property;
+ object_class->dispose = cc_wwan_data_dispose;
+
+ properties[PROP_ERROR] =
+ g_param_spec_boolean ("error",
+ "Error",
+ "Set if some Error occurs",
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_ENABLED] =
+ g_param_spec_boolean ("enabled",
+ "Enabled",
+ "Get if the data is enabled",
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+cc_wwan_data_init (CcWwanData *self)
+{
+ self->home_only = TRUE;
+ self->priority = CC_WWAN_APN_PRIORITY_LOW;
+}
+
+/**
+ * cc_wwan_data_new:
+ * @mm_object: An #MMObject
+ * @nm_client: An #NMClient
+ *
+ * Create a new device data representing the given
+ * @mm_object. If @mm_object isn’t a 3G/CDMA/LTE
+ * modem, %NULL will be returned
+ *
+ * Returns: A #CcWwanData or %NULL.
+ */
+CcWwanData *
+cc_wwan_data_new (MMObject *mm_object,
+ NMClient *nm_client)
+{
+ CcWwanData *self;
+ NMDevice *nm_device = NULL;
+ g_autoptr(MMModem) modem = NULL;
+ NMDeviceModemCapabilities capabilities = 0;
+
+ g_return_val_if_fail (MM_IS_OBJECT (mm_object), NULL);
+ g_return_val_if_fail (NM_CLIENT (nm_client), NULL);
+
+ modem = mm_object_get_modem (mm_object);
+
+ if (modem)
+ nm_device = nm_client_get_device_by_iface (nm_client,
+ mm_modem_get_primary_port (modem));
+
+ if (NM_IS_DEVICE_MODEM (nm_device))
+ capabilities = nm_device_modem_get_current_capabilities (NM_DEVICE_MODEM (nm_device));
+
+ if (!(capabilities & (NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS
+ | NM_DEVICE_MODEM_CAPABILITY_LTE)))
+ return NULL;
+
+ self = g_object_new (CC_TYPE_WWAN_DATA, NULL);
+
+ self->nm_client = g_object_ref (nm_client);
+ self->mm_object = g_object_ref (mm_object);
+ self->modem = g_steal_pointer (&modem);
+ self->sim = mm_modem_get_sim_sync (self->modem, NULL, NULL);
+ self->sim_id = mm_sim_dup_identifier (self->sim);
+ self->operator_code = mm_sim_dup_operator_identifier (self->sim);
+ self->nm_device = g_object_ref (nm_device);
+ self->active_connection = nm_device_get_active_connection (nm_device);
+
+ if (!self->operator_code)
+ {
+ MMModem3gpp *modem_3gpp;
+
+ modem_3gpp = mm_object_peek_modem_3gpp (mm_object);
+ if (modem_3gpp)
+ {
+ g_signal_connect_object (modem_3gpp, "notify::operator-code",
+ G_CALLBACK (wwan_device_3gpp_operator_code_changd_cb),
+ self, G_CONNECT_SWAPPED);
+ wwan_device_3gpp_operator_code_changd_cb (self);
+ }
+ }
+
+ if (self->active_connection)
+ g_object_ref (self->active_connection);
+
+ g_signal_connect_object (self->nm_device, "notify::state",
+ G_CALLBACK (wwan_device_state_changed_cb),
+ self, G_CONNECT_SWAPPED);
+
+ wwan_data_update_apn_list (self);
+ wwan_data_update_apn_list_db (self);
+
+ return self;
+}
+
+GError *
+cc_wwan_data_get_error (CcWwanData *self)
+{
+ g_return_val_if_fail (CC_IS_WWAN_DATA (self), NULL);
+
+ return self->error;
+}
+
+const gchar *
+cc_wwan_data_get_simple_html_error (CcWwanData *self)
+{
+ g_return_val_if_fail (CC_IS_WWAN_DATA (self), NULL);
+
+ if (!self->error)
+ return NULL;
+
+ if (g_error_matches (self->error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return _("Operation Cancelled");
+
+ if (g_error_matches (self->error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED))
+ return _("<b>Error:</b> Access denied changing settings");
+
+ if (self->error->domain == MM_MOBILE_EQUIPMENT_ERROR)
+ return _("<b>Error:</b> Mobile Equipment Error");
+
+ return NULL;
+}
+
+GListModel *
+cc_wwan_data_get_apn_list (CcWwanData *self)
+{
+ g_return_val_if_fail (CC_IS_WWAN_DATA (self), NULL);
+
+ if (!self->apn_list)
+ wwan_data_update_apn_list (self);
+
+ return G_LIST_MODEL (self->apn_list);
+}
+
+static gboolean
+wwan_data_apn_is_new (CcWwanDataApn *apn)
+{
+ return apn->remote_connection == NULL;
+}
+
+static void
+wwan_data_update_apn (CcWwanData *self,
+ CcWwanDataApn *apn,
+ NMConnection *connection)
+{
+ NMSetting *setting;
+ const gchar *name, *username, *password, *apn_name;
+ gint dns_priority, route_metric;
+
+ setting = NM_SETTING (nm_connection_get_setting_connection (connection));
+
+ g_object_set (setting,
+ NM_SETTING_CONNECTION_AUTOCONNECT, self->data_enabled,
+ NULL);
+
+ setting = NM_SETTING (nm_connection_get_setting_gsm (connection));
+
+ g_object_set (setting,
+ NM_SETTING_GSM_HOME_ONLY, self->home_only,
+ NULL);
+
+ setting = NM_SETTING (nm_connection_get_setting_ip4_config (connection));
+ if (self->priority == CC_WWAN_APN_PRIORITY_HIGH &&
+ self->default_apn == apn)
+ {
+ dns_priority = CC_WWAN_DNS_PRIORITY_HIGH;
+ route_metric = CC_WWAN_ROUTE_PRIORITY_HIGH;
+ }
+ else
+ {
+ dns_priority = CC_WWAN_DNS_PRIORITY_LOW;
+ route_metric = CC_WWAN_ROUTE_PRIORITY_LOW;
+ }
+
+ g_object_set (setting,
+ NM_SETTING_IP_CONFIG_DNS_PRIORITY, dns_priority,
+ NM_SETTING_IP_CONFIG_ROUTE_METRIC, (gint64)route_metric,
+ NULL);
+
+ if (apn->access_method && !apn->remote_connection)
+ {
+ name = nma_mobile_access_method_get_name (apn->access_method);
+ username = nma_mobile_access_method_get_username (apn->access_method);
+ password = nma_mobile_access_method_get_password (apn->access_method);
+ apn_name = nma_mobile_access_method_get_3gpp_apn (apn->access_method);
+ }
+ else
+ {
+ return;
+ }
+
+ setting = NM_SETTING (nm_connection_get_setting_gsm (connection));
+ g_object_set (setting,
+ NM_SETTING_GSM_USERNAME, username,
+ NM_SETTING_GSM_PASSWORD, password,
+ NM_SETTING_GSM_APN, apn_name,
+ NULL);
+
+ setting = NM_SETTING (nm_connection_get_setting_connection (connection));
+
+ g_object_set (setting,
+ NM_SETTING_CONNECTION_ID, name,
+ NULL);
+}
+
+static gint
+wwan_data_get_apn_index (CcWwanData *self,
+ CcWwanDataApn *apn)
+{
+ GListModel *model;
+ guint i, n_items;
+
+ model = G_LIST_MODEL (self->apn_list);
+ n_items = g_list_model_get_n_items (model);
+
+ for (i = 0; i < n_items; i++)
+ {
+ g_autoptr(CcWwanDataApn) cached_apn = NULL;
+
+ cached_apn = g_list_model_get_item (model, i);
+
+ if (apn == cached_apn)
+ return i;
+ }
+
+ return -1;
+}
+
+static void
+cc_wwan_data_connection_updated_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CcWwanData *self;
+ CcWwanDataApn *apn;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ self = g_task_get_source_object (G_TASK (task));
+ apn = g_task_get_task_data (G_TASK (task));
+
+ nm_remote_connection_commit_changes_finish (apn->remote_connection,
+ result, &error);
+ if (!error)
+ {
+ guint apn_index;
+ apn_index = wwan_data_get_apn_index (self, apn);
+
+ if (apn_index >= 0)
+ g_list_model_items_changed (G_LIST_MODEL (self->apn_list),
+ apn_index, 1, 1);
+ else
+ g_warning ("APN ‘%s’ not in APN list",
+ cc_wwan_data_apn_get_name (apn));
+
+ apn->modified = FALSE;
+ g_task_return_boolean (task, TRUE);
+ }
+ else
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ }
+}
+
+static void
+cc_wwan_data_new_connection_added_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CcWwanData *self;
+ CcWwanDataApn *apn;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ self = g_task_get_source_object (G_TASK (task));
+ apn = g_task_get_task_data (G_TASK (task));
+ apn->remote_connection = nm_client_add_connection_finish (self->nm_client,
+ result, &error);
+ if (!error)
+ {
+ apn->modified = FALSE;
+
+ /* If APN has access method, it’s already on the list */
+ if (!apn->access_method)
+ {
+ g_list_store_append (self->apn_list, apn);
+ g_object_unref (apn);
+ }
+
+ g_task_return_pointer (task, apn, NULL);
+ }
+ else
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ }
+}
+
+void
+cc_wwan_data_save_apn (CcWwanData *self,
+ CcWwanDataApn *apn,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ NMConnection *connection = NULL;
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (CC_IS_WWAN_DATA (self));
+ g_return_if_fail (CC_IS_WWAN_DATA_APN (apn));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, apn, NULL);
+
+ connection = wwan_data_get_nm_connection (apn);
+
+ /* If the item has a remote connection, it should already be saved.
+ * We should save it again only if it got modified */
+ if (apn->remote_connection && !apn->modified)
+ {
+ g_task_return_pointer (task, apn, NULL);
+ return;
+ }
+
+ wwan_data_update_apn (self, apn, connection);
+ if (wwan_data_apn_is_new (apn))
+ {
+ nm_client_add_connection_async (self->nm_client, apn->nm_connection,
+ TRUE, cancellable,
+ cc_wwan_data_new_connection_added_cb,
+ g_steal_pointer (&task));
+ }
+ else
+ {
+ nm_remote_connection_commit_changes_async (apn->remote_connection, TRUE,
+ cancellable,
+ cc_wwan_data_connection_updated_cb,
+ g_steal_pointer (&task));
+ }
+}
+
+CcWwanDataApn *
+cc_wwan_data_save_apn_finish (CcWwanData *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (CC_IS_WWAN_DATA (self), NULL);
+ g_return_val_if_fail (G_IS_TASK (result), NULL);
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+cc_wwan_data_activated_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CcWwanData *self;
+ NMActiveConnection *connection;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ self = g_task_get_source_object (G_TASK (task));
+ connection = nm_client_activate_connection_finish (self->nm_client,
+ result, &error);
+ if (connection)
+ {
+ g_set_object (&self->active_connection, connection);
+ g_task_return_boolean (task, TRUE);
+ }
+ else
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ }
+
+ if (error)
+ g_warning ("Error: %s", error->message);
+}
+
+static void
+cc_wwan_data_disconnect_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CcWwanData *self;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ self = g_task_get_source_object (G_TASK (task));
+ if (nm_device_disconnect_finish (self->nm_device, result, &error))
+ {
+ g_clear_object (&self->active_connection);
+ g_task_return_boolean (task, TRUE);
+ }
+ else
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ }
+
+ if (error)
+ g_warning ("Error: %s", error->message);
+}
+
+static void
+cc_wwan_data_settings_saved_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CcWwanData *self;
+ GCancellable *cancellable;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ self = g_task_get_source_object (G_TASK (task));
+ cancellable = g_task_get_cancellable (G_TASK (task));
+
+ if (!cc_wwan_data_save_apn_finish (self, result, &error))
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ self->default_apn->modified = FALSE;
+
+ if (self->data_enabled)
+ {
+ nm_client_activate_connection_async (self->nm_client,
+ NM_CONNECTION (self->default_apn->remote_connection),
+ self->nm_device,
+ NULL, cancellable,
+ cc_wwan_data_activated_cb,
+ g_steal_pointer (&task));
+ }
+ else
+ {
+ nm_device_disconnect_async (self->nm_device,
+ cancellable,
+ cc_wwan_data_disconnect_cb,
+ g_steal_pointer (&task));
+ }
+}
+
+/**
+ * cc_wwan_data_save_settings:
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ * @callback: a #GAsyncReadyCallback, or %NULL
+ * @user_data: closure data for @callback
+ *
+ * Save default settings to disk and apply changes.
+ * If the default APN has data enabled, the data is
+ * activated after the settings are saved.
+ *
+ * It’s a programmer error to call this function without
+ * a default APN set.
+ * Finish with cc_wwan_data_save_settings_finish().
+ */
+void
+cc_wwan_data_save_settings (CcWwanData *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ NMConnection *connection;
+ NMSetting *setting;
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (CC_IS_WWAN_DATA (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+ g_return_if_fail (self->default_apn != NULL);
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ /* Reset old settings to default value */
+ if (self->old_default_apn && self->old_default_apn->remote_connection)
+ {
+ connection = NM_CONNECTION (self->old_default_apn->remote_connection);
+
+ setting = NM_SETTING (nm_connection_get_setting_gsm (connection));
+ g_object_set (G_OBJECT (setting),
+ NM_SETTING_GSM_HOME_ONLY, TRUE,
+ NM_SETTING_GSM_SIM_ID, NULL,
+ NULL);
+
+ setting = NM_SETTING (nm_connection_get_setting_ip4_config (connection));
+ g_object_set (setting,
+ NM_SETTING_IP_CONFIG_DNS_PRIORITY, CC_WWAN_DNS_PRIORITY_LOW,
+ NM_SETTING_IP_CONFIG_ROUTE_METRIC, (gint64)CC_WWAN_ROUTE_PRIORITY_LOW,
+ NULL);
+
+ setting = NM_SETTING (nm_connection_get_setting_connection (connection));
+ g_object_set (G_OBJECT (setting),
+ NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
+ NULL);
+
+ nm_remote_connection_commit_changes (NM_REMOTE_CONNECTION (connection),
+ TRUE, cancellable, NULL);
+ self->old_default_apn->modified = FALSE;
+ self->old_default_apn = NULL;
+ }
+
+ self->default_apn->modified = TRUE;
+ connection = wwan_data_get_nm_connection (self->default_apn);
+
+ setting = NM_SETTING (nm_connection_get_setting_gsm (connection));
+ g_object_set (G_OBJECT (setting),
+ NM_SETTING_GSM_HOME_ONLY, self->home_only,
+ NM_SETTING_GSM_SIM_ID, self->sim_id,
+ NULL);
+
+ cc_wwan_data_save_apn (self, self->default_apn, cancellable,
+ cc_wwan_data_settings_saved_cb,
+ g_steal_pointer (&task));
+}
+
+gboolean
+cc_wwan_data_save_settings_finish (CcWwanData *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (CC_IS_WWAN_DATA (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+gboolean
+cc_wwan_data_delete_apn (CcWwanData *self,
+ CcWwanDataApn *apn,
+ GCancellable *cancellable,
+ GError **error)
+{
+ NMRemoteConnection *connection = NULL;
+ gboolean ret = FALSE;
+ gint apn_index;
+
+ g_return_val_if_fail (CC_IS_WWAN_DATA (self), FALSE);
+ g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+ g_return_val_if_fail (CC_IS_WWAN_DATA_APN (apn), FALSE);
+ g_return_val_if_fail (error != NULL, FALSE);
+
+ apn_index = wwan_data_get_apn_index (self, apn);
+ if (apn_index == -1)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ "APN not found for the connection");
+ return FALSE;
+ }
+
+ connection = g_steal_pointer (&apn->remote_connection);
+ wwan_data_apn_reset (apn);
+
+ if (connection)
+ ret = nm_remote_connection_delete (connection, cancellable, error);
+
+ if (!ret)
+ {
+ apn->remote_connection = connection;
+ *error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Deleting APN from NetworkManager failed");
+ return ret;
+ }
+
+ g_object_unref (connection);
+
+ /* We remove the item only if it's not in the mobile provider database */
+ if (!apn->access_method)
+ {
+ if (self->default_apn == apn)
+ self->default_apn = NULL;
+
+ g_list_store_remove (self->apn_list, apn_index);
+
+ return TRUE;
+ }
+
+ *error = g_error_new (G_IO_ERROR, G_IO_ERROR_READ_ONLY,
+ "Deleting APN from NetworkManager failed");
+ return FALSE;
+}
+
+CcWwanDataApn *
+cc_wwan_data_get_default_apn (CcWwanData *self)
+{
+ g_return_val_if_fail (CC_IS_WWAN_DATA (self), NULL);
+
+ return self->default_apn;
+}
+
+gboolean
+cc_wwan_data_set_default_apn (CcWwanData *self,
+ CcWwanDataApn *apn)
+{
+ NMConnection *connection;
+ NMSetting *setting;
+
+ g_return_val_if_fail (CC_IS_WWAN_DATA (self), FALSE);
+ g_return_val_if_fail (CC_IS_WWAN_DATA_APN (apn), FALSE);
+
+ if (self->default_apn == apn)
+ return FALSE;
+
+ /*
+ * APNs are bound to the SIM, not the modem device.
+ * This will let the APN work if the same SIM inserted
+ * in a different device, and not enable data if a
+ * different SIM is inserted to the modem.
+ */
+ apn->modified = TRUE;
+ self->old_default_apn = self->default_apn;
+ self->default_apn = apn;
+ connection = wwan_data_get_nm_connection (apn);
+ setting = NM_SETTING (nm_connection_get_setting_gsm (connection));
+
+ if (self->sim_id)
+ g_object_set (G_OBJECT (setting),
+ NM_SETTING_GSM_SIM_ID, self->sim_id, NULL);
+
+ return TRUE;
+}
+
+gboolean
+cc_wwan_data_get_enabled (CcWwanData *self)
+{
+ NMSettingConnection *setting;
+ NMConnection *connection;
+ NMDeviceState state;
+
+ g_return_val_if_fail (CC_IS_WWAN_DATA (self), FALSE);
+
+ state = nm_device_get_state (self->nm_device);
+
+ if (state == NM_DEVICE_STATE_DISCONNECTED ||
+ state == NM_DEVICE_STATE_DEACTIVATING)
+ if (nm_device_get_state_reason (self->nm_device) == NM_DEVICE_STATE_REASON_USER_REQUESTED)
+ return FALSE;
+
+ if (nm_device_get_active_connection (self->nm_device) != NULL)
+ return TRUE;
+
+ if (!self->default_apn || !self->default_apn->remote_connection)
+ return FALSE;
+
+ connection = NM_CONNECTION (self->default_apn->remote_connection);
+ setting = nm_connection_get_setting_connection (connection);
+
+ return nm_setting_connection_get_autoconnect (setting);
+}
+
+/**
+ * cc_wwan_data_set_enabled:
+ * @self: A #CcWwanData
+ * @enable_data: whether to enable data
+ *
+ * Enable data for the device. The settings is
+ * saved to disk only after a default APN is set.
+ *
+ * If the data is enabled, the device will automatically
+ * turn data on everytime the same SIM is available.
+ * The data set is bound to the SIM, not the modem device.
+ *
+ * Use @cc_wwan_data_save_apn() with the default APN
+ * to save the changes and really enable/disable data.
+ */
+void
+cc_wwan_data_set_enabled (CcWwanData *self,
+ gboolean enable_data)
+{
+ g_return_if_fail (CC_IS_WWAN_DATA (self));
+
+ self->data_enabled = !!enable_data;
+
+ if (self->default_apn)
+ self->default_apn->modified = TRUE;
+}
+
+gboolean
+cc_wwan_data_get_roaming_enabled (CcWwanData *self)
+{
+ g_return_val_if_fail (CC_IS_WWAN_DATA (self), FALSE);
+
+ if (!self->default_apn)
+ return FALSE;
+
+ return !self->home_only;
+}
+
+/**
+ * cc_wwan_data_apn_set_roaming_enabled:
+ * @self: A #CcWwanData
+ * @enable_roaming: whether to enable roaming or not
+ *
+ * Enable roaming for the device. The settings is
+ * saved to disk only after a default APN is set.
+ *
+ * Use @cc_wwan_data_save_apn() with the default APN
+ * to save the changes and really enable/disable data.
+ */
+void
+cc_wwan_data_set_roaming_enabled (CcWwanData *self,
+ gboolean enable_roaming)
+{
+ g_return_if_fail (CC_IS_WWAN_DATA (self));
+
+ self->home_only = !enable_roaming;
+
+ if (self->default_apn)
+ self->default_apn->modified = TRUE;
+}
+
+static void
+cc_wwan_data_apn_finalize (GObject *object)
+{
+ CcWwanDataApn *apn = CC_WWAN_DATA_APN (object);
+
+ wwan_data_apn_reset (apn);
+ g_clear_pointer (&apn->access_method,
+ nma_mobile_access_method_unref);
+
+ G_OBJECT_CLASS (cc_wwan_data_parent_class)->finalize (object);
+}
+
+static void
+cc_wwan_data_apn_class_init (CcWwanDataApnClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = cc_wwan_data_apn_finalize;
+}
+
+static void
+cc_wwan_data_apn_init (CcWwanDataApn *apn)
+{
+}
+
+CcWwanDataApn *
+cc_wwan_data_apn_new (void)
+{
+ return g_object_new (CC_TYPE_WWAN_DATA_APN, NULL);
+}
+
+/**
+ * cc_wwan_data_apn_get_name:
+ * @apn: A #CcWwanDataApn
+ *
+ * Get the Name of @apn
+ *
+ * Returns: (transfer none): The Name of @apn
+ */
+const gchar *
+cc_wwan_data_apn_get_name (CcWwanDataApn *apn)
+{
+ g_return_val_if_fail (CC_IS_WWAN_DATA_APN (apn), "");
+
+ if (apn->remote_connection)
+ return nm_connection_get_id (NM_CONNECTION (apn->remote_connection));
+
+ if (apn->access_method)
+ return nma_mobile_access_method_get_name (apn->access_method);
+
+ return "";
+}
+
+/**
+ * cc_wwan_data_apn_set_name:
+ * @apn: A #CcWwanDataApn
+ * @name: The name to be given for APN, should not
+ * be empty
+ *
+ * Set the name of @apn to be @name.
+ *
+ * @apn is only modified, use @cc_wwan_data_save_apn()
+ * to save the changes.
+ */
+void
+cc_wwan_data_apn_set_name (CcWwanDataApn *apn,
+ const gchar *name)
+{
+ NMConnection *connection;
+ NMSettingConnection *setting;
+
+ g_return_if_fail (CC_IS_WWAN_DATA_APN (apn));
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (*name != '\0');
+
+ if (g_str_equal (cc_wwan_data_apn_get_name (apn), name))
+ return;
+
+ apn->modified = TRUE;
+ connection = wwan_data_get_nm_connection (apn);
+ setting = nm_connection_get_setting_connection (connection);
+ g_object_set (G_OBJECT (setting),
+ NM_SETTING_CONNECTION_ID, name,
+ NULL);
+}
+
+/**
+ * cc_wwan_data_apn_get_apn:
+ * @apn: A #CcWwanDataApn
+ *
+ * Get the APN of @apn
+ *
+ * Returns: (transfer none): The APN of @apn
+ */
+const gchar *
+cc_wwan_data_apn_get_apn (CcWwanDataApn *apn)
+{
+ const gchar *apn_name = "";
+
+ g_return_val_if_fail (CC_IS_WWAN_DATA_APN (apn), "");
+
+ if (apn->remote_connection)
+ {
+ NMSettingGsm *setting;
+
+ setting = nm_connection_get_setting_gsm (NM_CONNECTION (apn->remote_connection));
+ apn_name = nm_setting_gsm_get_apn (setting);
+ }
+ else if (apn->access_method)
+ {
+ apn_name = nma_mobile_access_method_get_3gpp_apn (apn->access_method);
+ }
+
+ return apn_name ? apn_name : "";
+}
+
+/**
+ * cc_wwan_data_apn_set_apn:
+ * @apn: A #CcWwanDataApn
+ * @apn_name: The apn to be used, should not be
+ * empty
+ *
+ * Set the APN of @apn to @apn_name. @apn_name is
+ * usually a URL like “example.com” or a simple string
+ * like “internet”
+ *
+ * @apn is only modified, use @cc_wwan_data_save_apn()
+ * to save the changes.
+ */
+void
+cc_wwan_data_apn_set_apn (CcWwanDataApn *apn,
+ const gchar *apn_name)
+{
+ NMConnection *connection;
+ NMSettingGsm *setting;
+
+ g_return_if_fail (CC_IS_WWAN_DATA_APN (apn));
+ g_return_if_fail (apn_name != NULL);
+ g_return_if_fail (*apn_name != '\0');
+
+ if (g_str_equal (cc_wwan_data_apn_get_apn (apn), apn_name))
+ return;
+
+ apn->modified = TRUE;
+ connection = wwan_data_get_nm_connection (apn);
+ setting = nm_connection_get_setting_gsm (connection);
+ g_object_set (G_OBJECT (setting),
+ NM_SETTING_GSM_APN, apn_name,
+ NULL);
+}
+
+/**
+ * cc_wwan_data_apn_get_username:
+ * @apn: A #CcWwanDataApn
+ *
+ * Get the Username of @apn
+ *
+ * Returns: (transfer none): The Username of @apn
+ */
+const gchar *
+cc_wwan_data_apn_get_username (CcWwanDataApn *apn)
+{
+ const gchar *username = "";
+
+ g_return_val_if_fail (CC_IS_WWAN_DATA_APN (apn), "");
+
+ if (apn->remote_connection)
+ {
+ NMSettingGsm *setting;
+
+ setting = nm_connection_get_setting_gsm (NM_CONNECTION (apn->remote_connection));
+ username = nm_setting_gsm_get_username (setting);
+ }
+ else if (apn->access_method)
+ {
+ username = nma_mobile_access_method_get_username (apn->access_method);
+ }
+
+ return username ? username : "";
+}
+
+/**
+ * cc_wwan_data_apn_set_username:
+ * @apn: A #CcWwanDataAPN
+ * @username: The username to be used
+ *
+ * Set the Username of @apn to @username.
+ *
+ * @apn is only modified, use @cc_wwan_data_save_apn()
+ * to save the changes.
+ */
+void
+cc_wwan_data_apn_set_username (CcWwanDataApn *apn,
+ const gchar *username)
+{
+ NMConnection *connection;
+ NMSettingGsm *setting;
+
+ g_return_if_fail (CC_IS_WWAN_DATA_APN (apn));
+
+ if (username && !*username)
+ username = NULL;
+
+ if (g_strcmp0 (cc_wwan_data_apn_get_username (apn), username) == 0)
+ return;
+
+ apn->modified = TRUE;
+ connection = wwan_data_get_nm_connection (apn);
+ setting = nm_connection_get_setting_gsm (connection);
+ g_object_set (G_OBJECT (setting),
+ NM_SETTING_GSM_USERNAME, username,
+ NULL);
+}
+
+/**
+ * cc_wwan_data_apn_get_password:
+ * @apn: A #CcWwanDataApn
+ *
+ * Get the Password of @apn
+ *
+ * Returns: (transfer none): The Password of @apn
+ */
+const gchar *
+cc_wwan_data_apn_get_password (CcWwanDataApn *apn)
+{
+ const gchar *password = "";
+
+ g_return_val_if_fail (CC_IS_WWAN_DATA_APN (apn), "");
+
+ if (NM_IS_REMOTE_CONNECTION (apn->remote_connection))
+ {
+ g_autoptr(GVariant) secrets = NULL;
+ g_autoptr(GError) error = NULL;
+
+ secrets = nm_remote_connection_get_secrets (NM_REMOTE_CONNECTION (apn->remote_connection),
+ "gsm", NULL, &error);
+
+ if (!error)
+ nm_connection_update_secrets (NM_CONNECTION (apn->remote_connection),
+ "gsm", secrets, &error);
+
+ if (error)
+ {
+ g_warning ("Error: %s", error->message);
+ return "";
+ }
+ }
+
+ if (apn->remote_connection)
+ {
+ NMSettingGsm *setting;
+
+ setting = nm_connection_get_setting_gsm (NM_CONNECTION (apn->remote_connection));
+ password = nm_setting_gsm_get_password (setting);
+ }
+ else if (apn->access_method)
+ {
+ password = nma_mobile_access_method_get_password (apn->access_method);
+ }
+
+ return password ? password : "";
+}
+
+/**
+ * cc_wwan_data_apn_set_password:
+ * @apn: A #CcWwanDataApn
+ * @password: The password to be used
+ *
+ * Set the Password of @apn to @password.
+ *
+ * @apn is only modified, use @cc_wwan_data_save_apn()
+ * to save the changes.
+ */
+void
+cc_wwan_data_apn_set_password (CcWwanDataApn *apn,
+ const gchar *password)
+{
+ NMConnection *connection;
+ NMSettingGsm *setting;
+
+ g_return_if_fail (CC_IS_WWAN_DATA_APN (apn));
+
+ if (password && !*password)
+ password = NULL;
+
+ if (g_strcmp0 (cc_wwan_data_apn_get_password (apn), password) == 0)
+ return;
+
+ apn->modified = TRUE;
+ connection = wwan_data_get_nm_connection (apn);
+ setting = nm_connection_get_setting_gsm (connection);
+ g_object_set (G_OBJECT (setting),
+ NM_SETTING_GSM_PASSWORD, password,
+ NULL);
+}
+
+gint
+cc_wwan_data_get_priority (CcWwanData *self)
+{
+ CcWwanDataApn *apn;
+ NMSettingIPConfig *setting;
+
+ g_return_val_if_fail (CC_IS_WWAN_DATA (self),
+ CC_WWAN_APN_PRIORITY_LOW);
+
+ apn = self->default_apn;
+
+ if (!apn || !apn->remote_connection)
+ return CC_WWAN_APN_PRIORITY_LOW;
+
+ setting = nm_connection_get_setting_ip4_config (NM_CONNECTION (apn->remote_connection));
+
+ /* Lower the number, higher the priority */
+ if (nm_setting_ip_config_get_route_metric (setting) <= CC_WWAN_ROUTE_PRIORITY_HIGH)
+ return CC_WWAN_APN_PRIORITY_HIGH;
+ else
+ return CC_WWAN_APN_PRIORITY_LOW;
+}
+
+void
+cc_wwan_data_set_priority (CcWwanData *self,
+ int priority)
+{
+ g_return_if_fail (CC_IS_WWAN_DATA (self));
+ g_return_if_fail (priority == CC_WWAN_APN_PRIORITY_LOW ||
+ priority == CC_WWAN_APN_PRIORITY_HIGH);
+
+ if (self->priority == priority)
+ return;
+
+ self->priority = priority;
+
+ if (self->default_apn)
+ self->default_apn->modified = TRUE;
+}