about summary refs log tree commit diff
path: root/src/nm-manager.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2014-07-06 02:16:10 +0200
committerMichael Biebl <biebl@debian.org>2014-07-06 02:16:10 +0200
commit33491bc4279481db8ae47213e34a6d695a0e8830 (patch)
tree097d2b0fdff3fae6885381ae5e57a182cd8cbbba /src/nm-manager.c
parent59c3714a494c3b3765657c0551ad82842d98a7d2 (diff)
Imported Upstream version 0.9.10.0 upstream/0.9.10.0
Diffstat (limited to 'src/nm-manager.c')
-rw-r--r--src/nm-manager.c4531
1 files changed, 2293 insertions, 2238 deletions
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 9037e649..09a1985a 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -21,6 +21,7 @@
 
 #include <config.h>
 
+#include <stdlib.h>
 #include <netinet/ether.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -38,37 +39,32 @@
 #include "nm-logging.h"
 #include "nm-dbus-manager.h"
 #include "nm-vpn-manager.h"
-#include "nm-modem-manager.h"
-#include "nm-device-bt.h"
 #include "nm-device.h"
 #include "nm-device-ethernet.h"
-#include "nm-device-wifi.h"
-#include "nm-device-olpc-mesh.h"
-#include "nm-device-modem.h"
 #include "nm-device-infiniband.h"
 #include "nm-device-bond.h"
+#include "nm-device-team.h"
 #include "nm-device-bridge.h"
 #include "nm-device-vlan.h"
-#include "nm-device-adsl.h"
-#include "nm-system.h"
-#include "nm-properties-changed-signal.h"
-#include "nm-setting-bluetooth.h"
+#include "nm-device-generic.h"
+#include "nm-device-veth.h"
+#include "nm-device-tun.h"
+#include "nm-device-macvlan.h"
+#include "nm-device-vxlan.h"
+#include "nm-device-gre.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-wireless.h"
 #include "nm-setting-vpn.h"
-#include "nm-marshal.h"
 #include "nm-dbus-glib-types.h"
-#include "nm-udev-manager.h"
-#include "nm-hostname-provider.h"
-#include "nm-bluez-manager.h"
-#include "nm-bluez-common.h"
+#include "nm-platform.h"
+#include "nm-rfkill-manager.h"
+#include "nm-dhcp-manager.h"
 #include "nm-settings.h"
 #include "nm-settings-connection.h"
 #include "nm-manager-auth.h"
 #include "NetworkManagerUtils.h"
 #include "nm-utils.h"
 #include "nm-device-factory.h"
-#include "wifi-utils.h"
 #include "nm-enum-types.h"
 #include "nm-sleep-monitor.h"
 #include "nm-connectivity.h"
@@ -118,10 +114,10 @@ static gboolean impl_manager_get_state (NMManager *manager,
                                         guint32 *state,
                                         GError **error);
 
-static gboolean impl_manager_set_logging (NMManager *manager,
-                                          const char *level,
-                                          const char *domains,
-                                          GError **error);
+static void impl_manager_set_logging (NMManager *manager,
+                                      const char *level,
+                                      const char *domains,
+                                      DBusGMethodInvocation *context);
 
 static void impl_manager_get_logging (NMManager *manager,
                                       char **level,
@@ -132,67 +128,37 @@ static void impl_manager_check_connectivity (NMManager *manager,
 
 #include "nm-manager-glue.h"
 
-static void bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr,
-                                           NMBluezDevice *bt_device,
-                                           const char *bdaddr,
-                                           const char *name,
-                                           const char *object_path,
-                                           guint32 uuids,
-                                           NMManager *manager);
-
-static void bluez_manager_bdaddr_removed_cb (NMBluezManager *bluez_mgr,
-                                             const char *bdaddr,
-                                             const char *object_path,
-                                             gpointer user_data);
-
-static void add_device (NMManager *self, NMDevice *device);
-
-static void hostname_provider_init (NMHostnameProvider *provider_class);
-
-static NMActiveConnection *internal_activate_device (NMManager *manager,
-                                                     NMDevice *device,
-                                                     NMConnection *connection,
-                                                     const char *specific_object,
-                                                     gboolean user_requested,
-                                                     gulong sender_uid,
-                                                     const char *dbus_sender,
-                                                     gboolean assumed,
-                                                     NMActiveConnection *master,
-                                                     GError **error);
-
-static NMDevice *find_device_by_ip_iface (NMManager *self, const gchar *iface);
+static void add_device (NMManager *self, NMDevice *device, gboolean generate_con);
+static void remove_device (NMManager *self, NMDevice *device, gboolean quitting);
 
-static GSList * remove_one_device (NMManager *manager,
-                                   GSList *list,
-                                   NMDevice *device,
-                                   gboolean quitting);
+static NMActiveConnection *_new_active_connection (NMManager *self,
+                                                   NMConnection *connection,
+                                                   const char *specific_object,
+                                                   NMDevice *device,
+                                                   NMAuthSubject *subject,
+                                                   GError **error);
 
-static void rfkill_change_wifi (const char *desc, gboolean enabled);
+static void policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data);
 
-#define SSD_POKE_INTERVAL 120
-#define ORIGDEV_TAG "originating-device"
+static NMDevice *find_device_by_ip_iface (NMManager *self, const gchar *iface);
 
-typedef struct PendingActivation PendingActivation;
-typedef void (*PendingActivationFunc) (PendingActivation *pending,
-                                       GError *error);
+static void rfkill_change (const char *desc, RfKillType rtype, gboolean enabled);
 
-struct PendingActivation {
-	NMManager *manager;
+static gboolean find_master (NMManager *self,
+                             NMConnection *connection,
+                             NMDevice *device,
+                             NMConnection **out_master_connection,
+                             NMDevice **out_master_device,
+                             NMActiveConnection **out_master_ac,
+                             GError **error);
 
-	DBusGMethodInvocation *context;
-	PendingActivationFunc callback;
-	NMAuthChain *chain;
-	const char *wifi_shared_permission;
+static void nm_manager_update_state (NMManager *manager);
 
-	char *connection_path;
-	NMConnection *connection;
-	char *specific_object_path;
-	char *device_path;
-};
+#define SSD_POKE_INTERVAL 120
+#define ORIGDEV_TAG "originating-device"
 
 typedef struct {
 	gboolean user_enabled;
-	gboolean daemon_enabled;
 	gboolean sw_enabled;
 	gboolean hw_enabled;
 	RfKillType rtype;
@@ -200,8 +166,6 @@ typedef struct {
 	const char *key;
 	const char *prop;
 	const char *hw_prop;
-	RfKillState (*other_enabled_func) (NMManager *);
-	RfKillState (*daemon_enabled_func) (NMManager *);
 } RadioState;
 
 typedef struct {
@@ -216,13 +180,13 @@ typedef struct {
 	NMState state;
 	NMConnectivity *connectivity;
 
+	int ignore_link_added_cb;
+
 	NMPolicy *policy;
 
 	NMDBusManager *dbus_mgr;
-	guint          dbus_connection_changed_id;
-	NMUdevManager *udev_mgr;
-	NMBluezManager *bluez_mgr;
-	NMSessionMonitor *session_monitor;
+	gboolean       prop_filter_added;
+	NMRfkillManager *rfkill_mgr;
 
 	/* List of NMDeviceFactoryFunc pointers sorted in priority order */
 	GSList *factories;
@@ -236,10 +200,6 @@ typedef struct {
 
 	NMVPNManager *vpn_manager;
 
-	NMModemManager *modem_manager;
-	guint modem_added_id;
-	guint modem_removed_id;
-
 	DBusGProxy *aipd_proxy;
 	NMSleepMonitor *sleep_monitor;
 
@@ -247,27 +207,21 @@ typedef struct {
 
 	/* Firmware dir monitor */
 	GFileMonitor *fw_monitor;
-	guint fw_monitor_id;
 	guint fw_changed_id;
 
 	guint timestamp_update_id;
 
-	GHashTable *nm_bridges;
-
-	gboolean disposed;
+	gboolean startup;
 } NMManagerPrivate;
 
 #define NM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MANAGER, NMManagerPrivate))
 
-G_DEFINE_TYPE_EXTENDED (NMManager, nm_manager, G_TYPE_OBJECT, 0,
-						G_IMPLEMENT_INTERFACE (NM_TYPE_HOSTNAME_PROVIDER,
-											   hostname_provider_init))
+G_DEFINE_TYPE (NMManager, nm_manager, G_TYPE_OBJECT)
 
 enum {
 	DEVICE_ADDED,
 	DEVICE_REMOVED,
 	STATE_CHANGED,
-	PROPERTIES_CHANGED,
 	CHECK_PERMISSIONS,
 	USER_PERMISSIONS_CHANGED,
 	ACTIVE_CONNECTION_ADDED,
@@ -282,6 +236,7 @@ enum {
 	PROP_0,
 	PROP_VERSION,
 	PROP_STATE,
+	PROP_STARTUP,
 	PROP_NETWORKING_ENABLED,
 	PROP_WIRELESS_ENABLED,
 	PROP_WIRELESS_HARDWARE_ENABLED,
@@ -293,6 +248,7 @@ enum {
 	PROP_CONNECTIVITY,
 	PROP_PRIMARY_CONNECTION,
 	PROP_ACTIVATING_CONNECTION,
+	PROP_DEVICES,
 
 	/* Not exported */
 	PROP_HOSTNAME,
@@ -320,13 +276,29 @@ nm_manager_error_quark (void)
 static void active_connection_state_changed (NMActiveConnection *active,
                                              GParamSpec *pspec,
                                              NMManager *self);
+static void active_connection_default_changed (NMActiveConnection *active,
+                                               GParamSpec *pspec,
+                                               NMManager *self);
 
-static void
-active_connection_removed (NMManager *self, NMActiveConnection *active)
+/* Returns: whether to notify D-Bus of the removal or not */
+static gboolean
+active_connection_remove (NMManager *self, NMActiveConnection *active)
 {
-	g_signal_emit (self, signals[ACTIVE_CONNECTION_REMOVED], 0, active);
-	g_signal_handlers_disconnect_by_func (active, active_connection_state_changed, self);
-	g_object_unref (active);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	gboolean notify = !!nm_active_connection_get_path (active);
+	GSList *found;
+
+	/* FIXME: switch to a GList for faster removal */
+	found = g_slist_find (priv->active_connections, active);
+	if (found) {
+		priv->active_connections = g_slist_remove (priv->active_connections, active);
+		g_signal_emit (self, signals[ACTIVE_CONNECTION_REMOVED], 0, active);
+		g_signal_handlers_disconnect_by_func (active, active_connection_state_changed, self);
+		g_signal_handlers_disconnect_by_func (active, active_connection_default_changed, self);
+		g_object_unref (active);
+	}
+
+	return found && notify;
 }
 
 static gboolean
@@ -335,24 +307,21 @@ _active_connection_cleanup (gpointer user_data)
 	NMManager *self = NM_MANAGER (user_data);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GSList *iter;
-	gboolean changed = FALSE;
 
 	priv->ac_cleanup_id = 0;
 
+	g_object_freeze_notify (G_OBJECT (self));
 	iter = priv->active_connections;
 	while (iter) {
 		NMActiveConnection *ac = iter->data;
 
 		iter = iter->next;
 		if (nm_active_connection_get_state (ac) == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) {
-			priv->active_connections = g_slist_remove (priv->active_connections, ac);
-			active_connection_removed (self, ac);
-			changed = TRUE;
+			if (active_connection_remove (self, ac))
+				g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS);
 		}
 	}
-
-	if (changed)
-		g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS);
+	g_object_thaw_notify (G_OBJECT (self));
 
 	return FALSE;
 }
@@ -375,8 +344,25 @@ active_connection_state_changed (NMActiveConnection *active,
 		if (!priv->ac_cleanup_id)
 			priv->ac_cleanup_id = g_idle_add (_active_connection_cleanup, self);
 	}
+
+	nm_manager_update_state (self);
+}
+
+static void
+active_connection_default_changed (NMActiveConnection *active,
+                                   GParamSpec *pspec,
+                                   NMManager *self)
+{
+	nm_manager_update_state (self);
 }
 
+/**
+ * active_connection_add():
+ * @self: the #NMManager
+ * @active: the #NMActiveConnection to manage
+ *
+ * Begins to track and manage @active.  Increases the refcount of @active.
+ */
 static void
 active_connection_add (NMManager *self, NMActiveConnection *active)
 {
@@ -384,13 +370,27 @@ active_connection_add (NMManager *self, NMActiveConnection *active)
 
 	g_return_if_fail (g_slist_find (priv->active_connections, active) == FALSE);
 
-	priv->active_connections = g_slist_prepend (priv->active_connections, active);
-	g_signal_connect (active, "notify::" NM_ACTIVE_CONNECTION_STATE,
+	priv->active_connections = g_slist_prepend (priv->active_connections,
+	                                            g_object_ref (active));
+
+	g_signal_connect (active,
+	                  "notify::" NM_ACTIVE_CONNECTION_STATE,
 	                  G_CALLBACK (active_connection_state_changed),
 	                  self);
+	g_signal_connect (active,
+	                  "notify::" NM_ACTIVE_CONNECTION_DEFAULT,
+	                  G_CALLBACK (active_connection_default_changed),
+	                  self);
+	g_signal_connect (active,
+	                  "notify::" NM_ACTIVE_CONNECTION_DEFAULT6,
+	                  G_CALLBACK (active_connection_default_changed),
+	                  self);
 
 	g_signal_emit (self, signals[ACTIVE_CONNECTION_ADDED], 0, active);
-	g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS);
+
+	/* Only notify D-Bus if the active connection is actually exported */
+	if (nm_active_connection_get_path (active))
+		g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS);
 }
 
 const GSList *
@@ -400,6 +400,55 @@ nm_manager_get_active_connections (NMManager *manager)
 }
 
 static NMActiveConnection *
+find_ac_for_connection (NMManager *manager, NMConnection *connection)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	GSList *iter;
+	NMActiveConnection *ac;
+	NMConnection *ac_connection;
+	NMActiveConnectionState ac_state;
+	const char *uuid;
+
+	uuid = nm_connection_get_uuid (connection);
+	for (iter = priv->active_connections; iter; iter = iter->next) {
+		ac = iter->data;
+		ac_connection = nm_active_connection_get_connection (ac);
+		ac_state = nm_active_connection_get_state (ac);
+
+		if (   !strcmp (nm_connection_get_uuid (ac_connection), uuid)
+		    && (ac_state < NM_ACTIVE_CONNECTION_STATE_DEACTIVATED))
+			return ac;
+	}
+
+	return NULL;
+}
+
+/* Filter out connections that are already active.
+ * nm_settings_get_connections() returns sorted list. We need to preserve the
+ * order so that we didn't change auto-activation order (recent timestamps
+ * are first).
+ * Caller is responsible for freeing the returned list with g_slist_free().
+ */
+GSList *
+nm_manager_get_activatable_connections (NMManager *manager)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	GSList *all_connections = nm_settings_get_connections (priv->settings);
+	GSList *connections = NULL, *iter;
+	NMConnection *connection;
+
+	for (iter = all_connections; iter; iter = iter->next) {
+		connection = iter->data;
+
+		if (!find_ac_for_connection (manager, connection))
+			connections = g_slist_prepend (connections, connection);
+	}
+
+	g_slist_free (all_connections);
+	return g_slist_reverse (connections);
+}
+
+static NMActiveConnection *
 active_connection_get_by_path (NMManager *manager, const char *path)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
@@ -411,7 +460,7 @@ active_connection_get_by_path (NMManager *manager, const char *path)
 	for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) {
 		NMActiveConnection *candidate = iter->data;
 
-		if (strcmp (path, nm_active_connection_get_path (candidate)) == 0)
+		if (g_strcmp0 (path, nm_active_connection_get_path (candidate)) == 0)
 			return candidate;
 	}
 	return NULL;
@@ -465,6 +514,21 @@ nm_manager_get_device_by_master (NMManager *manager, const char *master, const c
 	return NULL;
 }
 
+NMDevice *
+nm_manager_get_device_by_ifindex (NMManager *manager, int ifindex)
+{
+	GSList *iter;
+
+	for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) {
+		NMDevice *device = NM_DEVICE (iter->data);
+
+		if (nm_device_get_ifindex (device) == ifindex)
+			return device;
+	}
+
+	return NULL;
+}
+
 static gboolean
 manager_sleeping (NMManager *self)
 {
@@ -475,95 +539,41 @@ manager_sleeping (NMManager *self)
 	return FALSE;
 }
 
-static void
-modem_added (NMModemManager *modem_manager,
-			 NMModem *modem,
-			 const char *driver,
-			 gpointer user_data)
+static const char *
+_nm_state_to_string (NMState state)
 {
-	NMManager *self = NM_MANAGER (user_data);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *replace_device, *device = NULL;
-	const char *modem_iface;
-	GSList *iter;
-
-	/* Don't rely only on the data port; use the control port if available */
-	modem_iface = nm_modem_get_data_port (modem);
-	if (!modem_iface)
-		modem_iface = nm_modem_get_control_port (modem);
-	g_return_if_fail (modem_iface);
-
-	replace_device = find_device_by_ip_iface (NM_MANAGER (user_data), modem_iface);
-	if (replace_device) {
-		priv->devices = remove_one_device (NM_MANAGER (user_data),
-		                                   priv->devices,
-		                                   replace_device,
-		                                   FALSE);
-	}
-
-	/* Give Bluetooth DUN devices first chance to claim the modem */
-	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
-		if (nm_device_get_device_type (iter->data) == NM_DEVICE_TYPE_BT) {
-			if (nm_device_bt_modem_added (NM_DEVICE_BT (iter->data), modem, driver))
-				return;
-		}
-	}
-
-	/* If it was a Bluetooth modem and no bluetooth device claimed it, ignore
-	 * it.  The rfcomm port (and thus the modem) gets created automatically
-	 * by the Bluetooth code during the connection process.
-	 */
-	if (driver && !strcmp (driver, "bluetooth")) {
-		nm_log_info (LOGD_MB, "ignoring modem '%s' (no associated Bluetooth device)", modem_iface);
-		return;
+	switch (state) {
+	case NM_STATE_ASLEEP:
+		return "ASLEEP";
+	case NM_STATE_DISCONNECTED:
+		return "DISCONNECTED";
+	case NM_STATE_DISCONNECTING:
+		return "DISCONNECTING";
+	case NM_STATE_CONNECTING:
+		return "CONNECTING";
+	case NM_STATE_CONNECTED_LOCAL:
+		return "CONNECTED_LOCAL";
+	case NM_STATE_CONNECTED_SITE:
+		return "CONNECTED_SITE";
+	case NM_STATE_CONNECTED_GLOBAL:
+		return "CONNECTED_GLOBAL";
+	case NM_STATE_UNKNOWN:
+	default:
+		return "UNKNOWN";
 	}
-
-	/* Make the new modem device */
-	device = nm_device_modem_new (modem, driver);
-	if (device)
-		add_device (self, device);
 }
 
 static void
 set_state (NMManager *manager, NMState state)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	const char *state_str;
 
 	if (priv->state == state)
 		return;
 
 	priv->state = state;
 
-	switch (state) {
-	case NM_STATE_ASLEEP:
-		state_str = "ASLEEP";
-		break;
-	case NM_STATE_DISCONNECTED:
-		state_str = "DISCONNECTED";
-		break;
-	case NM_STATE_DISCONNECTING:
-		state_str = "DISCONNECTING";
-		break;
-	case NM_STATE_CONNECTING:
-		state_str = "CONNECTING";
-		break;
-	case NM_STATE_CONNECTED_LOCAL:
-		state_str = "CONNECTED_LOCAL";
-		break;
-	case NM_STATE_CONNECTED_SITE:
-		state_str = "CONNECTED_SITE";
-		break;
-	case NM_STATE_CONNECTED_GLOBAL:
-		state_str = "CONNECTED_GLOBAL";
-		break;
-	case NM_STATE_UNKNOWN:
-	default:
-		state_str = "UNKNOWN";
-		break;
-	}
-
-	nm_log_info (LOGD_CORE, "NetworkManager state is now %s", state_str);
+	nm_log_info (LOGD_CORE, "NetworkManager state is now %s", _nm_state_to_string (state));
 
 	g_object_notify (G_OBJECT (manager), NM_MANAGER_STATE);
 	g_signal_emit (manager, signals[STATE_CHANGED], 0, priv->state);
@@ -590,12 +600,59 @@ checked_connectivity (GObject *object, GAsyncResult *result, gpointer user_data)
 	g_object_unref (manager);
 }
 
+static NMState
+find_best_device_state (NMManager *manager, gboolean *want_connectivity_check)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMState best_state = NM_STATE_DISCONNECTED;
+	GSList *iter;
+
+	for (iter = priv->active_connections; iter; iter = iter->next) {
+		NMActiveConnection *ac = NM_ACTIVE_CONNECTION (iter->data);
+		NMActiveConnectionState ac_state = nm_active_connection_get_state (ac);
+
+		switch (ac_state) {
+		case NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
+			if (   nm_active_connection_get_default (ac)
+			    || nm_active_connection_get_default6 (ac)) {
+				nm_connectivity_set_online (priv->connectivity, TRUE);
+				if (nm_connectivity_get_state (priv->connectivity) == NM_CONNECTIVITY_FULL) {
+					*want_connectivity_check = FALSE;
+					return NM_STATE_CONNECTED_GLOBAL;
+				}
+
+				best_state = NM_STATE_CONNECTING;
+				*want_connectivity_check = TRUE;
+			} else {
+				if (best_state < NM_STATE_CONNECTING)
+					best_state = NM_STATE_CONNECTED_LOCAL;
+			}
+			break;
+		case NM_ACTIVE_CONNECTION_STATE_ACTIVATING:
+			if (!nm_active_connection_get_assumed (ac)) {
+				if (best_state != NM_STATE_CONNECTED_GLOBAL)
+					best_state = NM_STATE_CONNECTING;
+			}
+			break;
+		case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING:
+			if (!nm_active_connection_get_assumed (ac)) {
+				if (best_state < NM_STATE_DISCONNECTING)
+					best_state = NM_STATE_DISCONNECTING;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	return best_state;
+}
+
 static void
 nm_manager_update_state (NMManager *manager)
 {
 	NMManagerPrivate *priv;
 	NMState new_state = NM_STATE_DISCONNECTED;
-	GSList *iter;
 	gboolean want_connectivity_check = FALSE;
 
 	g_return_if_fail (NM_IS_MANAGER (manager));
@@ -604,30 +661,8 @@ nm_manager_update_state (NMManager *manager)
 
 	if (manager_sleeping (manager))
 		new_state = NM_STATE_ASLEEP;
-	else {
-		for (iter = priv->devices; iter; iter = iter->next) {
-			NMDevice *dev = NM_DEVICE (iter->data);
-			NMDeviceState state = nm_device_get_state (dev);
-
-			if (state == NM_DEVICE_STATE_ACTIVATED) {
-				nm_connectivity_set_online (priv->connectivity, TRUE);
-				if (nm_connectivity_get_state (priv->connectivity) != NM_CONNECTIVITY_FULL) {
-					new_state = NM_STATE_CONNECTING;
-					want_connectivity_check = TRUE;
-				} else {
-					new_state = NM_STATE_CONNECTED_GLOBAL;
-					break;
-				}
-			}
-
-			if (nm_device_is_activating (dev))
-				new_state = NM_STATE_CONNECTING;
-			else if (new_state != NM_STATE_CONNECTING) {
-				if (state == NM_DEVICE_STATE_DEACTIVATING)
-					new_state = NM_STATE_DISCONNECTING;
-			}
-		}
-	}
+	else
+		new_state = find_best_device_state (manager, &want_connectivity_check);
 
 	if (new_state == NM_STATE_CONNECTING && want_connectivity_check) {
 		nm_connectivity_check_async (priv->connectivity,
@@ -660,65 +695,99 @@ manager_device_state_changed (NMDevice *device,
 	default:
 		break;
 	}
+}
 
-	nm_manager_update_state (self);
+static void device_has_pending_action_changed (NMDevice *device,
+                                               GParamSpec *pspec,
+                                               NMManager *self);
+
+static void
+check_if_startup_complete (NMManager *self)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	GSList *iter;
+
+	if (!priv->startup)
+		return;
+
+	for (iter = priv->devices; iter; iter = iter->next) {
+		NMDevice *dev = iter->data;
+
+		if (nm_device_has_pending_action (dev)) {
+			nm_log_dbg (LOGD_CORE, "check_if_startup_complete returns FALSE because of %s",
+			            nm_device_get_iface (dev));
+			return;
+		}
+	}
+
+	nm_log_info (LOGD_CORE, "startup complete");
+
+	priv->startup = FALSE;
+	g_object_notify (G_OBJECT (self), "startup");
+
+	/* We don't have to watch notify::has-pending-action any more. */
+	for (iter = priv->devices; iter; iter = iter->next) {
+		NMDevice *dev = iter->data;
+
+		g_signal_handlers_disconnect_by_func (dev, G_CALLBACK (device_has_pending_action_changed), self);
+	}
+}
+
+static void
+device_has_pending_action_changed (NMDevice *device,
+                                   GParamSpec *pspec,
+                                   NMManager *self)
+{
+	check_if_startup_complete (self);
 }
 
-/* Removes a device from a device list; returns the start of the new device list */
-static GSList *
-remove_one_device (NMManager *manager,
-                   GSList *list,
-                   NMDevice *device,
-                   gboolean quitting)
+static void
+remove_device (NMManager *manager, NMDevice *device, gboolean quitting)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
 
 	if (nm_device_get_managed (device)) {
-		/* When quitting, we want to leave up interfaces & connections
-		 * that can be taken over again (ie, "assumed") when NM restarts
-		 * so that '/etc/init.d/NetworkManager restart' will not distrupt
-		 * networking for interfaces that support connection assumption.
-		 * All other devices get unmanaged when NM quits so that their
-		 * connections get torn down and the interface is deactivated.
-		 */
+		NMActRequest *req = nm_device_get_act_request (device);
+		gboolean unmanage = FALSE;
 
-		if (   !nm_device_can_assume_connections (device)
-		    || (nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED)
-		    || !quitting)
-			nm_device_set_managed (device, FALSE, NM_DEVICE_STATE_REASON_REMOVED);
+		/* Leave activated interfaces up when quitting so their configuration
+		 * can be taken over when NM restarts.  This ensures connectivity while
+		 * NM is stopped. Devices which do not support connection assumption
+		 * cannot be left up.
+		 */
+		if (!quitting)  /* Forced removal; device already gone */
+			unmanage = TRUE;
+		else if (!nm_device_can_assume_active_connection (device))
+			unmanage = TRUE;
+		else if (!req)
+			unmanage = TRUE;
+
+		if (unmanage) {
+			if (quitting)
+				nm_device_set_unmanaged_quitting (device);
+			else
+				nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED);
+		}
 	}
 
-	g_signal_handlers_disconnect_by_func (device, manager_device_state_changed, manager);
+	g_signal_handlers_disconnect_matched (device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, manager);
+
+	nm_settings_device_removed (priv->settings, device, quitting);
+	priv->devices = g_slist_remove (priv->devices, device);
 
-	nm_settings_device_removed (priv->settings, device);
 	g_signal_emit (manager, signals[DEVICE_REMOVED], 0, device);
+	g_object_notify (G_OBJECT (manager), NM_MANAGER_DEVICES);
+
+	nm_dbus_manager_unregister_object (priv->dbus_mgr, device);
 	g_object_unref (device);
 
-	return g_slist_remove (list, device);
+	check_if_startup_complete (manager);
 }
 
 static void
-modem_removed (NMModemManager *modem_manager,
-			   NMModem *modem,
-			   gpointer user_data)
+device_removed_cb (NMDevice *device, gpointer user_data)
 {
-	NMManager *self = NM_MANAGER (user_data);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *found;
-	GSList *iter;
-
-	/* Give Bluetooth DUN devices first chance to handle the modem removal */
-	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
-		if (nm_device_get_device_type (iter->data) == NM_DEVICE_TYPE_BT) {
-			if (nm_device_bt_modem_removed (NM_DEVICE_BT (iter->data), modem))
-				return;
-		}
-	}
-
-	/* Otherwise remove the standalone modem */
-	found = nm_manager_get_device_by_udi (self, nm_modem_get_path (modem));
-	if (found)
-		priv->devices = remove_one_device (self, priv->devices, found, FALSE);
+	remove_device (NM_MANAGER (user_data), device, FALSE);
 }
 
 static void
@@ -760,18 +829,6 @@ aipd_handle_event (DBusGProxy *proxy,
 		nm_log_warn (LOGD_AUTOIP4, "(%s): unhandled avahi-autoipd event", iface);
 }
 
-static const char *
-hostname_provider_get_hostname (NMHostnameProvider *provider)
-{
-	return NM_MANAGER_GET_PRIVATE (provider)->hostname;
-}
-
-static void
-hostname_provider_init (NMHostnameProvider *provider_class)
-{
-	provider_class->get_hostname = hostname_provider_get_hostname;
-}
-
 NMState
 nm_manager_get_state (NMManager *manager)
 {
@@ -780,316 +837,54 @@ nm_manager_get_state (NMManager *manager)
 	return NM_MANAGER_GET_PRIVATE (manager)->state;
 }
 
-static gboolean
-might_be_vpn (NMConnection *connection)
-{
-	NMSettingConnection *s_con;
-	const char *ctype = NULL;
-
-	if (nm_connection_get_setting_vpn (connection))
-		return TRUE;
-
-	/* Make sure it's not a VPN, which we can't autocomplete yet */
-	s_con = nm_connection_get_setting_connection (connection);
-	if (s_con)
-		ctype = nm_setting_connection_get_connection_type (s_con);
-
-	return (g_strcmp0 (ctype, NM_SETTING_VPN_SETTING_NAME) == 0);
-}
-
-static gboolean
-try_complete_vpn (NMConnection *connection, GSList *existing, GError **error)
-{
-	g_assert (might_be_vpn (connection) == TRUE);
-
-	if (!nm_connection_get_setting_vpn (connection)) {
-		g_set_error_literal (error,
-			                 NM_MANAGER_ERROR,
-			                 NM_MANAGER_ERROR_UNSUPPORTED_CONNECTION_TYPE,
-			                 "VPN connections require a 'vpn' setting");
-		return FALSE;
-	}
-
-	nm_utils_complete_generic (connection,
-	                           NM_SETTING_VPN_SETTING_NAME,
-	                           existing,
-	                           _("VPN connection %d"),
-	                           NULL,
-	                           FALSE); /* No IPv6 by default for now */
-
-	return TRUE;
-}
-
-static PendingActivation *
-pending_activation_new (NMManager *manager,
-                        DBusGMethodInvocation *context,
-                        const char *device_path,
-                        const char *connection_path,
-                        GHashTable *settings,
-                        const char *specific_object_path,
-                        PendingActivationFunc callback,
-                        GError **error)
-{
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	PendingActivation *pending;
-	NMDevice *device = NULL;
-	NMConnection *connection = NULL;
-	GSList *all_connections = NULL;
-	gboolean success;
-
-	g_return_val_if_fail (manager != NULL, NULL);
-	g_return_val_if_fail (context != NULL, NULL);
-	g_return_val_if_fail (device_path != NULL, NULL);
-
-	/* A object path of "/" means NULL */
-	if (g_strcmp0 (specific_object_path, "/") == 0)
-		specific_object_path = NULL;
-	if (g_strcmp0 (device_path, "/") == 0)
-		device_path = NULL;
-
-	/* Create the partial connection from the given settings */
-	if (settings) {
-		if (device_path)
-			device = nm_manager_get_device_by_path (manager, device_path);
-		if (!device) {
-			g_set_error_literal (error,
-				                 NM_MANAGER_ERROR,
-				                 NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-				                 "Device not found");
-			return NULL;
-		}
-
-		connection = nm_connection_new ();
-		nm_connection_replace_settings (connection, settings, NULL);
-
-		all_connections = nm_settings_get_connections (priv->settings);
-
-		if (might_be_vpn (connection)) {
-			/* Try to fill the VPN's connection setting and name at least */
-			success = try_complete_vpn (connection, all_connections, error);
-		} else {
-			/* Let each device subclass complete the connection */
-			success = nm_device_complete_connection (device,
-			                                         connection,
-			                                         specific_object_path,
-			                                         all_connections,
-			                                         error);
-		}
-		g_slist_free (all_connections);
-
-		if (success == FALSE) {
-			g_object_unref (connection);
-			return NULL;
-		}
-	}
-
-	pending = g_slice_new0 (PendingActivation);
-	pending->manager = manager;
-	pending->context = context;
-	pending->callback = callback;
-
-	pending->connection_path = g_strdup (connection_path);
-	pending->connection = connection;
-
-	/* "/" is special-cased to NULL to get through D-Bus */
-	if (specific_object_path && strcmp (specific_object_path, "/"))
-		pending->specific_object_path = g_strdup (specific_object_path);
-	if (device_path && strcmp (device_path, "/"))
-		pending->device_path = g_strdup (device_path);
-
-	return pending;
-}
-
-static void
-pending_auth_done (NMAuthChain *chain,
-                   GError *error,
-                   DBusGMethodInvocation *context,
-                   gpointer user_data)
-{
-	PendingActivation *pending = user_data;
-	NMAuthCallResult result;
-	GError *tmp_error = NULL;
-
-	/* Caller has had a chance to obtain authorization, so we only need to
-	 * check for 'yes' here.
-	 */
-	result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL));
-	if (result != NM_AUTH_CALL_RESULT_YES) {
-		tmp_error = g_error_new_literal (NM_MANAGER_ERROR,
-		                                 NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                                 "Not authorized to control networking.");
-		goto out;
-	}
-
-	if (pending->wifi_shared_permission) {
-		result = nm_auth_chain_get_result (chain, pending->wifi_shared_permission);
-		if (result != NM_AUTH_CALL_RESULT_YES) {
-			tmp_error = g_error_new_literal (NM_MANAGER_ERROR,
-			                                 NM_MANAGER_ERROR_PERMISSION_DENIED,
-			                                 "Not authorized to share connections via wifi.");
-			goto out;
-		}
-	}
-
-	/* Otherwise authorized and available to activate */
-
-out:
-	pending->callback (pending, tmp_error);
-	g_clear_error (&tmp_error);
-}
-
-static void
-pending_activation_check_authorized (PendingActivation *pending,
-                                     NMDBusManager *dbus_mgr)
-{
-	NMManagerPrivate *priv;
-	char *error_desc = NULL;
-	gulong sender_uid = G_MAXULONG;
-	GError *error;
-	const char *wifi_permission = NULL;
-	NMConnection *connection;
-
-	g_return_if_fail (pending != NULL);
-	g_return_if_fail (dbus_mgr != NULL);
-
-	priv = NM_MANAGER_GET_PRIVATE (pending->manager);
-
-	if (!nm_auth_get_caller_uid (pending->context, 
-	                             dbus_mgr,
-	                             &sender_uid,
-	                             &error_desc)) {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             error_desc);
-		pending->callback (pending, error);
-		g_error_free (error);
-		g_free (error_desc);
-		return;
-	}
-
-	/* Yay for root */
-	if (0 == sender_uid) {
-		pending->callback (pending, NULL);
-		return;
-	}
-
-	/* By this point we have an auto-completed connection (for AddAndActivate)
-	 * or an existing connection (for Activate).
-	 */
-	connection = pending->connection;
-	if (!connection)
-		connection = (NMConnection *) nm_settings_get_connection_by_path (priv->settings, pending->connection_path);
-
-	if (!connection) {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
-		                             "Connection could not be found.");
-		pending->callback (pending, error);
-		g_error_free (error);
-		return;
-	}
-
-	/* Ensure the subject has permissions for this connection */
-	if (!nm_auth_uid_in_acl (connection,
-	                         priv->session_monitor,
-	                         sender_uid,
-	                         &error_desc)) {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             error_desc);
-		pending->callback (pending, error);
-		g_error_free (error);
-		g_free (error_desc);
-		return;
-	}
-
-	/* First check if the user is allowed to use networking at all, giving
-	 * the user a chance to authenticate to gain the permission.
-	 */
-	pending->chain = nm_auth_chain_new (pending->context,
-	                                    NULL,
-	                                    pending_auth_done,
-	                                    pending);
-	g_assert (pending->chain);
-	nm_auth_chain_add_call (pending->chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE);
-
-	/* Shared wifi connections require special permissions too */
-	wifi_permission = nm_utils_get_shared_wifi_permission (connection);
-	if (wifi_permission) {
-		pending->wifi_shared_permission = wifi_permission;
-		nm_auth_chain_add_call (pending->chain, wifi_permission, TRUE);
-	}
-}
-
-static void
-pending_activation_destroy (PendingActivation *pending,
-                            GError *error,
-                            NMActiveConnection *ac)
-{
-	g_return_if_fail (pending != NULL);
-
-	if (error)
-		dbus_g_method_return_error (pending->context, error);
-	else if (ac) {
-		if (pending->connection) {
-			dbus_g_method_return (pending->context,
-			                      pending->connection_path,
-			                      nm_active_connection_get_path (ac));
-		} else {
-			dbus_g_method_return (pending->context,
-			                      nm_active_connection_get_path (ac));
-		}
-	}
-
-	g_free (pending->connection_path);
-	g_free (pending->specific_object_path);
-	g_free (pending->device_path);
-	if (pending->connection)
-		g_object_unref (pending->connection);
-
-	if (pending->chain)
-		nm_auth_chain_unref (pending->chain);
-
-	memset (pending, 0, sizeof (PendingActivation));
-	g_slice_free (PendingActivation, pending);
-}
-
 /*******************************************************************/
 /* Settings stuff via NMSettings                                   */
 /*******************************************************************/
 
 static NMDevice *
-get_device_from_hwaddr (NMManager *self, NMConnection *connection)
+get_device_from_hwaddr (NMManager *self, const GByteArray *setting_mac)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	const guint8 *device_mac;
+	guint device_mac_len;
 	GSList *iter;
 
+	if (!setting_mac)
+		return NULL;
+
 	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
-		if (nm_device_hwaddr_matches (NM_DEVICE (iter->data), connection, NULL, 0, TRUE))
-			return iter->data;
+		NMDevice *device = iter->data;
+
+		device_mac = nm_device_get_hw_address (iter->data, &device_mac_len);
+		if (   setting_mac->len == device_mac_len
+		    && memcmp (setting_mac->data, device_mac, device_mac_len) == 0)
+			return device;
 	}
 	return NULL;
 }
 
-static NMDevice*
+static NMDevice *
 find_vlan_parent (NMManager *self,
-                  NMConnection *connection,
-                  gboolean check_hwaddr)
+                  NMConnection *connection)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMSettingVlan *s_vlan;
+	NMSettingWired *s_wired;
 	NMConnection *parent_connection;
 	const char *parent_iface;
 	NMDevice *parent = NULL;
+	const GByteArray *setting_mac;
 	GSList *iter;
 
-	/* The 'parent' property could be either an interface name, a connection
-	 * UUID, or even given by the MAC address of the connection's ethernet
-	 * or WiFi setting.
+	/* The 'parent' property could be given by an interface name, a
+	 * connection UUID, or the MAC address of an NMSettingWired.
 	 */
 	s_vlan = nm_connection_get_setting_vlan (connection);
 	g_return_val_if_fail (s_vlan != NULL, NULL);
 
+	s_wired = nm_connection_get_setting_wired (connection);
+	setting_mac = s_wired ? nm_setting_wired_get_mac_address (s_wired) : NULL;
+
 	parent_iface = nm_setting_vlan_get_parent (s_vlan);
 	if (parent_iface) {
 		parent = find_device_by_ip_iface (self, parent_iface);
@@ -1114,18 +909,37 @@ find_vlan_parent (NMManager *self,
 				}
 
 				/* Check the hardware address of the parent connection */
-				if (check_hwaddr)
-					return get_device_from_hwaddr (self, parent_connection);
+				return get_device_from_hwaddr (self, setting_mac);
 			}
 			return NULL;
 		}
 	}
 
 	/* Try the hardware address from the VLAN connection's hardware setting */
-	if (check_hwaddr)
-		return get_device_from_hwaddr (self, connection);
+	return get_device_from_hwaddr (self, setting_mac);
+}
 
-	return NULL;
+static NMDevice *
+find_infiniband_parent (NMManager *self,
+                        NMConnection *connection)
+{
+	NMSettingInfiniband *s_infiniband;
+	const char *parent_iface;
+	NMDevice *parent = NULL;
+	const GByteArray *setting_mac;
+
+	s_infiniband = nm_connection_get_setting_infiniband (connection);
+	g_return_val_if_fail (s_infiniband != NULL, NULL);
+
+	parent_iface = nm_setting_infiniband_get_parent (s_infiniband);
+	if (parent_iface) {
+		parent = find_device_by_ip_iface (self, parent_iface);
+		if (parent)
+			return parent;
+	}
+
+	setting_mac = nm_setting_infiniband_get_mac_address (s_infiniband);
+	return get_device_from_hwaddr (self, setting_mac);
 }
 
 /**
@@ -1146,7 +960,6 @@ get_virtual_iface_name (NMManager *self,
                         NMConnection *connection,
                         NMDevice **out_parent)
 {
-	char *vname = NULL;
 	NMDevice *parent = NULL;
 
 	if (out_parent)
@@ -1155,17 +968,21 @@ get_virtual_iface_name (NMManager *self,
 	if (nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME))
 		return g_strdup (nm_connection_get_virtual_iface_name (connection));
 
+	if (nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME))
+		return g_strdup (nm_connection_get_virtual_iface_name (connection));
+
 	if (nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME))
 		return g_strdup (nm_connection_get_virtual_iface_name (connection));
 
 	if (nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME)) {
 		NMSettingVlan *s_vlan;
 		const char *ifname;
+		char *vname;
 
 		s_vlan = nm_connection_get_setting_vlan (connection);
 		g_return_val_if_fail (s_vlan != NULL, NULL);
 
-		parent = find_vlan_parent (self, connection, TRUE);
+		parent = find_vlan_parent (self, connection);
 		if (parent) {
 			ifname = nm_connection_get_virtual_iface_name (connection);
 
@@ -1189,113 +1006,56 @@ get_virtual_iface_name (NMManager *self,
 			}
 			if (out_parent)
 				*out_parent = parent;
+			return vname;
 		}
 	}
 
-	return vname;
+	if (nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)) {
+		const char *ifname;
+		char *name;
+
+		parent = find_infiniband_parent (self, connection);
+		if (parent) {
+			ifname = nm_connection_get_virtual_iface_name (connection);
+			if (ifname)
+				name = g_strdup (ifname);
+			else {
+				NMSettingInfiniband *s_infiniband;
+				int p_key;
+
+				ifname = nm_device_get_iface (parent);
+				s_infiniband = nm_connection_get_setting_infiniband (connection);
+				p_key = nm_setting_infiniband_get_p_key (s_infiniband);
+				name = g_strdup_printf ("%s.%04x", ifname, p_key);
+			}
+			if (out_parent)
+				*out_parent = parent;
+			return name;
+		}
+	}
+
+	return NULL;
 }
 
 static gboolean
 connection_needs_virtual_device (NMConnection *connection)
 {
 	if (   nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME)
+	    || nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME)
 	    || nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)
 	    || nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME))
 		return TRUE;
 
-	return FALSE;
-}
-
-static char *
-get_virtual_iface_placeholder_udi (void)
-{
-	static guint32 id = 0;
-
-	return g_strdup_printf ("/virtual/device/placeholder/%d", id++);
-}
-
-/***************************/
-
-/* FIXME: remove when we handle bridges non-destructively */
-
-#define NM_BRIDGE_FILE  NMRUNDIR "/nm-bridges"
-
-static void
-read_nm_created_bridges (NMManager *self)
-{
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	char *contents;
-	char **lines, **iter;
-	GTimeVal tv;
-	glong ts;
-
-	if (!g_file_get_contents (NM_BRIDGE_FILE, &contents, NULL, NULL))
-		return;
-
-	g_get_current_time (&tv);
-
-	lines = g_strsplit_set (contents, "\n", 0);
-	g_free (contents);
-
-	for (iter = lines; iter && *iter; iter++) {
-		if (g_str_has_prefix (*iter, "ts=")) {
-			errno = 0;
-			ts = strtol (*iter + 3, NULL, 10);
-			/* allow 30 minutes time difference before we ignore the file */
-			if (errno || ABS (tv.tv_sec - ts) > 1800)
-				goto out;
-		} else if (g_str_has_prefix (*iter, "iface="))
-			g_hash_table_insert (priv->nm_bridges, g_strdup (*iter + 6), GUINT_TO_POINTER (1));
-	}
-
-out:
-	g_strfreev (lines);
-	unlink (NM_BRIDGE_FILE);
-}
-
-static void
-write_nm_created_bridges (NMManager *self)
-{
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GString *br_list;
-	GSList *iter;
-	GError *error = NULL;
-	GTimeVal tv;
-	gboolean found = FALSE;
-
-	/* write out nm-created bridges list */
-	br_list = g_string_sized_new (50);
-
-	/* Timestamp is first line */
-	g_get_current_time (&tv);
-	g_string_append_printf (br_list, "ts=%ld\n", tv.tv_sec);
-
-	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
-		NMDevice *device = iter->data;
+	if (nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)) {
+		NMSettingInfiniband *s_infiniband;
 
-		if (nm_device_get_device_type (device) == NM_DEVICE_TYPE_BRIDGE) {
-			g_string_append_printf (br_list, "iface=%s\n", nm_device_get_iface (device));
-			found = TRUE;
-		}
+		s_infiniband = nm_connection_get_setting_infiniband (connection);
+		g_return_val_if_fail (s_infiniband != NULL, FALSE);
+		if (nm_setting_infiniband_get_p_key (s_infiniband) != -1)
+			return TRUE;
 	}
 
-	if (found) {
-		if (!g_file_set_contents (NM_BRIDGE_FILE, br_list->str, -1, &error)) {
-			nm_log_warn (LOGD_BRIDGE, "Failed to write NetworkManager-created bridge list; "
-			             "on restart bridges may not be recognized. (%s)",
-			             error ? error->message : "unknown");
-			g_clear_error (&error);
-		}
-	}
-	g_string_free (br_list, TRUE);
-}
-
-static gboolean
-bridge_created_by_nm (NMManager *self, const char *iface)
-{
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-
-	return (priv->nm_bridges && g_hash_table_lookup (priv->nm_bridges, iface));
+	return FALSE;
 }
 
 /***************************/
@@ -1315,7 +1075,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GSList *iter;
-	char *iface = NULL, *udi;
+	char *iface = NULL;
 	NMDevice *device = NULL, *parent = NULL;
 
 	iface = get_virtual_iface_name (self, connection, &parent);
@@ -1328,60 +1088,37 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 	/* Make sure we didn't create a device for this connection already */
 	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
 		NMDevice *candidate = iter->data;
-		GError *error = NULL;
 
 		if (   g_strcmp0 (nm_device_get_iface (candidate), iface) == 0
-		    || nm_device_check_connection_compatible (candidate, connection, &error)) {
-			g_clear_error (&error);
+		    || nm_device_check_connection_compatible (candidate, connection))
 			goto out;
-		}
-		g_clear_error (&error);
 	}
 
-	if (nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME)) {
-		if (!nm_system_add_bonding_master (iface)) {
-			nm_log_warn (LOGD_DEVICE, "(%s): failed to add bonding master interface for '%s'",
-			             iface, nm_connection_get_id (connection));
-			goto out;
-		}
+	/* Block notification of link added since we're creating the device
+	 * explicitly here, otherwise adding the platform/kernel device would
+	 * create it before this function can do the rest of the setup.
+	 */
+	priv->ignore_link_added_cb++;
 
-		udi = get_virtual_iface_placeholder_udi ();
-		device = nm_device_bond_new (udi, iface);
-		g_free (udi);
+	if (nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME)) {
+		device = nm_device_bond_new_for_connection (connection);
+	} else if (nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME)) {
+		device = nm_device_team_new_for_connection (connection);
 	} else if (nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) {
-		gboolean exists = FALSE;
-
-		if (!nm_system_create_bridge (iface, &exists)) {
-			nm_log_warn (LOGD_DEVICE, "(%s): failed to add bridging interface for '%s'",
-			             iface, nm_connection_get_id (connection));
-			goto out;
-		}
-
-		/* FIXME: remove when we handle bridges non-destructively */
-		if (exists && !bridge_created_by_nm (self, iface)) {
-			nm_log_warn (LOGD_DEVICE, "(%s): cannot use existing bridge for '%s'",
-			             iface, nm_connection_get_id (connection));
-			goto out;
-		}
-
-		udi = get_virtual_iface_placeholder_udi ();
-		device = nm_device_bridge_new (udi, iface);
-		g_free (udi);
+		device = nm_device_bridge_new_for_connection (connection);
 	} else if (nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME)) {
-		g_return_val_if_fail (parent != NULL, FALSE);
+		device = nm_device_vlan_new_for_connection (connection, parent);
+	} else if (nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)) {
+		device = nm_device_infiniband_new_partition (connection, parent);
+	}
 
-		if (!nm_system_add_vlan_iface (connection, iface, nm_device_get_ip_ifindex (parent))) {
-			nm_log_warn (LOGD_DEVICE, "(%s): failed to add VLAN interface for '%s'",
-			             iface, nm_connection_get_id (connection));
-			goto out;
-		}
-		udi = get_virtual_iface_placeholder_udi ();
-		device = nm_device_vlan_new (udi, iface, parent);
-		g_free (udi);
+	if (device) {
+		nm_device_set_nm_owned (device);
+		add_device (self, device, FALSE);
+		g_object_unref (device);
 	}
 
-	if (device)
-		add_device (self, device);
+	priv->ignore_link_added_cb--;
 
 out:
 	g_free (iface);
@@ -1399,14 +1136,11 @@ system_create_virtual_devices (NMManager *self)
 	connections = nm_settings_get_connections (priv->settings);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		NMConnection *connection = iter->data;
-		NMSettingConnection *s_con = nm_connection_get_setting_connection (connection);
 
-		g_assert (s_con);
-		if (connection_needs_virtual_device (connection)) {
-			/* We only create a virtual interface if the connection can autoconnect */
-			if (nm_setting_connection_get_autoconnect (s_con))
-				system_create_virtual_device (self, connection);
-		}
+		/* We only create a virtual interface if the connection can autoconnect */
+		if (   connection_needs_virtual_device (connection)
+		    && nm_settings_connection_can_autoconnect (NM_SETTINGS_CONNECTION (connection)))
+			system_create_virtual_device (self, connection);
 	}
 	g_slist_free (connections);
 }
@@ -1458,13 +1192,14 @@ system_unmanaged_devices_changed_cb (NMSettings *settings,
 	unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings);
 	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
 		NMDevice *device = NM_DEVICE (iter->data);
-		gboolean managed;
+		gboolean unmanaged;
 
-		managed = !nm_device_spec_match_list (device, unmanaged_specs);
-		nm_device_set_managed (device,
-		                       managed,
-		                       managed ? NM_DEVICE_STATE_REASON_NOW_MANAGED :
-		                                 NM_DEVICE_STATE_REASON_NOW_UNMANAGED);
+		unmanaged = nm_device_spec_match_list (device, unmanaged_specs);
+		nm_device_set_unmanaged (device,
+		                         NM_UNMANAGED_USER,
+		                         unmanaged,
+		                         unmanaged ? NM_DEVICE_STATE_REASON_NOW_UNMANAGED :
+		                                     NM_DEVICE_STATE_REASON_NOW_MANAGED);
 	}
 }
 
@@ -1487,6 +1222,8 @@ system_hostname_changed_cb (NMSettings *settings,
 	priv->hostname = (hostname && strlen (hostname)) ? g_strdup (hostname) : NULL;
 	g_object_notify (G_OBJECT (self), NM_MANAGER_HOSTNAME);
 
+	nm_dhcp_manager_set_default_hostname (nm_dhcp_manager_get (), priv->hostname);
+
 	g_free (hostname);
 }
 
@@ -1517,8 +1254,6 @@ write_value_to_state_file (const char *filename,
 	                      FALSE);
 
 	key_file = g_key_file_new ();
-	if (!key_file)
-		return FALSE;
 
 	g_key_file_set_list_separator (key_file, ',');
 	g_key_file_load_from_file (key_file, filename, G_KEY_FILE_KEEP_COMMENTS, NULL);
@@ -1550,11 +1285,8 @@ radio_enabled_for_rstate (RadioState *rstate, gboolean check_changeable)
 	gboolean enabled;
 
 	enabled = rstate->user_enabled && rstate->hw_enabled;
-	if (check_changeable) {
+	if (check_changeable)
 		enabled &= rstate->sw_enabled;
-		if (rstate->daemon_enabled_func)
-			enabled &= rstate->daemon_enabled;
-	}
 	return enabled;
 }
 
@@ -1598,95 +1330,24 @@ manager_update_radio_enabled (NMManager *self,
 }
 
 static void
-manager_hidden_ap_found (NMDevice *device,
-                         NMAccessPoint *ap,
-                         gpointer user_data)
-{
-	NMManager *manager = NM_MANAGER (user_data);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	const struct ether_addr *bssid;
-	GSList *iter;
-	GSList *connections;
-	gboolean done = FALSE;
-
-	g_return_if_fail (nm_ap_get_ssid (ap) == NULL);
-
-	bssid = nm_ap_get_address (ap);
-	g_assert (bssid);
-
-	/* Look for this AP's BSSID in the seen-bssids list of a connection,
-	 * and if a match is found, copy over the SSID */
-	connections = nm_settings_get_connections (priv->settings);
-	for (iter = connections; iter && !done; iter = g_slist_next (iter)) {
-		NMConnection *connection = NM_CONNECTION (iter->data);
-		NMSettingWireless *s_wifi;
-
-		s_wifi = nm_connection_get_setting_wireless (connection);
-		if (s_wifi) {
-			if (nm_settings_connection_has_seen_bssid (NM_SETTINGS_CONNECTION (connection), bssid))
-				nm_ap_set_ssid (ap, nm_setting_wireless_get_ssid (s_wifi));
-		}
-	}
-	g_slist_free (connections);
-}
-
-static RfKillState
-nm_manager_get_ipw_rfkill_state (NMManager *self)
-{
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GSList *iter;
-	RfKillState ipw_state = RFKILL_UNBLOCKED;
-
-	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
-		NMDevice *candidate = NM_DEVICE (iter->data);
-		RfKillState candidate_state;
-
-		if (nm_device_get_device_type (candidate) == NM_DEVICE_TYPE_WIFI) {
-			candidate_state = nm_device_wifi_get_ipw_rfkill_state (NM_DEVICE_WIFI (candidate));
-
-			if (candidate_state > ipw_state)
-				ipw_state = candidate_state;
-		}
-	}
-
-	return ipw_state;
-}
-
-static RfKillState
-nm_manager_get_modem_enabled_state (NMManager *self)
+update_rstate_from_rfkill (NMRfkillManager *rfkill_mgr, RadioState *rstate)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GSList *iter;
-	RfKillState wwan_state = RFKILL_UNBLOCKED;
-
-	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
-		NMDevice *candidate = NM_DEVICE (iter->data);
-		RfKillState candidate_state = RFKILL_UNBLOCKED;
-
-		if (nm_device_get_rfkill_type (candidate) == RFKILL_TYPE_WWAN) {
-			if (!nm_device_get_enabled (candidate))
-				candidate_state = RFKILL_SOFT_BLOCKED;
-
-			if (candidate_state > wwan_state)
-				wwan_state = candidate_state;
-		}
-	}
-
-	return wwan_state;
-}
-
-static void
-update_rstate_from_rfkill (RadioState *rstate, RfKillState rfkill)
-{
-	if (rfkill == RFKILL_UNBLOCKED) {
+	switch (nm_rfkill_manager_get_rfkill_state (rfkill_mgr, rstate->rtype)) {
+	case RFKILL_UNBLOCKED:
 		rstate->sw_enabled = TRUE;
 		rstate->hw_enabled = TRUE;
-	} else if (rfkill == RFKILL_SOFT_BLOCKED) {
+		break;
+	case RFKILL_SOFT_BLOCKED:
 		rstate->sw_enabled = FALSE;
 		rstate->hw_enabled = TRUE;
-	} else if (rfkill == RFKILL_HARD_BLOCKED) {
+		break;
+	case RFKILL_HARD_BLOCKED:
 		rstate->sw_enabled = FALSE;
 		rstate->hw_enabled = FALSE;
+		break;
+	default:
+		g_warn_if_reached ();
+		break;
 	}
 }
 
@@ -1696,51 +1357,19 @@ manager_rfkill_update_one_type (NMManager *self,
                                 RfKillType rtype)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	RfKillState udev_state = RFKILL_UNBLOCKED;
-	RfKillState other_state = RFKILL_UNBLOCKED;
-	RfKillState composite;
-	gboolean old_enabled, new_enabled, old_rfkilled, new_rfkilled;
-	gboolean old_hwe, old_daemon_enabled = FALSE;
+	gboolean old_enabled, new_enabled, old_rfkilled, new_rfkilled, old_hwe;
 
 	old_enabled = radio_enabled_for_rstate (rstate, TRUE);
 	old_rfkilled = rstate->hw_enabled && rstate->sw_enabled;
 	old_hwe = rstate->hw_enabled;
 
-	udev_state = nm_udev_manager_get_rfkill_state (priv->udev_mgr, rtype);
-
-	if (rstate->other_enabled_func)
-		other_state = rstate->other_enabled_func (self);
-
-	/* The composite state is the "worst" of either udev or other states */
-	if (udev_state == RFKILL_HARD_BLOCKED || other_state == RFKILL_HARD_BLOCKED)
-		composite = RFKILL_HARD_BLOCKED;
-	else if (udev_state == RFKILL_SOFT_BLOCKED || other_state == RFKILL_SOFT_BLOCKED)
-		composite = RFKILL_SOFT_BLOCKED;
-	else
-		composite = RFKILL_UNBLOCKED;
-
-	update_rstate_from_rfkill (rstate, composite);
-
-	/* If the device has a management daemon that can affect enabled state, check that now */
-	if (rstate->daemon_enabled_func) {
-		old_daemon_enabled = rstate->daemon_enabled;
-		rstate->daemon_enabled = (rstate->daemon_enabled_func (self) == RFKILL_UNBLOCKED);
-		if (old_daemon_enabled != rstate->daemon_enabled) {
-			nm_log_info (LOGD_RFKILL, "%s now %s by management service",
-				         rstate->desc,
-				         rstate->daemon_enabled ? "enabled" : "disabled");
-		}
-	}
+	/* recheck kernel rfkill state */
+	update_rstate_from_rfkill (priv->rfkill_mgr, rstate);
 
 	/* Print out all states affecting device enablement */
 	if (rstate->desc) {
-		if (rstate->daemon_enabled_func) {
-			nm_log_dbg (LOGD_RFKILL, "%s hw-enabled %d sw-enabled %d daemon-enabled %d",
-			            rstate->desc, rstate->hw_enabled, rstate->sw_enabled, rstate->daemon_enabled);
-		} else {
-			nm_log_dbg (LOGD_RFKILL, "%s hw-enabled %d sw-enabled %d",
-			            rstate->desc, rstate->hw_enabled, rstate->sw_enabled);
-		}
+		nm_log_dbg (LOGD_RFKILL, "%s hw-enabled %d sw-enabled %d",
+		            rstate->desc, rstate->hw_enabled, rstate->sw_enabled);
 	}
 
 	/* Log new killswitch state */
@@ -1783,20 +1412,6 @@ nm_manager_rfkill_update (NMManager *self, RfKillType rtype)
 }
 
 static void
-manager_ipw_rfkill_state_changed (NMDeviceWifi *device,
-                                  GParamSpec *pspec,
-                                  gpointer user_data)
-{
-	nm_manager_rfkill_update (NM_MANAGER (user_data), RFKILL_TYPE_WLAN);
-}
-
-static void
-manager_modem_enabled_changed (NMDevice *device, gpointer user_data)
-{
-	nm_manager_rfkill_update (NM_MANAGER (user_data), RFKILL_TYPE_WWAN);
-}
-
-static void
 device_auth_done_cb (NMAuthChain *chain,
                      GError *auth_error,
                      DBusGMethodInvocation *context,
@@ -1810,6 +1425,8 @@ device_auth_done_cb (NMAuthChain *chain,
 	const char *permission;
 	NMDeviceAuthRequestFunc callback;
 
+	g_assert (context);
+
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
 
 	permission = nm_auth_chain_get_data (chain, "requested-permission");
@@ -1819,7 +1436,8 @@ device_auth_done_cb (NMAuthChain *chain,
 	device = nm_auth_chain_get_data (chain, "device");
 	g_assert (device);
 
-	result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, permission));
+	result = nm_auth_chain_get_result (chain, permission);
+
 	if (auth_error) {
 		/* translate the auth error into a manager permission denied error */
 		nm_log_dbg (LOGD_CORE, "%s request failed: %s", permission, auth_error->message);
@@ -1858,112 +1476,283 @@ device_auth_request_cb (NMDevice *device,
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GError *error = NULL;
-	gulong sender_uid = G_MAXULONG;
+	NMAuthSubject *subject = NULL;
 	char *error_desc = NULL;
 	NMAuthChain *chain;
 
-	/* Get the caller's UID for the root check */
-	if (!nm_auth_get_caller_uid (context, priv->dbus_mgr, &sender_uid, &error_desc)) {
+	/* Validate the caller */
+	subject = nm_auth_subject_new_from_context (context);
+	if (!subject) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             error_desc);
-		callback (device, context, error, user_data);
-		g_error_free (error);
-		g_free (error_desc);
-		return;
+		                             "Failed to get request UID.");
+		goto done;
 	}
 
 	/* Ensure the subject has permissions for this connection */
-	if (!nm_auth_uid_in_acl (connection,
-	                         priv->session_monitor,
-	                         sender_uid,
-	                         &error_desc)) {
+	if (connection && !nm_auth_uid_in_acl (connection,
+	                                       nm_session_monitor_get (),
+	                                       nm_auth_subject_get_uid (subject),
+	                                       &error_desc)) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                             error_desc);
+		g_free (error_desc);
+		goto done;
+	}
+
+	/* Validate the request */
+	chain = nm_auth_chain_new_subject (subject, context, device_auth_done_cb, self);
+	if (!chain) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Unable to authenticate request.");
+		goto done;
+	}
+
+	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+	nm_auth_chain_set_data (chain, "device", g_object_ref (device), g_object_unref);
+	nm_auth_chain_set_data (chain, "requested-permission", g_strdup (permission), g_free);
+	nm_auth_chain_set_data (chain, "callback", callback, NULL);
+	nm_auth_chain_set_data (chain, "user-data", user_data, NULL);
+	nm_auth_chain_add_call (chain, permission, allow_interaction);
+
+done:
+	g_clear_object (&subject);
+	if (error)
 		callback (device, context, error, user_data);
+	g_clear_error (&error);
+}
+
+/* This should really be moved to gsystem. */
+#define free_slist __attribute__ ((cleanup(local_slist_free)))
+static void
+local_slist_free (void *loc)
+{
+	GSList **location = loc;
+
+	if (location)
+		g_slist_free (*location);
+}
+
+static gboolean
+match_connection_filter (NMConnection *connection, gpointer user_data)
+{
+	return nm_device_check_connection_compatible (NM_DEVICE (user_data), connection);
+}
+
+/**
+ * get_existing_connection:
+ * @manager: #NMManager instance
+ * @device: #NMDevice instance
+ *
+ * Returns: a #NMSettingsConnection to be assumed by the device, or %NULL if
+ *   the device does not support assuming existing connections.
+ */
+static NMConnection *
+get_existing_connection (NMManager *manager, NMDevice *device)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	free_slist GSList *connections = nm_manager_get_activatable_connections (manager);
+	NMConnection *connection = NULL, *matched;
+	NMSettingsConnection *added = NULL;
+	GError *error = NULL;
+
+	nm_device_capture_initial_config (device);
+
+	/* The core of the API is nm_device_generate_connection() function and
+	 * update_connection() virtual method and the convenient connection_type
+	 * class attribute. Subclasses supporting the new API must have
+	 * update_connection() implemented, otherwise nm_device_generate_connection()
+	 * returns NULL.
+	 */
+	connection = nm_device_generate_connection (device);
+	if (!connection)
+		return NULL;
+
+	/* Now we need to compare the generated connection to each configured
+	 * connection. The comparison function is the heart of the connection
+	 * assumption implementation and it must compare the connections very
+	 * carefully to sort out various corner cases. Also, the comparison is
+	 * not entirely symmetric.
+	 *
+	 * When no configured connection matches the generated connection, we keep
+	 * the generated connection instead.
+	 */
+	connections = g_slist_reverse (g_slist_sort (connections, nm_settings_sort_connections));
+	matched = nm_utils_match_connection (connections,
+	                                     connection,
+	                                     nm_device_has_carrier (device),
+	                                     match_connection_filter,
+	                                     device);
+	if (matched) {
+		nm_log_info (LOGD_DEVICE, "(%s): found matching connection '%s'",
+		             nm_device_get_iface (device),
+		             nm_connection_get_id (matched));
+		g_object_unref (connection);
+		return matched;
+	}
+
+	nm_log_dbg (LOGD_DEVICE, "(%s): generated connection '%s'",
+	            nm_device_get_iface (device),
+	            nm_connection_get_id (connection));
+
+	added = nm_settings_add_connection (priv->settings, connection, FALSE, &error);
+	if (added)
+		nm_settings_connection_set_nm_generated (added);
+	else {
+		nm_log_warn (LOGD_SETTINGS, "(%s) Couldn't save generated connection '%s': %s",
+		             nm_device_get_iface (device),
+		             nm_connection_get_id (connection),
+		             (error && error->message) ? error->message : "(unknown)");
+		g_clear_error (&error);
+	}
+	g_object_unref (connection);
+
+	return added ? NM_CONNECTION (added) : NULL;
+}
+
+static gboolean
+assume_connection (NMManager *self, NMDevice *device, NMConnection *connection)
+{
+	NMActiveConnection *active, *master_ac;
+	NMAuthSubject *subject;
+	GError *error = NULL;
+
+	nm_log_dbg (LOGD_DEVICE, "(%s): will attempt to assume connection",
+	            nm_device_get_iface (device));
+
+	/* Move device to DISCONNECTED to activate the connection */
+	if (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE) {
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_DISCONNECTED,
+		                         NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+	}
+	g_return_val_if_fail (nm_device_get_state (device) >= NM_DEVICE_STATE_DISCONNECTED, FALSE);
+
+	subject = nm_auth_subject_new_internal ();
+	active = _new_active_connection (self, connection, NULL, device, subject, &error);
+	g_object_unref (subject);
+
+	if (!active) {
+		nm_log_warn (LOGD_DEVICE, "assumed connection %s failed to activate: (%d) %s",
+		             nm_connection_get_path (connection),
+		             error ? error->code : -1,
+		             error && error->message ? error->message : "(unknown)");
 		g_error_free (error);
-		g_free (error_desc);
+		return FALSE;
+	}
+
+	/* If the device is a slave or VLAN, find the master ActiveConnection */
+	master_ac = NULL;
+	if (find_master (self, connection, device, NULL, NULL, &master_ac, NULL) && master_ac)
+		nm_active_connection_set_master (active, master_ac);
+
+	nm_active_connection_set_assumed (active, TRUE);
+	nm_active_connection_export (active);
+	active_connection_add (self, active);
+	nm_device_queue_activation (device, NM_ACT_REQUEST (active));
+	g_object_unref (active);
+
+	return TRUE;
+}
+
+static void
+recheck_assume_connection (NMDevice *device, gpointer user_data)
+{
+	NMManager *self = user_data;
+	NMConnection *connection;
+	gboolean was_unmanaged = FALSE;
+
+	if (manager_sleeping (self))
+		return;
+	if (nm_device_get_unmanaged_flag (device, NM_UNMANAGED_USER))
+		return;
+
+	connection = get_existing_connection (self, device);
+	if (!connection) {
+		nm_log_dbg (LOGD_DEVICE, "(%s): can't assume; no connection",
+		            nm_device_get_iface (device));
 		return;
 	}
 
-	/* Yay for root */
-	if (0 == sender_uid)
-		callback (device, context, NULL, user_data);
-	else {
-		/* Otherwise validate the non-root request */
-		chain = nm_auth_chain_new (context, NULL, device_auth_done_cb, self);
-		g_assert (chain);
-		priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+	if (nm_device_get_state (device) == NM_DEVICE_STATE_UNMANAGED) {
+		was_unmanaged = TRUE;
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_UNAVAILABLE,
+		                         NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+	}
 
-		nm_auth_chain_set_data (chain, "device", g_object_ref (device), g_object_unref);
-		nm_auth_chain_set_data (chain, "requested-permission", g_strdup (permission), g_free);
-		nm_auth_chain_set_data (chain, "callback", callback, NULL);
-		nm_auth_chain_set_data (chain, "user-data", user_data, NULL);
-		nm_auth_chain_add_call (chain, permission, allow_interaction);
+	if (!assume_connection (self, device, connection)) {
+		if (was_unmanaged) {
+			nm_device_state_changed (device,
+			                         NM_DEVICE_STATE_UNAVAILABLE,
+			                         NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+			nm_device_state_changed (device,
+			                         NM_DEVICE_STATE_UNMANAGED,
+			                         NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+		}
 	}
 }
 
+/**
+ * add_device:
+ * @self: the #NMManager
+ * @device: the #NMDevice to add
+ * @generate_con: %TRUE if existing connection (if any) should be assumed
+ *
+ * If successful, this function will increase the references count of @device.
+ * Callers should decrease the reference count.
+ */
 static void
-add_device (NMManager *self, NMDevice *device)
+add_device (NMManager *self, NMDevice *device, gboolean generate_con)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	const char *iface, *driver, *type_desc;
-	char *path;
-	static guint32 devcount = 0;
 	const GSList *unmanaged_specs;
-	NMConnection *existing = NULL;
-	gboolean managed = FALSE, enabled = FALSE;
+	gboolean user_unmanaged, sleeping;
+	NMConnection *connection = NULL;
+	gboolean enabled = FALSE;
 	RfKillType rtype;
-	NMDeviceType devtype;
+	GSList *iter, *remove = NULL;
 
-	iface = nm_device_get_ip_iface (device);
-	g_assert (iface);
-
-	devtype = nm_device_get_device_type (device);
+	/* No duplicates */
+	if (nm_manager_get_device_by_udi (self, nm_device_get_udi (device)))
+		return;
 
-	/* Ignore the device if we already know about it.  But some modems will
-	 * provide pseudo-ethernet devices that NM has already claimed while
-	 * ModemManager is still detecting the modem's serial ports, so when the
-	 * MM modem object finally shows up it may have the same IP interface as the
-	 * ethernet interface we've already detected.  In this case we skip the
-	 * check for an existing device with the same IP interface name and kill
-	 * the ethernet device later in favor of the modem device.
+	/* Remove existing devices owned by the new device; eg remove ethernet
+	 * ports that are owned by a WWAN modem, since udev may announce them
+	 * before the modem is fully discovered.
+	 *
+	 * FIXME: use parent/child device relationships instead of removing
+	 * the child NMDevice entirely
 	 */
-	if ((devtype != NM_DEVICE_TYPE_MODEM) && find_device_by_ip_iface (self, iface)) {
-		g_object_unref (device);
-		return;
+	for (iter = priv->devices; iter; iter = iter->next) {
+		iface = nm_device_get_ip_iface (iter->data);
+		if (nm_device_owns_iface (device, iface))
+			remove = g_slist_prepend (remove, iter->data);
 	}
+	for (iter = remove; iter; iter = iter->next)
+		remove_device (self, NM_DEVICE (iter->data), FALSE);
+	g_slist_free (remove);
 
-	nm_device_set_connection_provider (device, NM_CONNECTION_PROVIDER (priv->settings));
-
-	priv->devices = g_slist_append (priv->devices, device);
+	priv->devices = g_slist_append (priv->devices, g_object_ref (device));
 
 	g_signal_connect (device, "state-changed",
-					  G_CALLBACK (manager_device_state_changed),
-					  self);
+	                  G_CALLBACK (manager_device_state_changed),
+	                  self);
 
 	g_signal_connect (device, NM_DEVICE_AUTH_REQUEST,
 	                  G_CALLBACK (device_auth_request_cb),
 	                  self);
 
-	if (devtype == NM_DEVICE_TYPE_WIFI) {
-		/* Attach to the access-point-added signal so that the manager can fill
-		 * non-SSID-broadcasting APs with an SSID.
-		 */
-		g_signal_connect (device, "hidden-ap-found",
-						  G_CALLBACK (manager_hidden_ap_found),
-						  self);
+	g_signal_connect (device, NM_DEVICE_REMOVED,
+	                  G_CALLBACK (device_removed_cb),
+	                  self);
 
-		/* Hook up rfkill handling for ipw-based cards until they get converted
-		 * to use the kernel's rfkill subsystem in 2.6.33.
-		 */
-		g_signal_connect (device, "notify::" NM_DEVICE_WIFI_IPW_RFKILL_STATE,
-		                  G_CALLBACK (manager_ipw_rfkill_state_changed),
-		                  self);
-	} else if (devtype == NM_DEVICE_TYPE_MODEM) {
-		g_signal_connect (device, NM_DEVICE_MODEM_ENABLE_CHANGED,
-		                  G_CALLBACK (manager_modem_enabled_changed),
+	if (priv->startup) {
+		g_signal_connect (device, "notify::" NM_DEVICE_HAS_PENDING_ACTION,
+		                  G_CALLBACK (device_has_pending_action_changed),
 		                  self);
 	}
 
@@ -1978,6 +1767,9 @@ add_device (NMManager *self, NMDevice *device)
 		nm_device_set_enabled (device, enabled);
 	}
 
+	iface = nm_device_get_iface (device);
+	g_assert (iface);
+
 	type_desc = nm_device_get_type_desc (device);
 	g_assert (type_desc);
 	driver = nm_device_get_driver (device);
@@ -1986,121 +1778,47 @@ add_device (NMManager *self, NMDevice *device)
 	nm_log_info (LOGD_HW, "(%s): new %s device (driver: '%s' ifindex: %d)",
 	             iface, type_desc, driver, nm_device_get_ifindex (device));
 
-	path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++);
-	nm_device_set_path (device, path);
-	dbus_g_connection_register_g_object (nm_dbus_manager_get_connection (priv->dbus_mgr),
-	                                     path,
-	                                     G_OBJECT (device));
-	nm_log_info (LOGD_CORE, "(%s): exported as %s", iface, path);
-	g_free (path);
+	unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings);
+	user_unmanaged = nm_device_spec_match_list (device, unmanaged_specs);
+	nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_USER, user_unmanaged);
 
-	/* Check if we should assume the device's active connection by matching its
-	 * config with an existing system connection.
-	 */
-	if (nm_device_can_assume_connections (device)) {
-		GSList *connections = NULL;
+	sleeping = manager_sleeping (self);
+	nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_INTERNAL, sleeping);
 
-		connections = nm_settings_get_connections (priv->settings);
-		existing = nm_device_connection_match_config (device, (const GSList *) connections);
-		g_slist_free (connections);
+	nm_device_dbus_export (device);
 
-		if (existing)
-			nm_log_dbg (LOGD_DEVICE, "(%s): found existing device connection '%s'",
-			            nm_device_get_iface (device),
-			            nm_connection_get_id (existing));
-	}
+	/* Don't generate a connection e.g. for devices NM just created, or
+	 * for the loopback, or when we're sleeping. */
+	if (generate_con && !user_unmanaged && !sleeping)
+		connection = get_existing_connection (self, device);
 
-	/* Start the device if it's supposed to be managed */
-	unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings);
-	if (   !manager_sleeping (self)
-	    && !nm_device_spec_match_list (device, unmanaged_specs)) {
-		nm_device_set_managed (device,
-		                       TRUE,
-		                       existing ? NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED :
-		                                  NM_DEVICE_STATE_REASON_NOW_MANAGED);
-		managed = TRUE;
+	/* Start the device if it's supposed to be managed. Note that this will
+	 * manage default-unmanaged devices if they have a generated connection.
+	 */
+	if (nm_device_get_managed (device) || connection) {
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_UNAVAILABLE,
+		                         connection ? NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED :
+		                                      NM_DEVICE_STATE_REASON_NOW_MANAGED);
 	}
 
 	nm_settings_device_added (priv->settings, device);
 	g_signal_emit (self, signals[DEVICE_ADDED], 0, device);
+	g_object_notify (G_OBJECT (self), NM_MANAGER_DEVICES);
 
 	/* New devices might be master interfaces for virtual interfaces; so we may
 	 * need to create new virtual interfaces now.
 	 */
 	system_create_virtual_devices (self);
 
-	/* If the device has a connection it can assume, do that now */
-	if (existing && managed && nm_device_is_available (device)) {
-		NMActiveConnection *ac;
-		GError *error = NULL;
-
-		nm_log_dbg (LOGD_DEVICE, "(%s): will attempt to assume existing connection",
-		            nm_device_get_iface (device));
-
-		ac = internal_activate_device (self, device, existing, NULL, FALSE, 0, NULL, TRUE, NULL, &error);
-		if (!ac) {
-			nm_log_warn (LOGD_DEVICE, "assumed connection %s failed to activate: (%d) %s",
-			             nm_connection_get_path (existing),
-			             error ? error->code : -1,
-			             error && error->message ? error->message : "(unknown)");
-			g_error_free (error);
-		}
-	}
-}
-
-static void
-bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr,
-                               NMBluezDevice *bt_device,
-                               const char *bdaddr,
-                               const char *name,
-                               const char *object_path,
-                               guint32 capabilities,
-                               NMManager *manager)
-{
-	NMDevice *device;
-	gboolean has_dun = (capabilities & NM_BT_CAPABILITY_DUN);
-	gboolean has_nap = (capabilities & NM_BT_CAPABILITY_NAP);
-
-	g_return_if_fail (bdaddr != NULL);
-	g_return_if_fail (name != NULL);
-	g_return_if_fail (object_path != NULL);
-	g_return_if_fail (capabilities != NM_BT_CAPABILITY_NONE);
-	g_return_if_fail (NM_IS_BLUEZ_DEVICE (bt_device));
-
-	/* Make sure the device is not already in the device list */
-	if (nm_manager_get_device_by_udi (manager, object_path))
-		return;
-
-	device = nm_device_bt_new (bt_device, object_path, bdaddr, name, capabilities, FALSE);
-	if (device) {
-		nm_log_info (LOGD_HW, "BT device %s (%s) added (%s%s%s)",
-		             name,
-		             bdaddr,
-		             has_dun ? "DUN" : "",
-		             has_dun && has_nap ? " " : "",
-		             has_nap ? "NAP" : "");
-
-		add_device (manager, device);
-	}
-}
-
-static void
-bluez_manager_bdaddr_removed_cb (NMBluezManager *bluez_mgr,
-                                 const char *bdaddr,
-                                 const char *object_path,
-                                 gpointer user_data)
-{
-	NMManager *self = NM_MANAGER (user_data);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
-
-	g_return_if_fail (bdaddr != NULL);
-	g_return_if_fail (object_path != NULL);
-
-	device = nm_manager_get_device_by_udi (self, object_path);
-	if (device) {
-		nm_log_info (LOGD_HW, "BT device %s removed", bdaddr);
-		priv->devices = remove_one_device (self, priv->devices, device, FALSE);
+	/* If the device has a connection it can assume, do that now. If it's a
+	 * device that we might ever want to assume a connection on, then set that up.
+	 */
+	if (connection)
+		assume_connection (self, device, connection);
+	if (generate_con) {
+		g_signal_connect (device, NM_DEVICE_RECHECK_ASSUME,
+		                  G_CALLBACK (recheck_assume_connection), self);
 	}
 }
 
@@ -2134,25 +1852,33 @@ find_device_by_ifindex (NMManager *self, guint32 ifindex)
 	return NULL;
 }
 
-#define PLUGIN_PREFIX "libnm-device-plugin-"
-
-typedef struct {
-	NMDeviceType t;
-	guint priority;
-	NMDeviceFactoryCreateFunc create_func;
-} PluginInfo;
+static void
+factory_device_added_cb (NMDeviceFactory *factory,
+                         NMDevice *device,
+                         gpointer user_data)
+{
+	add_device (NM_MANAGER (user_data), device, FALSE);
+}
 
-static gint
-plugin_sort (PluginInfo *a, PluginInfo *b)
+static gboolean
+factory_component_added_cb (NMDeviceFactory *factory,
+                            GObject *component,
+                            gpointer user_data)
 {
-	/* Higher priority means sort earlier in the list (ie, return -1) */
-	if (a->priority > b->priority)
-		return -1;
-	else if (a->priority < b->priority)
-		return 1;
-	return 0;
+	NMManager *self = NM_MANAGER (user_data);
+	GSList *iter;
+
+	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) {
+		if (nm_device_notify_component_added (NM_DEVICE (iter->data), component))
+			return TRUE;
+	}
+	return FALSE;
 }
 
+#define PLUGIN_PREFIX "libnm-device-plugin-"
+#define PLUGIN_PATH_TAG "NMManager-plugin-path"
+#define PLUGIN_TYPEFUNC_TAG "typefunc"
+
 static void
 load_device_factories (NMManager *self)
 {
@@ -2161,7 +1887,7 @@ load_device_factories (NMManager *self)
 	GError *error = NULL;
 	const char *item;
 	char *path;
-	GSList *list = NULL, *iter;
+	GSList *iter;
 
 	dir = g_dir_open (NMPLUGINDIR, 0, &error);
 	if (!dir) {
@@ -2174,14 +1900,16 @@ load_device_factories (NMManager *self)
 
 	while ((item = g_dir_read_name (dir))) {
 		GModule *plugin;
+		NMDeviceFactory *factory;
 		NMDeviceFactoryCreateFunc create_func;
-		NMDeviceFactoryPriorityFunc priority_func;
-		NMDeviceFactoryTypeFunc type_func;
-		PluginInfo *info = NULL;
-		NMDeviceType plugin_type;
+		NMDeviceFactoryDeviceTypeFunc type_func;
+		NMDeviceType dev_type;
+		const char *found = NULL;
 
 		if (!g_str_has_prefix (item, PLUGIN_PREFIX))
 			continue;
+		if (g_str_has_suffix (item, ".la"))
+			continue;
 
 		path = g_module_build_path (NMPLUGINDIR, item);
 		g_assert (path);
@@ -2193,158 +1921,92 @@ load_device_factories (NMManager *self)
 			continue;
 		}
 
-		if (!g_module_symbol (plugin, "nm_device_factory_get_type", (gpointer) (&type_func))) {
-			nm_log_warn (LOGD_HW, "(%s): failed to find device factory: %s", item, g_module_error ());
+		if (!g_module_symbol (plugin, "nm_device_factory_get_device_type", (gpointer) &type_func)) {
+			nm_log_warn (LOGD_HW, "(%s): failed to find device factory type: %s", item, g_module_error ());
 			g_module_close (plugin);
 			continue;
 		}
 
 		/* Make sure we don't double-load plugins */
-		plugin_type = type_func ();
-		for (iter = list; iter; iter = g_slist_next (iter)) {
-			PluginInfo *candidate = iter->data;
+		dev_type = type_func ();
+		for (iter = priv->factories; iter; iter = iter->next) {
+			NMDeviceFactoryDeviceTypeFunc loaded_type_func;
 
-			if (plugin_type == candidate->t) {
-				info = candidate;
+			loaded_type_func = g_object_get_data (G_OBJECT (iter->data), PLUGIN_TYPEFUNC_TAG);
+			if (dev_type == loaded_type_func ()) {
+				found = g_object_get_data (G_OBJECT (iter->data), PLUGIN_PATH_TAG);
 				break;
 			}
 		}
-		if (info) {
+		if (found) {
+			nm_log_warn (LOGD_HW, "Found multiple device plugins for same type: %s vs %s",
+			             found, g_module_name (plugin));
 			g_module_close (plugin);
 			continue;
 		}
 
-		if (!g_module_symbol (plugin, "nm_device_factory_create_device", (gpointer) (&create_func))) {
-			nm_log_warn (LOGD_HW, "(%s): failed to find device creator: %s", item, g_module_error ());
+		if (!g_module_symbol (plugin, "nm_device_factory_create", (gpointer) &create_func)) {
+			nm_log_warn (LOGD_HW, "(%s): failed to find device factory creator: %s", item, g_module_error ());
 			g_module_close (plugin);
 			continue;
 		}
 
-		info = g_malloc0 (sizeof (*info));
-		info->create_func = create_func;
-		info->t = plugin_type;
-
-		/* Grab priority; higher number equals higher priority */
-		if (g_module_symbol (plugin, "nm_device_factory_get_priority", (gpointer) (&priority_func)))
-			info->priority = priority_func ();
-		else {
-			nm_log_dbg (LOGD_HW, "(%s): failed to find device factory priority func: %s",
-			            item, g_module_error ());
+		factory = create_func (&error);
+		if (!factory) {
+			nm_log_warn (LOGD_HW, "(%s): failed to initialize device factory: %s",
+			             item, error ? error->message : "unknown");
+			g_clear_error (&error);
+			g_module_close (plugin);
+			continue;
 		}
+		g_clear_error (&error);
 
 		g_module_make_resident (plugin);
-		list = g_slist_insert_sorted (list, info, (GCompareFunc) plugin_sort);
+		priv->factories = g_slist_prepend (priv->factories, factory);
 
-		nm_log_info (LOGD_HW, "Loaded device factory: %s", g_module_name (plugin));
+		g_signal_connect (factory,
+		                  NM_DEVICE_FACTORY_DEVICE_ADDED,
+		                  G_CALLBACK (factory_device_added_cb),
+		                  self);
+		g_signal_connect (factory,
+		                  NM_DEVICE_FACTORY_COMPONENT_ADDED,
+		                  G_CALLBACK (factory_component_added_cb),
+		                  self);
+		g_object_set_data_full (G_OBJECT (factory), PLUGIN_PATH_TAG,
+		                        g_strdup (g_module_name (plugin)), g_free);
+		g_object_set_data (G_OBJECT (factory), PLUGIN_TYPEFUNC_TAG, type_func);
+
+		nm_log_info (LOGD_HW, "Loaded device plugin: %s", g_module_name (plugin));
 	};
 	g_dir_close (dir);
 
-	/* Ditch the priority info and copy the factory functions to our private data */
-	for (iter = list; iter; iter = g_slist_next (iter)) {
-		PluginInfo *info = iter->data;
-
-		priv->factories = g_slist_append (priv->factories, info->create_func);
-		g_free (info);
-	}
-	g_slist_free (list);
-}
-
-static gboolean
-is_wireless (GUdevDevice *device)
-{
-	const char *tmp;
-
-	/* Check devtype, newer kernels (2.6.32+) have this */
-	tmp = g_udev_device_get_property (device, "DEVTYPE");
-	if (g_strcmp0 (tmp, "wlan") == 0)
-		return TRUE;
-
-	/* Otherwise hit up WEXT directly */
-	return wifi_utils_is_wifi (g_udev_device_get_name (device),
-	                           g_udev_device_get_sysfs_path (device));
-}
-
-static gboolean
-is_olpc_mesh (GUdevDevice *device)
-{
-	const gchar *prop = g_udev_device_get_property (device, "ID_NM_OLPC_MESH");
-	return (prop != NULL);
-}
-
-static gboolean
-is_infiniband (GUdevDevice *device)
-{
-	gint etype = g_udev_device_get_sysfs_attr_as_int (device, "type");
-	return etype == ARPHRD_INFINIBAND;
-}
-
-static gboolean
-is_bond (int ifindex)
-{
-	return (nm_system_get_iface_type (ifindex, NULL) == NM_IFACE_TYPE_BOND);
-}
-
-static gboolean
-is_bridge (int ifindex)
-{
-	return (nm_system_get_iface_type (ifindex, NULL) == NM_IFACE_TYPE_BRIDGE);
-}
-
-static gboolean
-is_vlan (int ifindex)
-{
-	return (nm_system_get_iface_type (ifindex, NULL) == NM_IFACE_TYPE_VLAN);
-}
-
-static gboolean
-is_adsl (GUdevDevice *device)
-{
-	return (g_strcmp0 (g_udev_device_get_subsystem (device), "atm") == 0);
+	priv->factories = g_slist_reverse (priv->factories);
 }
 
 static void
-udev_device_added_cb (NMUdevManager *udev_mgr,
-                      GUdevDevice *udev_device,
-                      const char *iface,
-                      const char *sysfs_path,
-                      const char *driver,
-                      int ifindex,
-                      gpointer user_data)
+platform_link_added (NMManager *self,
+                     int ifindex,
+                     NMPlatformLink *plink,
+                     NMPlatformReason reason)
 {
-	NMManager *self = NM_MANAGER (user_data);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMDevice *device = NULL;
 	GSList *iter;
 	GError *error = NULL;
 
-	g_return_if_fail (udev_device != NULL);
-	g_return_if_fail (iface != NULL);
-	g_return_if_fail (sysfs_path != NULL);
-	g_return_if_fail (driver != NULL);
+	g_return_if_fail (ifindex > 0);
 
-	/* Most devices will have an ifindex here */
-	if (ifindex > 0) {
-		device = find_device_by_ifindex (self, ifindex);
-		if (device) {
-			/* If it's a virtual device we may need to update its UDI */
-			if (nm_system_get_iface_type (ifindex, iface) != NM_IFACE_TYPE_UNSPEC)
-				g_object_set (G_OBJECT (device), NM_DEVICE_UDI, sysfs_path, NULL);
-			return;
-		}
-	} else {
-		/* But ATM/ADSL devices don't */
-		g_return_if_fail (is_adsl (udev_device));
-		device = find_device_by_ip_iface (self, iface);
-		if (device)
-			return;
-	}
+	if (priv->ignore_link_added_cb > 0)
+		return;
+
+	if (find_device_by_ifindex (self, ifindex))
+		return;
 
 	/* Try registered device factories */
-	for (iter = priv->factories; iter; iter = g_slist_next (iter)) {
-		NMDeviceFactoryCreateFunc create_func = iter->data;
+	for (iter = priv->factories; iter; iter = iter->next) {
+		NMDeviceFactory *factory = NM_DEVICE_FACTORY (iter->data);
 
-		g_clear_error (&error);
-		device = (NMDevice *) create_func (udev_device, sysfs_path, iface, driver, &error);
+		device = nm_device_factory_new_link (factory, plink, &error);
 		if (device && NM_IS_DEVICE (device)) {
 			g_assert_no_error (error);
 			break;  /* success! */
@@ -2352,7 +2014,7 @@ udev_device_added_cb (NMUdevManager *udev_mgr,
 
 		if (error) {
 			nm_log_warn (LOGD_HW, "%s: factory failed to create device: (%d) %s",
-			             sysfs_path,
+			             plink->udi,
 			             error ? error->code : -1,
 			             error ? error->message : "(unknown)");
 			g_clear_error (&error);
@@ -2360,102 +2022,128 @@ udev_device_added_cb (NMUdevManager *udev_mgr,
 		}
 	}
 
+	/* Ignore Bluetooth PAN interfaces; they are handled by their NMDeviceBt
+	 * parent and don't get a separate interface.
+	 */
+	if (!strncmp (plink->name, "bnep", STRLEN ("bnep")))
+		return;
+
 	if (device == NULL) {
-		if (is_olpc_mesh (udev_device)) /* must be before is_wireless */
-			device = nm_device_olpc_mesh_new (sysfs_path, iface, driver);
-		else if (is_wireless (udev_device))
-			device = nm_device_wifi_new (sysfs_path, iface, driver);
-		else if (is_infiniband (udev_device))
-			device = nm_device_infiniband_new (sysfs_path, iface, driver);
-		else if (is_bond (ifindex))
-			device = nm_device_bond_new (sysfs_path, iface);
-		else if (is_bridge (ifindex)) {
-
-			/* FIXME: always create device when we handle bridges non-destructively */
-			if (bridge_created_by_nm (self, iface))
-				device = nm_device_bridge_new (sysfs_path, iface);
-			else
-				nm_log_info (LOGD_BRIDGE, "(%s): ignoring bridge not created by NetworkManager", iface);
-		} else if (is_vlan (ifindex)) {
-			int parent_ifindex = -1;
-			NMDevice *parent;
+		int parent_ifindex = -1;
+		NMDevice *parent;
 
+		switch (plink->type) {
+		case NM_LINK_TYPE_ETHERNET:
+			device = nm_device_ethernet_new (plink);
+			break;
+		case NM_LINK_TYPE_INFINIBAND:
+			device = nm_device_infiniband_new (plink);
+			break;
+		case NM_LINK_TYPE_BOND:
+			device = nm_device_bond_new (plink);
+			break;
+		case NM_LINK_TYPE_TEAM:
+			device = nm_device_team_new (plink);
+			break;
+		case NM_LINK_TYPE_BRIDGE:
+			device = nm_device_bridge_new (plink);
+			break;
+		case NM_LINK_TYPE_VLAN:
 			/* Have to find the parent device */
-			if (nm_system_get_iface_vlan_info (ifindex, &parent_ifindex, NULL)) {
+			if (nm_platform_vlan_get_info (ifindex, &parent_ifindex, NULL)) {
 				parent = find_device_by_ifindex (self, parent_ifindex);
 				if (parent)
-					device = nm_device_vlan_new (sysfs_path, iface, parent);
+					device = nm_device_vlan_new (plink, parent);
 				else {
 					/* If udev signaled the VLAN interface before it signaled
 					 * the VLAN's parent at startup we may not know about the
 					 * parent device yet.  But we'll find it on the second pass
 					 * from nm_manager_start().
 					 */
-					nm_log_dbg (LOGD_HW, "(%s): VLAN parent interface unknown", iface);
+					nm_log_dbg (LOGD_HW, "(%s): VLAN parent interface unknown", plink->name);
 				}
 			} else
-				nm_log_err (LOGD_HW, "(%s): failed to get VLAN parent ifindex", iface);
-		} else if (is_adsl (udev_device))
-			device = nm_device_adsl_new (sysfs_path, iface, driver);
-		else
-			device = nm_device_ethernet_new (sysfs_path, iface, driver);
+				nm_log_err (LOGD_HW, "(%s): failed to get VLAN parent ifindex", plink->name);
+			break;
+		case NM_LINK_TYPE_VETH:
+			device = nm_device_veth_new (plink);
+			break;
+		case NM_LINK_TYPE_TUN:
+		case NM_LINK_TYPE_TAP:
+			device = nm_device_tun_new (plink);
+			break;
+		case NM_LINK_TYPE_MACVLAN:
+		case NM_LINK_TYPE_MACVTAP:
+			device = nm_device_macvlan_new (plink);
+			break;
+		case NM_LINK_TYPE_VXLAN:
+			device = nm_device_vxlan_new (plink);
+			break;
+		case NM_LINK_TYPE_GRE:
+		case NM_LINK_TYPE_GRETAP:
+			device = nm_device_gre_new (plink);
+			break;
+
+		case NM_LINK_TYPE_WWAN_ETHERNET:
+			/* WWAN pseudo-ethernet interfaces are handled automatically by
+			 * their NMDeviceModem and don't get a separate NMDevice object.
+			 */
+			break;
+
+		case NM_LINK_TYPE_OLPC_MESH:
+		case NM_LINK_TYPE_WIFI:
+		case NM_LINK_TYPE_WIMAX:
+			nm_log_info (LOGD_HW, "(%s): '%s' plugin not available; creating generic device",
+			             plink->name, plink->type_name);
+			/* fall through */
+		default:
+			device = nm_device_generic_new (plink);
+			break;
+		}
 	}
 
-	if (device)
-		add_device (self, device);
+	if (device) {
+		add_device (self, device, plink->type != NM_LINK_TYPE_LOOPBACK);
+		g_object_unref (device);
+	}
 }
 
 static void
-udev_device_removed_cb (NMUdevManager *manager,
-                        GUdevDevice *udev_device,
-                        gpointer user_data)
-{
-	NMManager *self = NM_MANAGER (user_data);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
-	guint32 ifindex;
-
-	ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
-	device = find_device_by_ifindex (self, ifindex);
-	if (!device) {
-		GSList *iter;
-		const char *iface = g_udev_device_get_name (udev_device);
-
-		/* On removal we aren't always be able to read properties like IFINDEX
-		 * anymore, as they may have already been removed from sysfs.  So we
-		 * have to fall back on device name (eg, interface name).
-		 *
-		 * Also, some devices (namely PPPoE (pppX), ADSL (nasX, pppX), and
-		 * mobile broadband (pppX, bnepX)) create a kernel netdevice for IP
-		 * communication (called the "IP interface" in NM) as part of the
-		 * connection process and thus the IP interface lifetime does not
-		 * correspond to the NMDevice lifetime.  For these devices we must
-		 * ignore removal events for the IP interface name otherwise the
-		 * NMDevice would be removed. Hence the usage here of
-		 * nm_device_get_iface() rather than nm_device_get_ip_iface().
-		 */
-		for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
-			if (g_strcmp0 (nm_device_get_iface (NM_DEVICE (iter->data)), iface) == 0) {
-				device = iter->data;
-				break;
-			}
-		}
-	}
+platform_link_cb (NMPlatform *platform,
+                  int ifindex,
+                  NMPlatformLink *plink,
+                  NMPlatformSignalChangeType change_type,
+                  NMPlatformReason reason,
+                  gpointer user_data)
+{
+	switch (change_type) {
+	case NM_PLATFORM_SIGNAL_ADDED:
+		platform_link_added (NM_MANAGER (user_data), ifindex, plink, reason);
+		break;
+	case NM_PLATFORM_SIGNAL_REMOVED: {
+		NMManager *self = NM_MANAGER (user_data);
+		NMDevice *device;
 
-	if (device)
-		priv->devices = remove_one_device (self, priv->devices, device, FALSE);
+		device = find_device_by_ifindex (self, ifindex);
+		if (device)
+			remove_device (self, device, FALSE);
+		break;
+	 }
+	 default:
+		break;
+	 }
 }
 
 static void
-udev_manager_rfkill_changed_cb (NMUdevManager *udev_mgr,
-                                RfKillType rtype,
-                                RfKillState udev_state,
-                                gpointer user_data)
+rfkill_manager_rfkill_changed_cb (NMRfkillManager *rfkill_mgr,
+                                  RfKillType rtype,
+                                  RfKillState udev_state,
+                                  gpointer user_data)
 {
 	nm_manager_rfkill_update (NM_MANAGER (user_data), rtype);
 }
 
-GSList *
+const GSList *
 nm_manager_get_devices (NMManager *manager)
 {
 	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
@@ -2503,54 +2191,18 @@ impl_manager_get_device_by_ip_iface (NMManager *self,
 	return path ? TRUE : FALSE;
 }
 
-static NMActiveConnection *
-internal_activate_device (NMManager *manager,
-                          NMDevice *device,
-                          NMConnection *connection,
-                          const char *specific_object,
-                          gboolean user_requested,
-                          gulong sender_uid,
-                          const char *dbus_sender,
-                          gboolean assumed,
-                          NMActiveConnection *master,
-                          GError **error)
+static gboolean
+is_compatible_with_slave (NMConnection *master, NMConnection *slave)
 {
-	NMActRequest *req;
-	NMDevice *master_device = NULL;
-
-	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
-	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	/* Ensure the requested connection is compatible with the device */
-	if (!nm_device_check_connection_compatible (device, connection, error))
-		return NULL;
-
-	/* Tear down any existing connection */
-	if (nm_device_get_act_request (device)) {
-		nm_log_info (LOGD_DEVICE, "(%s): disconnecting for new activation request.",
-		             nm_device_get_iface (device));
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_DISCONNECTED,
-		                         NM_DEVICE_STATE_REASON_NONE);
-	}
+	NMSettingConnection *s_con;
 
-	if (master)
-		master_device = nm_active_connection_get_device (master);
+	g_return_val_if_fail (master, FALSE);
+	g_return_val_if_fail (slave, FALSE);
 
-	req = nm_act_request_new (connection,
-	                          specific_object,
-	                          user_requested,
-	                          sender_uid,
-	                          dbus_sender,
-	                          assumed,
-	                          device,
-	                          master_device);
-	g_assert (req);
-	active_connection_add (manager, NM_ACTIVE_CONNECTION (req));
-	nm_device_activate (device, req);
+	s_con = nm_connection_get_setting_connection (slave);
+	g_assert (s_con);
 
-	return NM_ACTIVE_CONNECTION (req);
+	return nm_connection_is_type (master, nm_setting_connection_get_slave_type (s_con));
 }
 
 /**
@@ -2562,14 +2214,35 @@ internal_activate_device (NMManager *manager,
  *   that master connection was found
  * @out_master_device: on success, the master device of @connection if that
  *   master device was found
+ * @out_master_ac: on success, the master ActiveConnection of @connection if
+ *   there already is one
+ * @error: the error, if an error occurred
+ *
+ * Given an #NMConnection, attempts to find its master. If @connection has
+ * no master, this will return %TRUE and @out_master_connection and
+ * @out_master_device will be untouched.
  *
- * Given an #NMConnection, attempts to find its master connection and/or its
- * master device.  This function may return a master connection, a master device,
- * or both.  If only a connection is returned, that master connection is not
- * currently active on any device.  If only a device is returned, that device
- * is not currently activated with any connection.  If both are returned, then
- * the device is currently activated or activating with the returned master
- * connection.
+ * If @connection does have a master, then the outputs depend on what is in its
+ * #NMSettingConnection:master property:
+ *
+ * If "master" is the ifname of an existing #NMDevice, and that device has a
+ * compatible master connection activated or activating on it, then
+ * @out_master_device, @out_master_connection, and @out_master_ac will all be
+ * set. If the device exists and is idle, only @out_master_device will be set.
+ * If the device exists and has an incompatible connection on it, an error
+ * will be returned.
+ *
+ * If "master" is the ifname of a non-existent device, then @out_master_device
+ * will be %NULL, and @out_master_connection will be a connection whose
+ * activation would cause the creation of that device. @out_master_ac MAY be
+ * set in this case as well (if the connection has started activating, but has
+ * not yet created its device).
+ *
+ * If "master" is the UUID of a compatible master connection, then
+ * @out_master_connection will be the identified connection, and @out_master_device
+ * and/or @out_master_ac will be set if the connection is currently activating.
+ * (@out_master_device will not be set if the device exists but does not have
+ * @out_master_connection active/activating on it.)
  *
  * Returns: %TRUE if the master device and/or connection could be found or if
  *  the connection did not require a master, %FALSE otherwise
@@ -2579,7 +2252,9 @@ find_master (NMManager *self,
              NMConnection *connection,
              NMDevice *device,
              NMConnection **out_master_connection,
-             NMDevice **out_master_device)
+             NMDevice **out_master_device,
+             NMActiveConnection **out_master_ac,
+             GError **error)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMSettingConnection *s_con;
@@ -2598,9 +2273,20 @@ find_master (NMManager *self,
 	/* Try as an interface name first */
 	master_device = find_device_by_ip_iface (self, master);
 	if (master_device) {
-		/* A device obviously can't be its own master */
-		if (master_device == device)
+		if (master_device == device) {
+			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED,
+			                     "Device cannot be its own master");
 			return FALSE;
+		}
+
+		master_connection = nm_device_get_connection (master_device);
+		if (master_connection && !is_compatible_with_slave (master_connection, connection)) {
+			g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED,
+			             "The active connection on %s is not a valid master for '%s'",
+			             nm_device_get_iface (master_device),
+			             nm_connection_get_id (connection));
+			return FALSE;
+		}
 	} else {
 		/* Try master as a connection UUID */
 		master_connection = (NMConnection *) nm_settings_get_connection_by_uuid (priv->settings, master);
@@ -2623,14 +2309,15 @@ find_master (NMManager *self,
 			 * virtual interfaces and see if one of their virtual interface
 			 * names matches the master.
 			 */
-			connections = nm_settings_get_connections (priv->settings);
+			connections = nm_manager_get_activatable_connections (self);
 			for (iter = connections; iter && !master_connection; iter = g_slist_next (iter)) {
 				NMConnection *candidate = iter->data;
 				char *vname;
 
 				if (connection_needs_virtual_device (candidate)) {
 					vname = get_virtual_iface_name (self, candidate, NULL);
-					if (g_strcmp0 (master, vname) == 0)
+					if (   g_strcmp0 (master, vname) == 0
+					    && is_compatible_with_slave (candidate, connection))
 						master_connection = candidate;
 					g_free (vname);
 				}
@@ -2643,46 +2330,49 @@ find_master (NMManager *self,
 		*out_master_connection = master_connection;
 	if (out_master_device)
 		*out_master_device = master_device;
+	if (out_master_ac && master_connection)
+		*out_master_ac = find_ac_for_connection (self, master_connection);
 
-    return master_device || master_connection;
-}
-
-static gboolean
-is_compatible_with_slave (NMConnection *master, NMConnection *slave)
-{
-	NMSettingConnection *s_con;
-
-	g_return_val_if_fail (master, FALSE);
-	g_return_val_if_fail (slave, FALSE);
-
-	s_con = nm_connection_get_setting_connection (slave);
-	g_assert (s_con);
-
-	return nm_connection_is_type (master, nm_setting_connection_get_slave_type (s_con));
+	if (master_device || master_connection)
+		return TRUE;
+	else {
+		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+		                     "Master connection not found or invalid");
+		return FALSE;
+	}
 }
 
 /**
  * ensure_master_active_connection:
- *
  * @self: the #NMManager
- * @dbus_sender: if the request was initiated by a user via D-Bus, the
- *   dbus sender name of the client that requested the activation; for auto
- *   activated connections use %NULL
+ * @subject: the #NMAuthSubject representing the requestor of this activation
  * @connection: the connection that should depend on @master_connection
  * @device: the #NMDevice, if any, which will activate @connection
- * @master_connection: the master connection
- * @master_device: the master device
+ * @master_connection: the master connection, or %NULL
+ * @master_device: the master device, or %NULL
  * @error: the error, if an error occurred
  *
  * Determines whether a given #NMConnection depends on another connection to
  * be activated, and if so, finds that master connection or creates it.
  *
+ * If @master_device and @master_connection are both set then @master_connection
+ * MUST already be activated or activating on @master_device, and the function will
+ * return the existing #NMActiveConnection.
+ *
+ * If only @master_device is set, and it has an #NMActiveConnection, then the
+ * function will return it if it is a compatible master, or an error if not. If it
+ * doesn't have an AC, then the function will create one if a compatible master
+ * connection exists, or return an error if not.
+ *
+ * If only @master_connection is set, then this will try to find or create a compatible
+ * #NMDevice, and either activate @master_connection on that device or return an error.
+ *
  * Returns: the master #NMActiveConnection that the caller should depend on, or
  * %NULL if an error occurred
  */
 static NMActiveConnection *
 ensure_master_active_connection (NMManager *self,
-                                 const char *dbus_sender,
+                                 NMAuthSubject *subject,
                                  NMConnection *connection,
                                  NMDevice *device,
                                  NMConnection *master_connection,
@@ -2701,20 +2391,29 @@ ensure_master_active_connection (NMManager *self,
 	 * compatible connection.  If it's already activating we can just proceed.
 	 */
 	if (master_device) {
+		NMConnection *device_connection = nm_device_get_connection (master_device);
+
 		/* If we're passed a connection and a device, we require that connection
 		 * be already activated on the device, eg returned from find_master().
 		 */
-		if (master_connection)
-			g_assert (nm_device_get_connection (master_device) == master_connection);
+		g_assert (!master_connection || master_connection == device_connection);
+		if (device_connection && !is_compatible_with_slave (device_connection, connection)) {
+			g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED,
+			             "The active connection on %s is not a valid master for '%s'",
+			             nm_device_get_iface (master_device),
+			             nm_connection_get_id (connection));
+			return NULL;
+		}
 
 		master_state = nm_device_get_state (master_device);
 		if (   (master_state == NM_DEVICE_STATE_ACTIVATED)
 		    || nm_device_is_activating (master_device)) {
 			/* Device already using master_connection */
+			g_assert (device_connection);
 			return NM_ACTIVE_CONNECTION (nm_device_get_act_request (master_device));
 		}
 
-		/* If the device is disconnected, find a compabile connection and
+		/* If the device is disconnected, find a compatible connection and
 		 * activate it on the device.
 		 */
 		if (master_state == NM_DEVICE_STATE_DISCONNECTED) {
@@ -2723,20 +2422,22 @@ ensure_master_active_connection (NMManager *self,
 			g_assert (master_connection == NULL);
 
 			/* Find a compatible connection and activate this device using it */
-			connections = nm_settings_get_connections (priv->settings);
+			connections = nm_manager_get_activatable_connections (self);
 			for (iter = connections; iter; iter = g_slist_next (iter)) {
 				NMConnection *candidate = NM_CONNECTION (iter->data);
 
-				/* Ensure eg bond slave and the candidate master is a bond master */
+				/* Ensure eg bond/team slave and the candidate master is a
+				 * bond/team master
+				 */
 				if (!is_compatible_with_slave (candidate, connection))
 					continue;
 
-				if (nm_device_check_connection_compatible (master_device, candidate, NULL)) {
+				if (nm_device_connection_is_available (master_device, candidate, TRUE)) {
 					master_ac = nm_manager_activate_connection (self,
 					                                            candidate,
 					                                            NULL,
-					                                            nm_device_get_path (master_device),
-					                                            dbus_sender,
+					                                            master_device,
+					                                            subject,
 					                                            error);
 					if (!master_ac)
 						g_prefix_error (error, "%s", "Master device activation failed: ");
@@ -2772,7 +2473,7 @@ ensure_master_active_connection (NMManager *self,
 				continue;
 			}
 
-			if (!nm_device_check_connection_compatible (candidate, master_connection, NULL))
+			if (!nm_device_connection_is_available (candidate, master_connection, TRUE))
 				continue;
 
 			found_device = TRUE;
@@ -2783,8 +2484,8 @@ ensure_master_active_connection (NMManager *self,
 			master_ac = nm_manager_activate_connection (self,
 			                                            master_connection,
 			                                            NULL,
-			                                            nm_device_get_path (candidate),
-			                                            dbus_sender,
+			                                            candidate,
+			                                            subject,
 			                                            error);
 			if (!master_ac)
 				g_prefix_error (error, "%s", "Master device activation failed: ");
@@ -2799,7 +2500,7 @@ ensure_master_active_connection (NMManager *self,
 			                                            master_connection,
 			                                            NULL,
 			                                            NULL,
-			                                            dbus_sender,
+			                                            subject,
 			                                            error);
 			if (!master_ac)
 				g_prefix_error (error, "%s", "Master device activation failed: ");
@@ -2817,239 +2518,133 @@ ensure_master_active_connection (NMManager *self,
 	return NULL;
 }
 
-static NMActiveConnection *
-activate_vpn_connection (NMManager *self,
-                         NMConnection *connection,
-                         const char *specific_object,
-                         gulong sender_uid,
-                         GError **error)
+static gboolean
+_internal_activate_vpn (NMManager *self, NMActiveConnection *active, GError **error)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMActiveConnection *parent = NULL, *ac;
-	NMDevice *device = NULL;
-	GSList *iter;
-
-	if (specific_object) {
-		/* Find the specifc connection the client requested we use */
-		parent = active_connection_get_by_path (self, specific_object);
-		if (!parent) {
-			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE,
-			                     "Base connection for VPN connection not active.");
-			return NULL;
-		}
-	} else {
-		for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) {
-			NMActiveConnection *candidate = iter->data;
-
-			if (nm_active_connection_get_default (candidate)) {
-				parent = candidate;
-				break;
-			}
-		}
-	}
+	gboolean success;
 
-	if (!parent) {
-		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
-		                     "Could not find source connection.");
-		return NULL;
-	}
+	g_assert (NM_IS_VPN_CONNECTION (active));
 
-	device = nm_active_connection_get_device (parent);
-	if (!device) {
-		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-		                     "Source connection had no active device.");
-		return NULL;
+	success = nm_vpn_manager_activate_connection (NM_MANAGER_GET_PRIVATE (self)->vpn_manager,
+	                                              NM_VPN_CONNECTION (active),
+	                                              error);
+	if (success) {
+		nm_active_connection_export (active);
+		g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS);
 	}
-
-	ac = nm_vpn_manager_activate_connection (priv->vpn_manager,
-	                                         connection,
-	                                         device,
-	                                         nm_active_connection_get_path (parent),
-	                                         TRUE,
-	                                         sender_uid,
-	                                         error);
-	if (ac)
-		active_connection_add (self, ac);
-	return ac;
+	return success;
 }
 
-NMActiveConnection *
-nm_manager_activate_connection (NMManager *manager,
-                                NMConnection *connection,
-                                const char *specific_object,
-                                const char *device_path,
-                                const char *dbus_sender,
-                                GError **error)
+static gboolean
+_internal_activate_device (NMManager *self, NMActiveConnection *active, GError **error)
 {
-	NMManagerPrivate *priv;
-	NMDevice *device = NULL;
-	gulong sender_uid = 0;
-	DBusError dbus_error;
-	NMDeviceState state;
-	char *iface;
-	NMDevice *master_device = NULL;
+	NMDevice *device, *master_device = NULL;
+	NMConnection *connection;
 	NMConnection *master_connection = NULL;
-	NMConnection *existing_connection = NULL;
 	NMActiveConnection *master_ac = NULL;
-	gboolean matched;
-	char *error_desc = NULL;
 
-	g_return_val_if_fail (manager != NULL, NULL);
-	g_return_val_if_fail (connection != NULL, NULL);
-	g_return_val_if_fail (error != NULL, NULL);
-	g_return_val_if_fail (*error == NULL, NULL);
+	g_return_val_if_fail (NM_IS_MANAGER (self), FALSE);
+	g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (active), FALSE);
+	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-	priv = NM_MANAGER_GET_PRIVATE (manager);
+	g_assert (NM_IS_VPN_CONNECTION (active) == FALSE);
 
-	/* Get the UID of the user that originated the request, if any */
-	if (dbus_sender) {
-		dbus_error_init (&dbus_error);
-		sender_uid = dbus_bus_get_unix_user (nm_dbus_manager_get_dbus_connection (priv->dbus_mgr),
-		                                     dbus_sender,
-		                                     &dbus_error);
-		if (dbus_error_is_set (&dbus_error)) {
-			g_set_error_literal (error,
-			                     NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED,
-			                     "Failed to get unix user for dbus sender");
-			dbus_error_free (&dbus_error);
-			return NULL;
+	connection = nm_active_connection_get_connection (active);
+	g_assert (connection);
+
+	device = nm_active_connection_get_device (active);
+	if (!device) {
+		if (!connection_needs_virtual_device (connection)) {
+			NMSettingConnection *s_con = nm_connection_get_setting_connection (connection);
+
+			g_assert (s_con);
+			g_set_error (error,
+			             NM_MANAGER_ERROR,
+			             NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+			             "Unsupported virtual interface type '%s'",
+			             nm_setting_connection_get_connection_type (s_con));
+			return FALSE;
 		}
 
-		/* Ensure the subject has permissions for this connection */
-		if (!nm_auth_uid_in_acl (connection,
-		                         priv->session_monitor,
-		                         sender_uid,
-		                         &error_desc)) {
+		device = system_create_virtual_device (self, connection);
+		if (!device) {
 			g_set_error_literal (error,
 			                     NM_MANAGER_ERROR,
-			                     NM_MANAGER_ERROR_PERMISSION_DENIED,
-			                     error_desc);
-			g_free (error_desc);
-			return NULL;
+			                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+			                     "Failed to create virtual interface");
+			return FALSE;
 		}
-	}
-
-	/* VPN ? */
-	if (nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME))
-		return activate_vpn_connection (manager, connection, specific_object, sender_uid, error);
 
-	/* Device-based connection */
-	if (device_path) {
-		device = nm_manager_get_device_by_path (manager, device_path);
-		if (!device) {
-			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-			                     "Device not found");
-			return NULL;
+		if (!nm_active_connection_set_device (active, device)) {
+			g_set_error_literal (error,
+			                     NM_MANAGER_ERROR,
+			                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+			                     "The device could not be activated with this connection");
+			return FALSE;
 		}
 
-		/* If it's a virtual interface make sure the device given by the
-		 * path matches the connection's interface details.
+		/* A newly created device, if allowed to be managed by NM, will be
+		 * in the UNAVAILABLE state here.  To ensure it can be activated
+		 * immediately, we transition it to DISCONNECTED.
 		 */
-		if (connection_needs_virtual_device (connection)) {
-			iface = get_virtual_iface_name (manager, connection, NULL);
-			if (!iface) {
-				g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-					                 "Failed to determine connection's virtual interface name");
-				return NULL;
-			}
-
-			matched = g_str_equal (iface, nm_device_get_ip_iface (device));
-			g_free (iface);
-			if (!matched) {
-				g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-					                 "Device given by path did not match connection's virtual interface name");
-				return NULL;
-			}
+		if (   nm_device_is_available (device)
+			&& (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) {
+			nm_device_state_changed (device,
+			                         NM_DEVICE_STATE_DISCONNECTED,
+			                         NM_DEVICE_STATE_REASON_NONE);
 		}
 	} else {
-		/* Virtual connections (VLAN, bond, etc) may not specify a device
-		 * path because the device may not be created yet, or it be given
-		 * by the connection's properties instead.  Find the device the
-		 * connection refers to, or create it if needed.
-		 */
-		if (!connection_needs_virtual_device (connection)) {
-			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-				                 "This connection requires an existing device.");
-			return NULL;
-		}
-
-		iface = get_virtual_iface_name (manager, connection, NULL);
-		if (!iface) {
-			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-				                 "Failed to determine connection's virtual interface name");
-			return NULL;
-		}
-
-		device = find_device_by_ip_iface (manager, iface);
-		g_free (iface);
-		if (!device) {
-			/* Create it */
-			device = system_create_virtual_device (manager, connection);
-			if (!device) {
-				g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-						             "Failed to create virtual interface");
-				return NULL;
-			}
-
-			/* A newly created device, if allowed to be managed by NM, will be
-			 * in the UNAVAILABLE state here.  Since we want to use it right
-			 * away, we transition it immediately to DISCONNECTED.
-			 */
-			if (   nm_device_is_available (device)
-			    && (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) {
-				nm_device_state_changed (device,
-				                         NM_DEVICE_STATE_DISCONNECTED,
-				                         NM_DEVICE_STATE_REASON_NONE);
-			}
-		}
-	}
+		NMConnection *existing_connection = NULL;
+		NMAuthSubject *subject;
+		char *error_desc = NULL;
 
-	state = nm_device_get_state (device);
-	if (state < NM_DEVICE_STATE_DISCONNECTED) {
-		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNMANAGED_DEVICE,
-			                 "Device not managed by NetworkManager or unavailable");
-		return NULL;
-	}
-
-	/* If this is an autoconnect request, but the device isn't allowing autoconnect
-	 * right now, we reject it.
-	 */
-	if (!dbus_sender && !nm_device_autoconnect_allowed (device)) {
-		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_AUTOCONNECT_NOT_ALLOWED,
-		             "%s does not allow automatic connections at this time",
-		             nm_device_get_iface (device));
-		return NULL;
-	}
-
-	if (dbus_sender) {
 		/* If the device is active and its connection is not visible to the
 		 * user that's requesting this new activation, fail, since other users
 		 * should not be allowed to implicitly deactivate private connections
 		 * by activating a connection of their own.
 		 */
 		existing_connection = nm_device_get_connection (device);
+		subject = nm_active_connection_get_subject (active);
 		if (existing_connection &&
 		    !nm_auth_uid_in_acl (existing_connection,
-		                         priv->session_monitor,
-		                         sender_uid,
-		                         &error_desc)) {
+			                     nm_session_monitor_get (),
+			                     nm_auth_subject_get_uid (subject),
+			                     &error_desc)) {
 			g_set_error (error,
-			             NM_MANAGER_ERROR,
-			             NM_MANAGER_ERROR_PERMISSION_DENIED,
-			             "Private connection already active on the device: %s",
-			             error_desc);
+					     NM_MANAGER_ERROR,
+					     NM_MANAGER_ERROR_PERMISSION_DENIED,
+					     "Private connection already active on the device: %s",
+					     error_desc);
 			g_free (error_desc);
 			return FALSE;
 		}
 	}
 
-	/* Try to find the master connection/device if the connection has a dependency */
-	if (!find_master (manager, connection, device, &master_connection, &master_device)) {
-		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-			                 "Master connection not found or invalid");
-		return NULL;
+	/* Final connection must be available on device */
+	if (!nm_device_connection_is_available (device, connection, TRUE)) {
+		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+		             "Connection '%s' is not available on the device %s at this time.",
+		             nm_connection_get_id (connection), nm_device_get_iface (device));
+		return FALSE;
+	}
+
+	/* If this is an autoconnect request, but the device isn't allowing autoconnect
+	 * right now, we reject it.
+	 */
+	if (!nm_active_connection_get_user_requested (active) &&
+	    !nm_device_autoconnect_allowed (device)) {
+		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_AUTOCONNECT_NOT_ALLOWED,
+		             "%s does not allow automatic connections at this time",
+		             nm_device_get_iface (device));
+		return FALSE;
 	}
 
+	/* Try to find the master connection/device if the connection has a dependency */
+	if (!find_master (self, connection, device,
+	                  &master_connection, &master_device, &master_ac,
+	                  error))
+		return FALSE;
+
 	/* Ensure there's a master active connection the new connection we're
 	 * activating can depend on.
 	 */
@@ -3068,94 +2663,395 @@ nm_manager_activate_connection (NMManager *manager,
 		/* Ensure eg bond slave and the candidate master is a bond master */
 		if (master_connection && !is_compatible_with_slave (master_connection, connection)) {
 			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED,
-					             "The master connection was not compatible");
-			return NULL;
+			                     "The master connection was not compatible");
+			return FALSE;
 		}
 
-		master_ac = ensure_master_active_connection (manager,
-		                                             dbus_sender,
-		                                             connection,
-		                                             device,
-		                                             master_connection,
-		                                             master_device,
-		                                             error);
 		if (!master_ac) {
-			if (error)
-				g_assert (*error);
-			return NULL;
+			master_ac = ensure_master_active_connection (self,
+			                                             nm_active_connection_get_subject (active),
+			                                             connection,
+			                                             device,
+			                                             master_connection,
+			                                             master_device,
+			                                             error);
+			if (!master_ac) {
+				if (error)
+					g_assert (*error);
+				return FALSE;
+			}
 		}
 
+		nm_active_connection_set_master (active, master_ac);
 		nm_log_dbg (LOGD_CORE, "Activation of '%s' depends on active connection %s",
 		            nm_connection_get_id (connection),
 		            nm_active_connection_get_path (master_ac));
 	}
 
-	return internal_activate_device (manager,
-	                                 device,
+	/* Export the new ActiveConnection to clients and start it on the device */
+	nm_active_connection_export (active);
+	g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS);
+	nm_device_queue_activation (device, NM_ACT_REQUEST (active));
+	return TRUE;
+}
+
+static gboolean
+_internal_activate_generic (NMManager *self, NMActiveConnection *active, GError **error)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	gboolean success = FALSE;
+
+	/* Ensure activation request is still valid, eg that its device hasn't gone
+	 * away or that some other dependency has not failed.
+	 */
+	if (nm_active_connection_get_state (active) >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) {
+		g_set_error_literal (error,
+		                     NM_MANAGER_ERROR,
+		                     NM_MANAGER_ERROR_DEPENDENCY_FAILED,
+		                     "Activation failed because dependencies failed.");
+		return FALSE;
+	}
+
+	if (NM_IS_VPN_CONNECTION (active))
+		success = _internal_activate_vpn (self, active, error);
+	else
+		success = _internal_activate_device (self, active, error);
+
+	if (success) {
+		/* Force an update of the Manager's activating-connection property.
+		 * The device changes state before the AC gets exported, which causes
+		 * the manager's 'activating-connection' property to be NULL since the
+		 * AC only gets a D-Bus path when it's exported.  So now that the AC
+		 * is exported, make sure the manager's activating-connection property
+		 * is up-to-date.
+		 */
+		policy_activating_device_changed (G_OBJECT (priv->policy), NULL, self);
+	}
+
+	return success;
+}
+
+static NMActiveConnection *
+_new_vpn_active_connection (NMManager *self, 
+                            NMConnection *connection,
+                            const char *specific_object,
+                            NMAuthSubject *subject,
+                            GError **error)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMActiveConnection *parent = NULL;
+	NMDevice *device = NULL;
+
+	if (specific_object) {
+		/* Find the specific connection the client requested we use */
+		parent = active_connection_get_by_path (self, specific_object);
+		if (!parent) {
+			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE,
+			                     "Base connection for VPN connection not active.");
+			return NULL;
+		}
+	} else
+		parent = priv->primary_connection;
+
+	if (!parent) {
+		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+		                     "Could not find source connection.");
+		return NULL;
+	}
+
+	device = nm_active_connection_get_device (parent);
+	if (!device) {
+		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+		                     "Source connection had no active device.");
+		return NULL;
+	}
+
+	return (NMActiveConnection *) nm_vpn_connection_new (connection,
+	                                                     device,
+	                                                     nm_active_connection_get_path (parent),
+	                                                     subject);
+}
+
+static NMActiveConnection *
+_new_active_connection (NMManager *self,
+                        NMConnection *connection,
+                        const char *specific_object,
+                        NMDevice *device,
+                        NMAuthSubject *subject,
+                        GError **error)
+{
+	NMActiveConnection *existing_ac;
+
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+	g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL);
+
+	/* Can't create new AC for already-active connection */
+	existing_ac = find_ac_for_connection (self, connection);
+	if (NM_IS_VPN_CONNECTION (existing_ac)) {
+		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE,
+		             "Connection '%s' is already active",
+		             nm_connection_get_id (connection));
+		return NULL;
+	}
+
+	if (existing_ac) {
+		NMDevice *existing_device = nm_active_connection_get_device (existing_ac);
+
+		if (existing_device != device) {
+			g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE,
+			             "Connection '%s' is already active on %s",
+			             nm_connection_get_id (connection),
+			             nm_device_get_iface (existing_device));
+			return NULL;
+		}
+	}
+
+	/* Normalize the specific object */
+	if (specific_object && g_strcmp0 (specific_object, "/") == 0)
+		specific_object = NULL;
+
+	if (nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME)) {
+		return _new_vpn_active_connection (self,
+		                                   connection,
+		                                   specific_object,
+		                                   subject,
+		                                   error);
+	}
+
+	return (NMActiveConnection *) nm_act_request_new (connection,
+	                                                  specific_object,
+	                                                  subject,
+	                                                  device);
+}
+
+static void
+_internal_activation_failed (NMManager *self,
+                             NMActiveConnection *active,
+                             const char *error_desc)
+{
+	nm_log_warn (LOGD_CORE, "Failed to activate '%s': %s",
+	             nm_connection_get_id (nm_active_connection_get_connection (active)),
+	             error_desc);
+
+	if (nm_active_connection_get_state (active) <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
+		nm_active_connection_set_state (active, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING);
+		nm_active_connection_set_state (active, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED);
+	}
+	active_connection_remove (self, active);
+}
+
+static void
+_internal_activation_auth_done (NMActiveConnection *active,
+                                gboolean success,
+                                const char *error_desc,
+                                gpointer user_data1,
+                                gpointer user_data2)
+{
+	NMManager *self = user_data1;
+	GError *error = NULL;
+
+	if (success) {
+		if (_internal_activate_generic (self, active, &error)) {
+			g_object_unref (active);
+			return;
+		}
+	}
+
+	g_assert (error_desc || error);
+	_internal_activation_failed (self, active, error_desc ? error_desc : error->message);
+	g_object_unref (active);
+	g_clear_error (&error);
+}
+
+/**
+ * nm_manager_activate_connection():
+ * @self: the #NMManager
+ * @connection: the #NMConnection to activate on @device
+ * @specific_object: the specific object path, if any, for the activation
+ * @device: the #NMDevice to activate @connection on
+ * @subject: the subject which requested activation
+ * @error: return location for an error
+ *
+ * Begins a new internally-initiated activation of @connection on @device.
+ * @subject should be the subject of the activation that triggered this
+ * one, or if this is an autoconnect request, a new internal subject.
+ * The returned #NMActiveConnection is owned by the Manager and should be
+ * referenced by the caller if the caller continues to use it.
+ *
+ * Returns: (transfer none): the new #NMActiveConnection that tracks
+ * activation of @connection on @device
+ */
+NMActiveConnection *
+nm_manager_activate_connection (NMManager *self,
+                                NMConnection *connection,
+                                const char *specific_object,
+                                NMDevice *device,
+                                NMAuthSubject *subject,
+                                GError **error)
+{
+	NMActiveConnection *active;
+	char *error_desc = NULL;
+
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (connection != NULL, NULL);
+	g_return_val_if_fail (error != NULL, NULL);
+	g_return_val_if_fail (*error == NULL, NULL);
+
+	/* Ensure the subject has permissions for this connection */
+	if (!nm_auth_uid_in_acl (connection,
+	                         nm_session_monitor_get (),
+	                         nm_auth_subject_get_uid (subject),
+	                         &error_desc)) {
+		g_set_error_literal (error,
+		                     NM_MANAGER_ERROR,
+		                     NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                     error_desc);
+		g_free (error_desc);
+		return NULL;
+	}
+
+	active = _new_active_connection (self,
 	                                 connection,
 	                                 specific_object,
-	                                 dbus_sender ? TRUE : FALSE,
-	                                 dbus_sender ? sender_uid : 0,
-	                                 dbus_sender,
-	                                 FALSE,
-	                                 master_ac,
+	                                 device,
+	                                 subject,
 	                                 error);
+	if (active) {
+		nm_active_connection_authorize (active, _internal_activation_auth_done, self, NULL);
+		active_connection_add (self, active);
+	}
+	return active;
 }
 
-/* 
- * TODO this function was created and named in the era of user settings, where
- * we could get activation requests for a connection before we got the settings
- * data of that connection. Now that user settings are gone, flatten or rename
- * it.
- */
-static void
-pending_activate (NMManager *self, PendingActivation *pending)
+static NMAuthSubject *
+validate_activation_request (NMManager *self,
+                             DBusGMethodInvocation *context,
+                             NMConnection *connection,
+                             const char *device_path,
+                             NMDevice **out_device,
+                             gboolean *out_vpn,
+                             GError **error)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMSettingsConnection *connection;
-	NMActiveConnection *ac = NULL;
-	GError *error = NULL;
-	char *sender;
+	NMDevice *device = NULL;
+	gboolean vpn = FALSE;
+	NMAuthSubject *subject = NULL;
+	char *error_desc = NULL;
 
-	/* Ok, we're authorized */
+	g_assert (connection);
+	g_assert (out_device);
+	g_assert (out_vpn);
 
-	connection = nm_settings_get_connection_by_path (priv->settings, pending->connection_path);
-	if (!connection) {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
-		                             "Connection could not be found.");
-		goto out;
+	/* Validate the caller */
+	subject = nm_auth_subject_new_from_context (context);
+	if (!subject) {
+		g_set_error_literal (error,
+		                     NM_MANAGER_ERROR,
+		                     NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                     "Failed to get request UID.");
+		return NULL;
 	}
 
-	sender = dbus_g_method_get_sender (pending->context);
-	g_assert (sender);
-	ac = nm_manager_activate_connection (self,
-	                                     NM_CONNECTION (connection),
-	                                     pending->specific_object_path,
-	                                     pending->device_path,
-	                                     sender,
-	                                     &error);
-	g_free (sender);
+	/* Ensure the subject has permissions for this connection */
+	if (!nm_auth_uid_in_acl (connection,
+	                         nm_session_monitor_get (),
+	                         nm_auth_subject_get_uid (subject),
+	                         &error_desc)) {
+		g_set_error_literal (error,
+		                     NM_MANAGER_ERROR,
+		                     NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                     error_desc);
+		g_free (error_desc);
+		goto error;
+	}
 
-	if (!ac) {
-		nm_log_warn (LOGD_CORE, "connection %s failed to activate: (%d) %s",
-		             pending->connection_path,
-		             error ? error->code : -1,
-		             error && error->message ? error->message : "(unknown)");
+	/* Check whether it's a VPN or not */
+	if (   nm_connection_get_setting_vpn (connection)
+	    || nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME))
+		vpn = TRUE;
+
+	/* Normalize device path */
+	if (device_path && g_strcmp0 (device_path, "/") == 0)
+		device_path = NULL;
+
+	/* And validate it */
+	if (device_path) {
+		device = nm_manager_get_device_by_path (self, device_path);
+		if (!device) {
+			g_set_error_literal (error,
+			                     NM_MANAGER_ERROR,
+			                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+			                     "Device not found");
+			goto error;
+		}
+	} else {
+		gboolean is_software = connection_needs_virtual_device (connection);
+
+		/* VPN and software-device connections don't need a device yet */
+		if (!vpn && !is_software) {
+			g_set_error_literal (error,
+			                     NM_MANAGER_ERROR,
+			                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+			                     "This connection requires an existing device.");
+			goto error;
+		}
+
+		if (is_software) {
+			/* Look for an existing device with the connection's interface name */
+			char *iface;
+
+			iface = get_virtual_iface_name (self, connection, NULL);
+			if (!iface) {
+				g_set_error_literal (error,
+				                     NM_MANAGER_ERROR,
+				                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+				                     "Failed to determine connection's virtual interface name");
+				goto error;
+			}
+
+			device = find_device_by_ip_iface (self, iface);
+			g_free (iface);
+		}
 	}
 
-out:
-	pending_activation_destroy (pending, error, ac);
-	g_clear_error (&error);
+	*out_device = device;
+	*out_vpn = vpn;
+	return subject;
+
+error:
+	g_object_unref (subject);
+	return NULL;
 }
 
+/***********************************************************************/
+
 static void
-activation_auth_done (PendingActivation *pending, GError *error)
-{
-	if (error)
-		pending_activation_destroy (pending, error, NULL);
-	else
-		pending_activate (pending->manager, pending);
+_activation_auth_done (NMActiveConnection *active,
+                       gboolean success,
+                       const char *error_desc,
+                       gpointer user_data1,
+                       gpointer user_data2)
+{
+	NMManager *self = user_data1;
+	DBusGMethodInvocation *context = user_data2;
+	GError *error = NULL;
+
+	if (success) {
+		if (_internal_activate_generic (self, active, &error)) {
+			dbus_g_method_return (context, nm_active_connection_get_path (active));
+			g_object_unref (active);
+			return;
+		}
+	} else {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+			                         NM_MANAGER_ERROR_PERMISSION_DENIED,
+			                         error_desc);
+	}
+
+	g_assert (error);
+	dbus_g_method_return_error (context, error);
+	_internal_activation_failed (self, active, error->message);
+	g_object_unref (active);
+	g_error_free (error);
 }
 
 static void
@@ -3166,64 +3062,185 @@ impl_manager_activate_connection (NMManager *self,
                                   DBusGMethodInvocation *context)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	PendingActivation *pending;
+	NMActiveConnection *active = NULL;
+	NMAuthSubject *subject = NULL;
+	NMConnection *connection;
+	NMDevice *device = NULL;
+	gboolean is_vpn = FALSE;
 	GError *error = NULL;
 
-	/* Need to check the caller's permissions and stuff before we can
-	 * activate the connection.
+	/* Normalize object paths */
+	if (g_strcmp0 (connection_path, "/") == 0)
+		connection_path = NULL;
+	if (g_strcmp0 (specific_object_path, "/") == 0)
+		specific_object_path = NULL;
+	if (g_strcmp0 (device_path, "/") == 0)
+		device_path = NULL;
+
+	/* If the connection path is given and valid, that connection is activated.
+	 * Otherwise the "best" connection for the device is chosen and activated,
+	 * regardless of whether that connection is autoconnect-enabled or not
+	 * (since this is an explicit request, not an auto-activation request).
 	 */
-	pending = pending_activation_new (self,
-	                                  context,
-	                                  device_path,
-	                                  connection_path,
-	                                  NULL,
-	                                  specific_object_path,
-	                                  activation_auth_done,
-	                                  &error);
-	if (pending)
-		pending_activation_check_authorized (pending, priv->dbus_mgr);
-	else {
-		g_assert (error);
-		dbus_g_method_return_error (context, error);
-		g_error_free (error);
+	if (!connection_path) {
+		GPtrArray *available;
+		guint64 best_timestamp = 0;
+		guint i;
+
+		/* If no connection is given, find a suitable connection for the given device path */
+		if (!device_path) {
+			error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+			                             "Only devices may be activated without a specifying a connection");
+			goto error;
+		}
+		device = nm_manager_get_device_by_path (self, device_path);
+		if (!device) {
+			error = g_error_new (NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+			                     "Cannot activate unknown device %s", device_path);
+			goto error;
+		}
+
+		available = nm_device_get_available_connections (device, specific_object_path);
+		for (i = 0; available && i < available->len; i++) {
+			NMSettingsConnection *candidate = g_ptr_array_index (available, i);
+			guint64 candidate_timestamp = 0;
+
+			nm_settings_connection_get_timestamp (candidate, &candidate_timestamp);
+			if (!connection_path || (candidate_timestamp > best_timestamp)) {
+				connection_path = nm_connection_get_path (NM_CONNECTION (candidate));
+				best_timestamp = candidate_timestamp;
+			}
+		}
+
+		if (available)
+			g_ptr_array_free (available, TRUE);
+
+		if (!connection_path) {
+			error = g_error_new_literal (NM_MANAGER_ERROR,
+			                             NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+			                             "The device has no connections available.");
+			goto error;
+		}
+	}
+
+	g_assert (connection_path);
+	connection = (NMConnection *) nm_settings_get_connection_by_path (priv->settings, connection_path);
+	if (!connection) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+		                             "Connection could not be found.");
+		goto error;
 	}
+
+	subject = validate_activation_request (self,
+	                                       context,
+	                                       connection,
+	                                       device_path,
+	                                       &device,
+	                                       &is_vpn,
+	                                       &error);
+	if (!subject)
+		goto error;
+
+	active = _new_active_connection (self,
+	                                 connection,
+	                                 specific_object_path,
+	                                 device,
+	                                 subject,
+	                                 &error);
+	if (!active)
+		goto error;
+
+	nm_active_connection_authorize (active, _activation_auth_done, self, context);
+	active_connection_add (self, active);
+	g_clear_object (&subject);
+	return;
+
+error:
+	g_clear_object (&active);
+	g_clear_object (&subject);
+
+	g_assert (error);
+	dbus_g_method_return_error (context, error);
+	g_error_free (error);
 }
 
+/***********************************************************************/
+
+typedef struct {
+	NMManager *manager;
+	NMActiveConnection *active;
+} AddAndActivateInfo;
+
 static void
 activation_add_done (NMSettings *self,
-                     NMSettingsConnection *connection,
+                     NMSettingsConnection *new_connection,
                      GError *error,
                      DBusGMethodInvocation *context,
                      gpointer user_data)
 {
-	PendingActivation *pending = user_data;
+	AddAndActivateInfo *info = user_data;
+	GError *local = NULL;
 
-	if (error)
-		pending_activation_destroy (pending, error, NULL);
-	else {
-		/* Save the new connection's D-Bus path */
-		pending->connection_path = g_strdup (nm_connection_get_path (NM_CONNECTION (connection)));
+	if (!error) {
+		nm_active_connection_set_connection (info->active, NM_CONNECTION (new_connection));
 
-		/* And activate it */
-		pending_activate (pending->manager, pending);
+		if (_internal_activate_generic (info->manager, info->active, &local)) {
+			dbus_g_method_return (context,
+			                      nm_connection_get_path (NM_CONNECTION (new_connection)),
+			                      nm_active_connection_get_path (info->active));
+			goto done;
+		}
+		error = local;
 	}
+
+	g_assert (error);
+	_internal_activation_failed (info->manager, info->active, error->message);
+	dbus_g_method_return_error (context, error);
+	g_clear_error (&local);
+
+done:
+	g_object_unref (info->active);
+	g_free (info);
 }
 
 static void
-add_and_activate_auth_done (PendingActivation *pending, GError *error)
+_add_and_activate_auth_done (NMActiveConnection *active,
+                             gboolean success,
+                             const char *error_desc,
+                             gpointer user_data1,
+                             gpointer user_data2)
 {
-	if (error)
-		pending_activation_destroy (pending, error, NULL);
-	else {
-		NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (pending->manager);
+	NMManager *self = user_data1;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	DBusGMethodInvocation *context = user_data2;
+	AddAndActivateInfo *info;
+	GError *error = NULL;
+
+	if (success) {
+		info = g_malloc0 (sizeof (*info));
+		info->manager = self;
+		info->active = g_object_ref (active);
 
 		/* Basic sender auth checks performed; try to add the connection */
-		nm_settings_add_connection (priv->settings,
-                                    pending->connection,
-                                    pending->context,
-                                    activation_add_done,
-                                    pending);
+		nm_settings_add_connection_dbus (priv->settings,
+		                                 nm_active_connection_get_connection (active),
+		                                 TRUE,
+		                                 context,
+		                                 activation_add_done,
+		                                 info);
+	} else {
+		active_connection_remove (self, active);
+
+		g_assert (error_desc);
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             error_desc);
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
 	}
+
+	g_object_unref (active);
 }
 
 static void
@@ -3234,29 +3251,105 @@ impl_manager_add_and_activate_connection (NMManager *self,
                                           DBusGMethodInvocation *context)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	PendingActivation *pending;
+	NMConnection *connection = NULL;
+	GSList *all_connections = NULL;
+	NMActiveConnection *active = NULL;
+	NMAuthSubject *subject = NULL;
 	GError *error = NULL;
+	NMDevice *device = NULL;
+	gboolean vpn = FALSE;
 
-	/* Need to check the caller's permissions and stuff before we can
-	 * activate the connection.
+	/* Normalize object paths */
+	if (g_strcmp0 (specific_object_path, "/") == 0)
+		specific_object_path = NULL;
+	if (g_strcmp0 (device_path, "/") == 0)
+		device_path = NULL;
+
+	/* Try to create a new connection with the given settings.
+	 * We allow empty settings for AddAndActivateConnection(). In that case,
+	 * the connection will be completed in nm_utils_complete_generic() or
+	 * nm_device_complete_connection() below. Just make sure we don't expect
+	 * specific data being in the connection till then (especially in
+	 * validate_activation_request()).
 	 */
-	pending = pending_activation_new (self,
-	                                  context,
-	                                  device_path,
-	                                  NULL,
-	                                  settings,
-	                                  specific_object_path,
-	                                  add_and_activate_auth_done,
-	                                  &error);
-	if (pending)
-		pending_activation_check_authorized (pending, priv->dbus_mgr);
-	else {
-		g_assert (error);
-		dbus_g_method_return_error (context, error);
-		g_error_free (error);
-	}
+	connection = nm_connection_new ();
+	if (settings && g_hash_table_size (settings))
+		nm_connection_replace_settings (connection, settings, NULL);
+
+	subject = validate_activation_request (self,
+	                                       context,
+	                                       connection,
+	                                       device_path,
+	                                       &device,
+	                                       &vpn,
+	                                       &error);
+	if (!subject)
+		goto error;
+
+	/* AddAndActivate() requires a device to complete the connection with */
+	if (!device) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+		                             "This connection requires an existing device.");
+		goto error;
+	}
+
+	all_connections = nm_settings_get_connections (priv->settings);
+	if (vpn) {
+		/* Try to fill the VPN's connection setting and name at least */
+		if (!nm_connection_get_setting_vpn (connection)) {
+			error = g_error_new_literal (NM_MANAGER_ERROR,
+			                             NM_MANAGER_ERROR_UNSUPPORTED_CONNECTION_TYPE,
+			                             "VPN connections require a 'vpn' setting");
+			goto error;
+		}
+
+		nm_utils_complete_generic (connection,
+		                           NM_SETTING_VPN_SETTING_NAME,
+		                           all_connections,
+		                           _("VPN connection %d"),
+		                           NULL,
+		                           FALSE); /* No IPv6 by default for now */
+	} else {
+		/* Let each device subclass complete the connection */
+		if (!nm_device_complete_connection (device,
+		                                    connection,
+		                                    specific_object_path,
+		                                    all_connections,
+		                                    &error))
+			goto error;
+	}
+	g_slist_free (all_connections);
+	all_connections = NULL;
+
+	active = _new_active_connection (self,
+	                                 connection,
+	                                 specific_object_path,
+	                                 device,
+	                                 subject,
+	                                 &error);
+	if (!active)
+		goto error;
+
+	nm_active_connection_authorize (active, _add_and_activate_auth_done, self, context);
+	active_connection_add (self, active);
+	g_object_unref (connection);
+	g_object_unref (subject);
+	return;
+
+error:
+	g_clear_object (&connection);
+	g_slist_free (all_connections);
+	g_clear_object (&subject);
+	g_clear_object (&active);
+
+	g_assert (error);
+	dbus_g_method_return_error (context, error);
+	g_error_free (error);
 }
 
+/***********************************************************************/
+
 gboolean
 nm_manager_deactivate_connection (NMManager *manager,
                                   const char *connection_path,
@@ -3286,9 +3379,8 @@ nm_manager_deactivate_connection (NMManager *manager,
 			                     "The VPN connection was not active.");
 	} else {
 		g_assert (NM_IS_ACT_REQUEST (active));
-		/* FIXME: use DEACTIVATING state */
 		nm_device_state_changed (nm_active_connection_get_device (active),
-		                         NM_DEVICE_STATE_DISCONNECTED,
+		                         NM_DEVICE_STATE_DEACTIVATING,
 		                         reason);
 		success = TRUE;
 	}
@@ -3309,11 +3401,13 @@ deactivate_net_auth_done_cb (NMAuthChain *chain,
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GError *error = NULL;
 	NMAuthCallResult result;
-	const char *active_path;
+
+	g_assert (context);
 
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
 
-	result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL));
+	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL);
+
 	if (auth_error) {
 		nm_log_dbg (LOGD_CORE, "Disconnect request failed: %s", auth_error->message);
 		error = g_error_new (NM_MANAGER_ERROR,
@@ -3326,9 +3420,8 @@ deactivate_net_auth_done_cb (NMAuthChain *chain,
 		                             "Not authorized to deactivate connections");
 	} else {
 		/* success; deactivation allowed */
-		active_path = nm_auth_chain_get_data (chain, "path");
 		if (!nm_manager_deactivate_connection (self,
-		                                       active_path,
+		                                       nm_auth_chain_get_data (chain, "path"),
 		                                       NM_DEVICE_STATE_REASON_USER_REQUESTED,
 		                                       &error))
 			g_assert (error);
@@ -3351,9 +3444,9 @@ impl_manager_deactivate_connection (NMManager *self,
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMConnection *connection = NULL;
 	GError *error = NULL;
+	NMAuthSubject *subject = NULL;
 	GSList *iter;
 	NMAuthChain *chain;
-	gulong sender_uid = G_MAXULONG;
 	char *error_desc = NULL;
 
 	/* Find the connection by its object path */
@@ -3370,85 +3463,100 @@ impl_manager_deactivate_connection (NMManager *self,
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE,
 		                             "The connection was not active.");
-		dbus_g_method_return_error (context, error);
-		g_error_free (error);
-		return;
+		goto done;
 	}
 
-	/* Need to check the caller's permissions and stuff before we can
-	 * deactivate the connection.
-	 */
-	if (!nm_auth_get_caller_uid (context, 
-		                         priv->dbus_mgr,
-	                             &sender_uid,
-	                             &error_desc)) {
+	/* Validate the caller */
+	subject = nm_auth_subject_new_from_context (context);
+	if (!subject) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             error_desc);
-		dbus_g_method_return_error (context, error);
-		g_error_free (error);
-		g_free (error_desc);
-		return;
+		                             "Failed to get request UID.");
+		goto done;
 	}
 
 	/* Ensure the subject has permissions for this connection */
 	if (!nm_auth_uid_in_acl (connection,
-	                         priv->session_monitor,
-	                         sender_uid,
+	                         nm_session_monitor_get (),
+	                         nm_auth_subject_get_uid (subject),
 	                         &error_desc)) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                             error_desc);
-		dbus_g_method_return_error (context, error);
-		g_error_free (error);
 		g_free (error_desc);
-		return;
+		goto done;
 	}
 
-	/* Yay for root */
-	if (0 == sender_uid) {
-		if (!nm_manager_deactivate_connection (self,
-		                                       active_path,
-		                                       NM_DEVICE_STATE_REASON_USER_REQUESTED,
-		                                       &error)) {
-			dbus_g_method_return_error (context, error);
-			g_clear_error (&error);
-		} else
-			dbus_g_method_return (context);
-
-		return;
+	/* Validate the user request */
+	chain = nm_auth_chain_new_subject (subject, context, deactivate_net_auth_done_cb, self);
+	if (!chain) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Unable to authenticate request.");
+		goto done;
 	}
 
-	/* Otherwise validate the user request */
-	chain = nm_auth_chain_new (context, NULL, deactivate_net_auth_done_cb, self);
-	g_assert (chain);
 	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
-
 	nm_auth_chain_set_data (chain, "path", g_strdup (active_path), g_free);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE);
+
+done:
+	g_clear_object (&subject);
+	if (error)
+		dbus_g_method_return_error (context, error);
+	g_clear_error (&error);
+}
+
+static gboolean
+device_is_wake_on_lan (NMDevice *device)
+{
+	return nm_platform_link_get_wake_on_lan (nm_device_get_ip_ifindex (device));
 }
 
 static void
-do_sleep_wake (NMManager *self)
+do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	const GSList *unmanaged_specs;
+	gboolean suspending, waking_from_suspend;
 	GSList *iter;
 
+	suspending = sleeping_changed && priv->sleeping;
+	waking_from_suspend = sleeping_changed && !priv->sleeping;
+
 	if (manager_sleeping (self)) {
-		nm_log_info (LOGD_SUSPEND, "sleeping or disabling...");
+		nm_log_info (LOGD_SUSPEND, suspending ? "sleeping..." : "disabling...");
 
-		/* Just deactivate and down all devices from the device list,
-		 * to keep things fast the device list will get resynced when
-		 * the manager wakes up.
+		/* FIXME: are there still hardware devices that need to be disabled around
+		 * suspend/resume?
 		 */
-		for (iter = priv->devices; iter; iter = iter->next)
-			nm_device_set_managed (NM_DEVICE (iter->data), FALSE, NM_DEVICE_STATE_REASON_SLEEPING);
+		for (iter = priv->devices; iter; iter = iter->next) {
+			NMDevice *device = iter->data;
 
+			/* FIXME: shouldn't we be unmanaging software devices if !suspending? */
+			if (nm_device_is_software (device))
+				continue;
+			/* Wake-on-LAN devices will be taken down post-suspend rather than pre- */
+			if (suspending && device_is_wake_on_lan (device))
+				continue;
+
+			nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_SLEEPING);
+		}
 	} else {
-		nm_log_info (LOGD_SUSPEND, "waking up and re-enabling...");
+		nm_log_info (LOGD_SUSPEND, waking_from_suspend ? "waking up..." : "re-enabling...");
+
+		if (waking_from_suspend) {
+			/* Belatedly take down Wake-on-LAN devices; ideally we wouldn't have to do this
+			 * but for now it's the only way to make sure we re-check their connectivity.
+			 */
+			for (iter = priv->devices; iter; iter = iter->next) {
+				NMDevice *device = iter->data;
 
-		unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings);
+				if (nm_device_is_software (device))
+					continue;
+				if (device_is_wake_on_lan (device))
+					nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_SLEEPING);
+			}
+		}
 
 		/* Ensure rfkill state is up-to-date since we don't respond to state
 		 * changes during sleep.
@@ -3460,6 +3568,9 @@ do_sleep_wake (NMManager *self)
 			NMDevice *device = NM_DEVICE (iter->data);
 			guint i;
 
+			if (nm_device_is_software (device))
+				continue;
+
 			/* enable/disable wireless devices since that we don't respond
 			 * to killswitch changes during sleep.
 			 */
@@ -3479,10 +3590,7 @@ do_sleep_wake (NMManager *self)
 
 			g_object_set (G_OBJECT (device), NM_DEVICE_AUTOCONNECT, TRUE, NULL);
 
-			if (nm_device_spec_match_list (device, unmanaged_specs))
-				nm_device_set_managed (device, FALSE, NM_DEVICE_STATE_REASON_NOW_UNMANAGED);
-			else
-				nm_device_set_managed (device, TRUE, NM_DEVICE_STATE_REASON_NOW_MANAGED);
+			nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, FALSE, NM_DEVICE_STATE_REASON_NOW_MANAGED);
 		}
 	}
 
@@ -3504,7 +3612,7 @@ _internal_sleep (NMManager *self, gboolean do_sleep)
 
 	priv->sleeping = do_sleep;
 
-	do_sleep_wake (self);
+	do_sleep_wake (self, TRUE);
 
 	g_object_notify (G_OBJECT (self), NM_MANAGER_SLEEPING);
 }
@@ -3524,7 +3632,7 @@ sleep_auth_done_cb (NMAuthChain *chain,
 
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
 
-	result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_SLEEP_WAKE));
+	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_SLEEP_WAKE);
 	if (error) {
 		nm_log_dbg (LOGD_SUSPEND, "Sleep/wake request failed: %s", error->message);
 		ret_error = g_error_new (NM_MANAGER_ERROR,
@@ -3559,7 +3667,6 @@ impl_manager_sleep (NMManager *self,
 	GError *error = NULL;
 #if 0
 	NMAuthChain *chain;
-	gulong sender_uid = G_MAXULONG;
 	const char *error_desc = NULL;
 #endif
 
@@ -3589,28 +3696,18 @@ impl_manager_sleep (NMManager *self,
 	return;
 
 #if 0
-	if (!nm_auth_get_caller_uid (context, priv->dbus_mgr, &sender_uid, &error_desc)) {
+	chain = nm_auth_chain_new (context, sleep_auth_done_cb, self, &error_desc);
+	if (chain) {
+		priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+		nm_auth_chain_set_data (chain, "sleep", GUINT_TO_POINTER (do_sleep), NULL);
+		nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SLEEP_WAKE, TRUE);
+	} else {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                             error_desc);
 		dbus_g_method_return_error (context, error);
 		g_error_free (error);
-		return;
 	}
-
-	/* Root doesn't need PK authentication */
-	if (0 == sender_uid) {
-		_internal_sleep (self, do_sleep);
-		dbus_g_method_return (context);
-		return;
-	}
-
-	chain = nm_auth_chain_new (context, NULL, sleep_auth_done_cb, self);
-	g_assert (chain);
-	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
-
-	nm_auth_chain_set_data (chain, "sleep", GUINT_TO_POINTER (do_sleep), NULL);
-	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SLEEP_WAKE, TRUE);
 #endif
 }
 
@@ -3655,7 +3752,7 @@ _internal_enable (NMManager *self, gboolean enable)
 
 	priv->net_enabled = enable;
 
-	do_sleep_wake (self);
+	do_sleep_wake (self, FALSE);
 
 	g_object_notify (G_OBJECT (self), NM_MANAGER_NETWORKING_ENABLED);
 }
@@ -3668,27 +3765,25 @@ enable_net_done_cb (NMAuthChain *chain,
 {
 	NMManager *self = NM_MANAGER (user_data);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GError *ret_error;
+	GError *ret_error = NULL;
 	NMAuthCallResult result;
 	gboolean enable;
 
+	g_assert (context);
+
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
 
-	result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK));
+	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK);
 	if (error) {
 		nm_log_dbg (LOGD_CORE, "Enable request failed: %s", error->message);
 		ret_error = g_error_new (NM_MANAGER_ERROR,
 		                         NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                         "Enable request failed: %s",
 		                         error->message);
-		dbus_g_method_return_error (context, ret_error);
-		g_error_free (ret_error);
 	} else if (result != NM_AUTH_CALL_RESULT_YES) {
 		ret_error = g_error_new_literal (NM_MANAGER_ERROR,
 		                                 NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                                 "Not authorized to enable/disable networking");
-		dbus_g_method_return_error (context, ret_error);
-		g_error_free (ret_error);
 	} else {
 		/* Auth success */
 		enable = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "enable"));
@@ -3696,6 +3791,11 @@ enable_net_done_cb (NMAuthChain *chain,
 		dbus_g_method_return (context);
 	}
 
+	if (ret_error) {
+		dbus_g_method_return_error (context, ret_error);
+		g_error_free (ret_error);
+	}
+
 	nm_auth_chain_unref (chain);
 }
 
@@ -3707,8 +3807,6 @@ impl_manager_enable (NMManager *self,
 	NMManagerPrivate *priv;
 	NMAuthChain *chain;
 	GError *error = NULL;
-	gulong sender_uid = G_MAXULONG;
-	char *error_desc = NULL;
 
 	g_return_if_fail (NM_IS_MANAGER (self));
 
@@ -3718,34 +3816,25 @@ impl_manager_enable (NMManager *self,
 		error = g_error_new (NM_MANAGER_ERROR,
 		                     NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED,
 		                     "Already %s", enable ? "enabled" : "disabled");
-		dbus_g_method_return_error (context, error);
-		g_error_free (error);
-		return;
+		goto done;
 	}
 
-	if (!nm_auth_get_caller_uid (context, priv->dbus_mgr, &sender_uid, &error_desc)) {
+	chain = nm_auth_chain_new_context (context, enable_net_done_cb, self);
+	if (!chain) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             error_desc);
-		dbus_g_method_return_error (context, error);
-		g_error_free (error);
-		g_free (error_desc);
-		return;
-	}
-
-	/* Root doesn't need PK authentication */
-	if (0 == sender_uid) {
-		_internal_enable (self, enable);
-		dbus_g_method_return (context);
-		return;
+		                             "Unable to authenticate request.");
+		goto done;
 	}
 
-	chain = nm_auth_chain_new (context, NULL, enable_net_done_cb, self);
-	g_assert (chain);
 	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
-
 	nm_auth_chain_set_data (chain, "enable", GUINT_TO_POINTER (enable), NULL);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK, TRUE);
+
+done:
+	if (error)
+		dbus_g_method_return_error (context, error);
+	g_clear_error (&error);
 }
 
 /* Permissions */
@@ -3755,7 +3844,7 @@ get_perm_add_result (NMAuthChain *chain, GHashTable *results, const char *permis
 {
 	NMAuthCallResult result;
 
-	result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, permission));
+	result = nm_auth_chain_get_result (chain, permission);
 	if (result == NM_AUTH_CALL_RESULT_YES)
 		g_hash_table_insert (results, (char *) permission, "yes");
 	else if (result == NM_AUTH_CALL_RESULT_NO)
@@ -3778,6 +3867,8 @@ get_permissions_done_cb (NMAuthChain *chain,
 	GError *ret_error;
 	GHashTable *results;
 
+	g_assert (context);
+
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
 	if (error) {
 		nm_log_dbg (LOGD_CORE, "Permissions request failed: %s", error->message);
@@ -3789,6 +3880,7 @@ get_permissions_done_cb (NMAuthChain *chain,
 		g_error_free (ret_error);
 	} else {
 		results = g_hash_table_new (g_str_hash, g_str_equal);
+
 		get_perm_add_result (chain, results, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK);
 		get_perm_add_result (chain, results, NM_AUTH_PERMISSION_SLEEP_WAKE);
 		get_perm_add_result (chain, results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI);
@@ -3800,6 +3892,7 @@ get_permissions_done_cb (NMAuthChain *chain,
 		get_perm_add_result (chain, results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM);
 		get_perm_add_result (chain, results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN);
 		get_perm_add_result (chain, results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME);
+
 		dbus_g_method_return (context, results);
 		g_hash_table_destroy (results);
 	}
@@ -3813,11 +3906,19 @@ impl_manager_get_permissions (NMManager *self,
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMAuthChain *chain;
+	GError *error = NULL;
 
-	chain = nm_auth_chain_new (context, NULL, get_permissions_done_cb, self);
-	g_assert (chain);
-	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+	chain = nm_auth_chain_new_context (context, get_permissions_done_cb, self);
+	if (!chain) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Unable to authenticate request.");
+		dbus_g_method_return_error (context, error);
+		g_clear_error (&error);
+		return;
+	}
 
+	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK, FALSE);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SLEEP_WAKE, FALSE);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI, FALSE);
@@ -3839,22 +3940,46 @@ impl_manager_get_state (NMManager *manager, guint32 *state, GError **error)
 	return TRUE;
 }
 
-static gboolean
+static void
 impl_manager_set_logging (NMManager *manager,
                           const char *level,
                           const char *domains,
-                          GError **error)
+                          DBusGMethodInvocation *context)
 {
-	if (nm_logging_setup (level, domains, error)) {
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	GError *error = NULL;
+	gulong caller_uid = G_MAXULONG;
+
+	if (!nm_dbus_manager_get_caller_info (priv->dbus_mgr, context, NULL, &caller_uid, NULL)) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Failed to get request UID.");
+		goto done;
+	}
+
+	if (0 != caller_uid) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Permission denied");
+		goto done;
+	}
+
+	if (nm_logging_setup (level, domains, NULL, &error)) {
+		char *new_level = nm_logging_level_to_string ();
 		char *new_domains = nm_logging_domains_to_string ();
 
 		nm_log_info (LOGD_CORE, "logging: level '%s' domains '%s'",
-		             nm_logging_level_to_string (),
-		             new_domains);
+		             new_level, new_domains);
+		g_free (new_level);
 		g_free (new_domains);
-		return TRUE;
 	}
-	return FALSE;
+
+done:
+	if (error) {
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+	} else
+		dbus_g_method_return (context);
 }
 
 static void
@@ -3862,8 +3987,8 @@ impl_manager_get_logging (NMManager *manager,
                           char **level,
                           char **domains)
 {
-	*level = g_strdup (nm_logging_level_to_string ());
-	*domains = g_strdup (nm_logging_domains_to_string ());
+	*level = nm_logging_level_to_string ();
+	*domains = nm_logging_domains_to_string ();
 }
 
 static void
@@ -3929,37 +4054,20 @@ impl_manager_check_connectivity (NMManager *manager,
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
 	NMAuthChain *chain;
-	gulong sender_uid = G_MAXULONG;
-	char *error_desc = NULL;
-	GError *error;
+	GError *error = NULL;
 
-	/* Need to check the caller's permissions and stuff first */
-	if (!nm_auth_get_caller_uid (context, 
-	                             priv->dbus_mgr,
-	                             &sender_uid,
-	                             &error_desc)) {
+	/* Validate the request */
+	chain = nm_auth_chain_new_context (context, check_connectivity_auth_done_cb, manager);
+	if (!chain) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             error_desc);
+		                             "Unable to authenticate request.");
 		dbus_g_method_return_error (context, error);
-		g_error_free (error);
-		g_free (error_desc);
-		return;
-	}
-
-	/* Yay for root */
-	if (0 == sender_uid) {
-		nm_connectivity_check_async (priv->connectivity,
-		                             connectivity_check_done,
-		                             context);
+		g_clear_error (&error);
 		return;
 	}
 
-	/* Validate the user request */
-	chain = nm_auth_chain_new (context, NULL, check_connectivity_auth_done_cb, manager);
-	g_assert (chain);
 	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
-
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE);
 }
 
@@ -3972,14 +4080,13 @@ nm_manager_start (NMManager *self)
 	/* Set initial radio enabled/disabled state */
 	for (i = 0; i < RFKILL_TYPE_MAX; i++) {
 		RadioState *rstate = &priv->radio_states[i];
-		RfKillState udev_state;
 		gboolean enabled;
 
 		if (!rstate->desc)
 			continue;
 
-		udev_state = nm_udev_manager_get_rfkill_state (priv->udev_mgr, i);
-		update_rstate_from_rfkill (rstate, udev_state);
+		/* recheck kernel rfkill state */
+		update_rstate_from_rfkill (priv->rfkill_mgr, rstate);
 
 		if (rstate->desc) {
 			nm_log_info (LOGD_RFKILL, "%s %s by radio killswitch; %s by state file",
@@ -3998,23 +4105,7 @@ nm_manager_start (NMManager *self)
 	system_unmanaged_devices_changed_cb (priv->settings, NULL, self);
 	system_hostname_changed_cb (priv->settings, NULL, self);
 
-	/* FIXME: remove when we handle bridges non-destructively */
-	/* Read a list of bridges NM managed when it last quit, and only
-	 * manage those bridges to avoid conflicts with external tools.
-	 */
-	priv->nm_bridges = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-	read_nm_created_bridges (self);
-
-	nm_udev_manager_query_devices (priv->udev_mgr);
-	nm_bluez_manager_query_devices (priv->bluez_mgr);
-
-	/* Query devices again to ensure that we catch all virtual interfaces (like
-	 * VLANs) that require a parent.  If during the first pass the VLAN
-	 * interface was detected first, the parent wouldn't exist yet and creating
-	 * the VLAN would fail.  The second query ensures that we'll have a valid
-	 * parent for the VLAN during the second pass.
-	 */
-	nm_udev_manager_query_devices (priv->udev_mgr);
+	nm_platform_query_devices ();
 
 	/*
 	 * Connections added before the manager is started do not emit
@@ -4022,9 +4113,7 @@ nm_manager_start (NMManager *self)
 	 */
 	system_create_virtual_devices (self);
 
-	/* FIXME: remove when we handle bridges non-destructively */
-	g_hash_table_unref (priv->nm_bridges);
-	priv->nm_bridges = NULL;
+	check_if_startup_complete (self);
 }
 
 static gboolean
@@ -4089,9 +4178,7 @@ firmware_dir_changed (GFileMonitor *monitor,
 	switch (event_type) {
 	case G_FILE_MONITOR_EVENT_CREATED:
 	case G_FILE_MONITOR_EVENT_CHANGED:
-#if GLIB_CHECK_VERSION(2,23,4)
 	case G_FILE_MONITOR_EVENT_MOVED:
-#endif
 	case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
 	case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
 		if (!priv->fw_changed_id) {
@@ -4129,7 +4216,7 @@ policy_default_device_changed (GObject *object, GParamSpec *pspec, gpointer user
 	if (ac != priv->primary_connection) {
 		g_clear_object (&priv->primary_connection);
 		priv->primary_connection = ac ? g_object_ref (ac) : NULL;
-		nm_log_dbg (LOGD_CORE, "PrimaryConnection now %s", ac ? nm_active_connection_get_name (ac) : "(none)");
+		nm_log_dbg (LOGD_CORE, "PrimaryConnection now %s", ac ? nm_active_connection_get_id (ac) : "(none)");
 		g_object_notify (G_OBJECT (self), NM_MANAGER_PRIMARY_CONNECTION);
 	}
 }
@@ -4162,7 +4249,7 @@ policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer u
 	if (ac != priv->activating_connection) {
 		g_clear_object (&priv->activating_connection);
 		priv->activating_connection = ac ? g_object_ref (ac) : NULL;
-		nm_log_dbg (LOGD_CORE, "ActivatingConnection now %s", ac ? nm_active_connection_get_name (ac) : "(none)");
+		nm_log_dbg (LOGD_CORE, "ActivatingConnection now %s", ac ? nm_active_connection_get_id (ac) : "(none)");
 		g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVATING_CONNECTION);
 	}
 }
@@ -4178,73 +4265,37 @@ prop_set_auth_done_cb (NMAuthChain *chain,
 {
 	NMManager *self = NM_MANAGER (user_data);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	DBusGConnection *bus;
-	DBusConnection *dbus_connection;
+	DBusConnection *connection;
 	NMAuthCallResult result;
-	DBusMessage *reply = NULL, *request;
-	GError *ret_error;
-	const char *permission, *prop, *objpath;
+	DBusMessage *reply = NULL, *message;
+	const char *permission, *prop;
+	GObject *obj;
 	gboolean set_enabled = TRUE;
-	gboolean is_device = FALSE;
-	size_t objpath_len;
-	size_t devpath_len;
 
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
 
-	request = nm_auth_chain_get_data (chain, "message");
+	message = nm_auth_chain_get_data (chain, "message");
 	permission = nm_auth_chain_get_data (chain, "permission");
 	prop = nm_auth_chain_get_data (chain, "prop");
 	set_enabled = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "enabled"));
-	objpath = nm_auth_chain_get_data (chain, "objectpath");
+	obj = nm_auth_chain_get_data (chain, "object");
 
-	objpath_len = strlen (objpath);
-	devpath_len = strlen (NM_DBUS_PATH "/Devices");
-	if (   strncmp (objpath, NM_DBUS_PATH "/Devices", devpath_len) == 0
-	    && objpath_len > devpath_len)
-		is_device = TRUE;
-
-	if (error) {
-		reply = dbus_message_new_error (request, is_device ? DEV_PERM_DENIED_ERROR : NM_PERM_DENIED_ERROR,
+	result = nm_auth_chain_get_result (chain, permission);
+	if (error || (result != NM_AUTH_CALL_RESULT_YES)) {
+		reply = dbus_message_new_error (message,
+		                                NM_IS_DEVICE (obj) ? DEV_PERM_DENIED_ERROR : NM_PERM_DENIED_ERROR,
 		                                "Not authorized to perform this operation");
 	} else {
-		/* Caller has had a chance to obtain authorization, so we only need to
-		 * check for 'yes' here.
-		 */
-		result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, permission));
-		if (result != NM_AUTH_CALL_RESULT_YES) {
-			reply = dbus_message_new_error (request, is_device ? DEV_PERM_DENIED_ERROR : NM_PERM_DENIED_ERROR,
-			                                "Not authorized to perform this operation");
-		} else {
-			if (is_device) {
-				/* Find the device */
-				NMDevice *device = nm_manager_get_device_by_path (self, objpath);
-				if (device) {
-					g_object_set (device, prop, set_enabled, NULL);
-					reply = dbus_message_new_method_return (request);
-				}
-				else {
-					ret_error = g_error_new_literal (NM_MANAGER_ERROR,
-					                                 NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-					                                 "Can't find device for this operation");
-					dbus_g_method_return_error (context, ret_error);
-					g_error_free (ret_error);
-				}
-			} else {
-				g_object_set (self, prop, set_enabled, NULL);
-				reply = dbus_message_new_method_return (request);
-			}
-		}
+		g_object_set (obj, prop, set_enabled, NULL);
+		reply = dbus_message_new_method_return (message);
 	}
 
-	if (reply) {
-		bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
-		g_assert (bus);
-		dbus_connection = dbus_g_connection_get_connection (bus);
-		g_assert (dbus_connection);
+	g_assert (reply);
+	connection = nm_auth_chain_get_data (chain, "connection");
+	g_assert (connection);
+	dbus_connection_send (connection, reply, NULL);
+	dbus_message_unref (reply);
 
-		dbus_connection_send (dbus_connection, reply, NULL);
-		dbus_message_unref (reply);
-	}
 	nm_auth_chain_unref (chain);
 }
 
@@ -4259,14 +4310,12 @@ prop_filter (DBusConnection *connection,
 	DBusMessageIter sub;
 	const char *propiface = NULL;
 	const char *propname = NULL;
-	const char *sender = NULL;
-	const char *objpath = NULL;
 	const char *glib_propname = NULL, *permission = NULL;
-	DBusError dbus_error;
-	gulong uid = G_MAXULONG;
 	DBusMessage *reply = NULL;
 	gboolean set_enabled = FALSE;
+	NMAuthSubject *subject = NULL;
 	NMAuthChain *chain;
+	GObject *obj;
 
 	/* The sole purpose of this function is to validate property accesses
 	 * on the NMManager object since dbus-glib doesn't yet give us this
@@ -4315,173 +4364,47 @@ prop_filter (DBusConnection *connection,
 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 	dbus_message_iter_get_basic (&sub, &set_enabled);
 
-	sender = dbus_message_get_sender (message);
-	if (!sender) {
+	/* Make sure the object exists */
+	obj = dbus_g_connection_lookup_g_object (dbus_connection_get_g_connection (connection),
+	                                         dbus_message_get_path (message));
+	if (!obj) {
 		reply = dbus_message_new_error (message, NM_PERM_DENIED_ERROR,
-		                                "Could not determine D-Bus requestor");
+		                                "Object does not exist");
 		goto out;
 	}
 
-	objpath = dbus_message_get_path (message);
-	if (!objpath) {
+	subject = nm_auth_subject_new_from_message (connection, message);
+	if (!subject) {
 		reply = dbus_message_new_error (message, NM_PERM_DENIED_ERROR,
-		                                "Could not determine D-Bus object path");
+		                                "Could not determine request UID.");
 		goto out;
 	}
 
-	dbus_error_init (&dbus_error);
-	uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
-	if (dbus_error_is_set (&dbus_error)) {
+	/* Validate the user request */
+	chain = nm_auth_chain_new_subject (subject, NULL, prop_set_auth_done_cb, self);
+	if (!chain) {
 		reply = dbus_message_new_error (message, NM_PERM_DENIED_ERROR,
-		                                "Could not determine the user ID of the requestor");
-		dbus_error_free (&dbus_error);
+		                                "Could not authenticate request.");
 		goto out;
 	}
 
-	if (uid > 0) {
-		/* Otherwise validate the user request */
-		chain = nm_auth_chain_new_raw_message (message, prop_set_auth_done_cb, self);
-		g_assert (chain);
-		priv->auth_chains = g_slist_append (priv->auth_chains, chain);
-		nm_auth_chain_set_data (chain, "prop", g_strdup (glib_propname), g_free);
-		nm_auth_chain_set_data (chain, "permission", g_strdup (permission), g_free);
-		nm_auth_chain_set_data (chain, "enabled", GUINT_TO_POINTER (set_enabled), NULL);
-		nm_auth_chain_set_data (chain, "message", dbus_message_ref (message), (GDestroyNotify) dbus_message_unref);
-		nm_auth_chain_set_data (chain, "objectpath", g_strdup (objpath), g_free);
-		nm_auth_chain_add_call (chain, permission, TRUE);
-	} else {
-		/* Yay for root */
-		g_object_set (self, glib_propname, set_enabled, NULL);
-		reply = dbus_message_new_method_return (message);
-	}
+	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+	nm_auth_chain_set_data (chain, "prop", g_strdup (glib_propname), g_free);
+	nm_auth_chain_set_data (chain, "permission", g_strdup (permission), g_free);
+	nm_auth_chain_set_data (chain, "enabled", GUINT_TO_POINTER (set_enabled), NULL);
+	nm_auth_chain_set_data (chain, "message", dbus_message_ref (message), (GDestroyNotify) dbus_message_unref);
+	nm_auth_chain_set_data (chain, "connection", dbus_connection_ref (connection), (GDestroyNotify) dbus_connection_unref);
+	nm_auth_chain_set_data (chain, "object", g_object_ref (obj), (GDestroyNotify) g_object_unref);
+	nm_auth_chain_add_call (chain, permission, TRUE);
 
 out:
 	if (reply) {
 		dbus_connection_send (connection, reply, NULL);
 		dbus_message_unref (reply);
 	}
-	return DBUS_HANDLER_RESULT_HANDLED;
-}
-
-static NMManager *singleton = NULL;
-
-NMManager *
-nm_manager_get (void)
-{
-	g_assert (singleton);
-	return g_object_ref (singleton);
-}
-
-NMManager *
-nm_manager_new (NMSettings *settings,
-                const char *state_file,
-                gboolean initial_net_enabled,
-                gboolean initial_wifi_enabled,
-                gboolean initial_wwan_enabled,
-                gboolean initial_wimax_enabled,
-                const gchar *connectivity_uri,
-                gint connectivity_interval,
-                const gchar *connectivity_response,
-                GError **error)
-{
-	NMManagerPrivate *priv;
-	DBusGConnection *bus;
-	DBusConnection *dbus_connection;
-
-	g_assert (settings);
-
-	/* Can only be called once */
-	g_assert (singleton == NULL);
-	singleton = (NMManager *) g_object_new (NM_TYPE_MANAGER, NULL);
-	g_assert (singleton);
-
-	priv = NM_MANAGER_GET_PRIVATE (singleton);
-
-	priv->policy = nm_policy_new (singleton, settings);
-	g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP4_DEVICE,
-	                  G_CALLBACK (policy_default_device_changed), singleton);
-	g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP6_DEVICE,
-	                  G_CALLBACK (policy_default_device_changed), singleton);
-	g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP4_DEVICE,
-	                  G_CALLBACK (policy_activating_device_changed), singleton);
-	g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP6_DEVICE,
-	                  G_CALLBACK (policy_activating_device_changed), singleton);
-
-	priv->connectivity = nm_connectivity_new (connectivity_uri, connectivity_interval, connectivity_response);
-	g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_STATE,
-	                  G_CALLBACK (connectivity_changed), singleton);
-
-	bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
-	g_assert (bus);
-	dbus_connection = dbus_g_connection_get_connection (bus);
-	g_assert (dbus_connection);
-
-	if (!dbus_connection_add_filter (dbus_connection, prop_filter, singleton, NULL)) {
-		nm_log_err (LOGD_CORE, "failed to register DBus connection filter");
-		g_object_unref (singleton);
-		return NULL;
-    }
-
-	priv->settings = g_object_ref (settings);
-
-	priv->state_file = g_strdup (state_file);
-
-	priv->net_enabled = initial_net_enabled;
-
-	priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = initial_wifi_enabled;
-	priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = initial_wwan_enabled;
-	priv->radio_states[RFKILL_TYPE_WIMAX].user_enabled = initial_wimax_enabled;
-
-	g_signal_connect (priv->settings, "notify::" NM_SETTINGS_UNMANAGED_SPECS,
-	                  G_CALLBACK (system_unmanaged_devices_changed_cb), singleton);
-	g_signal_connect (priv->settings, "notify::" NM_SETTINGS_HOSTNAME,
-	                  G_CALLBACK (system_hostname_changed_cb), singleton);
-	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED,
-	                  G_CALLBACK (connection_added), singleton);
-	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED,
-	                  G_CALLBACK (connection_changed), singleton);
-	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED,
-	                  G_CALLBACK (connection_removed), singleton);
-	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED,
-	                  G_CALLBACK (connection_changed), singleton);
-
-	dbus_g_connection_register_g_object (bus, NM_DBUS_PATH, G_OBJECT (singleton));
-
-	priv->udev_mgr = nm_udev_manager_new ();
-	g_signal_connect (priv->udev_mgr,
-	                  "device-added",
-	                  G_CALLBACK (udev_device_added_cb),
-	                  singleton);
-	g_signal_connect (priv->udev_mgr,
-	                  "device-removed",
-	                  G_CALLBACK (udev_device_removed_cb),
-	                  singleton);
-	g_signal_connect (priv->udev_mgr,
-	                  "rfkill-changed",
-	                  G_CALLBACK (udev_manager_rfkill_changed_cb),
-	                  singleton);
-
-	priv->bluez_mgr = nm_bluez_manager_get (NM_CONNECTION_PROVIDER (priv->settings));
-
-	g_signal_connect (priv->bluez_mgr,
-	                  NM_BLUEZ_MANAGER_BDADDR_ADDED,
-	                  G_CALLBACK (bluez_manager_bdaddr_added_cb),
-	                  singleton);
-
-	g_signal_connect (priv->bluez_mgr,
-	                  NM_BLUEZ_MANAGER_BDADDR_REMOVED,
-	                  G_CALLBACK (bluez_manager_bdaddr_removed_cb),
-	                  singleton);
-
-	priv->session_monitor = nm_session_monitor_get ();
-
-	/* Force kernel WiFi rfkill state to follow NM saved wifi state in case
-	 * the BIOS doesn't save rfkill state, and to be consistent with user
-	 * changes to the WirelessEnabled property which toggles kernel rfkill.
-	 */
-	rfkill_change_wifi (priv->radio_states[RFKILL_TYPE_WLAN].desc, initial_wifi_enabled);
+	g_clear_object (&subject);
 
-	return singleton;
+	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
 static void
@@ -4491,113 +4414,9 @@ authority_changed_cb (gpointer user_data)
 	g_signal_emit (NM_MANAGER (user_data), signals[CHECK_PERMISSIONS], 0);
 }
 
-static void
-dispose (GObject *object)
-{
-	NMManager *manager = NM_MANAGER (object);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	DBusGConnection *bus;
-	DBusConnection *dbus_connection;
-	GSList *iter;
-
-	if (priv->disposed) {
-		G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object);
-		return;
-	}
-	priv->disposed = TRUE;
-
-	g_slist_foreach (priv->auth_chains, (GFunc) nm_auth_chain_unref, NULL);
-	g_slist_free (priv->auth_chains);
-
-	nm_auth_changed_func_unregister (authority_changed_cb, manager);
-
-	/* FIXME: remove when we handle bridges non-destructively */
-	write_nm_created_bridges (manager);
-
-	/* Remove all devices */
-	while (g_slist_length (priv->devices)) {
-		priv->devices = remove_one_device (manager,
-		                                   priv->devices,
-		                                   NM_DEVICE (priv->devices->data),
-		                                   TRUE);
-	}
-
-	if (priv->ac_cleanup_id) {
-		g_source_remove (priv->ac_cleanup_id);
-		priv->ac_cleanup_id = 0;
-	}
-
-	for (iter = priv->active_connections; iter; iter = g_slist_next (iter))
-		active_connection_removed (manager, NM_ACTIVE_CONNECTION (iter->data));
-	g_slist_free (priv->active_connections);
-	priv->active_connections = NULL;
-	g_clear_object (&priv->primary_connection);
-	g_clear_object (&priv->activating_connection);
-
-	g_clear_object (&priv->connectivity);
-
-	g_free (priv->hostname);
-
-	g_signal_handlers_disconnect_by_func (priv->policy, G_CALLBACK (policy_default_device_changed), singleton);
-	g_signal_handlers_disconnect_by_func (priv->policy, G_CALLBACK (policy_activating_device_changed), singleton);
-	g_object_unref (priv->policy);
-
-	g_object_unref (priv->settings);
-	g_object_unref (priv->vpn_manager);
-	g_object_unref (priv->session_monitor);
-
-	if (priv->modem_added_id) {
-		g_source_remove (priv->modem_added_id);
-		priv->modem_added_id = 0;
-	}
-	if (priv->modem_removed_id) {
-		g_source_remove (priv->modem_removed_id);
-		priv->modem_removed_id = 0;
-	}
-	g_object_unref (priv->modem_manager);
-
-	/* Unregister property filter */
-	bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
-	if (bus) {
-		dbus_connection = dbus_g_connection_get_connection (bus);
-		g_assert (dbus_connection);
-		dbus_connection_remove_filter (dbus_connection, prop_filter, manager);
-	}
-	g_signal_handler_disconnect (priv->dbus_mgr, priv->dbus_connection_changed_id);
-	g_object_unref (priv->dbus_mgr);
-
-	if (priv->bluez_mgr)
-		g_object_unref (priv->bluez_mgr);
-
-	if (priv->aipd_proxy)
-		g_object_unref (priv->aipd_proxy);
-
-	if (priv->sleep_monitor)
-		g_object_unref (priv->sleep_monitor);
-
-	if (priv->fw_monitor) {
-		if (priv->fw_monitor_id)
-			g_signal_handler_disconnect (priv->fw_monitor, priv->fw_monitor_id);
-
-		if (priv->fw_changed_id)
-			g_source_remove (priv->fw_changed_id);
-
-		g_file_monitor_cancel (priv->fw_monitor);
-		g_object_unref (priv->fw_monitor);
-	}
-
-	g_slist_free (priv->factories);
-
-	if (priv->timestamp_update_id) {
-		g_source_remove (priv->timestamp_update_id);
-		priv->timestamp_update_id = 0;
-	}
-
-	G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object);
-}
-
 #define KERN_RFKILL_OP_CHANGE_ALL 3
 #define KERN_RFKILL_TYPE_WLAN     1
+#define KERN_RFKILL_TYPE_WWAN     5
 struct rfkill_event {
 	__u32 idx;
 	__u8  type;
@@ -4606,18 +4425,19 @@ struct rfkill_event {
 } __attribute__((packed));
 
 static void
-rfkill_change_wifi (const char *desc, gboolean enabled)
+rfkill_change (const char *desc, RfKillType rtype, gboolean enabled)
 {
 	int fd;
 	struct rfkill_event event;
 	ssize_t len;
 
+	g_return_if_fail (rtype == RFKILL_TYPE_WLAN || rtype == RFKILL_TYPE_WWAN);
+
 	errno = 0;
 	fd = open ("/dev/rfkill", O_RDWR);
 	if (fd < 0) {
 		if (errno == EACCES)
-			nm_log_warn (LOGD_RFKILL, "(%s): failed to open killswitch device "
-			             "for WiFi radio control", desc);
+			nm_log_warn (LOGD_RFKILL, "(%s): failed to open killswitch device", desc);
 		return;
 	}
 
@@ -4630,7 +4450,16 @@ rfkill_change_wifi (const char *desc, gboolean enabled)
 
 	memset (&event, 0, sizeof (event));
 	event.op = KERN_RFKILL_OP_CHANGE_ALL;
-	event.type = KERN_RFKILL_TYPE_WLAN;
+	switch (rtype) {
+	case RFKILL_TYPE_WLAN:
+		event.type = KERN_RFKILL_TYPE_WLAN;
+		break;
+	case RFKILL_TYPE_WWAN:
+		event.type = KERN_RFKILL_TYPE_WWAN;
+		break;
+	default:
+		g_assert_not_reached ();
+	}
 	event.soft = enabled ? 0 : 1;
 
 	len = write (fd, &event, sizeof (event));
@@ -4657,6 +4486,10 @@ manager_radio_user_toggled (NMManager *self,
 	GError *error = NULL;
 	gboolean old_enabled, new_enabled;
 
+	/* Don't touch devices if asleep/networking disabled */
+	if (manager_sleeping (self))
+		return;
+
 	if (rstate->desc) {
 		nm_log_dbg (LOGD_RFKILL, "(%s): setting radio %s by user",
 		            rstate->desc,
@@ -4690,114 +4523,11 @@ manager_radio_user_toggled (NMManager *self,
 	rstate->user_enabled = enabled;
 	new_enabled = radio_enabled_for_rstate (rstate, FALSE);
 	if (new_enabled != old_enabled) {
-		manager_update_radio_enabled (self, rstate, new_enabled);
-
-		/* For WiFi only (for now) set the actual kernel rfkill state */
-		if (rstate->rtype == RFKILL_TYPE_WLAN)
-			rfkill_change_wifi (rstate->desc, new_enabled);
-	}
-}
-
-static void
-set_property (GObject *object, guint prop_id,
-			  const GValue *value, GParamSpec *pspec)
-{
-	NMManager *self = NM_MANAGER (object);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-
-	switch (prop_id) {
-	case PROP_NETWORKING_ENABLED:
-		/* Construct only for now */
-		priv->net_enabled = g_value_get_boolean (value);
-		break;
-	case PROP_WIRELESS_ENABLED:
-		manager_radio_user_toggled (NM_MANAGER (object),
-		                            &priv->radio_states[RFKILL_TYPE_WLAN],
-		                            g_value_get_boolean (value));
-		break;
-	case PROP_WWAN_ENABLED:
-		manager_radio_user_toggled (NM_MANAGER (object),
-		                            &priv->radio_states[RFKILL_TYPE_WWAN],
-		                            g_value_get_boolean (value));
-		break;
-	case PROP_WIMAX_ENABLED:
-		manager_radio_user_toggled (NM_MANAGER (object),
-		                            &priv->radio_states[RFKILL_TYPE_WIMAX],
-		                            g_value_get_boolean (value));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
+		/* Try to change the kernel rfkill state */
+		if (rstate->rtype == RFKILL_TYPE_WLAN || rstate->rtype == RFKILL_TYPE_WWAN)
+			rfkill_change (rstate->desc, rstate->rtype, new_enabled);
 
-static void
-get_property (GObject *object, guint prop_id,
-			  GValue *value, GParamSpec *pspec)
-{
-	NMManager *self = NM_MANAGER (object);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GSList *iter;
-	GPtrArray *active;
-	const char *path;
-
-	switch (prop_id) {
-	case PROP_VERSION:
-		g_value_set_string (value, VERSION);
-		break;
-	case PROP_STATE:
-		nm_manager_update_state (self);
-		g_value_set_uint (value, priv->state);
-		break;
-	case PROP_NETWORKING_ENABLED:
-		g_value_set_boolean (value, priv->net_enabled);
-		break;
-	case PROP_WIRELESS_ENABLED:
-		g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WLAN, TRUE));
-		break;
-	case PROP_WIRELESS_HARDWARE_ENABLED:
-		g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WLAN].hw_enabled);
-		break;
-	case PROP_WWAN_ENABLED:
-		g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WWAN, TRUE));
-		break;
-	case PROP_WWAN_HARDWARE_ENABLED:
-		g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WWAN].hw_enabled);
-		break;
-	case PROP_WIMAX_ENABLED:
-		g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WIMAX, TRUE));
-		break;
-	case PROP_WIMAX_HARDWARE_ENABLED:
-		g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WIMAX].hw_enabled);
-		break;
-	case PROP_ACTIVE_CONNECTIONS:
-		active = g_ptr_array_sized_new (3);
-		for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) {
-			path = nm_active_connection_get_path (NM_ACTIVE_CONNECTION (iter->data));
-			g_ptr_array_add (active, g_strdup (path));
-		}
-		g_value_take_boxed (value, active);
-		break;
-	case PROP_CONNECTIVITY:
-		g_value_set_uint (value, nm_connectivity_get_state (priv->connectivity));
-		break;
-	case PROP_PRIMARY_CONNECTION:
-		path = priv->primary_connection ? nm_active_connection_get_path (priv->primary_connection) : "/";
-		g_value_set_boxed (value, path);
-		break;
-	case PROP_ACTIVATING_CONNECTION:
-		path = priv->activating_connection ? nm_active_connection_get_path (priv->activating_connection) : "/";
-		g_value_set_boxed (value, path);
-		break;
-	case PROP_HOSTNAME:
-		g_value_set_string (value, priv->hostname);
-		break;
-	case PROP_SLEEPING:
-		g_value_set_boolean (value, priv->sleeping);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
+		manager_update_radio_enabled (self, rstate, new_enabled);
 	}
 }
 
@@ -4827,14 +4557,140 @@ dbus_connection_changed_cb (NMDBusManager *dbus_mgr,
                             gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
+	gboolean success = FALSE;
 
 	if (dbus_connection) {
 		/* Register property filter on new connection; there's no reason this
 		 * should fail except out-of-memory or program error; if it does fail
 		 * then there's no Manager property access control, which is bad.
 		 */
-		g_assert (dbus_connection_add_filter (dbus_connection, prop_filter, self, NULL));
+		success = dbus_connection_add_filter (dbus_connection, prop_filter, self, NULL);
+		g_assert (success);
 	}
+	NM_MANAGER_GET_PRIVATE (self)->prop_filter_added = success;
+}
+
+/**********************************************************************/
+
+static NMManager *singleton = NULL;
+
+NMManager *
+nm_manager_get (void)
+{
+	g_assert (singleton);
+	return singleton;
+}
+
+NMConnectionProvider *
+nm_connection_provider_get (void)
+{
+	g_assert (singleton);
+	g_assert (NM_MANAGER_GET_PRIVATE (singleton)->settings);
+	return NM_CONNECTION_PROVIDER (NM_MANAGER_GET_PRIVATE (singleton)->settings);
+}
+
+NMManager *
+nm_manager_new (NMSettings *settings,
+                const char *state_file,
+                gboolean initial_net_enabled,
+                gboolean initial_wifi_enabled,
+                gboolean initial_wwan_enabled,
+                gboolean initial_wimax_enabled,
+                GError **error)
+{
+	NMManagerPrivate *priv;
+	DBusGConnection *bus;
+	DBusConnection *dbus_connection;
+
+	g_assert (settings);
+
+	/* Can only be called once */
+	g_assert (singleton == NULL);
+	singleton = (NMManager *) g_object_new (NM_TYPE_MANAGER, NULL);
+	g_assert (singleton);
+
+	priv = NM_MANAGER_GET_PRIVATE (singleton);
+
+	bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
+	if (!bus) {
+		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INTERNAL,
+		                     "Failed to initialize D-Bus connection");
+		g_object_unref (singleton);
+		return NULL;
+	}
+
+	dbus_connection = dbus_g_connection_get_connection (bus);
+	g_assert (dbus_connection);
+
+	priv->policy = nm_policy_new (singleton, settings);
+	g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP4_DEVICE,
+	                  G_CALLBACK (policy_default_device_changed), singleton);
+	g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP6_DEVICE,
+	                  G_CALLBACK (policy_default_device_changed), singleton);
+	g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP4_DEVICE,
+	                  G_CALLBACK (policy_activating_device_changed), singleton);
+	g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP6_DEVICE,
+	                  G_CALLBACK (policy_activating_device_changed), singleton);
+
+	priv->connectivity = nm_connectivity_new ();
+	g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_STATE,
+	                  G_CALLBACK (connectivity_changed), singleton);
+
+	if (!dbus_connection_add_filter (dbus_connection, prop_filter, singleton, NULL)) {
+		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INTERNAL,
+		                     "Failed to register DBus connection filter");
+		g_object_unref (singleton);
+		return NULL;
+	}
+	priv->prop_filter_added = TRUE;
+
+	priv->settings = g_object_ref (settings);
+
+	priv->state_file = g_strdup (state_file);
+
+	priv->net_enabled = initial_net_enabled;
+
+	priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = initial_wifi_enabled;
+	priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = initial_wwan_enabled;
+	priv->radio_states[RFKILL_TYPE_WIMAX].user_enabled = initial_wimax_enabled;
+
+	g_signal_connect (priv->settings, "notify::" NM_SETTINGS_UNMANAGED_SPECS,
+	                  G_CALLBACK (system_unmanaged_devices_changed_cb), singleton);
+	g_signal_connect (priv->settings, "notify::" NM_SETTINGS_HOSTNAME,
+	                  G_CALLBACK (system_hostname_changed_cb), singleton);
+	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED,
+	                  G_CALLBACK (connection_added), singleton);
+	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED,
+	                  G_CALLBACK (connection_changed), singleton);
+	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED,
+	                  G_CALLBACK (connection_removed), singleton);
+	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED,
+	                  G_CALLBACK (connection_changed), singleton);
+
+	nm_dbus_manager_register_object (priv->dbus_mgr, NM_DBUS_PATH, singleton);
+
+	g_signal_connect (nm_platform_get (),
+	                  NM_PLATFORM_SIGNAL_LINK_CHANGED,
+	                  G_CALLBACK (platform_link_cb),
+	                  singleton);
+
+	priv->rfkill_mgr = nm_rfkill_manager_new ();
+	g_signal_connect (priv->rfkill_mgr,
+	                  "rfkill-changed",
+	                  G_CALLBACK (rfkill_manager_rfkill_changed_cb),
+	                  singleton);
+
+	/* Force kernel WiFi/WWAN rfkill state to follow NM saved WiFi/WWAN state
+	 * in case the BIOS doesn't save rfkill state, and to be consistent with user
+	 * changes to the WirelessEnabled/WWANEnabled properties which toggle kernel
+	 * rfkill.
+	 */
+	rfkill_change (priv->radio_states[RFKILL_TYPE_WLAN].desc, RFKILL_TYPE_WLAN, initial_wifi_enabled);
+	rfkill_change (priv->radio_states[RFKILL_TYPE_WWAN].desc, RFKILL_TYPE_WWAN, initial_wwan_enabled);
+
+	load_device_factories (singleton);
+
+	return singleton;
 }
 
 static void
@@ -4853,7 +4709,6 @@ nm_manager_init (NMManager *manager)
 	priv->radio_states[RFKILL_TYPE_WLAN].prop = NM_MANAGER_WIRELESS_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WLAN].hw_prop = NM_MANAGER_WIRELESS_HARDWARE_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WLAN].desc = "WiFi";
-	priv->radio_states[RFKILL_TYPE_WLAN].other_enabled_func = nm_manager_get_ipw_rfkill_state;
 	priv->radio_states[RFKILL_TYPE_WLAN].rtype = RFKILL_TYPE_WLAN;
 
 	priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = TRUE;
@@ -4861,7 +4716,6 @@ nm_manager_init (NMManager *manager)
 	priv->radio_states[RFKILL_TYPE_WWAN].prop = NM_MANAGER_WWAN_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WWAN].hw_prop = NM_MANAGER_WWAN_HARDWARE_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WWAN].desc = "WWAN";
-	priv->radio_states[RFKILL_TYPE_WWAN].daemon_enabled_func = nm_manager_get_modem_enabled_state;
 	priv->radio_states[RFKILL_TYPE_WWAN].rtype = RFKILL_TYPE_WWAN;
 
 	priv->radio_states[RFKILL_TYPE_WIMAX].user_enabled = TRUE;
@@ -4869,7 +4723,6 @@ nm_manager_init (NMManager *manager)
 	priv->radio_states[RFKILL_TYPE_WIMAX].prop = NM_MANAGER_WIMAX_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WIMAX].hw_prop = NM_MANAGER_WIMAX_HARDWARE_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WIMAX].desc = "WiMAX";
-	priv->radio_states[RFKILL_TYPE_WIMAX].other_enabled_func = NULL;
 	priv->radio_states[RFKILL_TYPE_WIMAX].rtype = RFKILL_TYPE_WIMAX;
 
 	for (i = 0; i < RFKILL_TYPE_MAX; i++)
@@ -4877,18 +4730,13 @@ nm_manager_init (NMManager *manager)
 
 	priv->sleeping = FALSE;
 	priv->state = NM_STATE_DISCONNECTED;
+	priv->startup = TRUE;
 
 	priv->dbus_mgr = nm_dbus_manager_get ();
-	priv->dbus_connection_changed_id = g_signal_connect (priv->dbus_mgr,
-	                                                     NM_DBUS_MANAGER_DBUS_CONNECTION_CHANGED,
-	                                                     G_CALLBACK (dbus_connection_changed_cb),
-	                                                     manager);
-
-	priv->modem_manager = nm_modem_manager_get ();
-	priv->modem_added_id = g_signal_connect (priv->modem_manager, "modem-added",
-	                                         G_CALLBACK (modem_added), manager);
-	priv->modem_removed_id = g_signal_connect (priv->modem_manager, "modem-removed",
-	                                           G_CALLBACK (modem_removed), manager);
+	g_signal_connect (priv->dbus_mgr,
+	                  NM_DBUS_MANAGER_DBUS_CONNECTION_CHANGED,
+	                  G_CALLBACK (dbus_connection_changed_cb),
+	                  manager);
 
 	priv->vpn_manager = nm_vpn_manager_get ();
 
@@ -4900,7 +4748,7 @@ nm_manager_init (NMManager *manager)
 	                                              "/",
 	                                              NM_AUTOIP_DBUS_IFACE);
 	if (priv->aipd_proxy) {
-		dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_STRING_STRING,
+		dbus_g_object_register_marshaller (g_cclosure_marshal_generic,
 		                                   G_TYPE_NONE,
 		                                   G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
 		                                   G_TYPE_INVALID);
@@ -4935,9 +4783,9 @@ nm_manager_init (NMManager *manager)
 	}
 
 	if (priv->fw_monitor) {
-		priv->fw_monitor_id = g_signal_connect (priv->fw_monitor, "changed",
-		                                        G_CALLBACK (firmware_dir_changed),
-		                                        manager);
+		g_signal_connect (priv->fw_monitor, "changed",
+		                  G_CALLBACK (firmware_dir_changed),
+		                  manager);
 		nm_log_info (LOGD_CORE, "monitoring kernel firmware directory '%s'.",
 		             KERNEL_FIRMWARE_DIR);
 	} else {
@@ -4945,13 +4793,214 @@ nm_manager_init (NMManager *manager)
 		             KERNEL_FIRMWARE_DIR);
 	}
 
-	load_device_factories (manager);
-
 	/* Update timestamps in active connections */
 	priv->timestamp_update_id = g_timeout_add_seconds (300, (GSourceFunc) periodic_update_active_connection_timestamps, manager);
 }
 
 static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	NMManager *self = NM_MANAGER (object);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	GSList *iter;
+	GPtrArray *array;
+	const char *path;
+
+	switch (prop_id) {
+	case PROP_VERSION:
+		g_value_set_string (value, VERSION);
+		break;
+	case PROP_STATE:
+		nm_manager_update_state (self);
+		g_value_set_uint (value, priv->state);
+		break;
+	case PROP_STARTUP:
+		g_value_set_boolean (value, priv->startup);
+		break;
+	case PROP_NETWORKING_ENABLED:
+		g_value_set_boolean (value, priv->net_enabled);
+		break;
+	case PROP_WIRELESS_ENABLED:
+		g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WLAN, TRUE));
+		break;
+	case PROP_WIRELESS_HARDWARE_ENABLED:
+		g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WLAN].hw_enabled);
+		break;
+	case PROP_WWAN_ENABLED:
+		g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WWAN, TRUE));
+		break;
+	case PROP_WWAN_HARDWARE_ENABLED:
+		g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WWAN].hw_enabled);
+		break;
+	case PROP_WIMAX_ENABLED:
+		g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WIMAX, TRUE));
+		break;
+	case PROP_WIMAX_HARDWARE_ENABLED:
+		g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WIMAX].hw_enabled);
+		break;
+	case PROP_ACTIVE_CONNECTIONS:
+		array = g_ptr_array_sized_new (3);
+		for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) {
+			path = nm_active_connection_get_path (NM_ACTIVE_CONNECTION (iter->data));
+			if (path)
+				g_ptr_array_add (array, g_strdup (path));
+		}
+		g_value_take_boxed (value, array);
+		break;
+	case PROP_CONNECTIVITY:
+		g_value_set_uint (value, nm_connectivity_get_state (priv->connectivity));
+		break;
+	case PROP_PRIMARY_CONNECTION:
+		path = priv->primary_connection ? nm_active_connection_get_path (priv->primary_connection) : NULL;
+		g_value_set_boxed (value, path ? path : "/");
+		break;
+	case PROP_ACTIVATING_CONNECTION:
+		path = priv->activating_connection ? nm_active_connection_get_path (priv->activating_connection) : NULL;
+		g_value_set_boxed (value, path ? path : "/");
+		break;
+	case PROP_HOSTNAME:
+		g_value_set_string (value, priv->hostname);
+		break;
+	case PROP_SLEEPING:
+		g_value_set_boolean (value, priv->sleeping);
+		break;
+	case PROP_DEVICES:
+		array = g_ptr_array_sized_new (5);
+		for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+			path = nm_device_get_path (NM_DEVICE (iter->data));
+			if (path)
+				g_ptr_array_add (array, g_strdup (path));
+		}
+		g_value_take_boxed (value, array);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+			  const GValue *value, GParamSpec *pspec)
+{
+	NMManager *self = NM_MANAGER (object);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+	switch (prop_id) {
+	case PROP_NETWORKING_ENABLED:
+		/* Construct only for now */
+		priv->net_enabled = g_value_get_boolean (value);
+		break;
+	case PROP_WIRELESS_ENABLED:
+		manager_radio_user_toggled (NM_MANAGER (object),
+		                            &priv->radio_states[RFKILL_TYPE_WLAN],
+		                            g_value_get_boolean (value));
+		break;
+	case PROP_WWAN_ENABLED:
+		manager_radio_user_toggled (NM_MANAGER (object),
+		                            &priv->radio_states[RFKILL_TYPE_WWAN],
+		                            g_value_get_boolean (value));
+		break;
+	case PROP_WIMAX_ENABLED:
+		manager_radio_user_toggled (NM_MANAGER (object),
+		                            &priv->radio_states[RFKILL_TYPE_WIMAX],
+		                            g_value_get_boolean (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+dispose (GObject *object)
+{
+	NMManager *manager = NM_MANAGER (object);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	DBusGConnection *bus;
+	DBusConnection *dbus_connection;
+	GSList *iter;
+
+	g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_unref);
+	priv->auth_chains = NULL;
+
+	nm_auth_changed_func_unregister (authority_changed_cb, manager);
+
+	/* Remove all devices */
+	while (priv->devices)
+		remove_device (manager, NM_DEVICE (priv->devices->data), TRUE);
+
+	if (priv->ac_cleanup_id) {
+		g_source_remove (priv->ac_cleanup_id);
+		priv->ac_cleanup_id = 0;
+	}
+
+	while (priv->active_connections)
+		active_connection_remove (manager, NM_ACTIVE_CONNECTION (priv->active_connections->data));
+	g_clear_pointer (&priv->active_connections, g_slist_free);
+	g_clear_object (&priv->primary_connection);
+	g_clear_object (&priv->activating_connection);
+
+	g_clear_object (&priv->connectivity);
+
+	g_free (priv->hostname);
+
+	if (priv->policy) {
+		g_signal_handlers_disconnect_by_func (priv->policy, policy_default_device_changed, manager);
+		g_signal_handlers_disconnect_by_func (priv->policy, policy_activating_device_changed, manager);
+		g_clear_object (&priv->policy);
+	}
+
+	g_clear_object (&priv->settings);
+	g_clear_object (&priv->vpn_manager);
+
+	/* Unregister property filter */
+	if (priv->dbus_mgr) {
+		bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
+		if (bus) {
+			dbus_connection = dbus_g_connection_get_connection (bus);
+			if (dbus_connection && priv->prop_filter_added) {
+				dbus_connection_remove_filter (dbus_connection, prop_filter, manager);
+				priv->prop_filter_added = FALSE;
+			}
+		}
+		g_signal_handlers_disconnect_by_func (priv->dbus_mgr, dbus_connection_changed_cb, manager);
+		priv->dbus_mgr = NULL;
+	}
+
+	g_clear_object (&priv->aipd_proxy);
+	g_clear_object (&priv->sleep_monitor);
+
+	if (priv->fw_monitor) {
+		g_signal_handlers_disconnect_by_func (priv->fw_monitor, firmware_dir_changed, manager);
+
+		if (priv->fw_changed_id) {
+			g_source_remove (priv->fw_changed_id);
+			priv->fw_changed_id = 0;
+		}
+
+		g_file_monitor_cancel (priv->fw_monitor);
+		g_clear_object (&priv->fw_monitor);
+	}
+
+	for (iter = priv->factories; iter; iter = iter->next) {
+		NMDeviceFactory *factory = iter->data;
+
+		g_signal_handlers_disconnect_matched (factory, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, manager);
+		g_object_unref (factory);
+	}
+	g_clear_pointer (&priv->factories, g_slist_free);
+
+	if (priv->timestamp_update_id) {
+		g_source_remove (priv->timestamp_update_id);
+		priv->timestamp_update_id = 0;
+	}
+
+	G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object);
+}
+
+static void
 nm_manager_class_init (NMManagerClass *manager_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
@@ -4981,6 +5030,14 @@ nm_manager_class_init (NMManagerClass *manager_class)
 		                    G_PARAM_READABLE));
 
 	g_object_class_install_property
+		(object_class, PROP_STARTUP,
+		 g_param_spec_boolean (NM_MANAGER_STARTUP,
+		                       "Startup",
+		                       "Is NetworkManager still starting up",
+		                       TRUE,
+		                       G_PARAM_READABLE));
+
+	g_object_class_install_property
 		(object_class, PROP_NETWORKING_ENABLED,
 		 g_param_spec_boolean (NM_MANAGER_NETWORKING_ENABLED,
 		                       "NetworkingEnabled",
@@ -5075,7 +5132,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 		                      "Hostname",
 		                      "Hostname",
 		                      NULL,
-		                      G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT));
+		                      G_PARAM_READABLE));
 
 	/* Sleeping is not exported over D-Bus */
 	g_object_class_install_property
@@ -5084,7 +5141,15 @@ nm_manager_class_init (NMManagerClass *manager_class)
 		                       "Sleeping",
 		                       "Sleeping",
 		                       FALSE,
-		                       G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT));
+		                       G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_DEVICES,
+		 g_param_spec_boxed (NM_MANAGER_DEVICES,
+		                     "Devices",
+		                     "Devices",
+		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
+		                     G_PARAM_READABLE));
 
 	/* signals */
 	signals[DEVICE_ADDED] =
@@ -5092,8 +5157,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
 		              G_STRUCT_OFFSET (NMManagerClass, device_added),
-		              NULL, NULL,
-		              g_cclosure_marshal_VOID__OBJECT,
+		              NULL, NULL, NULL,
 		              G_TYPE_NONE, 1, G_TYPE_OBJECT);
 
 	signals[DEVICE_REMOVED] =
@@ -5101,8 +5165,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
 		              G_STRUCT_OFFSET (NMManagerClass, device_removed),
-		              NULL, NULL,
-		              g_cclosure_marshal_VOID__OBJECT,
+		              NULL, NULL, NULL,
 		              G_TYPE_NONE, 1, G_TYPE_OBJECT);
 
 	signals[STATE_CHANGED] =
@@ -5110,48 +5173,40 @@ nm_manager_class_init (NMManagerClass *manager_class)
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
 		              G_STRUCT_OFFSET (NMManagerClass, state_changed),
-		              NULL, NULL,
-		              g_cclosure_marshal_VOID__UINT,
+		              NULL, NULL, NULL,
 		              G_TYPE_NONE, 1, G_TYPE_UINT);
 
-	signals[PROPERTIES_CHANGED] =
-		nm_properties_changed_signal_new (object_class,
-		                                  G_STRUCT_OFFSET (NMManagerClass, properties_changed));
-
 	signals[CHECK_PERMISSIONS] =
 		g_signal_new ("check-permissions",
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
-		              0, NULL, NULL,
-		              g_cclosure_marshal_VOID__VOID,
+		              0, NULL, NULL, NULL,
 		              G_TYPE_NONE, 0);
 
 	signals[USER_PERMISSIONS_CHANGED] =
 		g_signal_new ("user-permissions-changed",
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
-		              0, NULL, NULL,
-		              g_cclosure_marshal_VOID__VOID,
+		              0, NULL, NULL, NULL,
 		              G_TYPE_NONE, 0);
 
 	signals[ACTIVE_CONNECTION_ADDED] =
 		g_signal_new (NM_MANAGER_ACTIVE_CONNECTION_ADDED,
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
-		              0, NULL, NULL,
-		              g_cclosure_marshal_VOID__OBJECT,
+		              0, NULL, NULL, NULL,
 		              G_TYPE_NONE, 1, G_TYPE_OBJECT);
 
 	signals[ACTIVE_CONNECTION_REMOVED] =
 		g_signal_new (NM_MANAGER_ACTIVE_CONNECTION_REMOVED,
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
-		              0, NULL, NULL,
-		              g_cclosure_marshal_VOID__OBJECT,
+		              0, NULL, NULL, NULL,
 		              G_TYPE_NONE, 1, G_TYPE_OBJECT);
 
-	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (manager_class),
-	                                 &dbus_glib_nm_manager_object_info);
+	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
+	                                        G_TYPE_FROM_CLASS (manager_class),
+	                                        &dbus_glib_nm_manager_object_info);
 
 	dbus_g_error_domain_register (NM_MANAGER_ERROR, NULL, NM_TYPE_MANAGER_ERROR);
 	dbus_g_error_domain_register (NM_LOGGING_ERROR, "org.freedesktop.NetworkManager.Logging", NM_TYPE_LOGGING_ERROR);