about summary refs log tree commit diff
path: root/src/nm-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm-manager.c')
-rw-r--r--src/nm-manager.c2437
1 files changed, 1402 insertions, 1035 deletions
diff --git a/src/nm-manager.c b/src/nm-manager.c
index a1e4fb4f..ffc89ec0 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -26,20 +26,13 @@
 #include <errno.h>
 #include <string.h>
 #include <unistd.h>
-#include <dbus/dbus-glib-lowlevel.h>
-#include <dbus/dbus-glib.h>
-#include <gio/gio.h>
-#include <glib/gi18n.h>
 
-#include "gsystem-local-alloc.h"
-#include "nm-glib-compat.h"
+#include "nm-default.h"
 #include "nm-manager.h"
-#include "nm-logging.h"
-#include "nm-dbus-manager.h"
+#include "nm-bus-manager.h"
 #include "nm-vpn-manager.h"
 #include "nm-device.h"
 #include "nm-device-generic.h"
-#include "nm-dbus-glib-types.h"
 #include "nm-platform.h"
 #include "nm-rfkill-manager.h"
 #include "nm-dhcp-manager.h"
@@ -58,65 +51,14 @@
 #include "nm-activation-request.h"
 #include "nm-core-internal.h"
 #include "nm-config.h"
+#include "nm-audit-manager.h"
+#include "nm-dbus-compat.h"
+#include "NetworkManagerUtils.h"
 
-#define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd"
-#define NM_AUTOIP_DBUS_IFACE   "org.freedesktop.nm_avahi_autoipd"
-
-static gboolean impl_manager_get_devices (NMManager *manager,
-                                          GPtrArray **devices,
-                                          GError **err);
-
-static gboolean impl_manager_get_device_by_ip_iface (NMManager *self,
-                                                     const char *iface,
-                                                     char **out_object_path,
-                                                     GError **error);
-
-static void impl_manager_activate_connection (NMManager *manager,
-                                              const char *connection_path,
-                                              const char *device_path,
-                                              const char *specific_object_path,
-                                              DBusGMethodInvocation *context);
-
-static void impl_manager_add_and_activate_connection (NMManager *manager,
-                                                      GHashTable *settings,
-                                                      const char *device_path,
-                                                      const char *specific_object_path,
-                                                      DBusGMethodInvocation *context);
-
-static void impl_manager_deactivate_connection (NMManager *manager,
-                                                const char *connection_path,
-                                                DBusGMethodInvocation *context);
-
-static void impl_manager_sleep (NMManager *manager,
-                                gboolean do_sleep,
-                                DBusGMethodInvocation *context);
-
-static void impl_manager_enable (NMManager *manager,
-                                 gboolean enable,
-                                 DBusGMethodInvocation *context);
-
-static void impl_manager_get_permissions (NMManager *manager,
-                                          DBusGMethodInvocation *context);
-
-static gboolean impl_manager_get_state (NMManager *manager,
-                                        guint32 *state,
-                                        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,
-                                      char **domains);
-
-static void impl_manager_check_connectivity (NMManager *manager,
-                                             DBusGMethodInvocation *context);
-
-#include "nm-manager-glue.h"
+#include "nmdbus-manager.h"
+#include "nmdbus-device.h"
 
-static void add_device (NMManager *self, NMDevice *device, gboolean try_assume);
+static gboolean add_device (NMManager *self, NMDevice *device, GError **error);
 
 static NMActiveConnection *_new_active_connection (NMManager *self,
                                                    NMConnection *connection,
@@ -132,15 +74,17 @@ static void rfkill_change (const char *desc, RfKillType rtype, gboolean enabled)
 static gboolean find_master (NMManager *self,
                              NMConnection *connection,
                              NMDevice *device,
-                             NMConnection **out_master_connection,
+                             NMSettingsConnection **out_master_connection,
                              NMDevice **out_master_device,
                              NMActiveConnection **out_master_ac,
                              GError **error);
 
 static void nm_manager_update_state (NMManager *manager);
 
-#define SSD_POKE_INTERVAL 120
-#define ORIGDEV_TAG "originating-device"
+static void connection_changed (NMSettings *settings, NMConnection *connection,
+                                NMManager *manager);
+
+#define TAG_ACTIVE_CONNETION_ADD_AND_ACTIVATE "act-con-add-and-activate"
 
 typedef struct {
 	gboolean user_enabled;
@@ -170,7 +114,11 @@ typedef struct {
 
 	NMPolicy *policy;
 
-	NMDBusManager *dbus_mgr;
+	NMBusManager  *dbus_mgr;
+	struct {
+		GDBusConnection *connection;
+		guint            id;
+	} prop_filter;
 	NMRfkillManager *rfkill_mgr;
 
 	NMSettings *settings;
@@ -182,7 +130,6 @@ typedef struct {
 
 	NMVpnManager *vpn_manager;
 
-	DBusGProxy *aipd_proxy;
 	NMSleepMonitor *sleep_monitor;
 
 	GSList *auth_chains;
@@ -199,11 +146,13 @@ typedef struct {
 
 #define NM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MANAGER, NMManagerPrivate))
 
-G_DEFINE_TYPE (NMManager, nm_manager, G_TYPE_OBJECT)
+G_DEFINE_TYPE (NMManager, nm_manager, NM_TYPE_EXPORTED_OBJECT)
 
 enum {
 	DEVICE_ADDED,
+	INTERNAL_DEVICE_ADDED,
 	DEVICE_REMOVED,
+	INTERNAL_DEVICE_REMOVED,
 	STATE_CHANGED,
 	CHECK_PERMISSIONS,
 	USER_PERMISSIONS_CHANGED,
@@ -220,6 +169,7 @@ enum {
 	PROP_0,
 	PROP_VERSION,
 	PROP_STATE,
+	PROP_STATE_FILE,
 	PROP_STARTUP,
 	PROP_NETWORKING_ENABLED,
 	PROP_WIRELESS_ENABLED,
@@ -235,6 +185,8 @@ enum {
 	PROP_ACTIVATING_CONNECTION,
 	PROP_DEVICES,
 	PROP_METERED,
+	PROP_GLOBAL_DNS_CONFIGURATION,
+	PROP_ALL_DEVICES,
 
 	/* Not exported */
 	PROP_HOSTNAME,
@@ -243,6 +195,25 @@ enum {
 	LAST_PROP
 };
 
+NM_DEFINE_SINGLETON_INSTANCE (NMManager);
+
+/************************************************************************/
+
+#define _NMLOG_DOMAIN           LOGD_CORE
+#define _NMLOG_PREFIX_NAME      "manager"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        char __sbuf[32]; \
+        const void *const __self = (self); \
+        \
+        nm_log ((level), _NMLOG_DOMAIN, \
+                "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME, \
+                (__self && __self != singleton_instance \
+                    ? (__self ? nm_sprintf_buf (__sbuf, "[%p]", __self) : "[]") \
+                    : "") \
+                _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+    } G_STMT_END
 
 /************************************************************************/
 
@@ -258,13 +229,13 @@ static gboolean
 active_connection_remove (NMManager *self, NMActiveConnection *active)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	gboolean notify = !!nm_active_connection_get_path (active);
+	gboolean notify = nm_exported_object_is_exported (NM_EXPORTED_OBJECT (active));
 	GSList *found;
 
 	/* FIXME: switch to a GList for faster removal */
 	found = g_slist_find (priv->active_connections, active);
 	if (found) {
-		NMConnection *connection;
+		NMSettingsConnection *connection;
 
 		priv->active_connections = g_slist_remove (priv->active_connections, active);
 		g_signal_emit (self, signals[ACTIVE_CONNECTION_REMOVED], 0, active);
@@ -272,18 +243,18 @@ active_connection_remove (NMManager *self, NMActiveConnection *active)
 		g_signal_handlers_disconnect_by_func (active, active_connection_default_changed, self);
 
 		if (   nm_active_connection_get_assumed (active)
-		    && (connection = nm_active_connection_get_connection (active))
-		    && nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection)))
+		    && (connection = nm_active_connection_get_settings_connection (active))
+		    && nm_settings_connection_get_nm_generated_assumed (connection))
 			g_object_ref (connection);
 		else
 			connection = NULL;
 
-		g_object_unref (active);
+		nm_exported_object_clear_and_unexport (&active);
 
 		if (   connection
 		    && nm_settings_has_connection (priv->settings, connection)) {
 			nm_log_dbg (LOGD_DEVICE, "Assumed connection disconnected. Deleting generated connection '%s' (%s)",
-			            nm_connection_get_id (connection), nm_connection_get_uuid (connection));
+			            nm_settings_connection_get_id (connection), nm_settings_connection_get_uuid (connection));
 			nm_settings_connection_delete (NM_SETTINGS_CONNECTION (connection), NULL, NULL);
 			g_object_unref (connection);
 		}
@@ -380,7 +351,7 @@ active_connection_add (NMManager *self, NMActiveConnection *active)
 	g_signal_emit (self, signals[ACTIVE_CONNECTION_ADDED], 0, active);
 
 	/* Only notify D-Bus if the active connection is actually exported */
-	if (nm_active_connection_get_path (active))
+	if (nm_exported_object_is_exported (NM_EXPORTED_OBJECT (active)))
 		g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS);
 }
 
@@ -395,19 +366,30 @@ 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;
+	const char *uuid = NULL;
+	gboolean is_settings_connection;
+
+	is_settings_connection = NM_IS_SETTINGS_CONNECTION (connection);
+
+	if (!is_settings_connection)
+		uuid = nm_connection_get_uuid (connection);
 
-	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);
+		NMActiveConnection *ac = iter->data;
+		NMSettingsConnection *con;
 
-		if (   !strcmp (nm_connection_get_uuid (ac_connection), uuid)
-		    && (ac_state < NM_ACTIVE_CONNECTION_STATE_DEACTIVATED))
+		con = nm_active_connection_get_settings_connection (ac);
+
+		/* depending on whether we have a NMSettingsConnection or a NMConnection,
+		 * we lookup by UUID or by reference. */
+		if (is_settings_connection) {
+			if (con != (NMSettingsConnection *) connection)
+				continue;
+		} else {
+			if (strcmp (uuid, nm_connection_get_uuid (NM_CONNECTION (con))) != 0)
+				continue;
+		}
+		if (nm_active_connection_get_state (ac) < NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)
 			return ac;
 	}
 
@@ -426,12 +408,12 @@ 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;
+	NMSettingsConnection *connection;
 
 	for (iter = all_connections; iter; iter = iter->next) {
 		connection = iter->data;
 
-		if (!find_ac_for_connection (manager, connection))
+		if (!find_ac_for_connection (manager, NM_CONNECTION (connection)))
 			connections = g_slist_prepend (connections, connection);
 	}
 
@@ -451,7 +433,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 (g_strcmp0 (path, nm_active_connection_get_path (candidate)) == 0)
+		if (g_strcmp0 (path, nm_exported_object_get_path (NM_EXPORTED_OBJECT (candidate))) == 0)
 			return candidate;
 	}
 	return NULL;
@@ -467,6 +449,9 @@ _config_changed_cb (NMConfig *config, NMConfigData *config_data, NMConfigChangeF
 	              NM_CONNECTIVITY_INTERVAL, nm_config_data_get_connectivity_interval (config_data),
 	              NM_CONNECTIVITY_RESPONSE, nm_config_data_get_connectivity_response (config_data),
 	              NULL);
+
+	if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG))
+		g_object_notify (G_OBJECT (self), NM_MANAGER_GLOBAL_DNS_CONFIGURATION);
 }
 
 /************************************************************************/
@@ -479,7 +464,7 @@ nm_manager_get_device_by_path (NMManager *manager, const char *path)
 	g_return_val_if_fail (path != NULL, NULL);
 
 	for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) {
-		if (!strcmp (nm_device_get_path (NM_DEVICE (iter->data)), path))
+		if (!strcmp (nm_exported_object_get_path (NM_EXPORTED_OBJECT (iter->data)), path))
 			return NM_DEVICE (iter->data);
 	}
 	return NULL;
@@ -526,22 +511,62 @@ find_device_by_ip_iface (NMManager *self, const gchar *iface)
 	g_return_val_if_fail (iface != NULL, NULL);
 
 	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = g_slist_next (iter)) {
-		if (g_strcmp0 (nm_device_get_ip_iface (NM_DEVICE (iter->data)), iface) == 0)
-			return NM_DEVICE (iter->data);
+		NMDevice *candidate = iter->data;
+
+		if (   nm_device_is_real (candidate)
+		    && g_strcmp0 (nm_device_get_ip_iface (candidate), iface) == 0)
+			return candidate;
 	}
 	return NULL;
 }
 
+/**
+ * find_device_by_iface:
+ * @self: the #NMManager
+ * @iface: the device interface to find
+ * @connection: a connection to ensure the returned device is compatible with
+ * @slave: a slave connection to ensure a master is compatible with
+ *
+ * Finds a device by interface name, preferring realized devices.  If @slave
+ * is given, this function will only return master devices and will ensure
+ * @slave, when activated, can be a slave of the returned master device.  If
+ * @connection is given, this function will only consider devices that are
+ * compatible with @connection.
+ *
+ * Returns: the matching #NMDevice
+ */
 static NMDevice *
-find_device_by_iface (NMManager *self, const gchar *iface)
+find_device_by_iface (NMManager *self,
+                      const char *iface,
+                      NMConnection *connection,
+                      NMConnection *slave)
 {
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDevice *fallback = NULL;
 	GSList *iter;
 
-	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = g_slist_next (iter)) {
-		if (g_strcmp0 (nm_device_get_iface (NM_DEVICE (iter->data)), iface) == 0)
-			return NM_DEVICE (iter->data);
+	g_return_val_if_fail (iface != NULL, NULL);
+
+	for (iter = priv->devices; iter; iter = iter->next) {
+		NMDevice *candidate = iter->data;
+
+		if (strcmp (nm_device_get_iface (candidate), iface))
+			continue;
+		if (connection && !nm_device_check_connection_compatible (candidate, connection))
+			continue;
+		if (slave) {
+			if (!nm_device_is_master (candidate))
+				continue;
+			if (!nm_device_check_slave_connection_compatible (candidate, slave))
+				continue;
+		}
+
+		if (nm_device_is_real (candidate))
+			return candidate;
+		else if (!fallback)
+			fallback = candidate;
 	}
-	return NULL;
+	return fallback;
 }
 
 static gboolean
@@ -717,6 +742,7 @@ manager_device_state_changed (NMDevice *device,
                               gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 
 	switch (new_state) {
 	case NM_DEVICE_STATE_UNMANAGED:
@@ -729,6 +755,10 @@ manager_device_state_changed (NMDevice *device,
 	default:
 		break;
 	}
+
+	if (   new_state == NM_DEVICE_STATE_UNAVAILABLE
+	    || new_state == NM_DEVICE_STATE_DISCONNECTED)
+		nm_settings_device_added (priv->settings, device);
 }
 
 static void device_has_pending_action_changed (NMDevice *device,
@@ -825,7 +855,7 @@ remove_device (NMManager *manager,
 			if (quitting)
 				nm_device_set_unmanaged_quitting (device);
 			else
-				nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED);
+				nm_device_set_unmanaged_flags (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED);
 		} else if (quitting && nm_config_get_configure_and_quit (nm_config_get ())) {
 			nm_device_spawn_iface_helper (device);
 		}
@@ -836,12 +866,15 @@ remove_device (NMManager *manager,
 	nm_settings_device_removed (priv->settings, device, quitting);
 	priv->devices = g_slist_remove (priv->devices, device);
 
-	g_signal_emit (manager, signals[DEVICE_REMOVED], 0, device);
-	g_object_notify (G_OBJECT (manager), NM_MANAGER_DEVICES);
-	nm_device_removed (device);
+	if (nm_device_is_real (device)) {
+		g_signal_emit (manager, signals[DEVICE_REMOVED], 0, device);
+		g_object_notify (G_OBJECT (manager), NM_MANAGER_DEVICES);
+		nm_device_removed (device);
+	}
+	g_signal_emit (manager, signals[INTERNAL_DEVICE_REMOVED], 0, device);
+	g_object_notify (G_OBJECT (manager), NM_MANAGER_ALL_DEVICES);
 
-	nm_dbus_manager_unregister_object (priv->dbus_mgr, device);
-	g_object_unref (device);
+	nm_exported_object_clear_and_unexport (&device);
 
 	check_if_startup_complete (manager);
 }
@@ -852,53 +885,6 @@ device_removed_cb (NMDevice *device, gpointer user_data)
 	remove_device (NM_MANAGER (user_data), device, FALSE, TRUE);
 }
 
-static void
-device_link_initialized_cb (NMDevice *device, gpointer user_data)
-{
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (user_data);
-
-	nm_settings_device_added (priv->settings, device);
-}
-
-static void
-aipd_handle_event (DBusGProxy *proxy,
-                   const char *event,
-                   const char *iface,
-                   const char *address,
-                   gpointer user_data)
-{
-	NMManager *manager = NM_MANAGER (user_data);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	GSList *iter;
-	gboolean handled = FALSE;
-
-	if (!event || !iface) {
-		nm_log_warn (LOGD_AUTOIP4, "incomplete message received from avahi-autoipd");
-		return;
-	}
-
-	if (   (strcmp (event, "BIND") != 0)
-	    && (strcmp (event, "CONFLICT") != 0)
-	    && (strcmp (event, "UNBIND") != 0)
-	    && (strcmp (event, "STOP") != 0)) {
-		nm_log_warn (LOGD_AUTOIP4, "unknown event '%s' received from avahi-autoipd", event);
-		return;
-	}
-
-	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
-		NMDevice *candidate = NM_DEVICE (iter->data);
-
-		if (!strcmp (nm_device_get_iface (candidate), iface)) {
-			nm_device_handle_autoip4_event (candidate, event, address);
-			handled = TRUE;
-			break;
-		}
-	}
-
-	if (!handled)
-		nm_log_warn (LOGD_AUTOIP4, "(%s): unhandled avahi-autoipd event", iface);
-}
-
 NMState
 nm_manager_get_state (NMManager *manager)
 {
@@ -915,10 +901,12 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection)
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMDeviceFactory *factory;
 	const char *parent_name = NULL;
-	NMConnection *parent_connection;
+	NMSettingsConnection *parent_connection;
 	NMDevice *parent, *first_compatible = NULL;
 	GSList *iter;
 
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
 	factory = nm_device_factory_manager_find_factory_for_connection (connection);
 	if (!factory)
 		return NULL;
@@ -927,8 +915,8 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection)
 	if (!parent_name)
 		return NULL;
 
-	/* Try as an interface name */
-	parent = find_device_by_ip_iface (self, parent_name);
+	/* Try as an interface name of a parent device */
+	parent = find_device_by_iface (self, parent_name, NULL, NULL);
 	if (parent)
 		return parent;
 
@@ -938,7 +926,7 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection)
 		return parent;
 
 	/* Maybe a connection UUID */
-	parent_connection = (NMConnection *) nm_settings_get_connection_by_uuid (priv->settings, parent_name);
+	parent_connection = nm_settings_get_connection_by_uuid (priv->settings, parent_name);
 	if (!parent_connection)
 		return NULL;
 
@@ -948,11 +936,11 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection)
 	for (iter = priv->devices; iter; iter = iter->next) {
 		NMDevice *candidate = iter->data;
 
-		if (nm_device_get_connection (candidate) == parent_connection)
+		if (nm_device_get_settings_connection (candidate) == parent_connection)
 			return candidate;
 
 		if (   !first_compatible
-		    && nm_device_check_connection_compatible (candidate, parent_connection))
+		    && nm_device_check_connection_compatible (candidate, NM_CONNECTION (parent_connection)))
 			first_compatible = candidate;
 	}
 
@@ -997,9 +985,6 @@ get_virtual_iface_name (NMManager *self,
 
 	factory = nm_device_factory_manager_find_factory_for_connection (connection);
 	if (!factory) {
-		nm_log_warn (LOGD_DEVICE, "(%s) NetworkManager plugin for '%s' unavailable",
-		             nm_connection_get_id (connection),
-		             nm_connection_get_connection_type (connection));
 		g_set_error (error,
 		             NM_MANAGER_ERROR,
 		             NM_MANAGER_ERROR_FAILED,
@@ -1013,8 +998,6 @@ get_virtual_iface_name (NMManager *self,
 	                                                  connection,
 	                                                  parent ? nm_device_get_ip_iface (parent) : NULL);
 	if (!iface) {
-		nm_log_warn (LOGD_DEVICE, "(%s) failed to determine virtual interface name",
-		             nm_connection_get_id (connection));
 		g_set_error_literal (error,
 		                     NM_MANAGER_ERROR,
 		                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
@@ -1031,125 +1014,149 @@ get_virtual_iface_name (NMManager *self,
  * system_create_virtual_device:
  * @self: the #NMManager
  * @connection: the connection which might require a virtual device
- * @error: the error set when return value is NULL
  *
  * If @connection requires a virtual device and one does not yet exist for it,
  * creates that device.
  *
- * Returns: the #NMDevice if successfully created, %NULL if not
+ * Returns: A #NMDevice that was just realized; %NULL if none
  */
 static NMDevice *
-system_create_virtual_device (NMManager *self, NMConnection *connection, GError **error)
+system_create_virtual_device (NMManager *self, NMConnection *connection)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMDeviceFactory *factory;
-	GSList *iter;
-	char *iface = NULL;
+	GSList *connections, *iter;
+	gs_free char *iface = NULL;
 	NMDevice *device = NULL, *parent = NULL;
-	gboolean nm_owned = FALSE;
+	GError *error = NULL;
 
 	g_return_val_if_fail (NM_IS_MANAGER (self), NULL);
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-	iface = get_virtual_iface_name (self, connection, &parent, error);
-	if (!iface)
+	iface = get_virtual_iface_name (self, connection, &parent, &error);
+	if (!iface) {
+		nm_log_warn (LOGD_DEVICE, "(%s) can't get a name of a virtual device: %s",
+		             nm_connection_get_id (connection), error->message);
+		g_error_free (error);
 		return NULL;
+	}
 
-	/* Make sure we didn't create a device for this connection already */
+	/* See if there's a device that is already compatible with this connection */
 	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
 		NMDevice *candidate = iter->data;
 
 		if (   g_strcmp0 (nm_device_get_iface (candidate), iface) == 0
-		    || nm_device_check_connection_compatible (candidate, connection)) {
-			nm_log_dbg (LOGD_DEVICE, "(%s) already created virtual interface name %s",
-			            nm_connection_get_id (connection), iface);
-			g_set_error (error,
-			             NM_MANAGER_ERROR,
-			             NM_MANAGER_ERROR_FAILED,
-			             "interface name '%s' already created", iface);
-			goto out;
+		    && nm_device_check_connection_compatible (candidate, connection)) {
+
+			if (nm_device_is_real (candidate)) {
+				nm_log_dbg (LOGD_DEVICE, "(%s) already created virtual interface name %s",
+				            nm_connection_get_id (connection), iface);
+				return NULL;
+			}
+
+			device = candidate;
+			break;
 		}
 	}
 
-	factory = nm_device_factory_manager_find_factory_for_connection (connection);
-	if (!factory) {
-		nm_log_err (LOGD_DEVICE, "(%s:%s) NetworkManager plugin for '%s' unavailable",
-		            nm_connection_get_id (connection), iface,
-		            nm_connection_get_connection_type (connection));
-		g_set_error (error,
-		             NM_MANAGER_ERROR,
-		             NM_MANAGER_ERROR_FAILED,
-		             "NetworkManager plugin for '%s' unavailable",
-		             nm_connection_get_connection_type (connection));
-		goto out;
-	}
+	if (!device) {
+		/* No matching device found. Proceed creating a new one. */
 
-	nm_owned = !nm_platform_link_get_by_ifname (NM_PLATFORM_GET, iface);
+		factory = nm_device_factory_manager_find_factory_for_connection (connection);
+		if (!factory) {
+			nm_log_err (LOGD_DEVICE, "(%s:%s) NetworkManager plugin for '%s' unavailable",
+			            nm_connection_get_id (connection), iface,
+			            nm_connection_get_connection_type (connection));
+			return NULL;
+		}
 
-	device = nm_device_factory_create_virtual_device_for_connection (factory,
-	                                                                 connection,
-	                                                                 parent,
-	                                                                 error);
-	if (device) {
-		if (nm_owned)
-			nm_device_set_nm_owned (device);
+		device = nm_device_factory_create_device (factory, iface, NULL, connection, NULL, &error);
+		if (!device) {
+			nm_log_warn (LOGD_DEVICE, "(%s) factory can't create the device: %s",
+			             nm_connection_get_id (connection), error->message);
+			g_error_free (error);
+			return NULL;
+		}
 
-		/* If it was created by NM there's no connection to assume, but if it
-		 * previously existed there might be one.
-		 */
-		add_device (self, device, !nm_owned);
+		if (!add_device (self, device, &error)) {
+			nm_log_warn (LOGD_DEVICE, "(%s) can't register the device with manager: %s",
+			             nm_connection_get_id (connection), error->message);
+			g_error_free (error);
+			g_object_unref (device);
+			return NULL;
+		}
 
+		/* Add device takes a reference that NMManager still owns, so it's
+		 * safe to unref here and still return @device.
+		 */
 		g_object_unref (device);
 	}
 
-out:
-	g_free (iface);
+	/* Create backing resources if the device has any autoconnect connections */
+	connections = nm_settings_get_connections (priv->settings);
+	for (iter = connections; iter; iter = g_slist_next (iter)) {
+		NMConnection *candidate = iter->data;
+		NMSettingConnection *s_con;
+
+		if (!nm_device_check_connection_compatible (device, candidate))
+			continue;
+
+		s_con = nm_connection_get_setting_connection (candidate);
+		g_assert (s_con);
+		if (!nm_setting_connection_get_autoconnect (s_con))
+			continue;
+
+		/* Create any backing resources the device needs */
+		if (!nm_device_create_and_realize (device, connection, parent, &error)) {
+			nm_log_warn (LOGD_DEVICE, "(%s) couldn't create the device: %s",
+			             nm_connection_get_id (connection), error->message);
+			g_error_free (error);
+			remove_device (self, device, FALSE, TRUE);
+			return NULL;
+		}
+		break;
+	}
+
 	return device;
 }
 
 static void
-system_create_virtual_devices (NMManager *self)
+retry_connections_for_parent_device (NMManager *self, NMDevice *device)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GSList *iter, *connections;
+	GSList *connections, *iter;
 
-	nm_log_dbg (LOGD_CORE, "creating virtual devices...");
+	g_return_if_fail (device);
 
 	connections = nm_settings_get_connections (priv->settings);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
-		NMConnection *connection = iter->data;
+		NMConnection *candidate = iter->data;
+		NMDevice *parent;
 
-		/* We only create a virtual interface if the connection can autoconnect */
-		if (   nm_connection_is_virtual (connection)
-		    && nm_settings_connection_can_autoconnect (NM_SETTINGS_CONNECTION (connection)))
-			system_create_virtual_device (self, connection, NULL);
+		parent = find_parent_device_for_connection (self, candidate);
+		if (parent == device)
+			connection_changed (priv->settings, candidate, self);
 	}
-	g_slist_free (connections);
 }
 
 static void
-connection_added (NMSettings *settings,
-                  NMSettingsConnection *settings_connection,
-                  NMManager *manager)
+connection_changed (NMSettings *settings,
+                    NMConnection *connection,
+                    NMManager *manager)
 {
-	NMConnection *connection = NM_CONNECTION (settings_connection);
+	NMDevice *device;
 
-	if (nm_connection_is_virtual (connection)) {
-		NMSettingConnection *s_con = nm_connection_get_setting_connection (connection);
+	if (!nm_connection_is_virtual (connection))
+		return;
 
-		g_assert (s_con);
-		if (nm_setting_connection_get_autoconnect (s_con))
-			system_create_virtual_device (manager, connection, NULL);
-	}
-}
+	device = system_create_virtual_device (manager, connection);
+	if (!device)
+		return;
 
-static void
-connection_changed (NMSettings *settings,
-                    NMSettingsConnection *connection,
-                    NMManager *manager)
-{
-	/* FIXME: Some virtual devices may need to be updated in the future. */
+	/* Maybe the device that was created was needed by some other
+	 * connection's device (parent of a VLAN). Let the connections
+	 * can use the newly created device as a parent know. */
+	retry_connections_for_parent_device (manager, device);
 }
 
 static void
@@ -1173,17 +1180,8 @@ system_unmanaged_devices_changed_cb (NMSettings *settings,
 	const GSList *unmanaged_specs, *iter;
 
 	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 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);
-	}
+	for (iter = priv->devices; iter; iter = g_slist_next (iter))
+		nm_device_set_unmanaged_flags_by_device_spec (NM_DEVICE (iter->data), unmanaged_specs);
 }
 
 static void
@@ -1405,7 +1403,7 @@ nm_manager_rfkill_update (NMManager *self, RfKillType rtype)
 static void
 device_auth_done_cb (NMAuthChain *chain,
                      GError *auth_error,
-                     DBusGMethodInvocation *context,
+                     GDBusMethodInvocation *context,
                      gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
@@ -1415,6 +1413,7 @@ device_auth_done_cb (NMAuthChain *chain,
 	NMDevice *device;
 	const char *permission;
 	NMDeviceAuthRequestFunc callback;
+	NMAuthSubject *subject;
 
 	g_assert (context);
 
@@ -1428,6 +1427,7 @@ device_auth_done_cb (NMAuthChain *chain,
 	g_assert (device);
 
 	result = nm_auth_chain_get_result (chain, permission);
+	subject = nm_auth_chain_get_subject (chain);
 
 	if (auth_error) {
 		/* translate the auth error into a manager permission denied error */
@@ -1448,6 +1448,7 @@ device_auth_done_cb (NMAuthChain *chain,
 
 	callback (device,
 	          context,
+	          subject,
 	          error,
 	          nm_auth_chain_get_data (chain, "user-data"));
 
@@ -1457,7 +1458,7 @@ device_auth_done_cb (NMAuthChain *chain,
 
 static void
 device_auth_request_cb (NMDevice *device,
-                        DBusGMethodInvocation *context,
+                        GDBusMethodInvocation *context,
                         NMConnection *connection,
                         const char *permission,
                         gboolean allow_interaction,
@@ -1482,7 +1483,6 @@ device_auth_request_cb (NMDevice *device,
 
 	/* Ensure the subject has permissions for this connection */
 	if (connection && !nm_auth_is_subject_in_acl (connection,
-	                                              nm_session_monitor_get (),
 	                                              subject,
 	                                              &error_desc)) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
@@ -1509,9 +1509,10 @@ device_auth_request_cb (NMDevice *device,
 	nm_auth_chain_add_call (chain, permission, allow_interaction);
 
 done:
-	g_clear_object (&subject);
 	if (error)
-		callback (device, context, error, user_data);
+		callback (device, context, subject, error, user_data);
+
+	g_clear_object (&subject);
 	g_clear_error (&error);
 }
 
@@ -1533,12 +1534,13 @@ match_connection_filter (NMConnection *connection, gpointer user_data)
  * Returns: a #NMSettingsConnection to be assumed by the device, or %NULL if
  *   the device does not support assuming existing connections.
  */
-static NMConnection *
+static NMSettingsConnection *
 get_existing_connection (NMManager *manager, NMDevice *device, gboolean *out_generated)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
 	gs_free_slist GSList *connections = nm_manager_get_activatable_connections (manager);
-	NMConnection *connection = NULL, *matched;
+	NMConnection *connection = NULL;
+	NMSettingsConnection *matched;
 	NMSettingsConnection *added = NULL;
 	GError *error = NULL;
 	NMDevice *master = NULL;
@@ -1587,15 +1589,15 @@ get_existing_connection (NMManager *manager, NMDevice *device, gboolean *out_gen
 	 * 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);
+	matched = NM_SETTINGS_CONNECTION (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));
+		             nm_settings_connection_get_id (matched));
 		g_object_unref (connection);
 		return matched;
 	}
@@ -1621,11 +1623,11 @@ get_existing_connection (NMManager *manager, NMDevice *device, gboolean *out_gen
 	}
 	g_object_unref (connection);
 
-	return added ? NM_CONNECTION (added) : NULL;
+	return added ? added : NULL;
 }
 
 static gboolean
-assume_connection (NMManager *self, NMDevice *device, NMConnection *connection)
+assume_connection (NMManager *self, NMDevice *device, NMSettingsConnection *connection)
 {
 	NMActiveConnection *active, *master_ac;
 	NMAuthSubject *subject;
@@ -1643,12 +1645,12 @@ assume_connection (NMManager *self, NMDevice *device, NMConnection *connection)
 	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);
+	active = _new_active_connection (self, NM_CONNECTION (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),
+		             nm_connection_get_path (NM_CONNECTION (connection)),
 		             error ? error->code : -1,
 		             error && error->message ? error->message : "(unknown)");
 		g_error_free (error);
@@ -1657,11 +1659,11 @@ assume_connection (NMManager *self, NMDevice *device, NMConnection *connection)
 
 	/* 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)
+	if (find_master (self, NM_CONNECTION (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);
+	nm_exported_object_export (NM_EXPORTED_OBJECT (active));
 	active_connection_add (self, active);
 	nm_device_queue_activation (device, NM_ACT_REQUEST (active));
 	g_object_unref (active);
@@ -1670,16 +1672,27 @@ assume_connection (NMManager *self, NMDevice *device, NMConnection *connection)
 }
 
 static gboolean
-recheck_assume_connection (NMDevice *device, gpointer user_data)
+can_start_device (NMManager *self, NMDevice *device)
 {
-	NMManager *self = NM_MANAGER (user_data);
-	NMConnection *connection;
-	gboolean was_unmanaged = FALSE, success, generated;
+	return    nm_device_is_real (device)
+	       && !manager_sleeping (self)
+	       && !nm_device_get_unmanaged_flags (device, NM_UNMANAGED_ALL & ~NM_UNMANAGED_DEFAULT);
+}
+
+static gboolean
+recheck_assume_connection (NMManager *self, NMDevice *device)
+{
+	NMSettingsConnection *connection;
+	gboolean was_unmanaged = FALSE, success, generated = FALSE;
 	NMDeviceState state;
 
-	if (manager_sleeping (self))
+	g_return_val_if_fail (NM_IS_MANAGER (self), FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+
+	if (nm_device_get_is_nm_owned (device))
 		return FALSE;
-	if (nm_device_get_unmanaged_flag (device, NM_UNMANAGED_ALL & ~NM_UNMANAGED_DEFAULT))
+
+	if (!can_start_device (self, device))
 		return FALSE;
 
 	state = nm_device_get_state (device);
@@ -1708,7 +1721,7 @@ recheck_assume_connection (NMDevice *device, gpointer user_data)
 			                         NM_DEVICE_STATE_REASON_CONFIG_FAILED);
 
 			/* Return default-unmanaged devices to their original state */
-			if (nm_device_get_unmanaged_flag (device, NM_UNMANAGED_DEFAULT)) {
+			if (nm_device_get_unmanaged_flags (device, NM_UNMANAGED_DEFAULT)) {
 				nm_device_state_changed (device,
 				                         NM_DEVICE_STATE_UNMANAGED,
 				                         NM_DEVICE_STATE_REASON_CONFIG_FAILED);
@@ -1719,7 +1732,7 @@ recheck_assume_connection (NMDevice *device, gpointer user_data)
 			nm_log_dbg (LOGD_DEVICE, "(%s): connection assumption failed. Deleting generated connection",
 			            nm_device_get_iface (device));
 
-			nm_settings_connection_delete (NM_SETTINGS_CONNECTION (connection), NULL, NULL);
+			nm_settings_connection_delete (connection, NULL, NULL);
 		}
 	}
 
@@ -1727,6 +1740,12 @@ recheck_assume_connection (NMDevice *device, gpointer user_data)
 }
 
 static void
+recheck_assume_connection_cb (NMDevice *device, gpointer user_data)
+{
+	recheck_assume_connection (user_data, device);
+}
+
+static void
 device_ip_iface_changed (NMDevice *device,
                          GParamSpec *pspec,
                          NMManager *self)
@@ -1743,53 +1762,80 @@ device_ip_iface_changed (NMDevice *device,
 		NMDevice *candidate = NM_DEVICE (iter->data);
 
 		if (   candidate != device
-		    && g_strcmp0 (nm_device_get_iface (candidate), ip_iface) == 0) {
+		    && g_strcmp0 (nm_device_get_iface (candidate), ip_iface) == 0
+		    && nm_device_is_real (candidate)) {
 			remove_device (self, candidate, FALSE, FALSE);
 			break;
 		}
 	}
 }
 
-static gboolean
-notify_component_added (NMManager *self, GObject *component)
+static void
+device_iface_changed (NMDevice *device,
+                      GParamSpec *pspec,
+                      NMManager *self)
 {
-	GSList *iter;
+	/* Virtual connections may refer to the new device name as
+	 * parent device, retry to activate them.
+	 */
+	retry_connections_for_parent_device (self, device);
+}
 
-	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;
+
+static void
+device_realized (NMDevice *device,
+                 GParamSpec *pspec,
+                 NMManager *self)
+{
+	int ifindex;
+
+	/* Emit D-Bus signals */
+	g_signal_emit (self, signals[DEVICE_ADDED], 0, device);
+	g_object_notify (G_OBJECT (self), NM_MANAGER_DEVICES);
+
+	/* Loopback device never gets managed */
+	ifindex = nm_device_get_ifindex (device);
+	if (ifindex > 0 && nm_platform_link_get_type (NM_PLATFORM_GET, ifindex) == NM_LINK_TYPE_LOOPBACK)
+		return;
+
+	if (!can_start_device (self, device))
+		return;
+
+	if (   !recheck_assume_connection (self, device)
+	    && nm_device_get_managed (device)) {
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_UNAVAILABLE,
+		                         NM_DEVICE_STATE_REASON_NOW_MANAGED);
 	}
-	return FALSE;
 }
 
 /**
  * add_device:
  * @self: the #NMManager
  * @device: the #NMDevice to add
- * @try_assume: %TRUE if existing connection (if any) should be assumed
+ * @error: (out): the #GError
  *
  * 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, gboolean try_assume)
+static gboolean
+add_device (NMManager *self, NMDevice *device, GError **error)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	const char *iface, *driver, *type_desc;
+	const char *iface, *type_desc;
 	const GSList *unmanaged_specs;
-	gboolean user_unmanaged, sleeping;
-	gboolean enabled = FALSE;
 	RfKillType rtype;
 	GSList *iter, *remove = NULL;
-	gboolean connection_assumed = FALSE;
 	int ifindex;
+	const char *dbus_path;
 
 	/* No duplicates */
 	ifindex = nm_device_get_ifindex (device);
-	if (ifindex > 0 && nm_manager_get_device_by_ifindex (self, ifindex))
-		return;
-	if (find_device_by_iface (self, nm_device_get_iface (device)))
-		return;
+	if (ifindex > 0 && nm_manager_get_device_by_ifindex (self, ifindex)) {
+		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
+		             "A device with ifindex %d already exits", ifindex);
+		return FALSE;
+	}
 
 	/* Remove existing devices owned by the new device; eg remove ethernet
 	 * ports that are owned by a WWAN modem, since udev may announce them
@@ -1799,9 +1845,11 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume)
 	 * the child NMDevice entirely
 	 */
 	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);
+		NMDevice *candidate = iter->data;
+
+		iface = nm_device_get_ip_iface (candidate);
+		if (nm_device_is_real (candidate) && nm_device_owns_iface (device, iface))
+			remove = g_slist_prepend (remove, candidate);
 	}
 	for (iter = remove; iter; iter = iter->next)
 		remove_device (self, NM_DEVICE (iter->data), FALSE, FALSE);
@@ -1809,7 +1857,7 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume)
 
 	priv->devices = g_slist_append (priv->devices, g_object_ref (device));
 
-	g_signal_connect (device, "state-changed",
+	g_signal_connect (device, NM_DEVICE_STATE_CHANGED,
 	                  G_CALLBACK (manager_device_state_changed),
 	                  self);
 
@@ -1821,14 +1869,22 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume)
 	                  G_CALLBACK (device_removed_cb),
 	                  self);
 
-	g_signal_connect (device, NM_DEVICE_LINK_INITIALIZED,
-	                  G_CALLBACK (device_link_initialized_cb),
+	g_signal_connect (device, NM_DEVICE_RECHECK_ASSUME,
+	                  G_CALLBACK (recheck_assume_connection_cb),
 	                  self);
 
 	g_signal_connect (device, "notify::" NM_DEVICE_IP_IFACE,
 	                  G_CALLBACK (device_ip_iface_changed),
 	                  self);
 
+	g_signal_connect (device, "notify::" NM_DEVICE_IFACE,
+	                  G_CALLBACK (device_iface_changed),
+	                  self);
+
+	g_signal_connect (device, "notify::" NM_DEVICE_REAL,
+	                  G_CALLBACK (device_realized),
+	                  self);
+
 	if (priv->startup) {
 		g_signal_connect (device, "notify::" NM_DEVICE_HAS_PENDING_ACTION,
 		                  G_CALLBACK (device_has_pending_action_changed),
@@ -1842,60 +1898,44 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume)
 	rtype = nm_device_get_rfkill_type (device);
 	if (rtype != RFKILL_TYPE_UNKNOWN) {
 		nm_manager_rfkill_update (self, rtype);
-		enabled = radio_enabled_for_type (self, rtype, TRUE);
-		nm_device_set_enabled (device, enabled);
+		nm_device_set_enabled (device, radio_enabled_for_type (self, rtype, TRUE));
 	}
 
 	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);
-	if (!driver)
-		driver = "unknown";
-	nm_log_info (LOGD_HW, "(%s): new %s device (carrier: %s, driver: '%s', ifindex: %d)",
-	             iface, type_desc,
-	             nm_device_has_capability (device, NM_DEVICE_CAP_CARRIER_DETECT)
-	                ? (nm_device_has_carrier (device) ? "ON" : "OFF")
-	                : "UNKNOWN",
-	             driver, nm_device_get_ifindex (device));
 
 	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);
+	nm_device_set_unmanaged_flags_initial (device,
+	                                       NM_UNMANAGED_USER,
+	                                       nm_device_spec_match_list (device, unmanaged_specs));
+	nm_device_set_unmanaged_flags_initial (device,
+	                                       NM_UNMANAGED_INTERNAL,
+	                                       manager_sleeping (self));
 
-	sleeping = manager_sleeping (self);
-	nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_INTERNAL, sleeping);
+	dbus_path = nm_exported_object_export (NM_EXPORTED_OBJECT (device));
+	nm_log_info (LOGD_DEVICE, "(%s): new %s device (%s)", iface, type_desc, dbus_path);
 
-	nm_device_dbus_export (device);
 	nm_device_finish_init (device);
 
-	if (try_assume) {
-		connection_assumed = recheck_assume_connection (device, self);
-		g_signal_connect (device, NM_DEVICE_RECHECK_ASSUME,
-		                  G_CALLBACK (recheck_assume_connection), self);
-	}
+	nm_settings_device_added (priv->settings, device);
+	g_signal_emit (self, signals[INTERNAL_DEVICE_ADDED], 0, device);
+	g_object_notify (G_OBJECT (self), NM_MANAGER_ALL_DEVICES);
 
-	if (!connection_assumed && nm_device_get_managed (device)) {
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_UNAVAILABLE,
-		                         NM_DEVICE_STATE_REASON_NOW_MANAGED);
+	for (iter = priv->devices; iter; iter = iter->next) {
+		NMDevice *d = iter->data;
+
+		if (d != device)
+			nm_device_notify_new_device_added (d, device);
 	}
 
-	/* Try to generate a default connection. If this fails because the link is
-	 * not initialized, we will retry again in device_link_initialized_cb().
+	/* Virtual connections may refer to the new device as
+	 * parent device, retry to activate them.
 	 */
-	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);
-
-	notify_component_added (self, G_OBJECT (device));
+	retry_connections_for_parent_device (self, device);
 
-	/* New devices might be master interfaces for virtual interfaces; so we may
-	 * need to create new virtual interfaces now.
-	 */
-	system_create_virtual_devices (self);
+	return TRUE;
 }
 
 /*******************************************************************/
@@ -1905,7 +1945,16 @@ factory_device_added_cb (NMDeviceFactory *factory,
                          NMDevice *device,
                          gpointer user_data)
 {
-	add_device (NM_MANAGER (user_data), device, TRUE);
+	GError *error = NULL;
+
+	if (nm_device_realize_start (device, NULL, NULL, &error)) {
+		add_device (NM_MANAGER (user_data), device, NULL);
+		nm_device_realize_finish (device, NULL);
+	} else {
+		nm_log_warn (LOGD_DEVICE, "(%s): failed to realize device: %s",
+		             nm_device_get_iface (device), error->message);
+		g_error_free (error);
+	}
 }
 
 static gboolean
@@ -1913,7 +1962,15 @@ factory_component_added_cb (NMDeviceFactory *factory,
                             GObject *component,
                             gpointer user_data)
 {
-	return notify_component_added (NM_MANAGER (user_data), component);
+	GSList *iter;
+
+	g_return_val_if_fail (NM_IS_MANAGER (user_data), FALSE);
+
+	for (iter = NM_MANAGER_GET_PRIVATE (user_data)->devices; iter; iter = iter->next) {
+		if (nm_device_notify_component_added ((NMDevice *) iter->data, component))
+			return TRUE;
+	}
+	return FALSE;
 }
 
 static void
@@ -1936,23 +1993,51 @@ _register_device_factory (NMDeviceFactory *factory, gpointer user_data)
 static void
 platform_link_added (NMManager *self,
                      int ifindex,
-                     NMPlatformLink *plink)
+                     const NMPlatformLink *plink)
 {
 	NMDeviceFactory *factory;
 	NMDevice *device = NULL;
 	GError *error = NULL;
+	gboolean nm_plugin_missing = FALSE;
+	GSList *iter;
 
 	g_return_if_fail (ifindex > 0);
 
 	if (nm_manager_get_device_by_ifindex (self, ifindex))
 		return;
 
+	/* Let unrealized devices try to realize themselves with the link */
+	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) {
+		NMDevice *candidate = iter->data;
+		gboolean compatible = TRUE;
+
+		if (strcmp (nm_device_get_iface (candidate), plink->name))
+			continue;
+
+		if (nm_device_is_real (candidate)) {
+			/* Ignore the link added event since there's already a realized
+			 * device with the link's name.
+			 */
+			return;
+		} else if (nm_device_realize_start (candidate, plink, &compatible, &error)) {
+			/* Success */
+			nm_device_realize_finish (candidate, plink);
+			return;
+		}
+
+		nm_log_dbg (LOGD_DEVICE, "(%s): failed to realize from plink: '%s'",
+		            plink->name, error->message);
+		g_clear_error (&error);
+
+		/* Try next unrealized device */
+	}
+
 	/* Try registered device factories */
 	factory = nm_device_factory_manager_find_factory_for_link_type (plink->type);
 	if (factory) {
 		gboolean ignore = FALSE;
 
-		device = nm_device_factory_new_link (factory, plink, &ignore, &error);
+		device = nm_device_factory_create_device (factory, plink->name, plink, NULL, &ignore, &error);
 		if (!device) {
 			if (!ignore) {
 				nm_log_warn (LOGD_HW, "%s: factory failed to create device: %s",
@@ -1970,9 +2055,9 @@ platform_link_added (NMManager *self,
 		case NM_LINK_TYPE_OLPC_MESH:
 		case NM_LINK_TYPE_TEAM:
 		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, nm_link_type_to_string (plink->type));
+			nm_plugin_missing = TRUE;
 			/* fall through */
 		default:
 			device = nm_device_generic_new (plink);
@@ -1981,7 +2066,16 @@ platform_link_added (NMManager *self,
 	}
 
 	if (device) {
-		add_device (self, device, plink->type != NM_LINK_TYPE_LOOPBACK);
+		if (nm_plugin_missing)
+			nm_device_set_nm_plugin_missing (device, TRUE);
+		if (nm_device_realize_start (device, plink, NULL, &error)) {
+			add_device (self, device, NULL);
+			nm_device_realize_finish (device, plink);
+		} else {
+			nm_log_warn (LOGD_DEVICE, "%s: failed to realize device: %s",
+			             plink->name, error->message);
+			g_clear_error (&error);
+		}
 		g_object_unref (device);
 	}
 }
@@ -1995,25 +2089,42 @@ static gboolean
 _platform_link_cb_idle (PlatformLinkCbData *data)
 {
 	NMManager *self = data->self;
+	const NMPlatformLink *l;
 
-	if (self) {
-		const NMPlatformLink *l;
+	if (!self)
+		goto out;
 
-		l = nm_platform_link_get (NM_PLATFORM_GET, data->ifindex);
-		if (l) {
-			NMPlatformLink pllink;
+	g_object_remove_weak_pointer (G_OBJECT (self), (gpointer *) &data->self);
 
-			pllink = *l; /* make a copy of the link instance */
-			platform_link_added (self, data->ifindex, &pllink);
-		} else {
-			NMDevice *device;
+	l = nm_platform_link_get (NM_PLATFORM_GET, data->ifindex);
+	if (l) {
+		NMPlatformLink pllink;
 
-			device = nm_manager_get_device_by_ifindex (self, data->ifindex);
-			if (device)
+		pllink = *l; /* make a copy of the link instance */
+		platform_link_added (self, data->ifindex, &pllink);
+	} else {
+		NMDevice *device;
+		GError *error = NULL;
+
+		device = nm_manager_get_device_by_ifindex (self, data->ifindex);
+		if (device) {
+			if (nm_device_is_software (device)) {
+				/* Our software devices stick around until their connection is removed */
+				if (!nm_device_unrealize (device, FALSE, &error)) {
+					nm_log_warn (LOGD_DEVICE, "(%s): failed to unrealize: %s",
+					             nm_device_get_iface (device),
+					             error->message);
+					g_clear_error (&error);
+					remove_device (self, device, FALSE, TRUE);
+				}
+			} else {
+				/* Hardware and external devices always get removed when their kernel link is gone */
 				remove_device (self, device, FALSE, TRUE);
+			}
 		}
-		g_object_remove_weak_pointer (G_OBJECT (self), (gpointer *) &data->self);
 	}
+
+out:
 	g_slice_free (PlatformLinkCbData, data);
 	return G_SOURCE_REMOVE;
 }
@@ -2024,7 +2135,6 @@ platform_link_cb (NMPlatform *platform,
                   int ifindex,
                   NMPlatformLink *plink,
                   NMPlatformSignalChangeType change_type,
-                  NMPlatformReason reason,
                   gpointer user_data)
 {
 	PlatformLinkCbData *data;
@@ -2113,44 +2223,67 @@ nm_manager_get_best_device_for_connection (NMManager *self,
 	return NULL;
 }
 
-static gboolean
-impl_manager_get_devices (NMManager *manager, GPtrArray **devices, GError **err)
+static void
+_get_devices (NMManager *self,
+              GDBusMethodInvocation *context,
+              gboolean all_devices)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	gs_free const char **paths = NULL;
+	guint i;
 	GSList *iter;
 
-	*devices = g_ptr_array_sized_new (g_slist_length (priv->devices));
+	paths = g_new (const char *, g_slist_length (priv->devices) + 1);
 
-	for (iter = priv->devices; iter; iter = iter->next)
-		g_ptr_array_add (*devices, g_strdup (nm_device_get_path (NM_DEVICE (iter->data))));
+	for (i = 0, iter = priv->devices; iter; iter = iter->next) {
+		const char *path;
 
-	return TRUE;
+		path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (iter->data));
+		if (   path
+		    && (all_devices || nm_device_is_real (iter->data)))
+			paths[i++] = path;
+	}
+	paths[i++] = NULL;
+
+	g_dbus_method_invocation_return_value (context,
+	                                       g_variant_new ("(^ao)", (char **) paths));
 }
 
-static gboolean
+static void
+impl_manager_get_devices (NMManager *self,
+                          GDBusMethodInvocation *context)
+{
+	_get_devices (self, context, FALSE);
+}
+
+static void
+impl_manager_get_all_devices (NMManager *self,
+                              GDBusMethodInvocation *context)
+{
+	_get_devices (self, context, TRUE);
+}
+
+static void
 impl_manager_get_device_by_ip_iface (NMManager *self,
-                                     const char *iface,
-                                     char **out_object_path,
-                                     GError **error)
+                                     GDBusMethodInvocation *context,
+                                     const char *iface)
 {
 	NMDevice *device;
 	const char *path = NULL;
 
 	device = find_device_by_ip_iface (self, iface);
-	if (device) {
-		path = nm_device_get_path (device);
-		if (path)
-			*out_object_path = g_strdup (path);
-	}
+	if (device)
+		path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device));
 
 	if (path == NULL) {
-		g_set_error_literal (error,
-		                     NM_MANAGER_ERROR,
-		                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-		                     "No device found for the requested iface.");
+		g_dbus_method_invocation_return_error (context,
+		                                       NM_MANAGER_ERROR,
+		                                       NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+		                                       "No device found for the requested iface.");
+	} else {
+		g_dbus_method_invocation_return_value (context,
+		                                       g_variant_new ("(o)", path));
 	}
-
-	return path ? TRUE : FALSE;
 }
 
 static gboolean
@@ -2213,7 +2346,7 @@ static gboolean
 find_master (NMManager *self,
              NMConnection *connection,
              NMDevice *device,
-             NMConnection **out_master_connection,
+             NMSettingsConnection **out_master_connection,
              NMDevice **out_master_device,
              NMActiveConnection **out_master_ac,
              GError **error)
@@ -2222,8 +2355,8 @@ find_master (NMManager *self,
 	NMSettingConnection *s_con;
 	const char *master;
 	NMDevice *master_device = NULL;
-	NMConnection *master_connection = NULL;
-	GSList *iter, *connections = NULL;
+	NMSettingsConnection *master_connection = NULL;
+	GSList *iter;
 
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
@@ -2233,7 +2366,7 @@ find_master (NMManager *self,
 		return TRUE;  /* success, but no master */
 
 	/* Try as an interface name first */
-	master_device = find_device_by_ip_iface (self, master);
+	master_device = find_device_by_iface (self, master, NULL, connection);
 	if (master_device) {
 		if (master_device == device) {
 			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED,
@@ -2241,8 +2374,8 @@ find_master (NMManager *self,
 			return FALSE;
 		}
 
-		master_connection = nm_device_get_connection (master_device);
-		if (master_connection && !is_compatible_with_slave (master_connection, connection)) {
+		master_connection = nm_device_get_settings_connection (master_device);
+		if (master_connection && !is_compatible_with_slave (NM_CONNECTION (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),
@@ -2251,7 +2384,7 @@ find_master (NMManager *self,
 		}
 	} else {
 		/* Try master as a connection UUID */
-		master_connection = (NMConnection *) nm_settings_get_connection_by_uuid (priv->settings, master);
+		master_connection = nm_settings_get_connection_by_uuid (priv->settings, master);
 		if (master_connection) {
 			/* Check if the master connection is activated on some device already */
 			for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
@@ -2260,28 +2393,11 @@ find_master (NMManager *self,
 				if (candidate == device)
 					continue;
 
-				if (nm_device_get_connection (candidate) == master_connection) {
+				if (nm_device_get_settings_connection (candidate) == master_connection) {
 					master_device = candidate;
 					break;
 				}
 			}
-		} else {
-			/* Might be a virtual interface that hasn't been created yet, so
-			 * look through the interface names of connections that require
-			 * virtual interfaces and see if one of their virtual interface
-			 * names matches the master.
-			 */
-			connections = nm_manager_get_activatable_connections (self);
-			for (iter = connections; iter && !master_connection; iter = g_slist_next (iter)) {
-				NMConnection *candidate = iter->data;
-				char *vname;
-
-				vname = get_virtual_iface_name (self, candidate, NULL, NULL);
-				if (g_strcmp0 (master, vname) == 0 && is_compatible_with_slave (candidate, connection))
-					master_connection = candidate;
-				g_free (vname);
-			}
-			g_slist_free (connections);
 		}
 	}
 
@@ -2290,7 +2406,7 @@ find_master (NMManager *self,
 	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);
+		*out_master_ac = find_ac_for_connection (self, NM_CONNECTION (master_connection));
 
 	if (master_device || master_connection)
 		return TRUE;
@@ -2334,7 +2450,7 @@ ensure_master_active_connection (NMManager *self,
                                  NMAuthSubject *subject,
                                  NMConnection *connection,
                                  NMDevice *device,
-                                 NMConnection *master_connection,
+                                 NMSettingsConnection *master_connection,
                                  NMDevice *master_device,
                                  GError **error)
 {
@@ -2350,13 +2466,13 @@ 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);
+		NMSettingsConnection *device_connection = nm_device_get_settings_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().
 		 */
 		g_assert (!master_connection || master_connection == device_connection);
-		if (device_connection && !is_compatible_with_slave (device_connection, connection)) {
+		if (device_connection && !is_compatible_with_slave (NM_CONNECTION (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),
@@ -2375,7 +2491,7 @@ ensure_master_active_connection (NMManager *self,
 		/* If the device is disconnected, find a compatible connection and
 		 * activate it on the device.
 		 */
-		if (master_state == NM_DEVICE_STATE_DISCONNECTED) {
+		if (master_state == NM_DEVICE_STATE_DISCONNECTED || !nm_device_is_real (master_device)) {
 			GSList *connections;
 
 			g_assert (master_connection == NULL);
@@ -2383,15 +2499,15 @@ ensure_master_active_connection (NMManager *self,
 			/* Find a compatible connection and activate this device using it */
 			connections = nm_manager_get_activatable_connections (self);
 			for (iter = connections; iter; iter = g_slist_next (iter)) {
-				NMConnection *candidate = NM_CONNECTION (iter->data);
+				NMSettingsConnection *candidate = NM_SETTINGS_CONNECTION (iter->data);
 
 				/* Ensure eg bond/team slave and the candidate master is a
 				 * bond/team master
 				 */
-				if (!is_compatible_with_slave (candidate, connection))
+				if (!is_compatible_with_slave (NM_CONNECTION (candidate), connection))
 					continue;
 
-				if (nm_device_check_connection_available (master_device, candidate, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, NULL)) {
+				if (nm_device_check_connection_available (master_device, NM_CONNECTION (candidate), NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, NULL)) {
 					master_ac = nm_manager_activate_connection (self,
 					                                            candidate,
 					                                            NULL,
@@ -2432,13 +2548,15 @@ ensure_master_active_connection (NMManager *self,
 				continue;
 			}
 
-			if (!nm_device_check_connection_available (candidate, master_connection, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, NULL))
+			if (!nm_device_check_connection_available (candidate, NM_CONNECTION (master_connection), NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, NULL))
 				continue;
 
 			found_device = TRUE;
-			master_state = nm_device_get_state (candidate);
-			if (master_state != NM_DEVICE_STATE_DISCONNECTED)
-				continue;
+			if (!nm_device_is_software (candidate)) {
+				master_state = nm_device_get_state (candidate);
+				if (nm_device_is_real (candidate) && master_state != NM_DEVICE_STATE_DISCONNECTED)
+					continue;
+			}
 
 			master_ac = nm_manager_activate_connection (self,
 			                                            master_connection,
@@ -2454,7 +2572,7 @@ ensure_master_active_connection (NMManager *self,
 		/* Device described by master_connection may be a virtual one that's
 		 * not created yet.
 		 */
-		if (!found_device && nm_connection_is_virtual (master_connection)) {
+		if (!found_device && nm_connection_is_virtual (NM_CONNECTION (master_connection))) {
 			master_ac = nm_manager_activate_connection (self,
 			                                            master_connection,
 			                                            NULL,
@@ -2470,7 +2588,7 @@ ensure_master_active_connection (NMManager *self,
 		             NM_MANAGER_ERROR,
 		             NM_MANAGER_ERROR_UNKNOWN_DEVICE,
 		             "No compatible disconnected device found for master connection %s.",
-		             nm_connection_get_uuid (master_connection));
+		             nm_settings_connection_get_uuid (master_connection));
 	} else
 		g_assert_not_reached ();
 
@@ -2480,17 +2598,17 @@ ensure_master_active_connection (NMManager *self,
 /**
  * find_slaves:
  * @manager: #NMManager object
- * @connection: the master #NMConnection to find slave connections for
+ * @connection: the master #NMSettingsConnection to find slave connections for
  * @device: the master #NMDevice for the @connection
  *
- * Given an #NMConnection, attempts to find its slaves. If @connection is not
+ * Given an #NMSettingsConnection, attempts to find its slaves. If @connection is not
  * master, or has not any slaves, this will return %NULL.
  *
  * Returns: list of slave connections for given master @connection, or %NULL
  **/
 static GSList *
 find_slaves (NMManager *manager,
-             NMConnection *connection,
+             NMSettingsConnection *connection,
              NMDevice *device)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
@@ -2499,7 +2617,7 @@ find_slaves (NMManager *manager,
 	NMSettingConnection *s_con;
 	const char *master;
 
-	s_con = nm_connection_get_setting_connection (connection);
+	s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection));
 	g_assert (s_con);
 	master = nm_setting_connection_get_master (s_con);
 
@@ -2512,7 +2630,7 @@ find_slaves (NMManager *manager,
 	 */
 	all_connections = nm_settings_get_connections (priv->settings);
 	for (iter = all_connections; iter; iter = iter->next) {
-		NMConnection *master_connection = NULL;
+		NMSettingsConnection *master_connection = NULL;
 		NMDevice *master_device = NULL;
 		NMConnection *candidate = iter->data;
 
@@ -2558,34 +2676,34 @@ out:
 
 static gboolean
 autoconnect_slaves (NMManager *manager,
-                    NMConnection *master_connection,
+                    NMSettingsConnection *master_connection,
                     NMDevice *master_device,
                     NMAuthSubject *subject)
 {
 	GError *local_err = NULL;
 	gboolean ret = FALSE;
 
-	if (should_connect_slaves (master_connection, master_device)) {
+	if (should_connect_slaves (NM_CONNECTION (master_connection), master_device)) {
 		GSList *slaves, *iter;
 
 		iter = slaves = find_slaves (manager, master_connection, master_device);
 		ret = slaves != NULL;
 
 		while (iter) {
-			NMConnection *slave_connection = iter->data;
+			NMSettingsConnection *slave_connection = iter->data;
 
 			iter = iter->next;
 			nm_log_dbg (LOGD_CORE, "will activate slave connection '%s' (%s) as a dependency for master '%s' (%s)",
-			            nm_connection_get_id (slave_connection),
-			            nm_connection_get_uuid (slave_connection),
-			            nm_connection_get_id (master_connection),
-			            nm_connection_get_uuid (master_connection));
+			            nm_settings_connection_get_id (slave_connection),
+			            nm_settings_connection_get_uuid (slave_connection),
+			            nm_settings_connection_get_id (master_connection),
+			            nm_settings_connection_get_uuid (master_connection));
 
 			/* Schedule slave activation */
 			nm_manager_activate_connection (manager,
 			                                slave_connection,
 			                                NULL,
-			                                nm_manager_get_best_device_for_connection (manager, slave_connection, FALSE),
+			                                nm_manager_get_best_device_for_connection (manager, NM_CONNECTION (slave_connection), FALSE),
 			                                subject,
 			                                &local_err);
 			if (local_err) {
@@ -2605,13 +2723,14 @@ _internal_activate_vpn (NMManager *self, NMActiveConnection *active, GError **er
 
 	g_assert (NM_IS_VPN_CONNECTION (active));
 
+	nm_exported_object_export (NM_EXPORTED_OBJECT (active));
 	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);
+	if (success)
 		g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS);
-	}
+	else
+		nm_exported_object_unexport (NM_EXPORTED_OBJECT (active));
 	return success;
 }
 
@@ -2619,10 +2738,13 @@ static gboolean
 _internal_activate_device (NMManager *self, NMActiveConnection *active, GError **error)
 {
 	NMDevice *device, *existing, *master_device = NULL;
-	NMConnection *connection;
-	NMConnection *master_connection = NULL;
+	NMConnection *applied;
+	NMSettingsConnection *connection;
+	NMSettingsConnection *master_connection = NULL;
+	NMConnection *existing_connection = NULL;
 	NMActiveConnection *master_ac = NULL;
-	GError *local_err = NULL;
+	NMAuthSubject *subject;
+	char *error_desc = NULL;
 
 	g_return_val_if_fail (NM_IS_MANAGER (self), FALSE);
 	g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (active), FALSE);
@@ -2630,100 +2752,55 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 
 	g_assert (NM_IS_VPN_CONNECTION (active) == FALSE);
 
-	connection = nm_active_connection_get_connection (active);
+	connection = nm_active_connection_get_settings_connection (active);
 	g_assert (connection);
 
-	device = nm_active_connection_get_device (active);
-	if (!device) {
-		if (!nm_connection_is_virtual (connection)) {
-			NMSettingConnection *s_con = nm_connection_get_setting_connection (connection);
+	applied = nm_active_connection_get_applied_connection (active);
 
-			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;
-		}
-
-		device = system_create_virtual_device (self, connection, &local_err);
-		if (!device) {
-			g_set_error (error,
-			             NM_MANAGER_ERROR,
-			             NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-			             "Failed to create virtual interface: %s",
-			             local_err ? local_err->message : "(unknown)");
-			g_clear_error (&local_err);
-			return FALSE;
-		}
-
-		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;
-		}
+	device = nm_active_connection_get_device (active);
+	g_return_val_if_fail (device != NULL, FALSE);
 
-		/* 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 (   nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)
-			&& (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) {
-			nm_device_state_changed (device,
-			                         NM_DEVICE_STATE_DISCONNECTED,
-			                         NM_DEVICE_STATE_REASON_NONE);
-		}
-	} else {
-		NMConnection *existing_connection = NULL;
-		NMAuthSubject *subject;
-		char *error_desc = NULL;
-
-		/* 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_is_subject_in_acl (existing_connection,
-			                            nm_session_monitor_get (),
-			                            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);
-			g_free (error_desc);
-			return FALSE;
-		}
+	/* 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_applied_connection (device);
+	subject = nm_active_connection_get_subject (active);
+	if (existing_connection &&
+	    !nm_auth_is_subject_in_acl (existing_connection,
+	                                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);
+		g_free (error_desc);
+		return FALSE;
 	}
 
 	/* Final connection must be available on device */
-	if (!nm_device_check_connection_available (device, connection, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, NULL)) {
+	if (!nm_device_check_connection_available (device, applied, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, NULL)) {
 		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));
+		             nm_settings_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_CONNECTION_NOT_AVAILABLE,
-		             "%s does not allow automatic connections at this time",
-		             nm_device_get_iface (device));
-		return FALSE;
+	/* Create any backing resources the device needs */
+	if (!nm_device_is_real (device)) {
+		NMDevice *parent;
+
+		parent = find_parent_device_for_connection (self, (NMConnection *) connection);
+		if (!nm_device_create_and_realize (device, (NMConnection *) connection, parent, error)) {
+			g_prefix_error (error, "%s failed to create resources: ", 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,
+	if (!find_master (self, applied, device,
 	                  &master_connection, &master_device, &master_ac,
 	                  error))
 		return FALSE;
@@ -2734,17 +2811,17 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 	if (master_connection || master_device) {
 		if (master_connection) {
 			nm_log_dbg (LOGD_CORE, "Activation of '%s' requires master connection '%s'",
-			            nm_connection_get_id (connection),
-			            nm_connection_get_id (master_connection));
+			            nm_settings_connection_get_id (connection),
+			            nm_settings_connection_get_id (master_connection));
 		}
 		if (master_device) {
 			nm_log_dbg (LOGD_CORE, "Activation of '%s' requires master device '%s'",
-			            nm_connection_get_id (connection),
+			            nm_settings_connection_get_id (connection),
 			            nm_device_get_ip_iface (master_device));
 		}
 
 		/* Ensure eg bond slave and the candidate master is a bond master */
-		if (master_connection && !is_compatible_with_slave (master_connection, connection)) {
+		if (master_connection && !is_compatible_with_slave (NM_CONNECTION (master_connection), applied)) {
 			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED,
 			                     "The master connection was not compatible");
 			return FALSE;
@@ -2753,7 +2830,7 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 		if (!master_ac) {
 			master_ac = ensure_master_active_connection (self,
 			                                             nm_active_connection_get_subject (active),
-			                                             connection,
+			                                             applied,
 			                                             device,
 			                                             master_connection,
 			                                             master_device,
@@ -2766,21 +2843,35 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 		}
 
 		nm_active_connection_set_master (active, master_ac);
-		nm_log_dbg (LOGD_CORE, "Activation of '%s' depends on active connection %p",
-		            nm_connection_get_id (connection),
-		            master_ac);
+		nm_log_dbg (LOGD_CORE, "Activation of '%s' depends on active connection %p %s",
+		            nm_settings_connection_get_id (connection),
+		            master_ac,
+		            str_if_set (nm_exported_object_get_path (NM_EXPORTED_OBJECT  (master_ac)), ""));
 	}
 
 	/* Check slaves for master connection and possibly activate them */
 	autoconnect_slaves (self, connection, device, nm_active_connection_get_subject (active));
 
 	/* Disconnect the connection if connected or queued on another device */
-	existing = nm_manager_get_connection_device (self, connection);
+	existing = nm_manager_get_connection_device (self, NM_CONNECTION (connection));
 	if (existing)
 		nm_device_steal_connection (existing, connection);
 
+	if (nm_device_get_state (device) == NM_DEVICE_STATE_UNMANAGED) {
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_UNAVAILABLE,
+		                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
+	}
+
+	if (   nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST)
+	    && (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) {
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_DISCONNECTED,
+		                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
+	}
+
 	/* Export the new ActiveConnection to clients and start it on the device */
-	nm_active_connection_export (active);
+	nm_exported_object_export (NM_EXPORTED_OBJECT (active));
 	g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS);
 	nm_device_queue_activation (device, NM_ACT_REQUEST (active));
 	return TRUE;
@@ -2824,8 +2915,8 @@ _internal_activate_generic (NMManager *self, NMActiveConnection *active, GError
 }
 
 static NMActiveConnection *
-_new_vpn_active_connection (NMManager *self, 
-                            NMConnection *connection,
+_new_vpn_active_connection (NMManager *self,
+                            NMSettingsConnection *settings_connection,
                             const char *specific_object,
                             NMAuthSubject *subject,
                             GError **error)
@@ -2834,6 +2925,8 @@ _new_vpn_active_connection (NMManager *self,
 	NMActiveConnection *parent = NULL;
 	NMDevice *device = NULL;
 
+	g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL);
+
 	if (specific_object) {
 		/* Find the specific connection the client requested we use */
 		parent = active_connection_get_by_path (self, specific_object);
@@ -2858,9 +2951,9 @@ _new_vpn_active_connection (NMManager *self,
 		return NULL;
 	}
 
-	return (NMActiveConnection *) nm_vpn_connection_new (connection,
+	return (NMActiveConnection *) nm_vpn_connection_new (settings_connection,
 	                                                     device,
-	                                                     nm_active_connection_get_path (parent),
+	                                                     nm_exported_object_get_path (NM_EXPORTED_OBJECT (parent)),
 	                                                     subject);
 }
 
@@ -2872,7 +2965,9 @@ _new_active_connection (NMManager *self,
                         NMAuthSubject *subject,
                         GError **error)
 {
+	NMSettingsConnection *settings_connection = NULL;
 	NMActiveConnection *existing_ac;
+	gboolean is_vpn;
 
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 	g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL);
@@ -2890,15 +2985,20 @@ _new_active_connection (NMManager *self,
 	if (specific_object && g_strcmp0 (specific_object, "/") == 0)
 		specific_object = NULL;
 
-	if (nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME)) {
+	is_vpn = nm_connection_is_type (NM_CONNECTION (connection), NM_SETTING_VPN_SETTING_NAME);
+
+	if (NM_IS_SETTINGS_CONNECTION (connection))
+		settings_connection = (NMSettingsConnection *) connection;
+
+	if (is_vpn) {
 		return _new_vpn_active_connection (self,
-		                                   connection,
+		                                   settings_connection,
 		                                   specific_object,
 		                                   subject,
 		                                   error);
 	}
 
-	return (NMActiveConnection *) nm_act_request_new (connection,
+	return (NMActiveConnection *) nm_act_request_new (settings_connection,
 	                                                  specific_object,
 	                                                  subject,
 	                                                  device);
@@ -2910,7 +3010,7 @@ _internal_activation_failed (NMManager *self,
                              const char *error_desc)
 {
 	nm_log_dbg (LOGD_CORE, "Failed to activate '%s': %s",
-	            nm_connection_get_id (nm_active_connection_get_connection (active)),
+	            nm_active_connection_get_settings_connection_id (active),
 	            error_desc);
 
 	if (nm_active_connection_get_state (active) <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
@@ -2948,7 +3048,7 @@ _internal_activation_auth_done (NMActiveConnection *active,
 /**
  * nm_manager_activate_connection():
  * @self: the #NMManager
- * @connection: the #NMConnection to activate on @device
+ * @connection: the #NMSettingsConnection 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
@@ -2965,7 +3065,7 @@ _internal_activation_auth_done (NMActiveConnection *active,
  */
 NMActiveConnection *
 nm_manager_activate_connection (NMManager *self,
-                                NMConnection *connection,
+                                NMSettingsConnection *connection,
                                 const char *specific_object,
                                 NMDevice *device,
                                 NMAuthSubject *subject,
@@ -2982,8 +3082,7 @@ nm_manager_activate_connection (NMManager *self,
 	g_return_val_if_fail (*error == NULL, NULL);
 
 	/* Ensure the subject has permissions for this connection */
-	if (!nm_auth_is_subject_in_acl (connection,
-	                                nm_session_monitor_get (),
+	if (!nm_auth_is_subject_in_acl (NM_CONNECTION (connection),
 	                                subject,
 	                                &error_desc)) {
 		g_set_error_literal (error,
@@ -3002,7 +3101,7 @@ nm_manager_activate_connection (NMManager *self,
 	for (iter = priv->authorizing_connections; iter; iter = g_slist_next (iter)) {
 		active = iter->data;
 
-		if (   connection == nm_active_connection_get_connection (active)
+		if (   connection == nm_active_connection_get_settings_connection (active)
 		    && g_strcmp0 (nm_active_connection_get_specific_object (active), specific_object) == 0
 		    && nm_active_connection_get_device (active) == device
 		    && nm_auth_subject_is_internal (nm_active_connection_get_subject (active))
@@ -3011,21 +3110,38 @@ nm_manager_activate_connection (NMManager *self,
 	}
 
 	active = _new_active_connection (self,
-	                                 connection,
+	                                 NM_CONNECTION (connection),
 	                                 specific_object,
 	                                 device,
 	                                 subject,
 	                                 error);
 	if (active) {
 		priv->authorizing_connections = g_slist_prepend (priv->authorizing_connections, active);
-		nm_active_connection_authorize (active, _internal_activation_auth_done, self, NULL);
+		nm_active_connection_authorize (active, NULL, _internal_activation_auth_done, self, NULL);
 	}
 	return active;
 }
 
+/**
+ * validate_activation_request:
+ * @self: the #NMManager
+ * @context: the D-Bus context of the requestor
+ * @connection: the partial or complete #NMConnection to be activated
+ * @device_path: the object path of the device to be activated, or "/"
+ * @out_device: on successful reutrn, the #NMDevice to be activated with @connection
+ * @out_vpn: on successful return, %TRUE if @connection is a VPN connection
+ * @error: location to store an error on failure
+ *
+ * Performs basic validation on an activation request, including ensuring that
+ * the requestor is a valid Unix process, is not disallowed in @connection
+ * permissions, and that a device exists that can activate @connection.
+ *
+ * Returns: on success, the #NMAuthSubject representing the requestor, or
+ *   %NULL on error
+ */
 static NMAuthSubject *
 validate_activation_request (NMManager *self,
-                             DBusGMethodInvocation *context,
+                             GDBusMethodInvocation *context,
                              NMConnection *connection,
                              const char *device_path,
                              NMDevice **out_device,
@@ -3053,7 +3169,6 @@ validate_activation_request (NMManager *self,
 
 	/* Ensure the subject has permissions for this connection */
 	if (!nm_auth_is_subject_in_acl (connection,
-	                                nm_session_monitor_get (),
 	                                subject,
 	                                &error_desc)) {
 		g_set_error_literal (error,
@@ -3098,11 +3213,11 @@ validate_activation_request (NMManager *self,
 	} else
 		device = nm_manager_get_best_device_for_connection (self, connection, TRUE);
 
-	if (!device) {
+	if (!device && !vpn) {
 		gboolean is_software = nm_connection_is_virtual (connection);
 
 		/* VPN and software-device connections don't need a device yet */
-		if (!vpn && !is_software) {
+		if (!is_software) {
 			g_set_error_literal (error,
 			                     NM_MANAGER_ERROR,
 			                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
@@ -3118,11 +3233,19 @@ validate_activation_request (NMManager *self,
 			if (!iface)
 				goto error;
 
-			device = find_device_by_ip_iface (self, iface);
+			device = find_device_by_iface (self, iface, connection, NULL);
 			g_free (iface);
 		}
 	}
 
+	if ((!vpn || device_path) && !device) {
+		g_set_error_literal (error,
+		                     NM_MANAGER_ERROR,
+		                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+		                     "Failed to find a compatible device for this connection");
+		goto error;
+	}
+
 	*out_device = device;
 	*out_vpn = vpn;
 	return subject;
@@ -3142,39 +3265,50 @@ _activation_auth_done (NMActiveConnection *active,
                        gpointer user_data2)
 {
 	NMManager *self = user_data1;
-	DBusGMethodInvocation *context = user_data2;
+	GDBusMethodInvocation *context = user_data2;
 	GError *error = NULL;
+	NMAuthSubject *subject;
+	NMSettingsConnection *connection;
+
+	subject = nm_active_connection_get_subject (active);
+	connection = nm_active_connection_get_settings_connection (active);
 
 	if (success) {
 		if (_internal_activate_generic (self, active, &error)) {
-			dbus_g_method_return (context, nm_active_connection_get_path (active));
+			g_dbus_method_invocation_return_value (context,
+			                                       g_variant_new ("(o)",
+			                                       nm_exported_object_get_path (NM_EXPORTED_OBJECT (active))));
+			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, TRUE,
+			                            subject, NULL);
 			g_object_unref (active);
 			return;
 		}
 	} else {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
-			                         NM_MANAGER_ERROR_PERMISSION_DENIED,
-			                         error_desc);
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             error_desc);
 	}
 
 	g_assert (error);
-	dbus_g_method_return_error (context, error);
+	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE,
+	                            subject, error->message);
 	_internal_activation_failed (self, active, error->message);
+
 	g_object_unref (active);
-	g_error_free (error);
+	g_dbus_method_invocation_take_error (context, error);
 }
 
 static void
 impl_manager_activate_connection (NMManager *self,
+                                  GDBusMethodInvocation *context,
                                   const char *connection_path,
                                   const char *device_path,
-                                  const char *specific_object_path,
-                                  DBusGMethodInvocation *context)
+                                  const char *specific_object_path)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMActiveConnection *active = NULL;
 	NMAuthSubject *subject = NULL;
-	NMConnection *connection;
+	NMSettingsConnection *connection = NULL;
 	NMDevice *device = NULL;
 	gboolean is_vpn = FALSE;
 	GError *error = NULL;
@@ -3234,7 +3368,7 @@ impl_manager_activate_connection (NMManager *self,
 	}
 
 	g_assert (connection_path);
-	connection = (NMConnection *) nm_settings_get_connection_by_path (priv->settings, connection_path);
+	connection = 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,
@@ -3244,7 +3378,7 @@ impl_manager_activate_connection (NMManager *self,
 
 	subject = validate_activation_request (self,
 	                                       context,
-	                                       connection,
+	                                       NM_CONNECTION (connection),
 	                                       device_path,
 	                                       &device,
 	                                       &is_vpn,
@@ -3253,7 +3387,7 @@ impl_manager_activate_connection (NMManager *self,
 		goto error;
 
 	active = _new_active_connection (self,
-	                                 connection,
+	                                 NM_CONNECTION (connection),
 	                                 specific_object_path,
 	                                 device,
 	                                 subject,
@@ -3261,17 +3395,20 @@ impl_manager_activate_connection (NMManager *self,
 	if (!active)
 		goto error;
 
-	nm_active_connection_authorize (active, _activation_auth_done, self, context);
+	nm_active_connection_authorize (active, NULL, _activation_auth_done, self, context);
 	g_clear_object (&subject);
 	return;
 
 error:
+	if (connection) {
+		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE,
+		                            subject, error->message);
+	}
 	g_clear_object (&active);
 	g_clear_object (&subject);
 
 	g_assert (error);
-	dbus_g_method_return_error (context, error);
-	g_error_free (error);
+	g_dbus_method_invocation_take_error (context, error);
 }
 
 /***********************************************************************/
@@ -3282,35 +3419,54 @@ typedef struct {
 } AddAndActivateInfo;
 
 static void
-activation_add_done (NMSettings *self,
+activation_add_done (NMSettings *settings,
                      NMSettingsConnection *new_connection,
                      GError *error,
-                     DBusGMethodInvocation *context,
+                     GDBusMethodInvocation *context,
+                     NMAuthSubject *subject,
                      gpointer user_data)
 {
 	AddAndActivateInfo *info = user_data;
+	NMManager *self;
+	gs_unref_object NMActiveConnection *active = NULL;
 	GError *local = NULL;
 
-	if (!error) {
-		nm_active_connection_set_connection (info->active, NM_CONNECTION (new_connection));
+	self = info->manager;
+	active = info->active;
+	g_slice_free (AddAndActivateInfo, info);
 
-		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;
+	if (!error) {
+		nm_active_connection_set_settings_connection (active, new_connection);
+
+		if (_internal_activate_generic (self, active, &local)) {
+			nm_settings_connection_commit_changes (new_connection,
+			                                       NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED,
+			                                       NULL, NULL);
+			g_dbus_method_invocation_return_value (
+			    context,
+			    g_variant_new ("(oo)",
+			                   nm_connection_get_path (NM_CONNECTION (new_connection)),
+			                   nm_exported_object_get_path (NM_EXPORTED_OBJECT (active))));
+			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE,
+			                            nm_active_connection_get_settings_connection (active),
+			                            TRUE,
+			                            nm_active_connection_get_subject (active),
+			                            NULL);
+			return;
 		}
 		error = local;
 	}
 
 	g_assert (error);
-	_internal_activation_failed (info->manager, info->active, error->message);
-	dbus_g_method_return_error (context, error);
+	_internal_activation_failed (self, active, error->message);
+	nm_settings_connection_delete (new_connection, NULL, NULL);
+	g_dbus_method_invocation_return_gerror (context, error);
+	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE,
+	                            NULL,
+	                            FALSE,
+	                            nm_active_connection_get_subject (active),
+	                            error->message);
 	g_clear_error (&local);
-
-done:
-	g_object_unref (info->active);
-	g_free (info);
 }
 
 static void
@@ -3322,29 +3478,39 @@ _add_and_activate_auth_done (NMActiveConnection *active,
 {
 	NMManager *self = user_data1;
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	DBusGMethodInvocation *context = user_data2;
+	GDBusMethodInvocation *context = user_data2;
 	AddAndActivateInfo *info;
 	GError *error = NULL;
 
 	if (success) {
-		info = g_malloc0 (sizeof (*info));
+		NMConnection *connection;
+
+		connection = g_object_steal_data (G_OBJECT (active),
+		                                  TAG_ACTIVE_CONNETION_ADD_AND_ACTIVATE);
+
+		info = g_slice_new (AddAndActivateInfo);
 		info->manager = self;
 		info->active = g_object_ref (active);
 
 		/* Basic sender auth checks performed; try to add the connection */
 		nm_settings_add_connection_dbus (priv->settings,
-		                                 nm_active_connection_get_connection (active),
-		                                 TRUE,
+		                                 connection,
+		                                 FALSE,
 		                                 context,
 		                                 activation_add_done,
 		                                 info);
+		g_object_unref (connection);
 	} else {
 		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);
+		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE,
+		                            NULL,
+		                            FALSE,
+		                            nm_active_connection_get_subject (active),
+		                            error->message);
+		g_dbus_method_invocation_take_error (context, error);
 	}
 
 	g_object_unref (active);
@@ -3352,10 +3518,10 @@ _add_and_activate_auth_done (NMActiveConnection *active,
 
 static void
 impl_manager_add_and_activate_connection (NMManager *self,
-                                          GHashTable *settings,
+                                          GDBusMethodInvocation *context,
+                                          GVariant *settings,
                                           const char *device_path,
-                                          const char *specific_object_path,
-                                          DBusGMethodInvocation *context)
+                                          const char *specific_object_path)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMConnection *connection = NULL;
@@ -3380,12 +3546,8 @@ impl_manager_add_and_activate_connection (NMManager *self,
 	 * validate_activation_request()).
 	 */
 	connection = nm_simple_connection_new ();
-	if (settings && g_hash_table_size (settings)) {
-		GVariant *settings_dict = nm_utils_connection_hash_to_dict (settings);
-
-		nm_connection_replace_settings (connection, settings_dict, NULL);
-		g_variant_unref (settings_dict);
-	}
+	if (settings && g_variant_n_children (settings))
+		nm_connection_replace_settings (connection, settings, NULL);
 
 	subject = validate_activation_request (self,
 	                                       context,
@@ -3397,14 +3559,6 @@ impl_manager_add_and_activate_connection (NMManager *self,
 	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 */
@@ -3444,20 +3598,24 @@ impl_manager_add_and_activate_connection (NMManager *self,
 	if (!active)
 		goto error;
 
-	nm_active_connection_authorize (active, _add_and_activate_auth_done, self, context);
-	g_object_unref (connection);
+	g_object_set_data_full (G_OBJECT (active),
+	                        TAG_ACTIVE_CONNETION_ADD_AND_ACTIVATE,
+	                        connection,
+	                        g_object_unref);
+
+	nm_active_connection_authorize (active, connection, _add_and_activate_auth_done, self, context);
 	g_object_unref (subject);
 	return;
 
 error:
+	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE, NULL, FALSE, subject, error->message);
 	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);
+	g_dbus_method_invocation_take_error (context, error);
 }
 
 /***********************************************************************/
@@ -3468,7 +3626,6 @@ nm_manager_deactivate_connection (NMManager *manager,
                                   NMDeviceStateReason reason,
                                   GError **error)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
 	NMActiveConnection *active;
 	gboolean success = FALSE;
 
@@ -3484,7 +3641,7 @@ nm_manager_deactivate_connection (NMManager *manager,
 
 		if (reason == NM_DEVICE_STATE_REASON_CONNECTION_REMOVED)
 			vpn_reason = NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED;
-		if (nm_vpn_manager_deactivate_connection (priv->vpn_manager, NM_VPN_CONNECTION (active), vpn_reason))
+		if (nm_vpn_connection_deactivate (NM_VPN_CONNECTION (active), vpn_reason, FALSE))
 			success = TRUE;
 		else
 			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE,
@@ -3506,19 +3663,23 @@ nm_manager_deactivate_connection (NMManager *manager,
 static void
 deactivate_net_auth_done_cb (NMAuthChain *chain,
                              GError *auth_error,
-                             DBusGMethodInvocation *context,
+                             GDBusMethodInvocation *context,
                              gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GError *error = NULL;
 	NMAuthCallResult result;
+	NMActiveConnection *active;
+	char *path;
 
 	g_assert (context);
 
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
 
+	path = nm_auth_chain_get_data (chain, "path");
 	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL);
+	active = active_connection_get_by_path (self, path);
 
 	if (auth_error) {
 		nm_log_dbg (LOGD_CORE, "Disconnect request failed: %s", auth_error->message);
@@ -3533,43 +3694,45 @@ deactivate_net_auth_done_cb (NMAuthChain *chain,
 	} else {
 		/* success; deactivation allowed */
 		if (!nm_manager_deactivate_connection (self,
-		                                       nm_auth_chain_get_data (chain, "path"),
+		                                       path,
 		                                       NM_DEVICE_STATE_REASON_USER_REQUESTED,
 		                                       &error))
 			g_assert (error);
 	}
 
+	if (active) {
+		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DEACTIVATE,
+		                            nm_active_connection_get_settings_connection (active),
+		                            !error,
+		                            nm_auth_chain_get_subject (chain),
+		                            error ? error->message : NULL);
+	}
+
 	if (error)
-		dbus_g_method_return_error (context, error);
+		g_dbus_method_invocation_take_error (context, error);
 	else
-		dbus_g_method_return (context);
+		g_dbus_method_invocation_return_value (context, NULL);
 
-	g_clear_error (&error);
 	nm_auth_chain_unref (chain);
 }
 
 static void
 impl_manager_deactivate_connection (NMManager *self,
-                                    const char *active_path,
-                                    DBusGMethodInvocation *context)
+                                    GDBusMethodInvocation *context,
+                                    const char *active_path)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMConnection *connection = NULL;
+	NMActiveConnection *ac;
+	NMSettingsConnection *connection = NULL;
 	GError *error = NULL;
 	NMAuthSubject *subject = NULL;
-	GSList *iter;
 	NMAuthChain *chain;
 	char *error_desc = NULL;
 
 	/* Find the connection by its object path */
-	for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) {
-		NMActiveConnection *ac = iter->data;
-
-		if (g_strcmp0 (nm_active_connection_get_path (ac), active_path) == 0) {
-			connection = nm_active_connection_get_connection (ac);
-			break;
-		}
-	}
+	ac = active_connection_get_by_path (self, active_path);
+	if (ac)
+		connection = nm_active_connection_get_settings_connection (ac);
 
 	if (!connection) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
@@ -3588,8 +3751,7 @@ impl_manager_deactivate_connection (NMManager *self,
 	}
 
 	/* Ensure the subject has permissions for this connection */
-	if (!nm_auth_is_subject_in_acl (connection,
-	                                nm_session_monitor_get (),
+	if (!nm_auth_is_subject_in_acl (NM_CONNECTION (connection),
 	                                subject,
 	                                &error_desc)) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
@@ -3613,10 +3775,14 @@ impl_manager_deactivate_connection (NMManager *self,
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE);
 
 done:
+	if (error) {
+		if (connection) {
+			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DEACTIVATE, connection, FALSE,
+			                            subject, error->message);
+		}
+		g_dbus_method_invocation_take_error (context, error);
+	}
 	g_clear_object (&subject);
-	if (error)
-		dbus_g_method_return_error (context, error);
-	g_clear_error (&error);
 }
 
 static gboolean
@@ -3651,7 +3817,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 			if (suspending && device_is_wake_on_lan (device))
 				continue;
 
-			nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_SLEEPING);
+			nm_device_set_unmanaged_flags (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_SLEEPING);
 		}
 	} else {
 		nm_log_info (LOGD_SUSPEND, "%s...", waking_from_suspend ? "waking up" : "re-enabling");
@@ -3666,7 +3832,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 				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);
+					nm_device_set_unmanaged_flags (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_SLEEPING);
 			}
 		}
 
@@ -3700,9 +3866,9 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 					nm_device_set_enabled (device, enabled);
 			}
 
-			g_object_set (G_OBJECT (device), NM_DEVICE_AUTOCONNECT, TRUE, NULL);
+			nm_device_set_autoconnect (device, TRUE);
 
-			nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, FALSE, NM_DEVICE_STATE_REASON_NOW_MANAGED);
+			nm_device_set_unmanaged_flags (device, NM_UNMANAGED_INTERNAL, FALSE, NM_DEVICE_STATE_REASON_NOW_MANAGED);
 		}
 	}
 
@@ -3733,7 +3899,7 @@ _internal_sleep (NMManager *self, gboolean do_sleep)
 static void
 sleep_auth_done_cb (NMAuthChain *chain,
                     GError *error,
-                    DBusGMethodInvocation *context,
+                    GDBusMethodInvocation *context,
                     gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
@@ -3751,19 +3917,17 @@ sleep_auth_done_cb (NMAuthChain *chain,
 		                         NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                         "Sleep/wake request failed: %s",
 		                         error->message);
-		dbus_g_method_return_error (context, ret_error);
-		g_error_free (ret_error);
+		g_dbus_method_invocation_take_error (context, 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 sleep/wake");
-		dbus_g_method_return_error (context, ret_error);
-		g_error_free (ret_error);
+		g_dbus_method_invocation_take_error (context, ret_error);
 	} else {
 		/* Auth success */
 		do_sleep = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "sleep"));
 		_internal_sleep (self, do_sleep);
-		dbus_g_method_return (context);
+		g_dbus_method_invocation_return_value (context, NULL);
 	}
 
 	nm_auth_chain_unref (chain);
@@ -3772,11 +3936,12 @@ sleep_auth_done_cb (NMAuthChain *chain,
 
 static void
 impl_manager_sleep (NMManager *self,
-                    gboolean do_sleep,
-                    DBusGMethodInvocation *context)
+                    GDBusMethodInvocation *context,
+                    gboolean do_sleep)
 {
 	NMManagerPrivate *priv;
 	GError *error = NULL;
+	gs_unref_object NMAuthSubject *subject = NULL;
 #if 0
 	NMAuthChain *chain;
 	const char *error_desc = NULL;
@@ -3785,13 +3950,15 @@ impl_manager_sleep (NMManager *self,
 	g_return_if_fail (NM_IS_MANAGER (self));
 
 	priv = NM_MANAGER_GET_PRIVATE (self);
+	subject = nm_auth_subject_new_unix_process_from_context (context);
 
 	if (priv->sleeping == do_sleep) {
 		error = g_error_new (NM_MANAGER_ERROR,
 		                     NM_MANAGER_ERROR_ALREADY_ASLEEP_OR_AWAKE,
 		                     "Already %s", do_sleep ? "asleep" : "awake");
-		dbus_g_method_return_error (context, error);
-		g_error_free (error);
+		nm_audit_log_control_op (NM_AUDIT_OP_SLEEP_CONTROL, do_sleep ? "on" : "off", FALSE, subject,
+		                         error->message);
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
@@ -3804,7 +3971,8 @@ impl_manager_sleep (NMManager *self,
 	 * D-Bus permissions to restrict the call to root.
 	 */
 	_internal_sleep (self, do_sleep);
-	dbus_g_method_return (context);
+	nm_audit_log_control_op (NM_AUDIT_OP_SLEEP_CONTROL, do_sleep ? "on" : "off", TRUE, subject, NULL);
+	g_dbus_method_invocation_return_value (context, NULL);
 	return;
 
 #if 0
@@ -3817,21 +3985,20 @@ impl_manager_sleep (NMManager *self,
 		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_dbus_method_invocation_take_error (context, error);
 	}
 #endif
 }
 
 static void
-sleeping_cb (DBusGProxy *proxy, gpointer user_data)
+sleeping_cb (NMSleepMonitor *monitor, gpointer user_data)
 {
 	nm_log_dbg (LOGD_SUSPEND, "Received sleeping signal");
 	_internal_sleep (NM_MANAGER (user_data), TRUE);
 }
 
 static void
-resuming_cb (DBusGProxy *proxy, gpointer user_data)
+resuming_cb (NMSleepMonitor *monitor, gpointer user_data)
 {
 	nm_log_dbg (LOGD_SUSPEND, "Received resuming signal");
 	_internal_sleep (NM_MANAGER (user_data), FALSE);
@@ -3872,7 +4039,7 @@ _internal_enable (NMManager *self, gboolean enable)
 static void
 enable_net_done_cb (NMAuthChain *chain,
                     GError *error,
-                    DBusGMethodInvocation *context,
+                    GDBusMethodInvocation *context,
                     gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
@@ -3880,10 +4047,13 @@ enable_net_done_cb (NMAuthChain *chain,
 	GError *ret_error = NULL;
 	NMAuthCallResult result;
 	gboolean enable;
+	NMAuthSubject *subject;
 
 	g_assert (context);
 
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
+	enable = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "enable"));
+	subject = nm_auth_chain_get_subject (chain);
 
 	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK);
 	if (error) {
@@ -3898,14 +4068,16 @@ enable_net_done_cb (NMAuthChain *chain,
 		                                 "Not authorized to enable/disable networking");
 	} else {
 		/* Auth success */
-		enable = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "enable"));
 		_internal_enable (self, enable);
-		dbus_g_method_return (context);
+		g_dbus_method_invocation_return_value (context, NULL);
+		nm_audit_log_control_op (NM_AUDIT_OP_NET_CONTROL, enable ? "on" : "off", TRUE,
+		                         subject, NULL);
 	}
 
 	if (ret_error) {
-		dbus_g_method_return_error (context, ret_error);
-		g_error_free (ret_error);
+		nm_audit_log_control_op (NM_AUDIT_OP_NET_CONTROL, enable ? "on" : "off", FALSE,
+		                         subject, ret_error->message);
+		g_dbus_method_invocation_take_error (context, ret_error);
 	}
 
 	nm_auth_chain_unref (chain);
@@ -3913,8 +4085,8 @@ enable_net_done_cb (NMAuthChain *chain,
 
 static void
 impl_manager_enable (NMManager *self,
-                     gboolean enable,
-                     DBusGMethodInvocation *context)
+                     GDBusMethodInvocation *context,
+                     gboolean enable)
 {
 	NMManagerPrivate *priv;
 	NMAuthChain *chain;
@@ -3945,24 +4117,23 @@ impl_manager_enable (NMManager *self,
 
 done:
 	if (error)
-		dbus_g_method_return_error (context, error);
-	g_clear_error (&error);
+		g_dbus_method_invocation_take_error (context, error);
 }
 
 /* Permissions */
 
 static void
-get_perm_add_result (NMAuthChain *chain, GHashTable *results, const char *permission)
+get_perm_add_result (NMAuthChain *chain, GVariantBuilder *results, const char *permission)
 {
 	NMAuthCallResult result;
 
 	result = nm_auth_chain_get_result (chain, permission);
 	if (result == NM_AUTH_CALL_RESULT_YES)
-		g_hash_table_insert (results, (char *) permission, "yes");
+		g_variant_builder_add (results, "{ss}", permission, "yes");
 	else if (result == NM_AUTH_CALL_RESULT_NO)
-		g_hash_table_insert (results, (char *) permission, "no");
+		g_variant_builder_add (results, "{ss}", permission, "no");
 	else if (result == NM_AUTH_CALL_RESULT_AUTH)
-		g_hash_table_insert (results, (char *) permission, "auth");
+		g_variant_builder_add (results, "{ss}", permission, "auth");
 	else {
 		nm_log_dbg (LOGD_CORE, "unknown auth chain result %d", result);
 	}
@@ -3971,13 +4142,13 @@ get_perm_add_result (NMAuthChain *chain, GHashTable *results, const char *permis
 static void
 get_permissions_done_cb (NMAuthChain *chain,
                          GError *error,
-                         DBusGMethodInvocation *context,
+                         GDBusMethodInvocation *context,
                          gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GError *ret_error;
-	GHashTable *results;
+	GVariantBuilder results;
 
 	g_assert (context);
 
@@ -3988,25 +4159,24 @@ get_permissions_done_cb (NMAuthChain *chain,
 		                         NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                         "Permissions request failed: %s",
 		                         error->message);
-		dbus_g_method_return_error (context, ret_error);
-		g_error_free (ret_error);
+		g_dbus_method_invocation_take_error (context, ret_error);
 	} else {
-		results = g_hash_table_new (g_str_hash, g_str_equal);
+		g_variant_builder_init (&results, G_VARIANT_TYPE ("a{ss}"));
 
-		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);
-		get_perm_add_result (chain, results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN);
-		get_perm_add_result (chain, results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX);
-		get_perm_add_result (chain, results, NM_AUTH_PERMISSION_NETWORK_CONTROL);
-		get_perm_add_result (chain, results, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED);
-		get_perm_add_result (chain, results, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN);
-		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);
+		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);
+		get_perm_add_result (chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN);
+		get_perm_add_result (chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX);
+		get_perm_add_result (chain, &results, NM_AUTH_PERMISSION_NETWORK_CONTROL);
+		get_perm_add_result (chain, &results, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED);
+		get_perm_add_result (chain, &results, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN);
+		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);
+		g_dbus_method_invocation_return_value (context,
+		                                       g_variant_new ("(a{ss})", &results));
 	}
 
 	nm_auth_chain_unref (chain);
@@ -4014,7 +4184,7 @@ get_permissions_done_cb (NMAuthChain *chain,
 
 static void
 impl_manager_get_permissions (NMManager *self,
-                              DBusGMethodInvocation *context)
+                              GDBusMethodInvocation *context)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMAuthChain *chain;
@@ -4025,8 +4195,7 @@ impl_manager_get_permissions (NMManager *self,
 		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);
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
@@ -4044,25 +4213,26 @@ impl_manager_get_permissions (NMManager *self,
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, FALSE);
 }
 
-static gboolean
-impl_manager_get_state (NMManager *manager, guint32 *state, GError **error)
+static void
+impl_manager_get_state (NMManager *self,
+                        GDBusMethodInvocation *context)
 {
-	nm_manager_update_state (manager);
-	*state = NM_MANAGER_GET_PRIVATE (manager)->state;
-	return TRUE;
+	nm_manager_update_state (self);
+	g_dbus_method_invocation_return_value (context,
+	                                       g_variant_new ("(u)", NM_MANAGER_GET_PRIVATE (self)->state));
 }
 
 static void
-impl_manager_set_logging (NMManager *manager,
+impl_manager_set_logging (NMManager *self,
+                          GDBusMethodInvocation *context,
                           const char *level,
-                          const char *domains,
-                          DBusGMethodInvocation *context)
+                          const char *domains)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GError *error = NULL;
 	gulong caller_uid = G_MAXULONG;
 
-	if (!nm_dbus_manager_get_caller_info (priv->dbus_mgr, context, NULL, &caller_uid, NULL)) {
+	if (!nm_bus_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.");
@@ -4082,20 +4252,20 @@ impl_manager_set_logging (NMManager *manager,
 	}
 
 done:
-	if (error) {
-		dbus_g_method_return_error (context, error);
-		g_error_free (error);
-	} else
-		dbus_g_method_return (context);
+	if (error)
+		g_dbus_method_invocation_take_error (context, error);
+	else
+		g_dbus_method_invocation_return_value (context, NULL);
 }
 
 static void
 impl_manager_get_logging (NMManager *manager,
-                          char **level,
-                          char **domains)
+                          GDBusMethodInvocation *context)
 {
-	*level = g_strdup (nm_logging_level_to_string ());
-	*domains = g_strdup (nm_logging_domains_to_string ());
+	g_dbus_method_invocation_return_value (context,
+	                                       g_variant_new ("(ss)",
+	                                                      nm_logging_level_to_string (),
+	                                                      nm_logging_domains_to_string ()));
 }
 
 static void
@@ -4103,23 +4273,24 @@ connectivity_check_done (GObject *object,
                          GAsyncResult *result,
                          gpointer user_data)
 {
-	DBusGMethodInvocation *context = user_data;
+	GDBusMethodInvocation *context = user_data;
 	NMConnectivityState state;
 	GError *error = NULL;
 
 	state = nm_connectivity_check_finish (NM_CONNECTIVITY (object), result, &error);
-	if (error) {
-		dbus_g_method_return_error (context, error);
-		g_error_free (error);
-	} else
-		dbus_g_method_return (context, state);
+	if (error)
+		g_dbus_method_invocation_take_error (context, error);
+	else {
+		g_dbus_method_invocation_return_value (context,
+		                                       g_variant_new ("(u)", state));
+	}
 }
 
 
 static void
 check_connectivity_auth_done_cb (NMAuthChain *chain,
                                  GError *auth_error,
-                                 DBusGMethodInvocation *context,
+                                 GDBusMethodInvocation *context,
                                  gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
@@ -4148,29 +4319,26 @@ check_connectivity_auth_done_cb (NMAuthChain *chain,
 		                             context);
 	}
 
-	if (error) {
-		dbus_g_method_return_error (context, error);
-		g_error_free (error);
-	}
+	if (error)
+		g_dbus_method_invocation_take_error (context, error);
 	nm_auth_chain_unref (chain);
 }
 
 static void
-impl_manager_check_connectivity (NMManager *manager,
-                                 DBusGMethodInvocation *context)
+impl_manager_check_connectivity (NMManager *self,
+                                 GDBusMethodInvocation *context)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMAuthChain *chain;
 	GError *error = NULL;
 
 	/* Validate the request */
-	chain = nm_auth_chain_new_context (context, check_connectivity_auth_done_cb, manager);
+	chain = nm_auth_chain_new_context (context, check_connectivity_auth_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);
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
@@ -4184,12 +4352,21 @@ start_factory (NMDeviceFactory *factory, gpointer user_data)
 	nm_device_factory_start (factory);
 }
 
-void
-nm_manager_start (NMManager *self)
+gboolean
+nm_manager_start (NMManager *self, GError **error)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	GSList *iter, *connections;
 	guint i;
 
+	if (!nm_settings_start (priv->settings, error))
+		return FALSE;
+
+	g_signal_connect (NM_PLATFORM_GET,
+	                  NM_PLATFORM_SIGNAL_LINK_CHANGED,
+	                  G_CALLBACK (platform_link_cb),
+	                  self);
+
 	/* Set initial radio enabled/disabled state */
 	for (i = 0; i < RFKILL_TYPE_MAX; i++) {
 		RadioState *rstate = &priv->radio_states[i];
@@ -4203,9 +4380,9 @@ nm_manager_start (NMManager *self)
 
 		if (rstate->desc) {
 			nm_log_info (LOGD_RFKILL, "%s %s by radio killswitch; %s by state file",
-				         rstate->desc,
-				         (rstate->hw_enabled && rstate->sw_enabled) ? "enabled" : "disabled",
-				         rstate->user_enabled ? "enabled" : "disabled");
+			             rstate->desc,
+			             (rstate->hw_enabled && rstate->sw_enabled) ? "enabled" : "disabled",
+			             rstate->user_enabled ? "enabled" : "disabled");
 		}
 		enabled = radio_enabled_for_rstate (rstate, TRUE);
 		manager_update_radio_enabled (self, rstate, enabled);
@@ -4219,19 +4396,28 @@ nm_manager_start (NMManager *self)
 	system_hostname_changed_cb (priv->settings, NULL, self);
 
 	/* Start device factories */
+	nm_device_factory_manager_load_factories (_register_device_factory, self);
 	nm_device_factory_manager_for_each_factory (start_factory, NULL);
 
 	platform_query_devices (self);
 
-	/*
-	 * Connections added before the manager is started do not emit
+	/* Load VPN plugins */
+	priv->vpn_manager = g_object_ref (nm_vpn_manager_get ());
+
+	/* Connections added before the manager is started do not emit
 	 * connection-added signals thus devices have to be created manually.
 	 */
-	system_create_virtual_devices (self);
+	nm_log_dbg (LOGD_CORE, "creating virtual devices...");
+	connections = nm_settings_get_connections (priv->settings);
+	for (iter = connections; iter; iter = iter->next)
+		connection_changed (priv->settings, NM_CONNECTION (iter->data), self);
+	g_slist_free (connections);
 
 	priv->devices_inited = TRUE;
 
 	check_if_startup_complete (self);
+
+	return TRUE;
 }
 
 void
@@ -4360,7 +4546,7 @@ policy_default_device_changed (GObject *object, GParamSpec *pspec, gpointer user
 			g_signal_connect (priv->primary_connection, NM_ACTIVE_CONNECTION_DEVICE_METERED_CHANGED,
 			                  G_CALLBACK (connection_metered_changed), self);
 		}
-		nm_log_dbg (LOGD_CORE, "PrimaryConnection now %s", ac ? nm_active_connection_get_id (ac) : "(none)");
+		nm_log_dbg (LOGD_CORE, "PrimaryConnection now %s", ac ? nm_active_connection_get_settings_connection_id (ac) : "(none)");
 		g_object_notify (G_OBJECT (self), NM_MANAGER_PRIMARY_CONNECTION);
 		g_object_notify (G_OBJECT (self), NM_MANAGER_PRIMARY_CONNECTION_TYPE);
 		nm_manager_update_metered (self);
@@ -4395,165 +4581,311 @@ 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_id (ac) : "(none)");
+		nm_log_dbg (LOGD_CORE, "ActivatingConnection now %s", ac ? nm_active_connection_get_settings_connection_id (ac) : "(none)");
 		g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVATING_CONNECTION);
 	}
 }
 
 #define NM_PERM_DENIED_ERROR "org.freedesktop.NetworkManager.PermissionDenied"
-#define DEV_PERM_DENIED_ERROR "org.freedesktop.NetworkManager.Device.PermissionDenied"
+
+typedef struct {
+	NMManager *self;
+	GDBusConnection *connection;
+	GDBusMessage *message;
+	NMAuthSubject *subject;
+	const char *permission;
+	const char *audit_op;
+	char *audit_prop_value;
+	GType interface_type;
+	const char *glib_propname;
+} PropertyFilterData;
+
+static void
+free_property_filter_data (PropertyFilterData *pfd)
+{
+	g_object_unref (pfd->self);
+	g_object_unref (pfd->connection);
+	g_object_unref (pfd->message);
+	g_clear_object (&pfd->subject);
+	g_free (pfd->audit_prop_value);
+	g_slice_free (PropertyFilterData, pfd);
+}
 
 static void
 prop_set_auth_done_cb (NMAuthChain *chain,
                        GError *error,
-                       DBusGMethodInvocation *context,
+                       GDBusMethodInvocation *context, /* NULL */
                        gpointer user_data)
 {
-	NMManager *self = NM_MANAGER (user_data);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	DBusConnection *connection;
+	PropertyFilterData *pfd = user_data;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (pfd->self);
 	NMAuthCallResult result;
-	DBusMessage *reply = NULL, *message;
-	const char *permission, *prop;
-	GObject *obj;
-	gboolean set_enabled = TRUE;
+	GDBusMessage *reply = NULL;
+	const char *error_message;
+	gs_unref_object NMExportedObject *object = NULL;
+	const NMGlobalDnsConfig *global_dns;
+	gs_unref_variant GVariant *value = NULL;
+	GVariant *args;
 
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
+	result = nm_auth_chain_get_result (chain, pfd->permission);
+	if (error || (result != NM_AUTH_CALL_RESULT_YES)) {
+		reply = g_dbus_message_new_method_error (pfd->message,
+		                                         NM_PERM_DENIED_ERROR,
+		                                         (error_message = "Not authorized to perform this operation"));
+		if (error)
+			error_message = error->message;
+		goto done;
+	}
 
-	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"));
-	obj = nm_auth_chain_get_data (chain, "object");
+	object = NM_EXPORTED_OBJECT (nm_bus_manager_get_registered_object (priv->dbus_mgr,
+	                                                                   g_dbus_message_get_path (pfd->message)));
+	if (!object) {
+		reply = g_dbus_message_new_method_error (pfd->message,
+		                                         "org.freedesktop.DBus.Error.UnknownObject",
+		                                         (error_message = "Object doesn't exist."));
+		goto done;
+	}
 
-	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");
+	/* do some extra type checking... */
+	if (!nm_exported_object_get_interface_by_type (object, pfd->interface_type)) {
+		reply = g_dbus_message_new_method_error (pfd->message,
+		                                         "org.freedesktop.DBus.Error.InvalidArgs",
+		                                         (error_message = "Object is of unexpected type."));
+		goto done;
+	}
+
+	args = g_dbus_message_get_body (pfd->message);
+	g_variant_get (args, "(&s&sv)", NULL, NULL, &value);
+	g_assert (pfd->glib_propname);
+
+	if (!strcmp (pfd->glib_propname, NM_MANAGER_GLOBAL_DNS_CONFIGURATION)) {
+		g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("a{sv}")));
+		global_dns = nm_config_data_get_global_dns_config (nm_config_get_data (priv->config));
+
+		if (global_dns && !nm_global_dns_config_is_internal (global_dns)) {
+			reply = g_dbus_message_new_method_error (pfd->message,
+			                                         NM_PERM_DENIED_ERROR,
+			                                         (error_message = "Global DNS configuration already set via configuration file"));
+			goto done;
+		}
+		/* ... but set the property on the @object itself. It would be correct to set the property
+		 * on the skeleton interface, but as it is now, the result is the same. */
+		g_object_set (object, pfd->glib_propname, value, NULL);
 	} else {
-		g_object_set (obj, prop, set_enabled, NULL);
-		reply = dbus_message_new_method_return (message);
+		g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN));
+		/* the same here */
+		g_object_set (object, pfd->glib_propname, g_variant_get_boolean (value), NULL);
 	}
 
-	g_assert (reply);
-	connection = nm_auth_chain_get_data (chain, "connection");
-	g_assert (connection);
-	dbus_connection_send (connection, reply, NULL);
-	dbus_message_unref (reply);
+	reply = g_dbus_message_new_method_reply (pfd->message);
+	g_dbus_message_set_body (reply, g_variant_new_tuple (NULL, 0));
+	error_message = NULL;
+done:
+	nm_audit_log_control_op (pfd->audit_op, pfd->audit_prop_value, !error_message, pfd->subject, error_message);
 
+	g_dbus_connection_send_message (pfd->connection, reply,
+	                                G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+	                                NULL, NULL);
+	g_object_unref (reply);
 	nm_auth_chain_unref (chain);
+
+	free_property_filter_data (pfd);
 }
 
-static DBusHandlerResult
-prop_filter (DBusConnection *connection,
-             DBusMessage *message,
-             void *user_data)
+static gboolean
+do_set_property_check (gpointer user_data)
 {
-	NMManager *self = NM_MANAGER (user_data);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	DBusMessageIter iter;
-	DBusMessageIter sub;
-	const char *propiface = NULL;
-	const char *propname = NULL;
-	const char *glib_propname = NULL, *permission = NULL;
-	DBusMessage *reply = NULL;
-	gboolean set_enabled = FALSE;
-	NMAuthSubject *subject = NULL;
+	PropertyFilterData *pfd = user_data;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (pfd->self);
+	GDBusMessage *reply = 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
-	 * functionality.
-	 */
+	const char *error_message = NULL;
 
-	if (!dbus_message_is_method_call (message, DBUS_INTERFACE_PROPERTIES, "Set"))
-		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
-	dbus_message_iter_init (message, &iter);
-
-	/* Get the D-Bus interface of the property to set */
-	if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
-		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-	dbus_message_iter_get_basic (&iter, &propiface);
-	if (!propiface || (strcmp (propiface, NM_DBUS_INTERFACE) && strcmp (propiface, NM_DBUS_INTERFACE_DEVICE)))
-		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-	dbus_message_iter_next (&iter);
-
-	/* Get the property name that's going to be set */
-	if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
-		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-	dbus_message_iter_get_basic (&iter, &propname);
-	dbus_message_iter_next (&iter);
-
-	if (!strcmp (propname, "WirelessEnabled")) {
-		glib_propname = NM_MANAGER_WIRELESS_ENABLED;
-		permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI;
-	} else if (!strcmp (propname, "WwanEnabled")) {
-		glib_propname = NM_MANAGER_WWAN_ENABLED;
-		permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN;
-	} else if (!strcmp (propname, "WimaxEnabled")) {
-		glib_propname = NM_MANAGER_WIMAX_ENABLED;
-		permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX;
-	} else if (!strcmp (propname, "Autoconnect")) {
-		glib_propname = NM_DEVICE_AUTOCONNECT;
-		permission = NM_AUTH_PERMISSION_NETWORK_CONTROL;
-	} else
-		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
-	/* Get the new value for the property */
-	if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_VARIANT)
-		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-	dbus_message_iter_recurse (&iter, &sub);
-	if (dbus_message_iter_get_arg_type (&sub) != DBUS_TYPE_BOOLEAN)
-		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-	dbus_message_iter_get_basic (&sub, &set_enabled);
-
-	/* 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,
-		                                "Object does not exist");
-		goto out;
-	}
-
-	subject = nm_auth_subject_new_unix_process_from_message (connection, message);
-	if (!subject) {
-		reply = dbus_message_new_error (message, NM_PERM_DENIED_ERROR,
-		                                "Could not determine request UID.");
+	pfd->subject = nm_auth_subject_new_unix_process_from_message (pfd->connection, pfd->message);
+	if (!pfd->subject) {
+		reply = g_dbus_message_new_method_error (pfd->message,
+		                                         NM_PERM_DENIED_ERROR,
+		                                         (error_message = "Could not determine request UID."));
 		goto out;
 	}
 
 	/* Validate the user request */
-	chain = nm_auth_chain_new_subject (subject, NULL, prop_set_auth_done_cb, self);
+	chain = nm_auth_chain_new_subject (pfd->subject, NULL, prop_set_auth_done_cb, pfd);
 	if (!chain) {
-		reply = dbus_message_new_error (message, NM_PERM_DENIED_ERROR,
-		                                "Could not authenticate request.");
+		reply = g_dbus_message_new_method_error (pfd->message,
+		                                         NM_PERM_DENIED_ERROR,
+		                                         (error_message = "Could not authenticate request."));
 		goto out;
 	}
 
 	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);
+	nm_auth_chain_add_call (chain, pfd->permission, TRUE);
 
 out:
 	if (reply) {
-		dbus_connection_send (connection, reply, NULL);
-		dbus_message_unref (reply);
+		nm_audit_log_control_op (pfd->audit_op, pfd->audit_prop_value, FALSE, pfd->subject, error_message);
+		g_dbus_connection_send_message (pfd->connection, reply,
+		                                G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+		                                NULL, NULL);
+		g_object_unref (reply);
+		free_property_filter_data (pfd);
 	}
-	g_clear_object (&subject);
 
-	return DBUS_HANDLER_RESULT_HANDLED;
+	return FALSE;
+}
+
+static GDBusMessage *
+prop_filter (GDBusConnection *connection,
+             GDBusMessage *message,
+             gboolean incoming,
+             gpointer user_data)
+{
+	gs_unref_object NMManager *self = NULL;
+	GVariant *args;
+	const char *propiface = NULL;
+	const char *propname = NULL;
+	const char *glib_propname = NULL, *permission = NULL;
+	const char *audit_op = NULL;
+	GType interface_type = G_TYPE_INVALID;
+	PropertyFilterData *pfd;
+	const GVariantType *expected_type = G_VARIANT_TYPE_BOOLEAN;
+	gs_unref_variant GVariant *value = NULL;
+
+	self = g_weak_ref_get (user_data);
+	if (!self)
+		return message;
+
+	/* The sole purpose of this function is to validate property accesses on the
+	 * NMManager object since gdbus doesn't give us this functionality.
+	 */
+
+	/* Only filter org.freedesktop.DBus.Properties.Set calls */
+	if (   !incoming
+	    || g_dbus_message_get_message_type (message) != G_DBUS_MESSAGE_TYPE_METHOD_CALL
+	    || g_strcmp0 (g_dbus_message_get_interface (message), DBUS_INTERFACE_PROPERTIES) != 0
+	    || g_strcmp0 (g_dbus_message_get_member (message), "Set") != 0)
+		return message;
+
+	args = g_dbus_message_get_body (message);
+	if (!g_variant_is_of_type (args, G_VARIANT_TYPE ("(ssv)")))
+		return message;
+	g_variant_get (args, "(&s&sv)", &propiface, &propname, &value);
+
+	/* Only filter calls to filtered properties, on existing objects */
+	if (!strcmp (propiface, NM_DBUS_INTERFACE)) {
+		if (!strcmp (propname, "WirelessEnabled")) {
+			glib_propname = NM_MANAGER_WIRELESS_ENABLED;
+			permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI;
+			audit_op = NM_AUDIT_OP_RADIO_CONTROL;
+		} else if (!strcmp (propname, "WwanEnabled")) {
+			glib_propname = NM_MANAGER_WWAN_ENABLED;
+			permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN;
+			audit_op = NM_AUDIT_OP_RADIO_CONTROL;
+		} else if (!strcmp (propname, "WimaxEnabled")) {
+			glib_propname = NM_MANAGER_WIMAX_ENABLED;
+			permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX;
+			audit_op = NM_AUDIT_OP_RADIO_CONTROL;
+		} else if (!strcmp (propname, "GlobalDnsConfiguration")) {
+			glib_propname = NM_MANAGER_GLOBAL_DNS_CONFIGURATION;
+			permission = NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS;
+			audit_op = NM_AUDIT_OP_NET_CONTROL;
+			expected_type = G_VARIANT_TYPE ("a{sv}");
+		} else
+			return message;
+		interface_type = NMDBUS_TYPE_MANAGER_SKELETON;
+	} else if (!strcmp (propiface, NM_DBUS_INTERFACE_DEVICE)) {
+		if (!strcmp (propname, "Autoconnect")) {
+			glib_propname = NM_DEVICE_AUTOCONNECT;
+			permission = NM_AUTH_PERMISSION_NETWORK_CONTROL;
+			audit_op = NM_AUDIT_OP_DEVICE_AUTOCONNECT;
+		} else if (!strcmp (propname, "Managed")) {
+			glib_propname = NM_DEVICE_MANAGED;
+			permission = NM_AUTH_PERMISSION_NETWORK_CONTROL;
+			audit_op = NM_AUDIT_OP_DEVICE_MANAGED;
+		} else
+			return message;
+		interface_type = NMDBUS_TYPE_DEVICE_SKELETON;
+	} else
+		return message;
+
+	if (!g_variant_is_of_type (value, expected_type))
+		return message;
+
+	/* This filter function is called from a gdbus worker thread which we can't
+	 * make other D-Bus calls from. In particular, we cannot call
+	 * org.freedesktop.DBus.GetConnectionUnixUser to find the remote UID.
+	 */
+	pfd = g_slice_new0 (PropertyFilterData);
+	pfd->self = self;
+	self = NULL;
+	pfd->connection = g_object_ref (connection);
+	pfd->message = message;
+	pfd->permission = permission;
+	pfd->interface_type = interface_type;
+	pfd->glib_propname = glib_propname;
+	pfd->audit_op = audit_op;
+	if (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN)) {
+		pfd->audit_prop_value = g_strdup_printf ("%s:%d", pfd->glib_propname,
+		                                         g_variant_get_boolean (value));
+	} else
+		pfd->audit_prop_value = g_strdup (pfd->glib_propname);
+
+	g_idle_add (do_set_property_check, pfd);
+
+	return NULL;
+}
+
+/******************************************************************************/
+
+static int
+_set_prop_filter_free2 (gpointer user_data)
+{
+	g_slice_free (GWeakRef, user_data);
+	return G_SOURCE_REMOVE;
+}
+
+static void
+_set_prop_filter_free (gpointer user_data)
+{
+	g_weak_ref_clear (user_data);
+
+	/* Delay the final deletion of the user_data. There is a race when
+	 * calling g_dbus_connection_remove_filter() that the callback and user_data
+	 * might have been copied and being executed after the destroy function
+	 * runs (bgo #704568).
+	 * This doesn't really fix the race, but it should work well enough. */
+	g_timeout_add_seconds (2, _set_prop_filter_free2, user_data);
 }
 
 static void
+_set_prop_filter (NMManager *self, GDBusConnection *connection)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+	nm_assert ((!priv->prop_filter.connection) == (!priv->prop_filter.id));
+
+	if (priv->prop_filter.connection == connection)
+		return;
+
+	if (priv->prop_filter.connection) {
+		g_dbus_connection_remove_filter (priv->prop_filter.connection, priv->prop_filter.id);
+		priv->prop_filter.id = 0;
+		g_clear_object (&priv->prop_filter.connection);
+	}
+	if (connection) {
+		GWeakRef *wptr;
+
+		wptr = g_slice_new (GWeakRef);
+		g_weak_ref_init  (wptr, self);
+		priv->prop_filter.id = g_dbus_connection_add_filter (connection, prop_filter, wptr, _set_prop_filter_free);
+		priv->prop_filter.connection = g_object_ref (connection);
+	}
+}
+
+/******************************************************************************/
+
+static void
 authority_changed_cb (NMAuthManager *auth_manager, gpointer user_data)
 {
 	/* Let clients know they should re-check their authorization */
@@ -4689,7 +5021,7 @@ periodic_update_active_connection_timestamps (gpointer user_data)
 		NMSettingsConnection *connection;
 
 		if (nm_active_connection_get_state (ac) == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
-			connection = NM_SETTINGS_CONNECTION (nm_active_connection_get_connection (ac));
+			connection = nm_active_connection_get_settings_connection (ac);
 			nm_settings_connection_update_timestamp (connection, (guint64) time (NULL), FALSE);
 		}
 	}
@@ -4698,148 +5030,126 @@ periodic_update_active_connection_timestamps (gpointer user_data)
 }
 
 static void
-dbus_connection_changed_cb (NMDBusManager *dbus_mgr,
-                            DBusConnection *dbus_connection,
+dbus_connection_changed_cb (NMBusManager *dbus_mgr,
+                            GDBusConnection *connection,
                             gpointer user_data)
 {
-	NMManager *self = NM_MANAGER (user_data);
-	gboolean success;
-
-	if (dbus_connection) {
-		/* Only fails on ENOMEM */
-		success = dbus_connection_add_filter (dbus_connection, prop_filter, self, NULL);
-		g_assert (success);
-	}
+	_set_prop_filter (NM_MANAGER (user_data), connection);
 }
 
 /**********************************************************************/
 
-static NMManager *singleton = NULL;
+NM_DEFINE_SINGLETON_REGISTER (NMManager);
 
 NMManager *
 nm_manager_get (void)
 {
-	g_assert (singleton);
-	return singleton;
+	g_assert (singleton_instance);
+	return singleton_instance;
 }
 
 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);
+	g_assert (singleton_instance);
+	g_assert (NM_MANAGER_GET_PRIVATE (singleton_instance)->settings);
+	return NM_CONNECTION_PROVIDER (NM_MANAGER_GET_PRIVATE (singleton_instance)->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)
+nm_manager_setup (const char *state_file,
+                  gboolean initial_net_enabled,
+                  gboolean initial_wifi_enabled,
+                  gboolean initial_wwan_enabled)
 {
-	NMManagerPrivate *priv;
-	DBusConnection *dbus_connection;
-	NMConfigData *config_data;
+	NMManager *self;
 
-	g_assert (settings);
+	g_assert (singleton_instance == NULL);
 
-	/* Can only be called once */
-	g_assert (singleton == NULL);
-	singleton = (NMManager *) g_object_new (NM_TYPE_MANAGER, NULL);
-	g_assert (singleton);
+	self = g_object_new (NM_TYPE_MANAGER,
+	                     NM_MANAGER_NETWORKING_ENABLED, initial_net_enabled,
+	                     NM_MANAGER_WIRELESS_ENABLED, initial_wifi_enabled,
+	                     NM_MANAGER_WWAN_ENABLED, initial_wwan_enabled,
+	                     NM_MANAGER_STATE_FILE, state_file,
+	                     NULL);
+	nm_assert (NM_IS_MANAGER (self));
+	singleton_instance = self;
 
-	priv = NM_MANAGER_GET_PRIVATE (singleton);
+	nm_singleton_instance_register ();
+	nm_log_dbg (LOGD_CORE, "setup %s singleton (%p)", "NMManager", singleton_instance);
 
-	dbus_connection = nm_dbus_manager_get_dbus_connection (priv->dbus_mgr);
-	if (dbus_connection) {
-		gboolean success;
+	nm_exported_object_export ((NMExportedObject *) self);
 
-		/* Only fails on ENOMEM */
-		success = dbus_connection_add_filter (dbus_connection, prop_filter, singleton, NULL);
-		g_assert (success);
-	}
+	return self;
+}
 
-	priv->policy = nm_policy_new (singleton, settings);
+static void
+constructed (GObject *object)
+{
+	NMManager *self = NM_MANAGER (object);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMConfigData *config_data;
+
+	G_OBJECT_CLASS (nm_manager_parent_class)->constructed (object);
+
+	_set_prop_filter (self, nm_bus_manager_get_connection (priv->dbus_mgr));
+
+	priv->settings = nm_settings_new ();
+	g_signal_connect (priv->settings, "notify::" NM_SETTINGS_STARTUP_COMPLETE,
+	                  G_CALLBACK (settings_startup_complete_changed), self);
+	g_signal_connect (priv->settings, "notify::" NM_SETTINGS_UNMANAGED_SPECS,
+	                  G_CALLBACK (system_unmanaged_devices_changed_cb), self);
+	g_signal_connect (priv->settings, "notify::" NM_SETTINGS_HOSTNAME,
+	                  G_CALLBACK (system_hostname_changed_cb), self);
+	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED,
+	                  G_CALLBACK (connection_changed), self);
+	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED_BY_USER,
+	                  G_CALLBACK (connection_changed), self);
+	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED,
+	                  G_CALLBACK (connection_removed), self);
+
+	priv->policy = nm_policy_new (self, priv->settings);
 	g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP4_DEVICE,
-	                  G_CALLBACK (policy_default_device_changed), singleton);
+	                  G_CALLBACK (policy_default_device_changed), self);
 	g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP6_DEVICE,
-	                  G_CALLBACK (policy_default_device_changed), singleton);
+	                  G_CALLBACK (policy_default_device_changed), self);
 	g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP4_DEVICE,
-	                  G_CALLBACK (policy_activating_device_changed), singleton);
+	                  G_CALLBACK (policy_activating_device_changed), self);
 	g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP6_DEVICE,
-	                  G_CALLBACK (policy_activating_device_changed), singleton);
+	                  G_CALLBACK (policy_activating_device_changed), self);
 
 	priv->config = g_object_ref (nm_config_get ());
 	g_signal_connect (G_OBJECT (priv->config),
 	                  NM_CONFIG_SIGNAL_CONFIG_CHANGED,
 	                  G_CALLBACK (_config_changed_cb),
-	                  singleton);
+	                  self);
 
 	config_data = nm_config_get_data (priv->config);
 	priv->connectivity = nm_connectivity_new (nm_config_data_get_connectivity_uri (config_data),
 	                                          nm_config_data_get_connectivity_interval (config_data),
 	                                          nm_config_data_get_connectivity_response (config_data));
 	g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_STATE,
-	                  G_CALLBACK (connectivity_changed), singleton);
-
-	priv->settings = g_object_ref (settings);
-	g_signal_connect (priv->settings, "notify::" NM_SETTINGS_STARTUP_COMPLETE,
-	                  G_CALLBACK (settings_startup_complete_changed), singleton);
-
-	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);
+	                  G_CALLBACK (connectivity_changed), self);
 
 	priv->rfkill_mgr = nm_rfkill_manager_new ();
 	g_signal_connect (priv->rfkill_mgr,
 	                  "rfkill-changed",
 	                  G_CALLBACK (rfkill_manager_rfkill_changed_cb),
-	                  singleton);
+	                  self);
 
 	/* 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);
-
-	nm_device_factory_manager_load_factories (_register_device_factory, singleton);
-
-	return singleton;
+	rfkill_change (priv->radio_states[RFKILL_TYPE_WLAN].desc, RFKILL_TYPE_WLAN, priv->radio_states[RFKILL_TYPE_WLAN].user_enabled);
+	rfkill_change (priv->radio_states[RFKILL_TYPE_WWAN].desc, RFKILL_TYPE_WWAN, priv->radio_states[RFKILL_TYPE_WWAN].user_enabled);
 }
 
 static void
 nm_manager_init (NMManager *manager)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	DBusGConnection *g_connection;
 	guint i;
 	GFile *file;
 
@@ -4860,13 +5170,6 @@ nm_manager_init (NMManager *manager)
 	priv->radio_states[RFKILL_TYPE_WWAN].desc = "WWAN";
 	priv->radio_states[RFKILL_TYPE_WWAN].rtype = RFKILL_TYPE_WWAN;
 
-	priv->radio_states[RFKILL_TYPE_WIMAX].user_enabled = TRUE;
-	priv->radio_states[RFKILL_TYPE_WIMAX].key = "WimaxEnabled";
-	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].rtype = RFKILL_TYPE_WIMAX;
-
 	for (i = 0; i < RFKILL_TYPE_MAX; i++)
 		priv->radio_states[i].hw_enabled = TRUE;
 
@@ -4874,39 +5177,12 @@ nm_manager_init (NMManager *manager)
 	priv->state = NM_STATE_DISCONNECTED;
 	priv->startup = TRUE;
 
-	priv->dbus_mgr = nm_dbus_manager_get ();
+	priv->dbus_mgr = g_object_ref (nm_bus_manager_get ());
 	g_signal_connect (priv->dbus_mgr,
-	                  NM_DBUS_MANAGER_DBUS_CONNECTION_CHANGED,
+	                  NM_BUS_MANAGER_DBUS_CONNECTION_CHANGED,
 	                  G_CALLBACK (dbus_connection_changed_cb),
 	                  manager);
 
-	priv->vpn_manager = g_object_ref (nm_vpn_manager_get ());
-
-	g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
-
-	/* avahi-autoipd stuff */
-	priv->aipd_proxy = dbus_g_proxy_new_for_name (g_connection,
-	                                              NM_AUTOIP_DBUS_SERVICE,
-	                                              "/",
-	                                              NM_AUTOIP_DBUS_IFACE);
-	if (priv->aipd_proxy) {
-		dbus_g_object_register_marshaller (g_cclosure_marshal_generic,
-		                                   G_TYPE_NONE,
-		                                   G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
-		                                   G_TYPE_INVALID);
-
-		dbus_g_proxy_add_signal (priv->aipd_proxy,
-		                         "Event",
-		                         G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
-		                         G_TYPE_INVALID);
-
-		dbus_g_proxy_connect_signal (priv->aipd_proxy, "Event",
-		                             G_CALLBACK (aipd_handle_event),
-		                             manager,
-		                             NULL);
-	} else
-		nm_log_warn (LOGD_AUTOIP4, "could not initialize avahi-autoipd D-Bus proxy");
-
 	/* sleep/wake handling */
 	priv->sleep_monitor = g_object_ref (nm_sleep_monitor_get ());
 	g_signal_connect (priv->sleep_monitor, NM_SLEEP_MONITOR_SLEEPING,
@@ -4945,15 +5221,20 @@ nm_manager_init (NMManager *manager)
 	priv->metered = NM_METERED_UNKNOWN;
 }
 
+static gboolean
+device_is_real (GObject *device, gpointer user_data)
+{
+	return nm_device_is_real (NM_DEVICE (device));
+}
+
 static void
 get_property (GObject *object, guint prop_id,
-			  GValue *value, GParamSpec *pspec)
+              GValue *value, GParamSpec *pspec)
 {
 	NMManager *self = NM_MANAGER (object);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GSList *iter;
-	GPtrArray *array;
-	const char *path;
+	NMConfigData *config_data;
+	const NMGlobalDnsConfig *dns_config;
 	const char *type;
 
 	switch (prop_id) {
@@ -4983,34 +5264,33 @@ get_property (GObject *object, guint prop_id,
 		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));
+		g_value_set_boolean (value, FALSE);
 		break;
 	case PROP_WIMAX_HARDWARE_ENABLED:
-		g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WIMAX].hw_enabled);
+		g_value_set_boolean (value, FALSE);
 		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);
+		nm_utils_g_value_set_object_path_array (value, priv->active_connections, NULL, NULL);
 		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 : "/");
+		nm_utils_g_value_set_object_path (value, priv->primary_connection);
 		break;
 	case PROP_PRIMARY_CONNECTION_TYPE:
-		type = priv->primary_connection ? nm_active_connection_get_connection_type (priv->primary_connection) : NULL;
+		type = NULL;
+		if (priv->primary_connection) {
+			NMConnection *con;
+
+			con = nm_active_connection_get_applied_connection (priv->primary_connection);
+			if (con)
+				type = nm_connection_get_connection_type (con);
+		}
 		g_value_set_string (value, type ? type : "");
 		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 : "/");
+		nm_utils_g_value_set_object_path (value, priv->activating_connection);
 		break;
 	case PROP_HOSTNAME:
 		g_value_set_string (value, priv->hostname);
@@ -5019,17 +5299,19 @@ get_property (GObject *object, guint prop_id,
 		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);
+		nm_utils_g_value_set_object_path_array (value, priv->devices, device_is_real, NULL);
 		break;
 	case PROP_METERED:
 		g_value_set_uint (value, priv->metered);
 		break;
+	case PROP_GLOBAL_DNS_CONFIGURATION:
+		config_data = nm_config_get_data (priv->config);
+		dns_config = nm_config_data_get_global_dns_config (config_data);
+		nm_global_dns_config_to_dbus (dns_config, value);
+		break;
+	case PROP_ALL_DEVICES:
+		nm_utils_g_value_set_object_path_array (value, priv->devices, NULL, NULL);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -5038,30 +5320,56 @@ get_property (GObject *object, guint prop_id,
 
 static void
 set_property (GObject *object, guint prop_id,
-			  const GValue *value, GParamSpec *pspec)
+              const GValue *value, GParamSpec *pspec)
 {
 	NMManager *self = NM_MANAGER (object);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMGlobalDnsConfig *dns_config;
+	GError *error = NULL;
 
 	switch (prop_id) {
+	case PROP_STATE_FILE:
+		/* construct-only */
+		priv->state_file = g_value_dup_string (value);
+		break;
 	case PROP_NETWORKING_ENABLED:
-		/* Construct only for now */
+		/* construct-only */
 		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));
+		if (!priv->rfkill_mgr) {
+			/* called during object construction. */
+			priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = g_value_get_boolean (value);
+		} else {
+			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));
+		if (!priv->rfkill_mgr) {
+			/* called during object construction. */
+			priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = g_value_get_boolean (value);
+		} else {
+			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));
+		/* WIMAX is depreacted. This does nothing. */
+		break;
+	case PROP_GLOBAL_DNS_CONFIGURATION:
+		dns_config = nm_global_dns_config_from_dbus (value, &error);
+		if (!error)
+			nm_config_set_global_dns (priv->config, dns_config, &error);
+
+		nm_global_dns_config_free (dns_config);
+
+		if (error) {
+			nm_log_dbg (LOGD_CORE, "set global DNS failed with error: %s", error->message);
+			g_error_free (error);
+		}
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -5080,7 +5388,6 @@ dispose (GObject *object)
 {
 	NMManager *manager = NM_MANAGER (object);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	DBusConnection *dbus_connection;
 
 	g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_unref);
 	priv->auth_chains = NULL;
@@ -5091,10 +5398,7 @@ dispose (GObject *object)
 
 	g_assert (priv->devices == NULL);
 
-	if (priv->ac_cleanup_id) {
-		g_source_remove (priv->ac_cleanup_id);
-		priv->ac_cleanup_id = 0;
-	}
+	nm_clear_g_source (&priv->ac_cleanup_id);
 
 	while (priv->active_connections)
 		active_connection_remove (manager, NM_ACTIVE_CONNECTION (priv->active_connections->data));
@@ -5123,25 +5427,21 @@ dispose (GObject *object)
 		g_signal_handlers_disconnect_by_func (priv->settings, settings_startup_complete_changed, manager);
 		g_signal_handlers_disconnect_by_func (priv->settings, system_unmanaged_devices_changed_cb, manager);
 		g_signal_handlers_disconnect_by_func (priv->settings, system_hostname_changed_cb, manager);
-		g_signal_handlers_disconnect_by_func (priv->settings, connection_added, manager);
 		g_signal_handlers_disconnect_by_func (priv->settings, connection_changed, manager);
 		g_signal_handlers_disconnect_by_func (priv->settings, connection_removed, manager);
 		g_clear_object (&priv->settings);
 	}
 
-	g_free (priv->state_file);
+	g_clear_pointer (&priv->state_file, g_free);
 	g_clear_object (&priv->vpn_manager);
 
 	/* Unregister property filter */
 	if (priv->dbus_mgr) {
-		dbus_connection = nm_dbus_manager_get_dbus_connection (priv->dbus_mgr);
-		if (dbus_connection)
-			dbus_connection_remove_filter (dbus_connection, prop_filter, manager);
 		g_signal_handlers_disconnect_by_func (priv->dbus_mgr, dbus_connection_changed_cb, manager);
-		priv->dbus_mgr = NULL;
+		g_clear_object (&priv->dbus_mgr);
 	}
+	_set_prop_filter (manager, NULL);
 
-	g_clear_object (&priv->aipd_proxy);
 	if (priv->sleep_monitor) {
 		g_signal_handlers_disconnect_by_func (priv->sleep_monitor, sleeping_cb, manager);
 		g_signal_handlers_disconnect_by_func (priv->sleep_monitor, resuming_cb, manager);
@@ -5151,10 +5451,7 @@ dispose (GObject *object)
 	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;
-		}
+		nm_clear_g_source (&priv->fw_changed_id);
 
 		g_file_monitor_cancel (priv->fw_monitor);
 		g_clear_object (&priv->fw_monitor);
@@ -5166,11 +5463,8 @@ dispose (GObject *object)
 	}
 
 	nm_device_factory_manager_for_each_factory (_deinit_device_factory, manager);
-	
-	if (priv->timestamp_update_id) {
-		g_source_remove (priv->timestamp_update_id);
-		priv->timestamp_update_id = 0;
-	}
+
+	nm_clear_g_source (&priv->timestamp_update_id);
 
 	G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object);
 }
@@ -5179,10 +5473,14 @@ static void
 nm_manager_class_init (NMManagerClass *manager_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (manager_class);
 
 	g_type_class_add_private (manager_class, sizeof (NMManagerPrivate));
 
+	exported_object_class->export_path = NM_DBUS_PATH;
+
 	/* virtual methods */
+	object_class->constructed = constructed;
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->dispose = dispose;
@@ -5195,6 +5493,14 @@ nm_manager_class_init (NMManagerClass *manager_class)
 		                      G_PARAM_READABLE |
 		                      G_PARAM_STATIC_STRINGS));
 
+	g_object_class_install_property (object_class,
+	                                 PROP_STATE_FILE,
+	                                 g_param_spec_string (NM_MANAGER_STATE_FILE, "", "",
+	                                                      NULL,
+	                                                      G_PARAM_WRITABLE |
+	                                                      G_PARAM_CONSTRUCT_ONLY |
+	                                                      G_PARAM_STATIC_STRINGS));
+
 	g_object_class_install_property
 		(object_class, PROP_STATE,
 		 g_param_spec_uint (NM_MANAGER_STATE, "", "",
@@ -5221,6 +5527,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 		 g_param_spec_boolean (NM_MANAGER_WIRELESS_ENABLED, "", "",
 		                       TRUE,
 		                       G_PARAM_READWRITE |
+		                       G_PARAM_CONSTRUCT |
 		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
@@ -5235,6 +5542,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 		 g_param_spec_boolean (NM_MANAGER_WWAN_ENABLED, "", "",
 		                       TRUE,
 		                       G_PARAM_READWRITE |
+		                       G_PARAM_CONSTRUCT |
 		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
@@ -5261,7 +5569,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	g_object_class_install_property
 		(object_class, PROP_ACTIVE_CONNECTIONS,
 		 g_param_spec_boxed (NM_MANAGER_ACTIVE_CONNECTIONS, "", "",
-		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
+		                     G_TYPE_STRV,
 		                     G_PARAM_READABLE |
 		                     G_PARAM_STATIC_STRINGS));
 
@@ -5274,10 +5582,10 @@ nm_manager_class_init (NMManagerClass *manager_class)
 
 	g_object_class_install_property
 		(object_class, PROP_PRIMARY_CONNECTION,
-		 g_param_spec_boxed (NM_MANAGER_PRIMARY_CONNECTION, "", "",
-		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READABLE |
-		                     G_PARAM_STATIC_STRINGS));
+		 g_param_spec_string (NM_MANAGER_PRIMARY_CONNECTION, "", "",
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_PRIMARY_CONNECTION_TYPE,
@@ -5289,10 +5597,10 @@ nm_manager_class_init (NMManagerClass *manager_class)
 
 	g_object_class_install_property
 		(object_class, PROP_ACTIVATING_CONNECTION,
-		 g_param_spec_boxed (NM_MANAGER_ACTIVATING_CONNECTION, "", "",
-		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READABLE |
-		                     G_PARAM_STATIC_STRINGS));
+		 g_param_spec_string (NM_MANAGER_ACTIVATING_CONNECTION, "", "",
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	/* Hostname is not exported over D-Bus */
 	g_object_class_install_property
@@ -5313,7 +5621,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	g_object_class_install_property
 		(object_class, PROP_DEVICES,
 		 g_param_spec_boxed (NM_MANAGER_DEVICES, "", "",
-		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
+		                     G_TYPE_STRV,
 		                     G_PARAM_READABLE |
 		                     G_PARAM_STATIC_STRINGS));
 
@@ -5322,7 +5630,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	 *
 	 * Whether the connectivity is metered.
 	 *
-	 * Since: 1.0.6
+	 * Since: 1.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_METERED,
@@ -5331,25 +5639,73 @@ nm_manager_class_init (NMManagerClass *manager_class)
 		                    G_PARAM_READABLE |
 		                    G_PARAM_STATIC_STRINGS));
 
+	/**
+	 * NMManager:global-dns-configuration:
+	 *
+	 * The global DNS configuration.
+	 *
+	 * Since: 1.2
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_GLOBAL_DNS_CONFIGURATION,
+		 g_param_spec_variant (NM_MANAGER_GLOBAL_DNS_CONFIGURATION, "", "",
+		                       G_VARIANT_TYPE ("a{sv}"),
+		                       NULL,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NMManager:all-devices:
+	 *
+	 * All devices, including those that are not realized.
+	 *
+	 * Since: 1.2
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_ALL_DEVICES,
+		 g_param_spec_boxed (NM_MANAGER_ALL_DEVICES, "", "",
+		                     G_TYPE_STRV,
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
+
 	/* signals */
+
+	/* D-Bus exported; emitted only for realized devices */
 	signals[DEVICE_ADDED] =
 		g_signal_new ("device-added",
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
 		              G_STRUCT_OFFSET (NMManagerClass, device_added),
 		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 1, NM_TYPE_DEVICE);
+
+	/* Emitted for both realized devices and placeholder devices */
+	signals[INTERNAL_DEVICE_ADDED] =
+		g_signal_new ("internal-device-added",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST, 0,
+		              NULL, NULL, NULL,
 		              G_TYPE_NONE, 1, G_TYPE_OBJECT);
 
+	/* D-Bus exported; emitted only for realized devices */
 	signals[DEVICE_REMOVED] =
 		g_signal_new ("device-removed",
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
 		              G_STRUCT_OFFSET (NMManagerClass, device_removed),
 		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 1, NM_TYPE_DEVICE);
+
+	/* Emitted for both realized devices and placeholder devices */
+	signals[INTERNAL_DEVICE_REMOVED] =
+		g_signal_new ("internal-device-removed",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST, 0,
+		              NULL, NULL, NULL,
 		              G_TYPE_NONE, 1, G_TYPE_OBJECT);
 
 	signals[STATE_CHANGED] =
-		g_signal_new ("state-changed",
+		g_signal_new (NM_MANAGER_STATE_CHANGED,
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
 		              G_STRUCT_OFFSET (NMManagerClass, state_changed),
@@ -5375,14 +5731,14 @@ nm_manager_class_init (NMManagerClass *manager_class)
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
 		              0, NULL, NULL, NULL,
-		              G_TYPE_NONE, 1, G_TYPE_OBJECT);
+		              G_TYPE_NONE, 1, NM_TYPE_ACTIVE_CONNECTION);
 
 	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, NULL,
-		              G_TYPE_NONE, 1, G_TYPE_OBJECT);
+		              G_TYPE_NONE, 1, NM_TYPE_ACTIVE_CONNECTION);
 
 	signals[CONFIGURE_QUIT] =
 		g_signal_new (NM_MANAGER_CONFIGURE_QUIT,
@@ -5391,10 +5747,21 @@ nm_manager_class_init (NMManagerClass *manager_class)
 		              0, NULL, NULL, NULL,
 		              G_TYPE_NONE, 0);
 
-	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, NM_DBUS_INTERFACE, NM_TYPE_MANAGER_ERROR);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (manager_class),
+	                                        NMDBUS_TYPE_MANAGER_SKELETON,
+	                                        "GetDevices", impl_manager_get_devices,
+	                                        "GetAllDevices", impl_manager_get_all_devices,
+	                                        "GetDeviceByIpIface", impl_manager_get_device_by_ip_iface,
+	                                        "ActivateConnection", impl_manager_activate_connection,
+	                                        "AddAndActivateConnection", impl_manager_add_and_activate_connection,
+	                                        "DeactivateConnection", impl_manager_deactivate_connection,
+	                                        "Sleep", impl_manager_sleep,
+	                                        "Enable", impl_manager_enable,
+	                                        "GetPermissions", impl_manager_get_permissions,
+	                                        "SetLogging", impl_manager_set_logging,
+	                                        "GetLogging", impl_manager_get_logging,
+	                                        "CheckConnectivity", impl_manager_check_connectivity,
+	                                        "state", impl_manager_get_state,
+	                                        NULL);
 }