core: add AvailableConnections property for NMDevice objects

Implements a new property that provides a list of currently
available connections a device could connect to. For example
if a connection for a particular wireless connection exists and
that wireless network appears in the scan list it would show in the
AvailableConnections property of the device.

(dcbw: found a slightly cleaner way to do this; it's a lot like the
check_connection_compatible class method, except it deals with
live network data too.  So convert the subclass methods to
just check additional live network data, and have the base
device class handle adding the connection to the hash and all
the associated signalling.  Also fix a bug where the available
connections were not updated when a device moved from UNAVAILABLE
to available, its available connections were not updated)
This commit is contained in:
Nathanael D. Noblet 2012-08-01 11:16:48 -06:00 committed by Dan Williams
parent b2d97cc96c
commit 808136ac02
6 changed files with 281 additions and 16 deletions

View file

@ -120,6 +120,11 @@
The general type of the network device; ie Ethernet, WiFi, etc.
</tp:docstring>
</property>
<property name="AvailableConnections" type="ao" access="read">
<tp:docstring>
An array of object paths of every configured connection that is currently 'available' through this device.
</tp:docstring>
</property>
<method name="Disconnect">
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_device_disconnect"/>

View file

@ -69,4 +69,6 @@ gboolean nm_device_match_ip_config (NMDevice *device, NMConnection *connection);
NMConnectionProvider *nm_device_get_connection_provider (NMDevice *device);
void nm_device_recheck_available_connections (NMDevice *device);
#endif /* NM_DEVICE_PRIVATE_H */

View file

@ -884,9 +884,13 @@ _set_hw_addr (NMDeviceWifi *self, const guint8 *addr, const char *detail)
}
static void
access_point_removed (NMDeviceWifi *device, NMAccessPoint *ap)
remove_access_point (NMDeviceWifi *device, NMAccessPoint *ap)
{
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(device);
g_signal_emit (device, signals[ACCESS_POINT_REMOVED], 0, ap);
priv->ap_list = g_slist_remove (priv->ap_list, ap);
g_object_unref(ap);
nm_device_recheck_available_connections (NM_DEVICE (device));
}
static void
@ -897,10 +901,7 @@ remove_all_aps (NMDeviceWifi *self)
/* Remove outdated APs */
while (g_slist_length (priv->ap_list)) {
NMAccessPoint *ap = NM_AP (priv->ap_list->data);
access_point_removed (self, ap);
priv->ap_list = g_slist_remove (priv->ap_list, ap);
g_object_unref (ap);
remove_access_point (self, ap);
}
g_slist_free (priv->ap_list);
priv->ap_list = NULL;
@ -952,9 +953,7 @@ real_deactivate (NMDevice *dev)
* and thus the AP culling never happens. (bgo #569241)
*/
if (orig_ap && nm_ap_get_fake (orig_ap)) {
access_point_removed (self, orig_ap);
priv->ap_list = g_slist_remove (priv->ap_list, orig_ap);
g_object_unref (orig_ap);
remove_access_point (self, orig_ap);
}
/* Reset MAC address back to initial address */
@ -1069,6 +1068,33 @@ real_check_connection_compatible (NMDevice *device,
return TRUE;
}
static gboolean
real_check_connection_available (NMDevice *device, NMConnection *connection)
{
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device);
NMSettingWireless *s_wifi;
const char *mode;
GSList *ap_iter = NULL;
s_wifi = nm_connection_get_setting_wireless (connection);
/* Ad-Hoc connections are always available because they may be started
* at any time.
*/
mode = nm_setting_wireless_get_mode (s_wifi);
if (g_strcmp0 (mode, "adhoc") == 0)
return TRUE;
/* check if its visible */
for (ap_iter = priv->ap_list; ap_iter; ap_iter = g_slist_next (ap_iter)) {
if (nm_ap_check_compatible (NM_AP (ap_iter->data), connection))
return TRUE;
}
return FALSE;
}
/*
* List of manufacturer default SSIDs that are often unchanged by users.
*
@ -1902,6 +1928,7 @@ merge_scanned_ap (NMDeviceWifi *self,
priv->ap_list = g_slist_prepend (priv->ap_list, merge_ap);
nm_ap_export_to_dbus (merge_ap);
g_signal_emit (self, signals[ACCESS_POINT_ADDED], 0, merge_ap);
nm_device_recheck_available_connections (NM_DEVICE (self));
}
}
@ -1964,9 +1991,7 @@ cull_scan_list (NMDeviceWifi *self)
ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
ssid ? "'" : "");
access_point_removed (self, outdated_ap);
priv->ap_list = g_slist_remove (priv->ap_list, outdated_ap);
g_object_unref (outdated_ap);
remove_access_point (self, outdated_ap);
removed++;
}
g_slist_free (outdated_list);
@ -1977,6 +2002,9 @@ cull_scan_list (NMDeviceWifi *self)
ap_list_dump (self);
if(removed > 0)
nm_device_recheck_available_connections (NM_DEVICE (self));
return FALSE;
}
@ -2839,6 +2867,7 @@ real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
priv->ap_list = g_slist_prepend (priv->ap_list, ap);
nm_ap_export_to_dbus (ap);
g_signal_emit (self, signals[ACCESS_POINT_ADDED], 0, ap);
nm_device_recheck_available_connections (NM_DEVICE (self));
}
nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), nm_ap_get_dbus_path (ap));
@ -3162,7 +3191,6 @@ static void
activation_failure_handler (NMDevice *dev)
{
NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
NMAccessPoint *ap;
NMConnection *connection;
@ -3180,9 +3208,7 @@ activation_failure_handler (NMDevice *dev)
* list because we don't have any scan or capability info
* for it, and they are pretty much useless.
*/
access_point_removed (self, ap);
priv->ap_list = g_slist_remove (priv->ap_list, ap);
g_object_unref (ap);
remove_access_point (self, ap);
}
}
}
@ -3544,6 +3570,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
parent_class->get_best_auto_connection = real_get_best_auto_connection;
parent_class->is_available = real_is_available;
parent_class->check_connection_compatible = real_check_connection_compatible;
parent_class->check_connection_available = real_check_connection_available;
parent_class->complete_connection = real_complete_connection;
parent_class->set_enabled = real_set_enabled;
@ -3633,7 +3660,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
g_signal_new ("access-point-removed",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMDeviceWifiClass, access_point_removed),
0,
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,

View file

@ -65,6 +65,7 @@
#include "nm-connection-provider.h"
#include "nm-posix-signals.h"
#include "nm-manager-auth.h"
#include "nm-dbus-glib-types.h"
static void impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context);
@ -123,6 +124,7 @@ enum {
PROP_TYPE_DESC,
PROP_RFKILL_TYPE,
PROP_IFINDEX,
PROP_AVAILABLE_CONNECTIONS,
LAST_PROP
};
@ -169,6 +171,7 @@ typedef struct {
gboolean managed; /* whether managed by NM or not */
RfKillType rfkill_type;
gboolean firmware_missing;
GHashTable * available_connections;
guint32 ip4_address;
@ -241,6 +244,12 @@ typedef struct {
NMDevice * master;
NMConnectionProvider *con_provider;
/* connection provider signals for available connections property */
guint cp_added_id;
guint cp_loaded_id;
guint cp_removed_id;
guint cp_updated_id;
} NMDevicePrivate;
static void nm_device_take_down (NMDevice *dev, gboolean wait, NMDeviceStateReason reason);
@ -258,10 +267,19 @@ static gboolean nm_device_set_ip6_config (NMDevice *dev,
static gboolean nm_device_activate_ip6_config_commit (gpointer user_data);
static gboolean real_check_connection_available (NMDevice *device, NMConnection *connection);
static void _clear_available_connections (NMDevice *device, gboolean do_signal);
static void dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release);
static const char *reason_to_string (NMDeviceStateReason reason);
static void cp_connection_added (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data);
static void cp_connections_loaded (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data);
static void cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data);
static void cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data);
static void
nm_device_init (NMDevice *self)
{
@ -274,6 +292,7 @@ nm_device_init (NMDevice *self)
priv->dhcp_timeout = 0;
priv->rfkill_type = RFKILL_TYPE_UNKNOWN;
priv->autoconnect = DEFAULT_AUTOCONNECT;
priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
}
static void
@ -712,6 +731,25 @@ nm_device_set_connection_provider (NMDevice *device,
g_return_if_fail (priv->con_provider == NULL);
priv->con_provider = provider;
priv->cp_added_id = g_signal_connect (priv->con_provider,
NM_CP_SIGNAL_CONNECTION_ADDED,
G_CALLBACK (cp_connection_added),
device);
priv->cp_loaded_id = g_signal_connect (priv->con_provider,
NM_CP_SIGNAL_CONNECTIONS_LOADED,
G_CALLBACK (cp_connections_loaded),
device);
priv->cp_removed_id = g_signal_connect (priv->con_provider,
NM_CP_SIGNAL_CONNECTION_REMOVED,
G_CALLBACK (cp_connection_removed),
device);
priv->cp_updated_id = g_signal_connect (priv->con_provider,
NM_CP_SIGNAL_CONNECTION_UPDATED,
G_CALLBACK (cp_connection_updated),
device);
}
NMConnectionProvider *
@ -3803,6 +3841,28 @@ dispose (GObject *object)
}
g_free (priv->ip6_privacy_tempaddr_path);
if (priv->cp_added_id) {
g_signal_handler_disconnect (priv->con_provider, priv->cp_added_id);
priv->cp_added_id = 0;
}
if (priv->cp_loaded_id) {
g_signal_handler_disconnect (priv->con_provider, priv->cp_loaded_id);
priv->cp_loaded_id = 0;
}
if (priv->cp_removed_id) {
g_signal_handler_disconnect (priv->con_provider, priv->cp_removed_id);
priv->cp_removed_id = 0;
}
if (priv->cp_updated_id) {
g_signal_handler_disconnect (priv->con_provider, priv->cp_updated_id);
priv->cp_updated_id = 0;
}
g_hash_table_unref (priv->available_connections);
activation_source_clear (self, TRUE, AF_INET);
activation_source_clear (self, TRUE, AF_INET6);
@ -3928,6 +3988,9 @@ get_property (GObject *object, guint prop_id,
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMDeviceState state;
const char *ac_path = NULL;
GPtrArray *array;
GHashTableIter iter;
NMConnection *connection;
state = nm_device_get_state (self);
@ -4020,6 +4083,13 @@ get_property (GObject *object, guint prop_id,
case PROP_RFKILL_TYPE:
g_value_set_uint (value, priv->rfkill_type);
break;
case PROP_AVAILABLE_CONNECTIONS:
array = g_ptr_array_sized_new (g_hash_table_size (priv->available_connections));
g_hash_table_iter_init (&iter, priv->available_connections);
while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL))
g_ptr_array_add (array, g_strdup (nm_connection_get_path (connection)));
g_value_take_boxed (value, array);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -4050,6 +4120,7 @@ nm_device_class_init (NMDeviceClass *klass)
klass->act_stage3_ip6_config_start = real_act_stage3_ip6_config_start;
klass->act_stage4_ip4_config_timeout = real_act_stage4_ip4_config_timeout;
klass->act_stage4_ip6_config_timeout = real_act_stage4_ip6_config_timeout;
klass->check_connection_available = real_check_connection_available;
/* Properties */
g_object_class_install_property
@ -4229,6 +4300,14 @@ nm_device_class_init (NMDeviceClass *klass)
0, G_MAXINT, 0,
G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT));
g_object_class_install_property
(object_class, PROP_AVAILABLE_CONNECTIONS,
g_param_spec_boxed (NM_DEVICE_AVAILABLE_CONNECTIONS,
"AvailableConnections",
"AvailableConnections",
DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
G_PARAM_READABLE));
/* Signals */
signals[STATE_CHANGED] =
g_signal_new ("state-changed",
@ -4495,6 +4574,14 @@ nm_device_state_changed (NMDevice *device,
(guint64) time (NULL), TRUE);
}
if (state <= NM_DEVICE_STATE_UNAVAILABLE)
_clear_available_connections (device, TRUE);
/* Update the available connections list when a device first becomes available */
if ( state >= NM_DEVICE_STATE_DISCONNECTED
&& old_state < NM_DEVICE_STATE_DISCONNECTED)
nm_device_recheck_available_connections (device);
/* Handle the new state here; but anything that could trigger
* another state change should be done below.
*/
@ -4855,3 +4942,113 @@ nm_device_get_autoconnect (NMDevice *device)
return NM_DEVICE_GET_PRIVATE (device)->autoconnect;
}
static void
_signal_available_connections_changed (NMDevice *device)
{
g_object_notify (G_OBJECT (device), NM_DEVICE_AVAILABLE_CONNECTIONS);
}
static void
_clear_available_connections (NMDevice *device, gboolean do_signal)
{
g_hash_table_remove_all (NM_DEVICE_GET_PRIVATE (device)->available_connections);
if (do_signal == TRUE)
_signal_available_connections_changed (device);
}
static gboolean
_try_add_available_connection (NMDevice *self, NMConnection *connection)
{
if (nm_device_get_state (self) < NM_DEVICE_STATE_DISCONNECTED)
return FALSE;
if (nm_device_check_connection_compatible (self, connection, NULL)) {
/* Let subclasses implement additional checks on the connection */
if ( NM_DEVICE_GET_CLASS (self)->check_connection_available
&& NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection)) {
g_hash_table_insert (NM_DEVICE_GET_PRIVATE (self)->available_connections,
g_object_ref (connection),
GUINT_TO_POINTER (1));
}
}
return FALSE;
}
static gboolean
_del_available_connection (NMDevice *device, NMConnection *connection)
{
return g_hash_table_remove (NM_DEVICE_GET_PRIVATE (device)->available_connections, connection);
}
static gboolean
real_check_connection_available (NMDevice *device, NMConnection *connection)
{
/* Default is to assume the connection is available unless a subclass
* overrides this with more specific checks.
*/
return TRUE;
}
void
nm_device_recheck_available_connections (NMDevice *device)
{
NMDevicePrivate *priv;
const GSList *connections, *iter;
g_return_if_fail (device != NULL);
g_return_if_fail (NM_IS_DEVICE (device));
priv = NM_DEVICE_GET_PRIVATE(device);
_clear_available_connections (device, FALSE);
connections = nm_connection_provider_get_connections (priv->con_provider);
for (iter = connections; iter; iter = g_slist_next (iter))
_try_add_available_connection (device, NM_CONNECTION (iter->data));
_signal_available_connections_changed (device);
}
static void
cp_connection_added (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
{
if (_try_add_available_connection (NM_DEVICE (user_data), connection))
_signal_available_connections_changed (NM_DEVICE (user_data));
}
static void
cp_connections_loaded (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
{
const GSList *connections, *iter;
gboolean added = FALSE;
connections = nm_connection_provider_get_connections (cp);
for (iter = connections; iter; iter = g_slist_next (iter))
added |= _try_add_available_connection (NM_DEVICE (user_data), NM_CONNECTION (iter->data));
if (added)
_signal_available_connections_changed (NM_DEVICE (user_data));
}
static void
cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
{
if (_del_available_connection (NM_DEVICE (user_data), connection))
_signal_available_connections_changed (NM_DEVICE (user_data));
}
static void
cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
{
gboolean added, deleted;
/* FIXME: don't remove it from the hash if it's just going to get re-added */
deleted = _del_available_connection (NM_DEVICE (user_data), connection);
added = _try_add_available_connection (NM_DEVICE (user_data), connection);
/* Only signal if the connection was removed OR added, but not both */
if (added != deleted)
_signal_available_connections_changed (NM_DEVICE (user_data));
}

View file

@ -59,6 +59,7 @@
#define NM_DEVICE_TYPE_DESC "type-desc" /* Internal only */
#define NM_DEVICE_RFKILL_TYPE "rfkill-type" /* Internal only */
#define NM_DEVICE_IFINDEX "ifindex" /* Internal only */
#define NM_DEVICE_AVAILABLE_CONNECTIONS "available-connections"
/* Internal signals */
#define NM_DEVICE_AUTH_REQUEST "auth-request"
@ -124,10 +125,21 @@ typedef struct {
GSList *connections,
char **specific_object);
/* Checks whether the connection is compatible with the device using
* only the devices type and characteristics. Does not use any live
* network information like WiFi/WiMAX scan lists etc.
*/
gboolean (* check_connection_compatible) (NMDevice *self,
NMConnection *connection,
GError **error);
/* Checks whether the connection is likely available to be activated,
* including any live network information like scan lists. Returns
* TRUE if the connection is available; FALSE if not.
*/
gboolean (* check_connection_available) (NMDevice *self,
NMConnection *connection);
gboolean (* complete_connection) (NMDevice *self,
NMConnection *connection,
const char *specific_object,

View file

@ -254,6 +254,8 @@ remove_all_nsps (NMDeviceWimax *self)
g_object_unref (nsp);
}
nm_device_recheck_available_connections (NM_DEVICE (self));
g_slist_free (priv->nsp_list);
priv->nsp_list = NULL;
}
@ -479,6 +481,21 @@ real_check_connection_compatible (NMDevice *device,
return TRUE;
}
static gboolean
real_check_connection_available (NMDevice *device, NMConnection *connection)
{
NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (device);
const GSList *ns_iter = NULL;
/* Ensure the connection applies to an NSP in the scan list */
for (ns_iter = priv->nsp_list; ns_iter; ns_iter = ns_iter->next) {
if (nm_wimax_nsp_check_compatible (NM_WIMAX_NSP (ns_iter->data), connection))
return TRUE;
}
return FALSE;
}
static gboolean
real_complete_connection (NMDevice *device,
NMConnection *connection,
@ -1070,6 +1087,9 @@ remove_outdated_nsps (NMDeviceWimax *self,
g_object_unref (nsp);
}
if (g_slist_length(to_remove) > 0)
nm_device_recheck_available_connections (NM_DEVICE (self));
g_slist_free (to_remove);
}
@ -1117,6 +1137,7 @@ wmx_scan_result_cb (struct wmxsdk *wmxsdk,
priv->nsp_list = g_slist_append (priv->nsp_list, nsp);
nm_wimax_nsp_export_to_dbus (nsp);
g_signal_emit (self, signals[NSP_ADDED], 0, nsp);
nm_device_recheck_available_connections (NM_DEVICE (self));
}
}
}
@ -1504,6 +1525,7 @@ nm_device_wimax_class_init (NMDeviceWimaxClass *klass)
device_class->hw_take_down = real_hw_take_down;
device_class->update_hw_address = real_update_hw_address;
device_class->check_connection_compatible = real_check_connection_compatible;
device_class->check_connection_available = real_check_connection_available;
device_class->complete_connection = real_complete_connection;
device_class->get_best_auto_connection = real_get_best_auto_connection;
device_class->get_generic_capabilities = real_get_generic_capabilities;