// SPDX-License-Identifier: GPL-2.0+
/* NetworkManager Applet -- allow user control over networking
*
* (C) Copyright 2012 Aleksander Morgado <aleksander@gnu.org>
*/
#include "nm-default.h"
#include <ctype.h>
#include <NetworkManager.h>
#include "applet.h"
#include "applet-device-broadband.h"
#include "applet-dialogs.h"
#include "mobile-helpers.h"
#include "mb-menu-item.h"
#define BROADBAND_INFO_TAG "devinfo"
typedef struct {
NMApplet *applet;
NMDevice *device;
MMObject *mm_object;
MMModem *mm_modem;
MMModem3gpp *mm_modem_3gpp;
MMModemCdma *mm_modem_cdma;
MMSim *mm_sim;
/* Operator info */
gchar *operator_name;
guint operator_name_update_id;
guint operator_code_update_id;
guint sid_update_id;
NMAMobileProvidersDatabase *mpd;
/* Unlock dialog stuff */
GtkWidget *dialog;
GCancellable *cancellable;
} BroadbandDeviceInfo;
/********************************************************************/
static gboolean
new_auto_connection (NMDevice *device,
gpointer dclass_data,
AppletNewAutoConnectionCallback callback,
gpointer callback_data)
{
return mobile_helper_wizard (nm_device_modem_get_current_capabilities (NM_DEVICE_MODEM (device)),
callback,
callback_data);
}
/********************************************************************/
typedef struct {
NMApplet *applet;
NMDevice *device;
} ConnectNetworkInfo;
static void
add_and_activate_connection_done (GObject *client,
GAsyncResult *result,
gpointer user_data)
{
GError *error = NULL;
if (!nm_client_add_and_activate_connection_finish (NM_CLIENT (client), result, &error)) {
g_warning ("Failed to add/activate connection: (%d) %s", error->code, error->message);
g_error_free (error);
}
}
static void
wizard_done (NMConnection *connection,
gboolean auto_created,
gboolean canceled,
gpointer user_data)
{
ConnectNetworkInfo *info = user_data;
if (canceled == FALSE) {
g_return_if_fail (connection != NULL);
/* Ask NM to add the new connection and activate it; NM will fill in the
* missing details based on the specific object and the device.
*/
nm_client_add_and_activate_connection_async (info->applet->nm_client,
connection,
info->device,
"/",
NULL,
add_and_activate_connection_done,
info->applet);
}
g_object_unref (info->device);
g_free (info);
}
void
applet_broadband_connect_network (NMApplet *applet,
NMDevice *device)
{
ConnectNetworkInfo *info;
info = g_malloc0 (sizeof (*info));
info->applet = applet;
info->device = g_object_ref (device);
if (!mobile_helper_wizard (nm_device_modem_get_current_capabilities (NM_DEVICE_MODEM (device)),
wizard_done,
info)) {
g_warning ("Couldn't run mobile wizard for broadband device");
g_object_unref (info->device);
g_free (info);
}
}
/********************************************************************/
static void unlock_dialog_new (NMDevice *device,
BroadbandDeviceInfo *info);
static void
unlock_dialog_destroy (BroadbandDeviceInfo *info)
{
gtk_widget_destroy (info->dialog);
info->dialog = NULL;
}
static void
dialog_sim_send_puk_ready (MMSim *sim,
GAsyncResult *res,
BroadbandDeviceInfo *info)
{
GError *error = NULL;
if (!mm_sim_send_puk_finish (sim, res, &error)) {
const gchar *msg;
if (g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD))
msg = _("Wrong PUK code; please contact your provider.");
else {
g_dbus_error_strip_remote_error (error);
msg = error->message;
}
applet_mobile_pin_dialog_stop_spinner (info->dialog, msg);
g_warning ("Failed to send PUK to devid: '%s' simid: '%s' : %s",
mm_modem_get_device_identifier (info->mm_modem),
mm_sim_get_identifier (info->mm_sim),
error->message);
g_error_free (error);
return;
}
/* Good */
unlock_dialog_destroy (info);
}
static void
dialog_sim_send_pin_ready (MMSim *sim,
GAsyncResult *res,
BroadbandDeviceInfo *info)
{
GError *error = NULL;
if (!mm_sim_send_pin_finish (sim, res, &error)) {
if (g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK)) {
/* Destroy previous dialog and launch a new one rebuilt to ask for PUK */
unlock_dialog_destroy (info);
unlock_dialog_new (info->device, info);
} else {
const gchar *msg = NULL;
/* Report error and re-try PIN request */
if (g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD))
msg = _("Wrong PIN code; please contact your provider.");
else {
g_dbus_error_strip_remote_error (error);
msg = error->message;
}
applet_mobile_pin_dialog_stop_spinner (info->dialog, msg);
}
g_warning ("Failed to send PIN to devid: '%s' simid: '%s' : %s",
mm_modem_get_device_identifier (info->mm_modem),
mm_sim_get_identifier (info->mm_sim),
error->message);
g_error_free (error);
return;
}
/* Good */
if (applet_mobile_pin_dialog_get_auto_unlock (info->dialog)) {
const gchar *code1;
code1 = applet_mobile_pin_dialog_get_entry1 (info->dialog);
mobile_helper_save_pin_in_keyring (mm_modem_get_device_identifier (info->mm_modem),
mm_sim_get_identifier (info->mm_sim),
code1);
} else
mobile_helper_delete_pin_in_keyring (mm_modem_get_device_identifier (info->mm_modem));
unlock_dialog_destroy (info);
}
static void
unlock_dialog_response (GtkDialog *dialog,
gint response,
gpointer user_data)
{
BroadbandDeviceInfo *info = user_data;
const char *code1, *code2;
MMModemLock lock;
if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT) {
unlock_dialog_destroy (info);
return;
}
lock = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (info->dialog), "unlock-code"));
g_assert (lock == MM_MODEM_LOCK_SIM_PIN || lock == MM_MODEM_LOCK_SIM_PUK);
/* Start the spinner to show the progress of the unlock */
applet_mobile_pin_dialog_start_spinner (info->dialog, _("Sending unlock code…"));
code1 = applet_mobile_pin_dialog_get_entry1 (info->dialog);
if (!code1 || !strlen (code1)) {
g_warn_if_reached ();
unlock_dialog_destroy (info);
return;
}
/* Send the code to ModemManager */
if (lock == MM_MODEM_LOCK_SIM_PIN) {
mm_sim_send_pin (info->mm_sim,
code1,
NULL, /* cancellable */
(GAsyncReadyCallback)dialog_sim_send_pin_ready,
info);
return;
}
if (lock == MM_MODEM_LOCK_SIM_PUK) {
code2 = applet_mobile_pin_dialog_get_entry2 (info->dialog);
if (!code2) {
g_warn_if_reached ();
unlock_dialog_destroy (info);
return;
}
mm_sim_send_puk (info->mm_sim,
code1, /* puk */
code2, /* new pin */
NULL, /* cancellable */
(GAsyncReadyCallback)dialog_sim_send_puk_ready,
info);
return;
}
g_assert_not_reached ();
}
static void
unlock_dialog_new (NMDevice *device,
BroadbandDeviceInfo *info)
{
MMModemLock lock;
const gchar *unlock_required;
if (info->dialog)
return;
/* We can only unlock PIN or PUK here */
lock = mm_modem_get_unlock_required (info->mm_modem);
if (lock == MM_MODEM_LOCK_SIM_PIN)
unlock_required = "sim-pin";
else if (lock == MM_MODEM_LOCK_SIM_PUK)
unlock_required = "sim-puk";
else {
g_warning ("Cannot unlock devid: '%s' simid: '%s' : unhandled lock code '%s'",
mm_modem_get_device_identifier (info->mm_modem),
mm_sim_get_identifier (info->mm_sim),
mm_modem_lock_get_string (lock));
return;
}
info->dialog = applet_mobile_pin_dialog_new (unlock_required,
nm_device_get_description (device));
g_object_set_data (G_OBJECT (info->dialog), "unlock-code", GUINT_TO_POINTER (lock));
g_signal_connect (info->dialog, "response", G_CALLBACK (unlock_dialog_response), info);
/* Need to resize the dialog after hiding widgets */
gtk_window_resize (GTK_WINDOW (info->dialog), 400, 100);
/* Show the dialog */
gtk_widget_realize (info->dialog);
gtk_window_present (GTK_WINDOW (info->dialog));
}
static void
autounlock_sim_send_pin_ready (MMSim *sim,
GAsyncResult *res,
BroadbandDeviceInfo *info)
{
GError *error = NULL;
if (!mm_sim_send_pin_finish (sim, res, &error)) {
g_warning ("Failed to auto-unlock devid: '%s' simid: '%s' : %s",
mm_modem_get_device_identifier (info->mm_modem),
mm_sim_get_identifier (info->mm_sim),
error->message);
g_error_free (error);
/* Remove PIN from keyring right away */
mobile_helper_delete_pin_in_keyring (mm_modem_get_device_identifier (info->mm_modem));
/* Ask the user */
unlock_dialog_new (info->device, info);
}
}
static void
keyring_pin_check_cb (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
BroadbandDeviceInfo *info = user_data;
GList *iter;
GList *list;
SecretItem *item;
SecretValue *pin = NULL;
GHashTable *attributes;
GError *error = NULL;
const char *simid;
list = secret_service_search_finish (NULL, result, &error);
if (error != NULL) {
/* No saved PIN, just ask the user */
unlock_dialog_new (info->device, info);
g_error_free (error);
return;
}
/* Look for a result with a matching "simid" attribute since that's
* better than just using a matching "devid". The PIN is really tied
* to the SIM, not the modem itself.
*/
simid = mm_sim_get_identifier (info->mm_sim);
if (simid) {
for (iter = list;
(pin == NULL) && iter;
iter = g_list_next (iter)) {
item = iter->data;
/* Look for a matching "simid" attribute */
attributes = secret_item_get_attributes (item);
if (g_strcmp0 (simid, g_hash_table_lookup (attributes, "simid")))
pin = secret_item_get_secret (item);
else
pin = NULL;
g_hash_table_unref (attributes);
if (pin != NULL)
break;
}
}
if (pin == NULL) {
/* Fall back to the first result's PIN if we have one */
if (list)
pin = secret_item_get_secret (list->data);
if (pin == NULL) {
unlock_dialog_new (info->device, info);
return;
}
}
/* Send the PIN code to ModemManager */
mm_sim_send_pin (info->mm_sim,
secret_value_get (pin, NULL),
NULL, /* cancellable */
(GAsyncReadyCallback)autounlock_sim_send_pin_ready,
info);
secret_value_unref (pin);
}
static void
modem_get_sim_ready (MMModem *modem,
GAsyncResult *res,
BroadbandDeviceInfo *info)
{
GHashTable *attrs;
info->mm_sim = mm_modem_get_sim_finish (modem, res, NULL);
if (!info->mm_sim)
/* Ok, the modem may not need it actually */
return;
/* Do nothing if we're not locked */
if (mm_modem_get_state (info->mm_modem) != MM_MODEM_STATE_LOCKED)
return;
/* If we have a device ID ask the keyring for any saved SIM-PIN codes */
if (mm_modem_get_device_identifier (info->mm_modem) &&
mm_modem_get_unlock_required (info->mm_modem) == MM_MODEM_LOCK_SIM_PIN) {
attrs = secret_attributes_build (&mobile_secret_schema, "devid",
mm_modem_get_device_identifier (info->mm_modem),
NULL);
secret_service_search (NULL, &mobile_secret_schema, attrs,
SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS,
info->cancellable, keyring_pin_check_cb, info);
g_hash_table_unref (attrs);
} else {
/* Couldn't get a device ID, but unlock required; present dialog */
unlock_dialog_new (info->device, info);
}
}
/********************************************************************/
static gboolean
get_secrets (SecretsRequest *req,
GError **error)
{
NMDevice *device;
BroadbandDeviceInfo *devinfo;
device = applet_get_device_for_connection (req->applet, req->connection);
if (!device) {
g_set_error (error,
NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_FAILED,
"%s.%d (%s): failed to find device for active connection.",
__FILE__, __LINE__, __func__);
return FALSE;
}
if (!mobile_helper_get_secrets (nm_device_modem_get_current_capabilities (NM_DEVICE_MODEM (device)),
req,
error))
return FALSE;
devinfo = g_object_get_data (G_OBJECT (device), BROADBAND_INFO_TAG);
if (!devinfo) {
g_set_error (error,
NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_FAILED,
"%s.%d (%s): ModemManager is not available for modem at %s",
__FILE__, __LINE__, __func__,
nm_device_get_udi (device));
return FALSE;
}
/* A GetSecrets PIN dialog overrides the initial unlock dialog */
if (devinfo->dialog)
unlock_dialog_destroy (devinfo);
return TRUE;
}
/********************************************************************/
static guint32
broadband_state_to_mb_state (BroadbandDeviceInfo *info)
{
MMModemState state;
state = mm_modem_get_state (info->mm_modem);
switch (state) {
case MM_MODEM_STATE_FAILED:
case MM_MODEM_STATE_UNKNOWN:
case MM_MODEM_STATE_INITIALIZING:
case MM_MODEM_STATE_LOCKED:
case MM_MODEM_STATE_DISABLED:
case MM_MODEM_STATE_DISABLING:
case MM_MODEM_STATE_ENABLING:
return MB_STATE_UNKNOWN;
case MM_MODEM_STATE_ENABLED:
return MB_STATE_IDLE;
case MM_MODEM_STATE_SEARCHING:
return MB_STATE_SEARCHING;
case MM_MODEM_STATE_REGISTERED:
case MM_MODEM_STATE_DISCONNECTING:
case MM_MODEM_STATE_CONNECTING:
case MM_MODEM_STATE_CONNECTED:
break;
default:
g_warn_if_reached ();
return MB_STATE_UNKNOWN;
}
/* home or roaming? */
if (info->mm_modem_3gpp) {
switch (mm_modem_3gpp_get_registration_state (info->mm_modem_3gpp)) {
case MM_MODEM_3GPP_REGISTRATION_STATE_HOME:
return MB_STATE_HOME;
case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING:
return MB_STATE_ROAMING;
default:
/* Skip, we may be registered in EVDO/CDMA1x instead... */
break;
}
}
if (info->mm_modem_cdma) {
/* EVDO state overrides 1X state for now */
switch (mm_modem_cdma_get_evdo_registration_state (info->mm_modem_cdma)) {
case MM_MODEM_CDMA_REGISTRATION_STATE_HOME:
return MB_STATE_HOME;
case MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING:
return MB_STATE_ROAMING;
case MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED:
/* Assume home... */
return MB_STATE_HOME;
default:
/* Skip, we may be registered in CDMA1x instead... */
break;
}
switch (mm_modem_cdma_get_cdma1x_registration_state (info->mm_modem_cdma)) {
case MM_MODEM_CDMA_REGISTRATION_STATE_HOME:
return MB_STATE_HOME;
case MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING:
return MB_STATE_ROAMING;
case MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED:
/* Assume home... */
return MB_STATE_HOME;
default:
break;
}
}
return MB_STATE_UNKNOWN;
}
static guint32
broadband_act_to_mb_act (BroadbandDeviceInfo *info)
{
MMModemAccessTechnology act;
act = mm_modem_get_access_technologies (info->mm_modem);
g_return_val_if_fail (act != MM_MODEM_ACCESS_TECHNOLOGY_ANY, MB_TECH_UNKNOWN);
/* We get a MASK of values, but we need to report only ONE.
* So just return the 'best' one found */
/* Prefer 4G technologies over 3G and 2G */
if (act & MM_MODEM_ACCESS_TECHNOLOGY_LTE)
return MB_TECH_LTE;
/* Prefer 3GPP 3G technologies over 3GPP 2G or 3GPP2 */
if (act & MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS)
return MB_TECH_HSPA_PLUS;
if (act & MM_MODEM_ACCESS_TECHNOLOGY_HSPA)
return MB_TECH_HSPA;
if (act & MM_MODEM_ACCESS_TECHNOLOGY_HSUPA)
return MB_TECH_HSUPA;
if (act & MM_MODEM_ACCESS_TECHNOLOGY_HSDPA)
return MB_TECH_HSDPA;
if (act & MM_MODEM_ACCESS_TECHNOLOGY_UMTS)
return MB_TECH_UMTS;
/* Prefer 3GPP2 3G technologies over 2G */
if (act & MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 ||
act & MM_MODEM_ACCESS_TECHNOLOGY_EVDOA ||
act & MM_MODEM_ACCESS_TECHNOLOGY_EVDOB)
return MB_TECH_EVDO;
/* Prefer 3GPP 2G technologies over 3GPP2 2G */
if (act & MM_MODEM_ACCESS_TECHNOLOGY_EDGE)
return MB_TECH_EDGE;
if (act & MM_MODEM_ACCESS_TECHNOLOGY_GPRS)
return MB_TECH_GPRS;
if (act & MM_MODEM_ACCESS_TECHNOLOGY_GSM ||
act & MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT)
return MB_TECH_GSM;
/* Last, 3GPP2 2G */
if (act & MM_MODEM_ACCESS_TECHNOLOGY_1XRTT)
return MB_TECH_1XRTT;
return MB_TECH_UNKNOWN;
}
static void
get_icon (NMDevice *device,
NMDeviceState state,
NMConnection *connection,
GdkPixbuf **out_pixbuf,
const char **out_icon_name,
char **tip,
NMApplet *applet)
{
BroadbandDeviceInfo *info;
g_return_if_fail (out_icon_name && !*out_icon_name);
g_return_if_fail (tip && !*tip);
if (!applet->mm1) {
g_warning ("ModemManager is not available for modem at %s", nm_device_get_udi (device));
return;
}
info = g_object_get_data (G_OBJECT (device), BROADBAND_INFO_TAG);
if (!info) {
g_warning ("ModemManager is not available for modem at %s",
nm_device_get_udi (device));
return;
}
mobile_helper_get_icon (device,
state,
connection,
out_pixbuf,
out_icon_name,
tip,
applet,
broadband_state_to_mb_state (info),
broadband_act_to_mb_act (info),
mm_modem_get_signal_quality (info->mm_modem, NULL),
(mm_modem_get_state (info->mm_modem) >= MM_MODEM_STATE_ENABLED));
}
/********************************************************************/
typedef struct {
NMApplet *applet;
NMDevice *device;
NMConnection *connection;
} BroadbandMenuItemInfo;
static void
menu_item_info_destroy (gpointer data, GClosure *closure)
{
BroadbandMenuItemInfo *info = data;
g_object_unref (G_OBJECT (info->device));
if (info->connection)
g_object_unref (info->connection);
g_slice_free (BroadbandMenuItemInfo, info);
}
static void
menu_item_activate (GtkMenuItem *item,
BroadbandMenuItemInfo *info)
{
applet_menu_item_activate_helper (info->device,
info->connection,
"/",
info->applet,
info);
}
static void
add_connection_item (NMDevice *device,
NMConnection *connection,
GtkWidget *item,
GtkWidget *menu,
NMApplet *applet)
{
BroadbandMenuItemInfo *info;
info = g_slice_new0 (BroadbandMenuItemInfo);
info->applet = applet;
info->device = g_object_ref (device);
info->connection = connection ? g_object_ref (connection) : NULL;
g_signal_connect_data (item, "activate",
G_CALLBACK (menu_item_activate),
info,
(GClosureNotify) menu_item_info_destroy, 0);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
gtk_widget_show (item);
}
static void
add_menu_item (NMDevice *device,
gboolean multiple_devices,
const GPtrArray *connections,
NMConnection *active,
GtkWidget *menu,
NMApplet *applet)
{
BroadbandDeviceInfo *info;
char *text;
GtkWidget *item;
int i;
info = g_object_get_data (G_OBJECT (device), BROADBAND_INFO_TAG);
if (!info) {
g_warning ("ModemManager is not available for modem at %s",
nm_device_get_udi (device));
return;
}
if (multiple_devices) {
const char *desc;
desc = nm_device_get_description (device);
text = g_strdup_printf (_("Mobile Broadband (%s)"), desc);
} else {
text = g_strdup (_("Mobile Broadband"));
}
item = applet_menu_item_create_device_item_helper (device, applet, text);
gtk_widget_set_sensitive (item, FALSE);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
gtk_widget_show (item);
g_free (text);
/* Add the active connection */
if (active) {
NMSettingConnection *s_con;
s_con = nm_connection_get_setting_connection (active);
g_assert (s_con);
item = nm_mb_menu_item_new (nm_setting_connection_get_id (s_con),
mm_modem_get_signal_quality (info->mm_modem, NULL),
info->operator_name,
TRUE,
broadband_act_to_mb_act (info),
broadband_state_to_mb_state (info),
mm_modem_get_state (info->mm_modem) >= MM_MODEM_STATE_ENABLED,
applet);
gtk_widget_set_sensitive (GTK_WIDGET (item), TRUE);
add_connection_item (device, active, item, menu, applet);
}
/* Notify user of unmanaged or unavailable device */
if (nm_device_get_state (device) > NM_DEVICE_STATE_DISCONNECTED) {
item = nma_menu_device_get_menu_item (device, applet, NULL);
if (item) {
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
gtk_widget_show (item);
}
} else {
/* Otherwise show idle registration state or disabled */
item = nm_mb_menu_item_new (NULL,
mm_modem_get_signal_quality (info->mm_modem, NULL),
info->operator_name,
FALSE,
broadband_act_to_mb_act (info),
broadband_state_to_mb_state (info),
mm_modem_get_state (info->mm_modem) >= MM_MODEM_STATE_ENABLED,
applet);
gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
gtk_widget_show (item);
}
/* Add the default / inactive connection items */
if (!nma_menu_device_check_unusable (device)) {
if ((!active && connections->len) || (active && connections->len > 1))
applet_menu_item_add_complex_separator_helper (menu, applet, _("Available"));
if (connections->len) {
for (i = 0; i < connections->len; i++) {
NMConnection *connection = NM_CONNECTION (connections->pdata[i]);
if (connection != active) {
item = applet_new_menu_item_helper (connection, NULL, FALSE);
add_connection_item (device, connection, item, menu, applet);
}
}
} else {
/* Default connection item */
item = gtk_check_menu_item_new_with_label (_("New Mobile Broadband connection…"));
add_connection_item (device, NULL, item, menu, applet);
}
}
}
/********************************************************************/
static void
notify_connected (NMDevice *device,
const char *msg,
NMApplet *applet)
{
applet_do_notify_with_pref (applet,
_("Connection Established"),
msg ? msg : _("You are now connected to the Mobile Broadband network."),
"nm-device-wwan",
PREF_DISABLE_CONNECTED_NOTIFICATIONS);
}
/********************************************************************/
static void
signal_quality_updated (GObject *object,
GParamSpec *pspec,
BroadbandDeviceInfo *info)
{
applet_schedule_update_icon (info->applet);
applet_schedule_update_menu (info->applet);
}
static void
access_technologies_updated (GObject *object,
GParamSpec *pspec,
BroadbandDeviceInfo *info)
{
applet_schedule_update_icon (info->applet);
applet_schedule_update_menu (info->applet);
}
static void
operator_info_updated (GObject *object,
GParamSpec *pspec,
BroadbandDeviceInfo *info)
{
g_free (info->operator_name);
info->operator_name = NULL;
/* Prefer 3GPP info if given */
if (info->mm_modem_3gpp) {
info->operator_name = (mobile_helper_parse_3gpp_operator_name (
&(info->mpd),
mm_modem_3gpp_get_operator_name (info->mm_modem_3gpp),
mm_modem_3gpp_get_operator_code (info->mm_modem_3gpp)));
if (info->operator_name)
return;
}
if (info->mm_modem_cdma)
info->operator_name = (mobile_helper_parse_3gpp2_operator_name (
&(info->mpd),
mm_modem_cdma_get_sid (info->mm_modem_cdma)));
}
static void
setup_signals (BroadbandDeviceInfo *info,
gboolean enable)
{
if (enable) {
g_assert (info->mm_modem_3gpp == NULL);
g_assert (info->mm_modem_cdma == NULL);
g_assert (info->operator_name_update_id == 0);
g_assert (info->operator_code_update_id == 0);
g_assert (info->sid_update_id == 0);
info->mm_modem_3gpp = mm_object_get_modem_3gpp (info->mm_object);
info->mm_modem_cdma = mm_object_get_modem_cdma (info->mm_object);
if (info->mm_modem_3gpp) {
info->operator_name_update_id = g_signal_connect (info->mm_modem_3gpp,
"notify::operator-name",
G_CALLBACK (operator_info_updated),
info);
info->operator_code_update_id = g_signal_connect (info->mm_modem_3gpp,
"notify::operator-code",
G_CALLBACK (operator_info_updated),
info);
}
if (info->mm_modem_cdma) {
info->sid_update_id = g_signal_connect (info->mm_modem_cdma,
"notify::sid",
G_CALLBACK (operator_info_updated),
info);
}
/* Load initial values */
operator_info_updated (NULL, NULL, info);
} else {
if (info->mm_modem_3gpp) {
if (info->operator_name_update_id) {
if (g_signal_handler_is_connected (info->mm_modem_3gpp, info->operator_name_update_id))
g_signal_handler_disconnect (info->mm_modem_3gpp, info->operator_name_update_id);
info->operator_name_update_id = 0;
}
if (info->operator_code_update_id) {
if (g_signal_handler_is_connected (info->mm_modem_3gpp, info->operator_code_update_id))
g_signal_handler_disconnect (info->mm_modem_3gpp, info->operator_code_update_id);
info->operator_code_update_id = 0;
}
g_clear_object (&info->mm_modem_3gpp);
}
if (info->mm_modem_cdma) {
if (info->sid_update_id) {
if (g_signal_handler_is_connected (info->mm_modem_cdma, info->sid_update_id))
g_signal_handler_disconnect (info->mm_modem_cdma, info->sid_update_id);
info->sid_update_id = 0;
}
g_clear_object (&info->mm_modem_cdma);
}
}
}
static void
modem_state_changed (MMModem *object,
gint old,
gint new,
guint reason,
BroadbandDeviceInfo *info)
{
/* Modem just got enabled */
if (old < MM_MODEM_STATE_ENABLED &&
new >= MM_MODEM_STATE_ENABLED) {
setup_signals (info, TRUE);
}
/* Modem just got disabled */
else if (old >= MM_MODEM_STATE_ENABLED &&
new < MM_MODEM_STATE_ENABLED) {
setup_signals (info, FALSE);
}
/* Modem just got registered */
if ((old < MM_MODEM_STATE_REGISTERED &&
new >= MM_MODEM_STATE_REGISTERED)) {
guint32 mb_state;
const char *signal_strength_icon;
signal_strength_icon = mobile_helper_get_quality_icon_name (mm_modem_get_signal_quality(info->mm_modem, NULL));
/* Notify about new registration info */
mb_state = broadband_state_to_mb_state (info);
if (mb_state == MB_STATE_HOME) {
applet_do_notify_with_pref (info->applet,
_("Mobile Broadband network."),
_("You are now registered on the home network."),
signal_strength_icon,
PREF_DISABLE_CONNECTED_NOTIFICATIONS);
} else if (mb_state == MB_STATE_ROAMING) {
applet_do_notify_with_pref (info->applet,
_("Mobile Broadband network."),
_("You are now registered on a roaming network."),
signal_strength_icon,
PREF_DISABLE_CONNECTED_NOTIFICATIONS);
}
}
}
/********************************************************************/
static void
broadband_device_info_free (BroadbandDeviceInfo *info)
{
setup_signals (info, FALSE);
g_free (info->operator_name);
if (info->mpd)
g_object_unref (info->mpd);
if (info->mm_sim)
g_object_unref (info->mm_sim);
if (info->mm_modem) {
g_signal_handlers_disconnect_matched (info->mm_modem, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, info);
g_object_unref (info->mm_modem);
}
if (info->mm_object)
g_object_unref (info->mm_object);
if (info->dialog)
unlock_dialog_destroy (info);
g_object_unref (info->cancellable);
g_slice_free (BroadbandDeviceInfo, info);
}
static void
device_added (NMDevice *device,
NMApplet *applet)
{
NMDeviceModem *modem = NM_DEVICE_MODEM (device);
BroadbandDeviceInfo *info;
const char *udi;
GDBusObject *modem_object;
udi = nm_device_get_udi (device);
if (!udi)
return;
if (g_object_get_data (G_OBJECT (modem), BROADBAND_INFO_TAG))
return;
if (!applet->mm1_running) {
g_warning ("Cannot grab information for modem at %s: No ModemManager support",
nm_device_get_udi (device));
return;
}
modem_object = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (applet->mm1),
nm_device_get_udi (device));
if (!modem_object) {
g_warning ("Cannot grab information for modem at %s: Not found",
nm_device_get_udi (device));
return;
}
info = g_slice_new0 (BroadbandDeviceInfo);
info->applet = applet;
info->device = device;
info->mm_object = MM_OBJECT (modem_object);
info->mm_modem = mm_object_get_modem (info->mm_object);
info->cancellable = g_cancellable_new ();
/* Setup signals */
g_signal_connect (info->mm_modem,
"state-changed",
G_CALLBACK (modem_state_changed),
info);
g_signal_connect (info->mm_modem,
"notify::signal-quality",
G_CALLBACK (signal_quality_updated),
info);
g_signal_connect (info->mm_modem,
"notify::access-technologies",
G_CALLBACK (access_technologies_updated),
info);
/* Load initial values */
signal_quality_updated (NULL, NULL, info);
access_technologies_updated (NULL, NULL, info);
if (mm_modem_get_state (info->mm_modem) >= MM_MODEM_STATE_ENABLED)
setup_signals (info, TRUE);
/* Asynchronously get SIM */
mm_modem_get_sim (info->mm_modem,
NULL, /* cancellable */
(GAsyncReadyCallback)modem_get_sim_ready,
info);
/* Store device info */
g_object_set_data_full (G_OBJECT (modem),
BROADBAND_INFO_TAG,
info,
(GDestroyNotify)broadband_device_info_free);
}
/********************************************************************/
NMADeviceClass *
applet_device_broadband_get_class (NMApplet *applet)
{
NMADeviceClass *dclass;
dclass = g_slice_new0 (NMADeviceClass);
if (!dclass)
return NULL;
dclass->new_auto_connection = new_auto_connection;
dclass->add_menu_item = add_menu_item;
dclass->device_added = device_added;
dclass->notify_connected = notify_connected;
dclass->get_icon = get_icon;
dclass->get_secrets = get_secrets;
dclass->secrets_request_size = sizeof (MobileHelperSecretsInfo);
return dclass;
}