summary refs log tree commit diff
path: root/src/nm-device-bt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm-device-bt.c')
-rw-r--r--src/nm-device-bt.c317
1 files changed, 242 insertions, 75 deletions
diff --git a/src/nm-device-bt.c b/src/nm-device-bt.c
index f61aeb57..cadedcf7 100644
--- a/src/nm-device-bt.c
+++ b/src/nm-device-bt.c
@@ -47,6 +47,8 @@
 #include "nm-enum-types.h"
 #include "nm-utils.h"
 
+#define MM_OLD_DBUS_SERVICE  "org.freedesktop.ModemManager"
+#define MM_NEW_DBUS_SERVICE  "org.freedesktop.ModemManager1"
 #define BLUETOOTH_DUN_UUID "dun"
 #define BLUETOOTH_NAP_UUID "nap"
 
@@ -57,6 +59,10 @@ G_DEFINE_TYPE (NMDeviceBt, nm_device_bt, NM_TYPE_DEVICE)
 static gboolean modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *reason);
 
 typedef struct {
+	NMDBusManager *dbus_mgr;
+	guint mm_watch_id;
+	gboolean mm_running;
+
 	char *bdaddr;
 	char *name;
 	guint32 capabilities;
@@ -141,9 +147,9 @@ get_connection_bt_type (NMConnection *connection)
 }
 
 static NMConnection *
-real_get_best_auto_connection (NMDevice *device,
-                               GSList *connections,
-                               char **specific_object)
+get_best_auto_connection (NMDevice *device,
+                          GSList *connections,
+                          char **specific_object)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
 	GSList *iter;
@@ -166,15 +172,19 @@ real_get_best_auto_connection (NMDevice *device,
 		if (!(bt_type & priv->capabilities))
 			continue;
 
+		/* Can't auto-activate a DUN connection without ModemManager */
+		if (bt_type == NM_BT_CAPABILITY_DUN && priv->mm_running == FALSE)
+			continue;
+
 		return connection;
 	}
 	return NULL;
 }
 
 static gboolean
-real_check_connection_compatible (NMDevice *device,
-                                  NMConnection *connection,
-                                  GError **error)
+check_connection_compatible (NMDevice *device,
+                             NMConnection *connection,
+                             GError **error)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
 	NMSettingConnection *s_con;
@@ -228,11 +238,28 @@ real_check_connection_compatible (NMDevice *device,
 }
 
 static gboolean
-real_complete_connection (NMDevice *device,
-                          NMConnection *connection,
-                          const char *specific_object,
-                          const GSList *existing_connections,
-                          GError **error)
+check_connection_available (NMDevice *device, NMConnection *connection)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+	guint32 bt_type;
+
+	bt_type = get_connection_bt_type (connection);
+	if (!(bt_type & priv->capabilities))
+		return FALSE;
+
+	/* DUN connections aren't available without ModemManager */
+	if (bt_type == NM_BT_CAPABILITY_DUN && priv->mm_running == FALSE)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+complete_connection (NMDevice *device,
+                     NMConnection *connection,
+                     const char *specific_object,
+                     const GSList *existing_connections,
+                     GError **error)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
 	NMSettingBluetooth *s_bt;
@@ -369,7 +396,7 @@ real_complete_connection (NMDevice *device,
 }
 
 static guint32
-real_get_generic_capabilities (NMDevice *dev)
+get_generic_capabilities (NMDevice *dev)
 {
 	return NM_DEVICE_CAP_NM_SUPPORTED;
 }
@@ -517,8 +544,7 @@ static void
 device_state_changed (NMDevice *device,
                       NMDeviceState new_state,
                       NMDeviceState old_state,
-                      NMDeviceStateReason reason,
-                      gpointer user_data)
+                      NMDeviceStateReason reason)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
 
@@ -528,7 +554,6 @@ device_state_changed (NMDevice *device,
 
 static void
 modem_ip4_config_result (NMModem *self,
-                         const char *iface,
                          NMIP4Config *config,
                          GError *error,
                          gpointer user_data)
@@ -545,12 +570,16 @@ modem_ip4_config_result (NMModem *self,
 		             error && error->message ? error->message : "(unknown)");
 
 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
-	} else {
-		if (iface)
-			nm_device_set_ip_iface (device, iface);
-
+	} else
 		nm_device_activate_schedule_ip4_config_result (device, config);
-	}
+}
+
+static void
+data_port_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+
+	nm_device_set_ip_iface (self, nm_modem_get_data_port (modem));
 }
 
 static gboolean
@@ -586,7 +615,8 @@ nm_device_bt_modem_added (NMDeviceBt *self,
                           const char *driver)
 {
 	NMDeviceBtPrivate *priv;
-	const char *modem_iface;
+	const gchar *modem_data_port;
+	const gchar *modem_control_port;
 	char *base;
 	NMDeviceState state;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
@@ -597,14 +627,15 @@ nm_device_bt_modem_added (NMDeviceBt *self,
 	g_return_val_if_fail (NM_IS_MODEM (modem), FALSE);
 
 	priv = NM_DEVICE_BT_GET_PRIVATE (self);
-	modem_iface = nm_modem_get_iface (modem);
-	g_return_val_if_fail (modem_iface != NULL, FALSE);
+	modem_data_port = nm_modem_get_data_port (modem);
+	modem_control_port = nm_modem_get_control_port (modem);
+	g_return_val_if_fail (modem_data_port != NULL || modem_control_port != NULL, FALSE);
 
 	if (!priv->rfcomm_iface)
 		return FALSE;
 
 	base = g_path_get_basename (priv->rfcomm_iface);
-	if (strcmp (base, modem_iface)) {
+	if (g_strcmp0 (base, modem_data_port) && g_strcmp0 (base, modem_control_port)) {
 		g_free (base);
 		return FALSE;
 	}
@@ -645,6 +676,13 @@ nm_device_bt_modem_added (NMDeviceBt *self,
 	g_signal_connect (modem, NM_MODEM_AUTH_REQUESTED, G_CALLBACK (modem_auth_requested), self);
 	g_signal_connect (modem, NM_MODEM_AUTH_RESULT, G_CALLBACK (modem_auth_result), self);
 
+	/* In the old ModemManager the data port is known from the very beginning;
+	 * while in the new ModemManager the data port is set afterwards when the bearer gets
+	 * created */
+	if (modem_data_port)
+		nm_device_set_ip_iface (NM_DEVICE (self), modem_data_port);
+	g_signal_connect (modem, "notify::" NM_MODEM_DATA_PORT, G_CALLBACK (data_port_changed_cb), self);
+
 	/* Kick off the modem connection */
 	if (!modem_stage1 (self, modem, &reason))
 		nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, reason);
@@ -854,11 +892,10 @@ bt_connect_timeout (gpointer user_data)
 }
 
 static NMActStageReturn
-real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
+act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
-	NMDBusManager *dbus_mgr;
-	DBusGConnection *g_connection;
+	DBusGConnection *bus;
 	gboolean dun = FALSE;
 	NMConnection *connection;
 
@@ -870,9 +907,10 @@ real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	dbus_mgr = nm_dbus_manager_get ();
-	g_connection = nm_dbus_manager_get_connection (dbus_mgr);
-	g_object_unref (dbus_mgr);
+	if (priv->bt_type == NM_BT_CAPABILITY_DUN && !priv->mm_running) {
+		*reason = NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE;
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
 
 	if (priv->bt_type == NM_BT_CAPABILITY_DUN)
 		dun = TRUE;
@@ -881,10 +919,11 @@ real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	else
 		g_assert_not_reached ();
 
-	priv->dev_proxy = dbus_g_proxy_new_for_name (g_connection,
-	                                               BLUEZ_SERVICE,
-	                                               nm_device_get_udi (device),
-	                                               BLUEZ_DEVICE_INTERFACE);
+	bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
+	priv->dev_proxy = dbus_g_proxy_new_for_name (bus,
+	                                             BLUEZ_SERVICE,
+	                                             nm_device_get_udi (device),
+	                                             BLUEZ_DEVICE_INTERFACE);
 	if (!priv->dev_proxy) {
 		// FIXME: set a reason code
 		return NM_ACT_STAGE_RETURN_FAILURE;
@@ -900,7 +939,7 @@ real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	dbus_g_proxy_connect_signal (priv->dev_proxy, "PropertyChanged",
 	                             G_CALLBACK (bluez_property_changed), device, NULL);
 
-	priv->type_proxy = dbus_g_proxy_new_for_name (g_connection,
+	priv->type_proxy = dbus_g_proxy_new_for_name (bus,
 	                                              BLUEZ_SERVICE,
 	                                              nm_device_get_udi (device),
 	                                              dun ? BLUEZ_SERIAL_INTERFACE : BLUEZ_NETWORK_INTERFACE);
@@ -929,9 +968,9 @@ real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 }
 
 static NMActStageReturn
-real_act_stage3_ip4_config_start (NMDevice *device,
-                                  NMIP4Config **out_config,
-                                  NMDeviceStateReason *reason)
+act_stage3_ip4_config_start (NMDevice *device,
+                             NMIP4Config **out_config,
+                             NMDeviceStateReason *reason)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
 	NMActStageReturn ret;
@@ -948,9 +987,9 @@ real_act_stage3_ip4_config_start (NMDevice *device,
 }
 
 static NMActStageReturn
-real_act_stage3_ip6_config_start (NMDevice *device,
-                                  NMIP6Config **out_config,
-                                  NMDeviceStateReason *reason)
+act_stage3_ip6_config_start (NMDevice *device,
+                             NMIP6Config **out_config,
+                             NMDeviceStateReason *reason)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
 	NMActStageReturn ret;
@@ -967,7 +1006,7 @@ real_act_stage3_ip6_config_start (NMDevice *device,
 }
 
 static void
-real_deactivate (NMDevice *device)
+deactivate (NMDevice *device)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
 
@@ -1032,6 +1071,109 @@ real_deactivate (NMDevice *device)
 
 /*****************************************************************************/
 
+static gboolean
+is_available (NMDevice *dev)
+{
+	NMDeviceBt *self = NM_DEVICE_BT (dev);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+
+	/* PAN doesn't need ModemManager, so devices that support it are always available */
+	if (priv->capabilities & NM_BT_CAPABILITY_NAP)
+		return TRUE;
+
+	/* DUN requires ModemManager */
+	return priv->mm_running;
+}
+
+static void
+handle_availability_change (NMDeviceBt *self,
+                            gboolean old_available,
+                            NMDeviceStateReason unavailable_reason)
+{
+	NMDevice *device = NM_DEVICE (self);
+	NMDeviceState state;
+	gboolean available;
+
+	state = nm_device_get_state (device);
+	if (state < NM_DEVICE_STATE_UNAVAILABLE) {
+		nm_log_dbg (LOGD_BT, "(%s): availability blocked by UNMANAGED state",
+		            nm_device_get_iface (device));
+		return;
+	}
+
+	available = nm_device_is_available (device);
+	if (available == old_available)
+		return;
+
+	if (available) {
+		if (state != NM_DEVICE_STATE_UNAVAILABLE)
+			nm_log_warn (LOGD_CORE | LOGD_BT, "not in expected unavailable state!");
+
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_DISCONNECTED,
+		                         NM_DEVICE_STATE_REASON_NONE);
+	} else {
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_UNAVAILABLE,
+		                         unavailable_reason);
+	}
+}
+
+static void
+set_mm_running (NMDeviceBt *self, gboolean running)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	gboolean old_available;
+
+	if (priv->mm_running == running)
+		return;
+
+	nm_log_dbg (LOGD_BT, "(%s): ModemManager now %s",
+	            nm_device_get_iface (NM_DEVICE (self)),
+	            running ? "available" : "unavailable");
+
+	old_available = nm_device_is_available (NM_DEVICE (self));
+	priv->mm_running = running;
+	handle_availability_change (self, old_available, NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE);
+
+	/* Need to recheck available connections whenever MM appears or disappears,
+	 * since the device could be both DUN and NAP capable and thus may not
+	 * change state (which rechecks available connections) when MM comes and goes.
+	 */
+	if (priv->capabilities & NM_BT_CAPABILITY_DUN)
+	    nm_device_recheck_available_connections (NM_DEVICE (self));
+}
+
+static void
+mm_name_owner_changed (NMDBusManager *dbus_mgr,
+                       const char *name,
+                       const char *old_owner,
+                       const char *new_owner,
+                       NMDeviceBt *self)
+{
+	gboolean old_owner_good;
+	gboolean new_owner_good;
+
+	/* Can't handle the signal if its not from the modem service */
+	if (strcmp (MM_OLD_DBUS_SERVICE, name) != 0)
+		return;
+
+#if WITH_MODEM_MANAGER_1
+	if (strcmp (MM_NEW_DBUS_SERVICE, name) != 0)
+		return;
+#endif
+
+	old_owner_good = (old_owner && strlen (old_owner));
+	new_owner_good = (new_owner && strlen (new_owner));
+
+	if (!old_owner_good && new_owner_good)
+		set_mm_running (self, TRUE);
+	else if (old_owner_good && !new_owner_good)
+		set_mm_running (self, FALSE);
+}
+
+/*****************************************************************************/
+
 NMDevice *
 nm_device_bt_new (const char *udi,
                   const char *bdaddr,
@@ -1039,33 +1181,44 @@ nm_device_bt_new (const char *udi,
                   guint32 capabilities,
                   gboolean managed)
 {
-	NMDevice *device;
-
 	g_return_val_if_fail (udi != NULL, NULL);
 	g_return_val_if_fail (bdaddr != NULL, NULL);
 	g_return_val_if_fail (name != NULL, NULL);
 	g_return_val_if_fail (capabilities != NM_BT_CAPABILITY_NONE, NULL);
 
-	device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_BT,
-	                                    NM_DEVICE_UDI, udi,
-	                                    NM_DEVICE_IFACE, bdaddr,
-	                                    NM_DEVICE_DRIVER, "bluez",
-	                                    NM_DEVICE_BT_HW_ADDRESS, bdaddr,
-	                                    NM_DEVICE_BT_NAME, name,
-	                                    NM_DEVICE_BT_CAPABILITIES, capabilities,
-	                                    NM_DEVICE_MANAGED, managed,
-	                                    NM_DEVICE_TYPE_DESC, "Bluetooth",
-	                                    NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BT,
-	                                    NULL);
-	if (device)
-		g_signal_connect (device, "state-changed", G_CALLBACK (device_state_changed), device);
-
-	return device;
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BT,
+	                                  NM_DEVICE_UDI, udi,
+	                                  NM_DEVICE_IFACE, bdaddr,
+	                                  NM_DEVICE_DRIVER, "bluez",
+	                                  NM_DEVICE_BT_HW_ADDRESS, bdaddr,
+	                                  NM_DEVICE_BT_NAME, name,
+	                                  NM_DEVICE_BT_CAPABILITIES, capabilities,
+	                                  NM_DEVICE_MANAGED, managed,
+	                                  NM_DEVICE_TYPE_DESC, "Bluetooth",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BT,
+	                                  NULL);
 }
 
 static void
 nm_device_bt_init (NMDeviceBt *self)
 {
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	gboolean mm_running;
+
+	priv->dbus_mgr = nm_dbus_manager_get ();
+
+	priv->mm_watch_id = g_signal_connect (priv->dbus_mgr,
+	                                      NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
+	                                      G_CALLBACK (mm_name_owner_changed),
+	                                      self);
+
+	/* Initial check to see if ModemManager is running */
+	mm_running = nm_dbus_manager_name_has_owner (priv->dbus_mgr, MM_OLD_DBUS_SERVICE);
+#if WITH_MODEM_MANAGER_1
+	if (!mm_running)
+		mm_running = nm_dbus_manager_name_has_owner (priv->dbus_mgr, MM_NEW_DBUS_SERVICE);
+#endif
+	set_mm_running (self, mm_running);
 }
 
 static void
@@ -1116,7 +1269,7 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-finalize (GObject *object)
+dispose (GObject *object)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object);
 
@@ -1125,14 +1278,23 @@ finalize (GObject *object)
 		priv->timeout_id = 0;
 	}
 
-	if (priv->type_proxy)
-		g_object_unref (priv->type_proxy);
+	if (priv->dbus_mgr && priv->mm_watch_id) {
+		g_signal_handler_disconnect (priv->dbus_mgr, priv->mm_watch_id);
+		priv->mm_watch_id = 0;
+	}
+	g_clear_object (&priv->dbus_mgr);
 
-	if (priv->dev_proxy)
-		g_object_unref (priv->dev_proxy);
+	g_clear_object (&priv->type_proxy);
+	g_clear_object (&priv->dev_proxy);
+	g_clear_object (&priv->modem);
 
-	if (priv->modem)
-		g_object_unref (priv->modem);
+	G_OBJECT_CLASS (nm_device_bt_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object);
 
 	g_free (priv->rfcomm_iface);
 	g_free (priv->bdaddr);
@@ -1151,17 +1313,22 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
 
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
+	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 
-	device_class->get_best_auto_connection = real_get_best_auto_connection;
-	device_class->get_generic_capabilities = real_get_generic_capabilities;
-	device_class->deactivate = real_deactivate;
-	device_class->act_stage2_config = real_act_stage2_config;
-	device_class->act_stage3_ip4_config_start = real_act_stage3_ip4_config_start;
-	device_class->act_stage3_ip6_config_start = real_act_stage3_ip6_config_start;
-	device_class->check_connection_compatible = real_check_connection_compatible;
-	device_class->complete_connection = real_complete_connection;
+	device_class->get_best_auto_connection = get_best_auto_connection;
+	device_class->get_generic_capabilities = get_generic_capabilities;
+	device_class->deactivate = deactivate;
+	device_class->act_stage2_config = act_stage2_config;
+	device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
+	device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
+	device_class->check_connection_compatible = check_connection_compatible;
+	device_class->check_connection_available = check_connection_available;
+	device_class->complete_connection = complete_connection;
 	device_class->hwaddr_matches = hwaddr_matches;
+	device_class->is_available = is_available;
+
+	device_class->state_changed = device_state_changed;
 
 	/* Properties */
 	g_object_class_install_property
@@ -1199,7 +1366,7 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
 		              G_TYPE_NONE, 2,
 		              G_TYPE_UINT, G_TYPE_UINT);
 
-	signals[PROPERTIES_CHANGED] = 
+	signals[PROPERTIES_CHANGED] =
 		nm_properties_changed_signal_new (object_class,
 		                                  G_STRUCT_OFFSET (NMDeviceBtClass, properties_changed));