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.c2525
1 files changed, 1407 insertions, 1118 deletions
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 8391dbf2..1ccfad8e 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -30,7 +30,7 @@
 #include <unistd.h>
 
 #include "nm-common-macros.h"
-#include "nm-bus-manager.h"
+#include "nm-dbus-manager.h"
 #include "vpn/nm-vpn-manager.h"
 #include "devices/nm-device.h"
 #include "devices/nm-device-generic.h"
@@ -56,12 +56,10 @@
 #include "nm-dbus-compat.h"
 #include "nm-checkpoint.h"
 #include "nm-checkpoint-manager.h"
+#include "nm-dbus-object.h"
 #include "nm-dispatcher.h"
 #include "NetworkManagerUtils.h"
 
-#include "introspection/org.freedesktop.NetworkManager.h"
-#include "introspection/org.freedesktop.NetworkManager.Device.h"
-
 /*****************************************************************************/
 
 typedef struct {
@@ -80,8 +78,6 @@ enum {
 	INTERNAL_DEVICE_ADDED,
 	DEVICE_REMOVED,
 	INTERNAL_DEVICE_REMOVED,
-	STATE_CHANGED,
-	CHECK_PERMISSIONS,
 	ACTIVE_CONNECTION_ADDED,
 	ACTIVE_CONNECTION_REMOVED,
 	CONFIGURE_QUIT,
@@ -114,6 +110,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMManager,
 	PROP_METERED,
 	PROP_GLOBAL_DNS_CONFIGURATION,
 	PROP_ALL_DEVICES,
+	PROP_CHECKPOINTS,
 
 	/* Not exported */
 	PROP_SLEEPING,
@@ -131,16 +128,14 @@ typedef struct {
 	NMActiveConnection *activating_connection;
 	NMMetered metered;
 
-	GSList *devices;
+	CList devices_lst_head;
+
 	NMState state;
 	NMConfig *config;
-	NMConnectivityState connectivity_state;
-
+	NMConnectivity *concheck_mgr;
 	NMPolicy *policy;
-
 	NMHostnameManager *hostname_manager;
 
-	NMBusManager  *dbus_mgr;
 	struct {
 		GDBusConnection *connection;
 		guint            id;
@@ -173,26 +168,30 @@ typedef struct {
 
 	guint devices_inited_id;
 
+	NMConnectivityState connectivity_state;
+
 	bool startup:1;
 	bool devices_inited:1;
 
 	bool sleeping:1;
 	bool net_enabled:1;
 
+	unsigned connectivity_check_enabled_last:2;
+
 	guint delete_volatile_connection_idle_id;
 	CList delete_volatile_connection_lst_head;
 } NMManagerPrivate;
 
 struct _NMManager {
-	NMExportedObject parent;
+	NMDBusObject parent;
 	NMManagerPrivate _priv;
 };
 
 typedef struct {
-	NMExportedObjectClass parent;
+	NMDBusObjectClass parent;
 } NMManagerClass;
 
-G_DEFINE_TYPE (NMManager, nm_manager, NM_TYPE_EXPORTED_OBJECT)
+G_DEFINE_TYPE (NMManager, nm_manager, NM_TYPE_DBUS_OBJECT)
 
 #define NM_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMManager, NM_IS_MANAGER)
 
@@ -269,15 +268,27 @@ NM_DEFINE_SINGLETON_INSTANCE (NMManager);
 
 /*****************************************************************************/
 
+static const NMDBusInterfaceInfoExtended interface_info_manager;
+static const GDBusSignalInfo signal_info_check_permissions;
+static const GDBusSignalInfo signal_info_state_changed;
+static const GDBusSignalInfo signal_info_device_added;
+static const GDBusSignalInfo signal_info_device_removed;
+
 static gboolean add_device (NMManager *self, NMDevice *device, GError **error);
 
+static void _emit_device_added_removed (NMManager *self,
+                                        NMDevice *device,
+                                        gboolean is_added);
+
 static NMActiveConnection *_new_active_connection (NMManager *self,
+                                                   gboolean is_vpn,
                                                    NMConnection *connection,
                                                    NMConnection *applied,
                                                    const char *specific_object,
                                                    NMDevice *device,
                                                    NMAuthSubject *subject,
                                                    NMActivationType activation_type,
+                                                   NMActivationReason activation_reason,
                                                    GError **error);
 
 static void policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data);
@@ -318,6 +329,8 @@ static NMActiveConnection *active_connection_find_first (NMManager *self,
                                                          const char *uuid,
                                                          NMActiveConnectionState max_state);
 
+static NMConnectivity *concheck_get_mgr (NMManager *self);
+
 /*****************************************************************************/
 
 static NM_CACHED_QUARK_FCN ("active-connection-add-and-activate", active_connection_add_and_activate_quark)
@@ -326,6 +339,73 @@ static NM_CACHED_QUARK_FCN ("autoconnect-root", autoconnect_root_quark)
 
 /*****************************************************************************/
 
+static gboolean
+_connection_is_vpn (NMConnection *connection)
+{
+	const char *type;
+
+	type = nm_connection_get_connection_type (connection);
+	if (type)
+		return nm_streq (type, NM_SETTING_VPN_SETTING_NAME);
+
+	/* we have an incomplete (invalid) connection at hand. That can only
+	 * happen during AddAndActivate. Determine whether it's VPN type based
+	 * on the existance of a [vpn] section. */
+	return !!nm_connection_get_setting_vpn (connection);
+}
+
+/*****************************************************************************/
+
+static gboolean
+concheck_enabled (NMManager *self, gboolean *out_changed)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	guint check_enabled;
+
+	check_enabled = nm_connectivity_check_enabled (concheck_get_mgr (self))
+	                ? 1 : 2;
+	if (priv->connectivity_check_enabled_last == check_enabled)
+		NM_SET_OUT (out_changed, FALSE);
+	else {
+		NM_SET_OUT (out_changed, TRUE);
+		priv->connectivity_check_enabled_last = check_enabled;
+	}
+	return check_enabled == 1;
+}
+
+static void
+concheck_config_changed_cb (NMConnectivity *connectivity,
+                            NMManager *self)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDevice *device;
+	gboolean changed;
+
+	concheck_enabled (self, &changed);
+	if (changed)
+		_notify (self, PROP_CONNECTIVITY_CHECK_ENABLED);
+
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst)
+		nm_device_check_connectivity_update_interval (device);
+}
+
+static NMConnectivity *
+concheck_get_mgr (NMManager *self)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+	if (G_UNLIKELY (!priv->concheck_mgr)) {
+		priv->concheck_mgr = g_object_ref (nm_connectivity_get ());
+		g_signal_connect (priv->concheck_mgr,
+		                  NM_CONNECTIVITY_CONFIG_CHANGED,
+		                  G_CALLBACK (concheck_config_changed_cb),
+		                  self);
+	}
+	return priv->concheck_mgr;
+}
+
+/*****************************************************************************/
+
 typedef struct {
 	int ifindex;
 	guint32 aspired_metric;
@@ -434,10 +514,10 @@ _device_route_metric_get (NMManager *self,
 				 * hence we skip it. */
 				continue;
 			}
-			if (!nm_g_hash_table_add (priv->device_route_metrics,
-			                          _device_route_metric_data_new (device_state->ifindex,
-			                                                         device_state->route_metric_default_aspired,
-			                                                         device_state->route_metric_default_effective)))
+			if (!g_hash_table_add (priv->device_route_metrics,
+			                       _device_route_metric_data_new (device_state->ifindex,
+			                                                      device_state->route_metric_default_aspired,
+			                                                      device_state->route_metric_default_effective)))
 				nm_assert_not_reached ();
 		}
 	}
@@ -530,7 +610,7 @@ again:
 	_LOGT (LOGD_DEVICE, "default-route-metric: ifindex %d reserves metric %u (aspired %u)",
 	       data->ifindex, data->effective_metric, data->aspired_metric);
 
-	if (!nm_g_hash_table_add (priv->device_route_metrics, data))
+	if (!g_hash_table_add (priv->device_route_metrics, data))
 		nm_assert_not_reached ();
 
 out:
@@ -577,7 +657,7 @@ _delete_volatile_connection_do (NMManager *self,
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 
 	if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (connection),
-	                   NM_SETTINGS_CONNECTION_FLAGS_VOLATILE))
+	                   NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE))
 		return;
 	if (active_connection_find_first (self,
 	                                  connection,
@@ -603,7 +683,7 @@ active_connection_remove (NMManager *self, NMActiveConnection *active)
 	nm_assert (NM_IS_ACTIVE_CONNECTION (active));
 	nm_assert (c_list_contains (&priv->active_connections_lst_head, &active->active_connections_lst));
 
-	notify = nm_exported_object_is_exported (NM_EXPORTED_OBJECT (active));
+	notify = nm_dbus_object_is_exported (NM_DBUS_OBJECT (active));
 
 	c_list_unlink (&active->active_connections_lst);
 	g_signal_emit (self, signals[ACTIVE_CONNECTION_REMOVED], 0, active);
@@ -613,7 +693,7 @@ active_connection_remove (NMManager *self, NMActiveConnection *active)
 
 	connection = nm_g_object_ref (nm_active_connection_get_settings_connection (active));
 
-	nm_exported_object_clear_and_unexport (&active);
+	nm_dbus_object_clear_and_unexport (&active);
 
 	if (connection)
 		_delete_volatile_connection_do (self, connection);
@@ -709,8 +789,8 @@ active_connection_add (NMManager *self,
 	                  G_CALLBACK (active_connection_default_changed),
 	                  self);
 
-	if (!nm_exported_object_is_exported (NM_EXPORTED_OBJECT (active)))
-		nm_exported_object_export (NM_EXPORTED_OBJECT (active));
+	if (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (active)))
+		nm_dbus_object_export (NM_DBUS_OBJECT (active));
 
 	g_signal_emit (self, signals[ACTIVE_CONNECTION_ADDED], 0, active);
 
@@ -774,7 +854,7 @@ _get_activatable_connections_filter (NMSettings *settings,
                                      gpointer user_data)
 {
 	if (NM_FLAGS_HAS (nm_settings_connection_get_flags (connection),
-	                  NM_SETTINGS_CONNECTION_FLAGS_VOLATILE))
+	                  NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE))
 		return FALSE;
 	return !active_connection_find_first (user_data, connection, NULL, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING);
 }
@@ -792,18 +872,20 @@ nm_manager_get_activatable_connections (NMManager *manager, guint *out_len, gboo
 }
 
 static NMActiveConnection *
-active_connection_get_by_path (NMManager *manager, const char *path)
+active_connection_get_by_path (NMManager *self, const char *path)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMActiveConnection *ac;
 
-	nm_assert (path);
+	ac = (NMActiveConnection *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)),
+	                                                           path);
+	if (   !ac
+	    || !NM_IS_ACTIVE_CONNECTION (ac)
+	    || c_list_is_empty (&ac->active_connections_lst))
+		return NULL;
 
-	c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) {
-		if (nm_streq0 (path, nm_exported_object_get_path (NM_EXPORTED_OBJECT (ac))))
-			return ac;
-	}
-	return NULL;
+	nm_assert (c_list_contains (&priv->active_connections_lst_head, &ac->active_connections_lst));
+	return ac;
 }
 
 /*****************************************************************************/
@@ -811,8 +893,14 @@ active_connection_get_by_path (NMManager *manager, const char *path)
 static void
 _config_changed_cb (NMConfig *config, NMConfigData *config_data, NMConfigChangeFlags changes, NMConfigData *old_data, NMManager *self)
 {
+	g_object_freeze_notify (G_OBJECT (self));
+
 	if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG))
 		_notify (self, PROP_GLOBAL_DNS_CONFIGURATION);
+	if ((!nm_config_data_get_connectivity_uri (config_data)) != (!nm_config_data_get_connectivity_uri (old_data)))
+		_notify (self, PROP_CONNECTIVITY_CHECK_AVAILABLE);
+
+	g_object_thaw_notify (G_OBJECT (self));
 }
 
 static void
@@ -883,28 +971,31 @@ _reload_auth_cb (NMAuthChain *chain,
 	g_dbus_method_invocation_return_value (context, NULL);
 
 out:
-	nm_auth_chain_unref (chain);
+	nm_auth_chain_destroy (chain);
 }
 
 static void
-impl_manager_reload (NMManager *self,
-                     GDBusMethodInvocation *context,
-                     guint32 flags)
-{
-	NMManagerPrivate *priv;
+impl_manager_reload (NMDBusObject *obj,
+                     const NMDBusInterfaceInfoExtended *interface_info,
+                     const NMDBusMethodInfoExtended *method_info,
+                     GDBusConnection *connection,
+                     const char *sender,
+                     GDBusMethodInvocation *invocation,
+                     GVariant *parameters)
+{
+	NMManager *self = NM_MANAGER (obj);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMAuthChain *chain;
-	GError *error = NULL;
-
-	g_return_if_fail (NM_IS_MANAGER (self));
+	guint32 flags;
 
-	priv = NM_MANAGER_GET_PRIVATE (self);
+	g_variant_get (parameters, "(u)", &flags);
 
-	chain = nm_auth_chain_new_context (context, _reload_auth_cb, self);
+	chain = nm_auth_chain_new_context (invocation, _reload_auth_cb, self);
 	if (!chain) {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             "Unable to authenticate request");
-		g_dbus_method_invocation_take_error (context, error);
+		g_dbus_method_invocation_return_error_literal (invocation,
+		                                               NM_MANAGER_ERROR,
+		                                               NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                                               "Unable to authenticate request");
 		return;
 	}
 
@@ -916,27 +1007,31 @@ impl_manager_reload (NMManager *self,
 /*****************************************************************************/
 
 NMDevice *
-nm_manager_get_device_by_path (NMManager *manager, const char *path)
+nm_manager_get_device_by_path (NMManager *self, const char *path)
 {
-	GSList *iter;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDevice *device;
 
-	g_return_val_if_fail (path != NULL, NULL);
+	g_return_val_if_fail (path, NULL);
 
-	for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) {
-		if (!strcmp (nm_exported_object_get_path (NM_EXPORTED_OBJECT (iter->data)), path))
-			return NM_DEVICE (iter->data);
-	}
-	return NULL;
+	device = (NMDevice *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)),
+	                                                     path);
+	if (   !device
+	    || !NM_IS_DEVICE (device)
+	    || c_list_is_empty (&device->devices_lst))
+		return NULL;
+
+	nm_assert (c_list_contains (&priv->devices_lst_head, &device->devices_lst));
+	return device;
 }
 
 NMDevice *
-nm_manager_get_device_by_ifindex (NMManager *manager, int ifindex)
+nm_manager_get_device_by_ifindex (NMManager *self, int ifindex)
 {
-	GSList *iter;
-
-	for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) {
-		NMDevice *device = NM_DEVICE (iter->data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDevice *device;
 
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
 		if (nm_device_get_ifindex (device) == ifindex)
 			return device;
 	}
@@ -945,19 +1040,24 @@ nm_manager_get_device_by_ifindex (NMManager *manager, int ifindex)
 }
 
 static NMDevice *
-find_device_by_permanent_hw_addr (NMManager *manager, const char *hwaddr)
+find_device_by_permanent_hw_addr (NMManager *self, const char *hwaddr)
 {
-	GSList *iter;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDevice *device;
 	const char *device_addr;
+	guint8 hwaddr_bin[NM_UTILS_HWADDR_LEN_MAX];
+	gsize hwaddr_len;
 
 	g_return_val_if_fail (hwaddr != NULL, NULL);
 
-	if (nm_utils_hwaddr_valid (hwaddr, -1)) {
-		for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) {
-			device_addr = nm_device_get_permanent_hw_address (NM_DEVICE (iter->data));
-			if (device_addr && nm_utils_hwaddr_matches (hwaddr, -1, device_addr, -1))
-				return NM_DEVICE (iter->data);
-		}
+	if (!_nm_utils_hwaddr_aton (hwaddr, hwaddr_bin, sizeof (hwaddr_bin), &hwaddr_len))
+		return NULL;
+
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+		device_addr = nm_device_get_permanent_hw_address (device);
+		if (   device_addr
+		    && nm_utils_hwaddr_matches (hwaddr_bin, hwaddr_len, device_addr, -1))
+			return device;
 	}
 	return NULL;
 }
@@ -965,16 +1065,15 @@ find_device_by_permanent_hw_addr (NMManager *manager, const char *hwaddr)
 static NMDevice *
 find_device_by_ip_iface (NMManager *self, const gchar *iface)
 {
-	GSList *iter;
-
-	g_return_val_if_fail (iface != NULL, NULL);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDevice *device;
 
-	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = g_slist_next (iter)) {
-		NMDevice *candidate = iter->data;
+	g_return_val_if_fail (iface, NULL);
 
-		if (   nm_device_is_real (candidate)
-		    && g_strcmp0 (nm_device_get_ip_iface (candidate), iface) == 0)
-			return candidate;
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+		if (   nm_device_is_real (device)
+		    && nm_streq0 (nm_device_get_ip_iface (device), iface))
+			return device;
 	}
 	return NULL;
 }
@@ -1002,12 +1101,11 @@ find_device_by_iface (NMManager *self,
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMDevice *fallback = NULL;
-	GSList *iter;
+	NMDevice *candidate;
 
 	g_return_val_if_fail (iface != NULL, NULL);
 
-	for (iter = priv->devices; iter; iter = iter->next) {
-		NMDevice *candidate = iter->data;
+	c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) {
 
 		if (strcmp (nm_device_get_iface (candidate), iface))
 			continue;
@@ -1062,22 +1160,6 @@ _nm_state_to_string (NMState state)
 	}
 }
 
-static void
-set_state (NMManager *self, NMState state)
-{
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-
-	if (priv->state == state)
-		return;
-
-	priv->state = state;
-
-	_LOGI (LOGD_CORE, "NetworkManager state is now %s", _nm_state_to_string (state));
-
-	_notify (self, PROP_STATE);
-	g_signal_emit (self, signals[STATE_CHANGED], 0, priv->state);
-}
-
 static NMState
 find_best_device_state (NMManager *manager)
 {
@@ -1148,26 +1230,38 @@ nm_manager_update_metered (NMManager *self)
 }
 
 static void
-nm_manager_update_state (NMManager *manager)
+nm_manager_update_state (NMManager *self)
 {
 	NMManagerPrivate *priv;
 	NMState new_state = NM_STATE_DISCONNECTED;
 
-	g_return_if_fail (NM_IS_MANAGER (manager));
+	g_return_if_fail (NM_IS_MANAGER (self));
 
-	priv = NM_MANAGER_GET_PRIVATE (manager);
+	priv = NM_MANAGER_GET_PRIVATE (self);
 
-	if (manager_sleeping (manager))
+	if (manager_sleeping (self))
 		new_state = NM_STATE_ASLEEP;
 	else
-		new_state = find_best_device_state (manager);
+		new_state = find_best_device_state (self);
 
 	if (   new_state >= NM_STATE_CONNECTED_LOCAL
 	    && priv->connectivity_state == NM_CONNECTIVITY_FULL) {
 		new_state = NM_STATE_CONNECTED_GLOBAL;
 	}
 
-	set_state (manager, new_state);
+	if (priv->state == new_state)
+		return;
+
+	priv->state = new_state;
+
+	_LOGI (LOGD_CORE, "NetworkManager state is now %s", _nm_state_to_string (new_state));
+
+	_notify (self, PROP_STATE);
+	nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
+	                            &interface_info_manager,
+	                            &signal_info_state_changed,
+	                            "(u)",
+	                            (guint32) priv->state);
 }
 
 static void
@@ -1180,6 +1274,10 @@ manager_device_state_changed (NMDevice *device,
 	NMManager *self = NM_MANAGER (user_data);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 
+	if (   old_state == NM_DEVICE_STATE_UNMANAGED
+	    && new_state > NM_DEVICE_STATE_UNMANAGED)
+		retry_connections_for_parent_device (self, device);
+
 	switch (new_state) {
 	case NM_DEVICE_STATE_UNMANAGED:
 	case NM_DEVICE_STATE_UNAVAILABLE:
@@ -1205,7 +1303,7 @@ static void
 check_if_startup_complete (NMManager *self)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GSList *iter;
+	NMDevice *device;
 
 	if (!priv->startup)
 		return;
@@ -1218,12 +1316,10 @@ check_if_startup_complete (NMManager *self)
 		return;
 	}
 
-	for (iter = priv->devices; iter; iter = iter->next) {
-		NMDevice *dev = iter->data;
-
-		if (nm_device_has_pending_action (dev)) {
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+		if (nm_device_has_pending_action (device)) {
 			_LOGD (LOGD_CORE, "check_if_startup_complete returns FALSE because of %s",
-			       nm_device_get_iface (dev));
+			       nm_device_get_iface (device));
 			return;
 		}
 	}
@@ -1235,8 +1331,8 @@ check_if_startup_complete (NMManager *self)
 	/* we no longer care about these signals. Startup-complete only
 	 * happens once. */
 	g_signal_handlers_disconnect_by_func (priv->settings, G_CALLBACK (settings_startup_complete_changed), self);
-	for (iter = priv->devices; iter; iter = iter->next) {
-		g_signal_handlers_disconnect_by_func (iter->data,
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+		g_signal_handlers_disconnect_by_func (device,
 		                                      G_CALLBACK (device_has_pending_action_changed),
 		                                      self);
 	}
@@ -1268,18 +1364,18 @@ _parent_notify_changed (NMManager *self,
                         NMDevice *device,
                         gboolean device_removed)
 {
-	GSList *iter;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDevice *candidate;
 
 	nm_assert (NM_IS_DEVICE (device));
-	nm_assert (NM_IS_MANAGER (self));
 
-	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; ) {
-		if (nm_device_parent_notify_changed (iter->data, device, device_removed)) {
+again:
+	c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) {
+		if (nm_device_parent_notify_changed (candidate, device, device_removed)) {
 			/* in the unlikely event that this changes anything, we start iterating
 			 * again, to be sure that the device list is up-to-date. */
-			iter = NM_MANAGER_GET_PRIVATE (self)->devices;
-		} else
-			iter = iter->next;
+			goto again;
+		}
 	}
 }
 
@@ -1319,7 +1415,8 @@ remove_device (NMManager *self,
 	g_signal_handlers_disconnect_matched (device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
 
 	nm_settings_device_removed (priv->settings, device, quitting);
-	priv->devices = g_slist_remove (priv->devices, device);
+
+	c_list_unlink (&device->devices_lst);
 
 	_parent_notify_changed (self, device, TRUE);
 
@@ -1337,8 +1434,7 @@ remove_device (NMManager *self,
 		 * Control that by passing @unconfigure_ip_config.  */
 		nm_device_removed (device, unconfigure_ip_config);
 
-		g_signal_emit (self, signals[DEVICE_REMOVED], 0, device);
-		_notify (self, PROP_DEVICES);
+		_emit_device_added_removed (self, device, FALSE);
 	} else {
 		/* unrealize() does not release a slave device from master and
 		 * clear IP configurations, do it here */
@@ -1348,7 +1444,7 @@ remove_device (NMManager *self,
 	g_signal_emit (self, signals[INTERNAL_DEVICE_REMOVED], 0, device);
 	_notify (self, PROP_ALL_DEVICES);
 
-	nm_exported_object_clear_and_unexport (&device);
+	nm_dbus_object_clear_and_unexport (&device);
 
 	check_if_startup_complete (self);
 }
@@ -1377,7 +1473,7 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection, NM
 	const char *parent_name = NULL;
 	NMSettingsConnection *parent_connection;
 	NMDevice *parent, *first_compatible = NULL;
-	GSList *iter;
+	NMDevice *candidate;
 
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 
@@ -1410,9 +1506,7 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection, NM
 	/* Check if the parent connection is currently activated or is comaptible
 	 * with some known device.
 	 */
-	for (iter = priv->devices; iter; iter = iter->next) {
-		NMDevice *candidate = iter->data;
-
+	c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) {
 		/* Unmanaged devices are not compatible with any connection */
 		if (!nm_device_get_managed (candidate, FALSE))
 			continue;
@@ -1528,18 +1622,15 @@ NMDevice *
 nm_manager_get_device (NMManager *self, const char *ifname, NMDeviceType device_type)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GSList *iter;
-	NMDevice *d;
+	NMDevice *device;
 
 	g_return_val_if_fail (ifname, NULL);
 	g_return_val_if_fail (device_type != NM_DEVICE_TYPE_UNKNOWN, NULL);
 
-	for (iter = priv->devices; iter; iter = iter->next) {
-		d = iter->data;
-
-		if (   nm_device_get_device_type (d) == device_type
-		    && nm_streq0 (nm_device_get_iface (d), ifname))
-			return d;
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+		if (   nm_device_get_device_type (device) == device_type
+		    && nm_streq0 (nm_device_get_iface (device), ifname))
+			return device;
 	}
 
 	return NULL;
@@ -1575,9 +1666,9 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 	NMDeviceFactory *factory;
 	gs_free NMSettingsConnection **connections = NULL;
 	guint i;
-	GSList *iter;
 	gs_free char *iface = NULL;
 	NMDevice *device = NULL, *parent = NULL;
+	NMDevice *dev_candidate;
 	GError *error = NULL;
 	NMLogLevel log_level;
 
@@ -1593,17 +1684,15 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 	}
 
 	/* 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 (nm_device_check_connection_compatible (candidate, connection)) {
-			if (nm_device_is_real (candidate)) {
+	c_list_for_each_entry (dev_candidate, &priv->devices_lst_head, devices_lst) {
+		if (nm_device_check_connection_compatible (dev_candidate, connection)) {
+			if (nm_device_is_real (dev_candidate)) {
 				_LOG3D (LOGD_DEVICE, connection, "already created virtual interface name %s",
 				       iface);
 				return NULL;
 			}
 
-			device = candidate;
+			device = dev_candidate;
 			break;
 		}
 	}
@@ -1799,7 +1888,7 @@ connection_flags_changed (NMSettings *settings,
 	DeleteVolatileConnectionData *data;
 
 	if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (connection),
-	                   NM_SETTINGS_CONNECTION_FLAGS_VOLATILE))
+	                   NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE))
 		return;
 
 	if (active_connection_find_first (self, connection, NULL, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)) {
@@ -1824,10 +1913,10 @@ system_unmanaged_devices_changed_cb (NMSettings *settings,
 {
 	NMManager *self = NM_MANAGER (user_data);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	const GSList *iter;
+	NMDevice *device;
 
-	for (iter = priv->devices; iter; iter = g_slist_next (iter))
-		nm_device_set_unmanaged_by_user_settings (NM_DEVICE (iter->data));
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst)
+		nm_device_set_unmanaged_by_user_settings (device);
 }
 
 static void
@@ -1873,7 +1962,7 @@ manager_update_radio_enabled (NMManager *self,
                               gboolean enabled)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GSList *iter;
+	NMDevice *device;
 
 	/* Do nothing for radio types not yet implemented */
 	if (!rstate->prop)
@@ -1886,9 +1975,7 @@ manager_update_radio_enabled (NMManager *self,
 		return;
 
 	/* enable/disable wireless devices as required */
-	for (iter = priv->devices; iter; iter = iter->next) {
-		NMDevice *device = NM_DEVICE (iter->data);
-
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
 		if (nm_device_get_rfkill_type (device) == rstate->rtype) {
 			_LOG2D (LOGD_RFKILL, device, "rfkill: setting radio %s", enabled ? "enabled" : "disabled");
 			nm_device_set_enabled (device, enabled);
@@ -2031,7 +2118,7 @@ device_auth_done_cb (NMAuthChain *chain,
 	          nm_auth_chain_get_data (chain, "user-data"));
 
 	g_clear_error (&error);
-	nm_auth_chain_unref (chain);
+	nm_auth_chain_destroy (chain);
 }
 
 static void
@@ -2047,7 +2134,6 @@ device_auth_request_cb (NMDevice *device,
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GError *error = NULL;
 	NMAuthSubject *subject = NULL;
-	char *error_desc = NULL;
 	NMAuthChain *chain;
 
 	/* Validate the caller */
@@ -2060,15 +2146,13 @@ device_auth_request_cb (NMDevice *device,
 	}
 
 	/* Ensure the subject has permissions for this connection */
-	if (connection && !nm_auth_is_subject_in_acl (connection,
-	                                              subject,
-	                                              &error_desc)) {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             error_desc);
-		g_free (error_desc);
+	if (   connection
+	    && !nm_auth_is_subject_in_acl_set_error (connection,
+	                                             subject,
+	                                             NM_MANAGER_ERROR,
+	                                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+	                                             &error))
 		goto done;
-	}
 
 	/* Validate the request */
 	chain = nm_auth_chain_new_subject (subject, context, device_auth_done_cb, self);
@@ -2112,13 +2196,14 @@ get_existing_connection (NMManager *self,
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingsConnection *added = NULL;
 	GError *error = NULL;
+	gs_free_error GError *gen_error = NULL;
 	NMDevice *master = NULL;
 	int ifindex = nm_device_get_ifindex (device);
 	NMSettingsConnection *matched;
 	NMSettingsConnection *connection_checked = NULL;
 	gboolean assume_state_guess_assume = FALSE;
 	const char *assume_state_connection_uuid = NULL;
-	gboolean maybe_later;
+	gboolean maybe_later, only_by_uuid = FALSE;
 
 	if (out_generated)
 		*out_generated = FALSE;
@@ -2151,14 +2236,18 @@ get_existing_connection (NMManager *self,
 	 * update_connection() implemented, otherwise nm_device_generate_connection()
 	 * returns NULL.
 	 */
-	connection = nm_device_generate_connection (device, master, &maybe_later, &error);
+	connection = nm_device_generate_connection (device, master, &maybe_later, &gen_error);
 	if (!connection) {
-		if (!maybe_later)
+		if (maybe_later) {
+			/* The device can generate a connection, but it failed for now.
+			 * Give it a chance to match a connection from the state file. */
+			only_by_uuid = TRUE;
+		} else {
 			nm_device_assume_state_reset (device);
-		_LOG2D (LOGD_DEVICE, device, "assume: cannot generate connection: %s",
-		        error->message);
-		g_error_free (error);
-		return NULL;
+			_LOG2D (LOGD_DEVICE, device, "assume: cannot generate connection: %s",
+			        gen_error->message);
+			return NULL;
+		}
 	}
 
 	nm_device_assume_state_get (device,
@@ -2179,21 +2268,31 @@ get_existing_connection (NMManager *self,
 	    && !active_connection_find_first (self, connection_checked, NULL,
 	                                      NM_ACTIVE_CONNECTION_STATE_DEACTIVATING)
 	    && nm_device_check_connection_compatible (device, NM_CONNECTION (connection_checked))) {
-		NMConnection *const connections[] = {
-			NM_CONNECTION (connection_checked),
-			NULL,
-		};
-
-		matched = NM_SETTINGS_CONNECTION (nm_utils_match_connection (connections,
-		                                                             connection,
-		                                                             TRUE,
-		                                                             nm_device_has_carrier (device),
-		                                                             nm_device_get_route_metric (device, AF_INET),
-		                                                             nm_device_get_route_metric (device, AF_INET6),
-		                                                             NULL, NULL));
+
+		if (connection) {
+			NMConnection *const connections[] = {
+				NM_CONNECTION (connection_checked),
+				NULL,
+			};
+
+			matched = NM_SETTINGS_CONNECTION (nm_utils_match_connection (connections,
+			                                                             connection,
+			                                                             TRUE,
+			                                                             nm_device_has_carrier (device),
+			                                                             nm_device_get_route_metric (device, AF_INET),
+			                                                             nm_device_get_route_metric (device, AF_INET6),
+			                                                             NULL, NULL));
+		} else
+			matched = connection_checked;
 	} else
 		matched = NULL;
 
+	if (!matched && only_by_uuid) {
+		_LOG2D (LOGD_DEVICE, device, "assume: cannot generate connection: %s",
+		        gen_error->message);
+		return NULL;
+	}
+
 	if (!matched && assume_state_guess_assume) {
 		gs_free NMSettingsConnection **connections = NULL;
 		guint len, i, j;
@@ -2250,8 +2349,8 @@ get_existing_connection (NMManager *self,
 	}
 
 	nm_settings_connection_set_flags (NM_SETTINGS_CONNECTION (added),
-	                                  NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED |
-	                                  NM_SETTINGS_CONNECTION_FLAGS_VOLATILE,
+	                                  NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED |
+	                                  NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE,
 	                                  TRUE);
 	NM_SET_OUT (out_generated, TRUE);
 	return added;
@@ -2315,14 +2414,20 @@ recheck_assume_connection (NMManager *self,
 		GError *error = NULL;
 
 		subject = nm_auth_subject_new_internal ();
-		active = _new_active_connection (self, NM_CONNECTION (connection), NULL, NULL,
-		                                 device, subject,
+		active = _new_active_connection (self,
+		                                 FALSE,
+		                                 NM_CONNECTION (connection),
+		                                 NULL,
+		                                 NULL,
+		                                 device,
+		                                 subject,
 		                                 generated ? NM_ACTIVATION_TYPE_EXTERNAL : NM_ACTIVATION_TYPE_ASSUME,
+		                                 NM_ACTIVATION_REASON_AUTOCONNECT,
 		                                 &error);
 
 		if (!active) {
 			_LOGW (LOGD_DEVICE, "assume: assumed connection %s failed to activate: %s",
-			       nm_connection_get_path (NM_CONNECTION (connection)),
+			       nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)),
 			       error->message);
 			g_error_free (error);
 
@@ -2373,18 +2478,17 @@ device_ip_iface_changed (NMDevice *device,
                          GParamSpec *pspec,
                          NMManager *self)
 {
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	const char *ip_iface = nm_device_get_ip_iface (device);
 	NMDeviceType device_type = nm_device_get_device_type (device);
-	GSList *iter;
+	NMDevice *candidate;
 
 	/* Remove NMDevice objects that are actually child devices of others,
 	 * when the other device finally knows its IP interface name.  For example,
 	 * remove the PPP interface that's a child of a WWAN device, since it's
 	 * not really a standalone NMDevice.
 	 */
-	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) {
-		NMDevice *candidate = NM_DEVICE (iter->data);
-
+	c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) {
 		if (   candidate != device
 		    && g_strcmp0 (nm_device_get_iface (candidate), ip_iface) == 0
 		    && nm_device_get_device_type (candidate) == device_type
@@ -2406,35 +2510,56 @@ device_iface_changed (NMDevice *device,
 	retry_connections_for_parent_device (self, device);
 }
 
+static void
+_emit_device_added_removed (NMManager *self,
+                            NMDevice *device,
+                            gboolean is_added)
+{
+	nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
+	                            &interface_info_manager,
+	                            is_added
+	                              ? &signal_info_device_added
+	                              : &signal_info_device_removed,
+	                            "(o)",
+	                            nm_dbus_object_get_path (NM_DBUS_OBJECT (device)));
+	g_signal_emit (self,
+	               signals[is_added ? DEVICE_ADDED : DEVICE_REMOVED],
+	               0,
+	               device);
+	_notify (self, PROP_DEVICES);
+}
 
 static void
 device_realized (NMDevice *device,
                  GParamSpec *pspec,
                  NMManager *self)
 {
-	gboolean real = nm_device_is_real (device);
-
-	/* Emit D-Bus signals */
-	g_signal_emit (self, signals[real ? DEVICE_ADDED : DEVICE_REMOVED], 0, device);
-	_notify (self, PROP_DEVICES);
+	_emit_device_added_removed (self, device, nm_device_is_real (device));
 }
 
-#if WITH_CONCHECK
 static void
 device_connectivity_changed (NMDevice *device,
-                             GParamSpec *pspec,
                              NMManager *self)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMConnectivityState best_state = NM_CONNECTIVITY_UNKNOWN;
 	NMConnectivityState state;
-	const GSList *devices;
+	NMDevice *dev;
 
-	for (devices = priv->devices; devices; devices = devices->next) {
-		state = nm_device_get_connectivity_state (NM_DEVICE (devices->data));
-		if (state > best_state)
+	best_state = nm_device_get_connectivity_state (device);
+	if (best_state < NM_CONNECTIVITY_FULL) {
+		c_list_for_each_entry (dev, &priv->devices_lst_head, devices_lst) {
+			state = nm_device_get_connectivity_state (dev);
+			if (state <= best_state)
+				continue;
 			best_state = state;
+			if (best_state >= NM_CONNECTIVITY_FULL) {
+				/* it doesn't get better than this. */
+				break;
+			}
+		}
 	}
+	nm_assert (best_state <= NM_CONNECTIVITY_FULL);
 
 	if (best_state != priv->connectivity_state) {
 		priv->connectivity_state = best_state;
@@ -2447,7 +2572,6 @@ device_connectivity_changed (NMDevice *device,
 		nm_dispatcher_call_connectivity (priv->connectivity_state, NULL, NULL, NULL);
 	}
 }
-#endif
 
 static void
 _device_realize_finish (NMManager *self,
@@ -2493,6 +2617,7 @@ add_device (NMManager *self, NMDevice *device, GError **error)
 	GSList *iter, *remove = NULL;
 	int ifindex;
 	const char *dbus_path;
+	NMDevice *candidate;
 
 	/* No duplicates */
 	ifindex = nm_device_get_ifindex (device);
@@ -2509,18 +2634,20 @@ add_device (NMManager *self, NMDevice *device, GError **error)
 	 * FIXME: use parent/child device relationships instead of removing
 	 * the child NMDevice entirely
 	 */
-	for (iter = priv->devices; iter; iter = iter->next) {
-		NMDevice *candidate = iter->data;
-
-		iface = nm_device_get_ip_iface (candidate);
-		if (nm_device_is_real (candidate) && nm_device_owns_iface (device, iface))
+	c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) {
+		if (   nm_device_is_real (candidate)
+		    && (iface = nm_device_get_ip_iface (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);
 	g_slist_free (remove);
 
-	priv->devices = g_slist_append (priv->devices, g_object_ref (device));
+	g_object_ref (device);
+
+	nm_assert (c_list_is_empty (&device->devices_lst));
+	c_list_link_tail (&priv->devices_lst_head, &device->devices_lst);
 
 	g_signal_connect (device, NM_DEVICE_STATE_CHANGED,
 	                  G_CALLBACK (manager_device_state_changed),
@@ -2554,11 +2681,9 @@ add_device (NMManager *self, NMDevice *device, GError **error)
 	                  G_CALLBACK (device_realized),
 	                  self);
 
-#if WITH_CONCHECK
-	g_signal_connect (device, "notify::" NM_DEVICE_CONNECTIVITY,
+	g_signal_connect (device, NM_DEVICE_CONNECTIVITY_CHANGED,
 	                  G_CALLBACK (device_connectivity_changed),
 	                  self);
-#endif
 
 	if (priv->startup) {
 		g_signal_connect (device, "notify::" NM_DEVICE_HAS_PENDING_ACTION,
@@ -2587,7 +2712,7 @@ add_device (NMManager *self, NMDevice *device, GError **error)
 	                               NM_UNMANAGED_SLEEPING,
 	                               manager_sleeping (self));
 
-	dbus_path = nm_exported_object_export (NM_EXPORTED_OBJECT (device));
+	dbus_path = nm_dbus_object_export (NM_DBUS_OBJECT (device));
 	_LOG2I (LOGD_DEVICE, device, "new %s device (%s)", type_desc, dbus_path);
 
 	nm_settings_device_added (priv->settings, device);
@@ -2634,12 +2759,11 @@ factory_component_added_cb (NMDeviceFactory *factory,
                             gpointer user_data)
 {
 	NMManager *self = user_data;
-	GSList *iter;
-
-	g_return_val_if_fail (self, FALSE);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDevice *device;
 
-	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) {
-		if (nm_device_notify_component_added ((NMDevice *) iter->data, component))
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+		if (nm_device_notify_component_added (device, component))
 			return TRUE;
 	}
 	return FALSE;
@@ -2669,9 +2793,10 @@ platform_link_added (NMManager *self,
                      gboolean guess_assume,
                      const NMConfigDeviceStateData *dev_state)
 {
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMDeviceFactory *factory;
 	NMDevice *device = NULL;
-	GSList *iter;
+	NMDevice *candidate;
 
 	g_return_if_fail (ifindex > 0);
 
@@ -2679,8 +2804,7 @@ platform_link_added (NMManager *self,
 		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;
+	c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) {
 		gboolean compatible = TRUE;
 		gs_free_error GError *error = NULL;
 
@@ -2907,12 +3031,12 @@ rfkill_manager_rfkill_changed_cb (NMRfkillManager *rfkill_mgr,
 	nm_manager_rfkill_update (NM_MANAGER (user_data), rtype);
 }
 
-const GSList *
+const CList *
 nm_manager_get_devices (NMManager *manager)
 {
 	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
 
-	return NM_MANAGER_GET_PRIVATE (manager)->devices;
+	return &NM_MANAGER_GET_PRIVATE (manager)->devices_lst_head;
 }
 
 static NMDevice *
@@ -2921,9 +3045,10 @@ nm_manager_get_best_device_for_connection (NMManager *self,
                                            gboolean for_user_request,
                                            GHashTable *unavailable_devices)
 {
-	const GSList *devices, *iter;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMActiveConnection *ac;
 	NMDevice *act_device;
+	NMDevice *device;
 	NMDeviceCheckConAvailableFlags flags;
 
 	ac = active_connection_find_first_by_connection (self, connection);
@@ -2936,9 +3061,7 @@ nm_manager_get_best_device_for_connection (NMManager *self,
 	flags = for_user_request ? NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST : NM_DEVICE_CHECK_CON_AVAILABLE_NONE;
 
 	/* Pick the first device that's compatible with the connection. */
-	devices = nm_manager_get_devices (self);
-	for (iter = devices; iter; iter = g_slist_next (iter)) {
-		NMDevice *device = NM_DEVICE (iter->data);
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
 
 		if (unavailable_devices && g_hash_table_contains (unavailable_devices, device))
 			continue;
@@ -2951,67 +3074,100 @@ nm_manager_get_best_device_for_connection (NMManager *self,
 	return NULL;
 }
 
-static void
-_get_devices (NMManager *self,
-              GDBusMethodInvocation *context,
-              gboolean all_devices)
+static const char **
+_get_devices_paths (NMManager *self,
+                    gboolean all_devices)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	gs_free const char **paths = NULL;
+	const char **paths = NULL;
 	guint i;
-	GSList *iter;
+	NMDevice *device;
 
-	paths = g_new (const char *, g_slist_length (priv->devices) + 1);
+	paths = g_new (const char *, c_list_length (&priv->devices_lst_head) + 1);
 
-	for (i = 0, iter = priv->devices; iter; iter = iter->next) {
+	i = 0;
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
 		const char *path;
 
-		path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (iter->data));
-		if (   path
-		    && (all_devices || nm_device_is_real (iter->data)))
-			paths[i++] = path;
+		path = nm_dbus_object_get_path (NM_DBUS_OBJECT (device));
+		if (!path)
+			continue;
+
+		if (   !all_devices
+		    && !nm_device_is_real (device))
+			continue;
+
+		paths[i++] = path;
 	}
 	paths[i++] = NULL;
 
-	g_dbus_method_invocation_return_value (context,
-	                                       g_variant_new ("(^ao)", (char **) paths));
+	return paths;
 }
 
 static void
-impl_manager_get_devices (NMManager *self,
-                          GDBusMethodInvocation *context)
-{
-	_get_devices (self, context, FALSE);
+impl_manager_get_devices (NMDBusObject *obj,
+                          const NMDBusInterfaceInfoExtended *interface_info,
+                          const NMDBusMethodInfoExtended *method_info,
+                          GDBusConnection *connection,
+                          const char *sender,
+                          GDBusMethodInvocation *invocation,
+                          GVariant *parameters)
+{
+	NMManager *self = NM_MANAGER (obj);
+	gs_free const char **paths = NULL;
+
+	paths = _get_devices_paths (self, FALSE);
+	g_dbus_method_invocation_return_value (invocation,
+	                                       g_variant_new ("(^ao)", (char **) paths));
 }
 
 static void
-impl_manager_get_all_devices (NMManager *self,
-                              GDBusMethodInvocation *context)
-{
-	_get_devices (self, context, TRUE);
+impl_manager_get_all_devices (NMDBusObject *obj,
+                              const NMDBusInterfaceInfoExtended *interface_info,
+                              const NMDBusMethodInfoExtended *method_info,
+                              GDBusConnection *connection,
+                              const char *sender,
+                              GDBusMethodInvocation *invocation,
+                              GVariant *parameters)
+{
+	NMManager *self = NM_MANAGER (obj);
+	gs_free const char **paths = NULL;
+
+	paths = _get_devices_paths (self, TRUE);
+	g_dbus_method_invocation_return_value (invocation,
+	                                       g_variant_new ("(^ao)", (char **) paths));
 }
 
 static void
-impl_manager_get_device_by_ip_iface (NMManager *self,
-                                     GDBusMethodInvocation *context,
-                                     const char *iface)
-{
+impl_manager_get_device_by_ip_iface (NMDBusObject *obj,
+                                     const NMDBusInterfaceInfoExtended *interface_info,
+                                     const NMDBusMethodInfoExtended *method_info,
+                                     GDBusConnection *connection,
+                                     const char *sender,
+                                     GDBusMethodInvocation *invocation,
+                                     GVariant *parameters)
+{
+	NMManager *self = NM_MANAGER (obj);
 	NMDevice *device;
 	const char *path = NULL;
+	const char *iface;
+
+	g_variant_get (parameters, "(&s)", &iface);
 
 	device = find_device_by_ip_iface (self, iface);
 	if (device)
-		path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device));
+		path = nm_dbus_object_get_path (NM_DBUS_OBJECT (device));
 
-	if (path == NULL) {
-		g_dbus_method_invocation_return_error (context,
+	if (!path) {
+		g_dbus_method_invocation_return_error (invocation,
 		                                       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;
 	}
+
+	g_dbus_method_invocation_return_value (invocation,
+	                                       g_variant_new ("(o)", path));
 }
 
 static gboolean
@@ -3084,7 +3240,6 @@ find_master (NMManager *self,
 	const char *master;
 	NMDevice *master_device = NULL;
 	NMSettingsConnection *master_connection = NULL;
-	GSList *iter;
 
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
@@ -3113,10 +3268,10 @@ find_master (NMManager *self,
 		/* Try master as a connection UUID */
 		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)) {
-				NMDevice *candidate = NM_DEVICE (iter->data);
+			NMDevice *candidate;
 
+			/* Check if the master connection is activated on some device already */
+			c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) {
 				if (candidate == device)
 					continue;
 
@@ -3154,6 +3309,7 @@ find_master (NMManager *self,
  * @device: the #NMDevice, if any, which will activate @connection
  * @master_connection: the master connection, or %NULL
  * @master_device: the master device, or %NULL
+ * @activation_reason: the reason for activation
  * @error: the error, if an error occurred
  *
  * Determines whether a given #NMConnection depends on another connection to
@@ -3181,12 +3337,12 @@ ensure_master_active_connection (NMManager *self,
                                  NMDevice *device,
                                  NMSettingsConnection *master_connection,
                                  NMDevice *master_device,
+                                 NMActivationReason activation_reason,
                                  GError **error)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMActiveConnection *master_ac = NULL;
 	NMDeviceState master_state;
-	GSList *iter;
 
 	g_assert (connection);
 	g_assert (master_connection || master_device);
@@ -3244,6 +3400,7 @@ ensure_master_active_connection (NMManager *self,
 					                                            master_device,
 					                                            subject,
 					                                            NM_ACTIVATION_TYPE_MANAGED,
+					                                            activation_reason,
 					                                            error);
 					return master_ac;
 				}
@@ -3262,12 +3419,10 @@ ensure_master_active_connection (NMManager *self,
 		             NM_MANAGER_ERROR_DEPENDENCY_FAILED,
 		             "Device unmanaged or not available for activation");
 	} else if (master_connection) {
-		gboolean found_device = FALSE;
+		NMDevice *candidate;
 
 		/* Find a compatible device and activate it using this connection */
-		for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
-			NMDevice *candidate = NM_DEVICE (iter->data);
-
+		c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) {
 			if (candidate == device) {
 				/* A device obviously can't be its own master */
 				continue;
@@ -3276,7 +3431,6 @@ ensure_master_active_connection (NMManager *self,
 			if (!nm_device_check_connection_available (candidate, NM_CONNECTION (master_connection), NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, NULL))
 				continue;
 
-			found_device = TRUE;
 			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)
@@ -3290,6 +3444,7 @@ ensure_master_active_connection (NMManager *self,
 			                                            candidate,
 			                                            subject,
 			                                            NM_ACTIVATION_TYPE_MANAGED,
+			                                            activation_reason,
 			                                            error);
 			return master_ac;
 		}
@@ -3341,7 +3496,7 @@ find_slaves (NMManager *manager,
 	s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection));
 	g_return_val_if_fail (s_con, NULL);
 
-	devices = g_hash_table_new (g_direct_hash, g_direct_equal);
+	devices = g_hash_table_new (nm_direct_hash, NULL);
 
 	/* Search through all connections, not only inactive ones, because
 	 * even if a slave was already active, it might be deactivated during
@@ -3512,6 +3667,7 @@ autoconnect_slaves (NMManager *self,
 			                                slave->device,
 			                                subject,
 			                                NM_ACTIVATION_TYPE_MANAGED,
+			                                NM_ACTIVATION_REASON_AUTOCONNECT_SLAVES,
 			                                &local_err);
 			if (local_err) {
 				_LOGW (LOGD_CORE, "Slave connection activation failed: %s", local_err->message);
@@ -3526,11 +3682,11 @@ _internal_activate_vpn (NMManager *self, NMActiveConnection *active, GError **er
 {
 	nm_assert (NM_IS_VPN_CONNECTION (active));
 
-	nm_exported_object_export (NM_EXPORTED_OBJECT (active));
+	nm_dbus_object_export (NM_DBUS_OBJECT (active));
 	if (!nm_vpn_manager_activate_connection (NM_MANAGER_GET_PRIVATE (self)->vpn_manager,
 	                                         NM_VPN_CONNECTION (active),
 	                                         error)) {
-		nm_exported_object_unexport (NM_EXPORTED_OBJECT (active));
+		nm_dbus_object_unexport (NM_DBUS_OBJECT (active));
 		return FALSE;
 	}
 
@@ -3625,7 +3781,6 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 	NMConnection *existing_connection = NULL;
 	NMActiveConnection *master_ac = 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);
@@ -3633,14 +3788,14 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 
 	g_assert (NM_IS_VPN_CONNECTION (active) == FALSE);
 
+	device = nm_active_connection_get_device (active);
+	g_return_val_if_fail (device != NULL, FALSE);
+
 	connection = nm_active_connection_get_settings_connection (active);
-	g_assert (connection);
+	nm_assert (connection);
 
 	applied = nm_active_connection_get_applied_connection (active);
 
-	device = nm_active_connection_get_device (active);
-	g_return_val_if_fail (device != NULL, 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
@@ -3648,16 +3803,13 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 	 */
 	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);
+	if (   existing_connection
+	    && !nm_auth_is_subject_in_acl_set_error (existing_connection,
+	                                             subject,
+	                                             NM_MANAGER_ERROR,
+	                                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+	                                             error)) {
+		g_prefix_error (error, "Private connection already active on the device: ");
 		return FALSE;
 	}
 
@@ -3669,6 +3821,9 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 		return FALSE;
 	}
 
+	if (nm_active_connection_get_activation_type (active) == NM_ACTIVATION_TYPE_MANAGED)
+		nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_MANAGED);
+
 	/* Create any backing resources the device needs */
 	if (!nm_device_is_real (device)) {
 		NMDevice *parent;
@@ -3686,7 +3841,10 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 			}
 
 			parent_ac = nm_manager_activate_connection (self, parent_con, NULL, NULL, parent,
-			                                            subject, NM_ACTIVATION_TYPE_MANAGED, error);
+			                                            subject,
+			                                            NM_ACTIVATION_TYPE_MANAGED,
+			                                            nm_active_connection_get_activation_reason (active),
+			                                            error);
 			if (!parent_ac) {
 				g_prefix_error (error, "%s failed to activate parent: ", nm_device_get_iface (device));
 				return FALSE;
@@ -3747,6 +3905,7 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 			                                             device,
 			                                             master_connection,
 			                                             master_device,
+			                                             nm_active_connection_get_activation_reason (active),
 			                                             error);
 			if (!master_ac) {
 				if (master_device) {
@@ -3773,7 +3932,7 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 		_LOGD (LOGD_CORE, "Activation of '%s' depends on active connection %p %s",
 		       nm_settings_connection_get_id (connection),
 		       master_ac,
-		       nm_exported_object_get_path (NM_EXPORTED_OBJECT  (master_ac)) ?: "");
+		       nm_dbus_object_get_path (NM_DBUS_OBJECT  (master_ac)) ?: "");
 	}
 
 	/* Check slaves for master connection and possibly activate them */
@@ -3845,101 +4004,95 @@ _internal_activate_generic (NMManager *self, NMActiveConnection *active, GError
 }
 
 static NMActiveConnection *
-_new_vpn_active_connection (NMManager *self,
-                            NMSettingsConnection *settings_connection,
-                            const char *specific_object,
-                            NMAuthSubject *subject,
-                            GError **error)
-{
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (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);
-		if (!parent) {
-			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE,
-			                     "Base connection for VPN connection not active.");
-			return NULL;
-		}
-	} else
-		parent = priv->primary_connection;
-
-	if (!parent) {
-		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
-		                     "Could not find source connection.");
-		return NULL;
-	}
-
-	device = nm_active_connection_get_device (parent);
-	if (!device) {
-		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-		                     "Source connection had no active device.");
-		return NULL;
-	}
-
-	return (NMActiveConnection *) nm_vpn_connection_new (settings_connection,
-	                                                     device,
-	                                                     nm_exported_object_get_path (NM_EXPORTED_OBJECT (parent)),
-	                                                     subject);
-}
-
-static NMActiveConnection *
 _new_active_connection (NMManager *self,
+                        gboolean is_vpn,
                         NMConnection *connection,
                         NMConnection *applied,
                         const char *specific_object,
                         NMDevice *device,
                         NMAuthSubject *subject,
                         NMActivationType activation_type,
+                        NMActivationReason activation_reason,
                         GError **error)
 {
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMSettingsConnection *settings_connection = NULL;
-	NMActiveConnection *existing_ac;
-	gboolean is_vpn;
+	NMDevice *parent_device;
 
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 	g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL);
 
-	/* Can't create new AC for already-active connection */
-	existing_ac = active_connection_find_first_by_connection (self, connection);
-	if (NM_IS_VPN_CONNECTION (existing_ac)) {
-		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE,
-		             "Connection '%s' is already active",
-		             nm_connection_get_id (connection));
-		return NULL;
-	}
-
-	/* Normalize the specific object */
-	if (specific_object && g_strcmp0 (specific_object, "/") == 0)
-		specific_object = NULL;
-
-	is_vpn = nm_connection_is_type (NM_CONNECTION (connection), NM_SETTING_VPN_SETTING_NAME);
+	nm_assert (is_vpn == _connection_is_vpn (connection));
+	nm_assert (is_vpn || NM_IS_DEVICE (device));
+	nm_assert (!nm_streq0 (specific_object, "/"));
 
 	if (NM_IS_SETTINGS_CONNECTION (connection))
 		settings_connection = (NMSettingsConnection *) connection;
 
 	if (is_vpn) {
+		NMActiveConnection *parent;
+
+		/* FIXME: for VPN connections, we don't allow re-activating an
+		 * already active connection. It's a bug, and should be fixed together
+		 * when reworking VPN handling. */
+		if (active_connection_find_first_by_connection (self, connection)) {
+			g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE,
+			             "Connection '%s' is already active",
+			             nm_connection_get_id (connection));
+			return NULL;
+		}
+
+		/* FIXME: apparently, activation here only works if @connection is
+		 * a settings-connection. Which is not the case during AddAndActivatate.
+		 * Probably, AddAndActivate is broken for VPN. */
 		if (activation_type != NM_ACTIVATION_TYPE_MANAGED)
 			g_return_val_if_reached (NULL);
-		return _new_vpn_active_connection (self,
-		                                   settings_connection,
-		                                   specific_object,
-		                                   subject,
-		                                   error);
-	}
 
-	if (device && (activation_type == NM_ACTIVATION_TYPE_MANAGED))
-		nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_MANAGED);
+		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);
+			if (!parent) {
+				g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE,
+				                     "Base connection for VPN connection not active.");
+				return NULL;
+			}
+		} else
+			parent = priv->primary_connection;
+
+		if (!parent) {
+			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+			                     "Could not find source connection.");
+			return NULL;
+		}
+
+		parent_device = nm_active_connection_get_device (parent);
+		if (!parent_device) {
+			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+			                     "Source connection had no active device");
+			return NULL;
+		}
+
+		if (device && device != parent_device) {
+			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+			                     "The device doesn't match the active connection.");
+			return NULL;
+		}
+
+		return (NMActiveConnection *) nm_vpn_connection_new (settings_connection,
+		                                                     parent_device,
+		                                                     nm_dbus_object_get_path (NM_DBUS_OBJECT (parent)),
+		                                                     activation_reason,
+		                                                     subject);
+	}
 
 	return (NMActiveConnection *) nm_act_request_new (settings_connection,
 	                                                  applied,
 	                                                  specific_object,
 	                                                  subject,
 	                                                  activation_type,
+	                                                  activation_reason,
 	                                                  device);
 }
 
@@ -3958,13 +4111,17 @@ _internal_activation_auth_done (NMActiveConnection *active,
 
 	priv->authorizing_connections = g_slist_remove (priv->authorizing_connections, active);
 
-	/* Don't continue with the activation if an equivalent active connection
-	 * already exists.  We also check this earlier, but there we may fail to
+	if (!success)
+		goto fail;
+
+	/* Don't continue with an internal activation if an equivalent active
+	 * connection already exists. Note that slave autoconnections always force a
+	 * reconnection.  We also check this earlier, but there we may fail to
 	 * detect a duplicate if the existing active connection is undergoing
 	 * authorization in impl_manager_activate_connection().
 	 */
-	if (   success
-	    && nm_auth_subject_is_internal (nm_active_connection_get_subject (active))) {
+	if (   nm_auth_subject_is_internal (nm_active_connection_get_subject (active))
+	    && nm_active_connection_get_activation_reason (active) != NM_ACTIVATION_REASON_AUTOCONNECT_SLAVES) {
 		c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) {
 			if (   nm_active_connection_get_device (ac) == nm_active_connection_get_device (active)
 			    && nm_active_connection_get_settings_connection (ac) == nm_active_connection_get_settings_connection (active)
@@ -3976,17 +4133,15 @@ _internal_activation_auth_done (NMActiveConnection *active,
 				             NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE,
 				             "Connection '%s' is already active",
 				             nm_active_connection_get_settings_connection_id (active));
-				success = FALSE;
-				break;
+				goto fail;
 			}
 		}
 	}
 
-	if (success) {
-		if (_internal_activate_generic (self, active, &error))
-			return;
-	}
+	if (_internal_activate_generic (self, active, &error))
+		return;
 
+fail:
 	nm_assert (error_desc || error);
 	nm_active_connection_set_state_fail (active,
 	                                     NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN,
@@ -4003,6 +4158,7 @@ _internal_activation_auth_done (NMActiveConnection *active,
  * @subject: the subject which requested activation
  * @activation_type: whether to assume the connection. That is, take over gracefully,
  *   non-destructible.
+ * @activation_reason: the reason for activation
  * @error: return location for an error
  *
  * Begins a new internally-initiated activation of @connection on @device.
@@ -4023,29 +4179,27 @@ nm_manager_activate_connection (NMManager *self,
                                 NMDevice *device,
                                 NMAuthSubject *subject,
                                 NMActivationType activation_type,
+                                NMActivationReason activation_reason,
                                 GError **error)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMManagerPrivate *priv;
 	NMActiveConnection *active;
-	char *error_desc = NULL;
 	GSList *iter;
 
-	g_return_val_if_fail (self != NULL, NULL);
-	g_return_val_if_fail (connection != NULL, NULL);
-	g_return_val_if_fail (error != NULL, NULL);
-	g_return_val_if_fail (*error == NULL, NULL);
+	g_return_val_if_fail (NM_IS_MANAGER (self), NULL);
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), NULL);
+	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+	g_return_val_if_fail (!error || !*error, NULL);
+	nm_assert (!nm_streq0 (specific_object, "/"));
 
-	/* Ensure the subject has permissions for this connection */
-	if (!nm_auth_is_subject_in_acl (NM_CONNECTION (connection),
-	                                subject,
-	                                &error_desc)) {
-		g_set_error_literal (error,
-		                     NM_MANAGER_ERROR,
-		                     NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                     error_desc);
-		g_free (error_desc);
+	priv = NM_MANAGER_GET_PRIVATE (self);
+
+	if (!nm_auth_is_subject_in_acl_set_error (NM_CONNECTION (connection),
+	                                          subject,
+	                                          NM_MANAGER_ERROR,
+	                                          NM_MANAGER_ERROR_PERMISSION_DENIED,
+	                                          error))
 		return NULL;
-	}
 
 	/* Look for a active connection that's equivalent and is already pending authorization
 	 * and eventual activation. This is used to de-duplicate concurrent activations which would
@@ -4056,25 +4210,29 @@ nm_manager_activate_connection (NMManager *self,
 		active = iter->data;
 
 		if (   connection == nm_active_connection_get_settings_connection (active)
-		    && g_strcmp0 (nm_active_connection_get_specific_object (active), specific_object) == 0
+		    && nm_streq0 (nm_active_connection_get_specific_object (active), specific_object)
 		    && nm_active_connection_get_device (active) == device
 		    && nm_auth_subject_is_internal (nm_active_connection_get_subject (active))
-		    && nm_auth_subject_is_internal (subject))
+		    && nm_auth_subject_is_internal (subject)
+		    && nm_active_connection_get_activation_reason (active) == activation_reason)
 			return active;
 	}
 
 	active = _new_active_connection (self,
+	                                 _connection_is_vpn (NM_CONNECTION (connection)),
 	                                 NM_CONNECTION (connection),
 	                                 applied,
 	                                 specific_object,
 	                                 device,
 	                                 subject,
 	                                 activation_type,
+	                                 activation_reason,
 	                                 error);
-	if (active) {
-		priv->authorizing_connections = g_slist_prepend (priv->authorizing_connections, active);
-		nm_active_connection_authorize (active, NULL, _internal_activation_auth_done, self, NULL);
-	}
+	if (!active)
+		return NULL;
+
+	priv->authorizing_connections = g_slist_prepend (priv->authorizing_connections, active);
+	nm_active_connection_authorize (active, NULL, _internal_activation_auth_done, self, NULL);
 	return active;
 }
 
@@ -4083,9 +4241,12 @@ nm_manager_activate_connection (NMManager *self,
  * @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 "/"
+ * @device_path: the object path of the device to be activated, or NULL
  * @out_device: on successful reutrn, the #NMDevice to be activated with @connection
- * @out_vpn: on successful return, %TRUE if @connection is a VPN connection
+ *   The caller may pass in a device which shortcuts the lookup by path.
+ *   In this case, the passed in device must have the matching @device_path
+ *   already.
+ * @out_is_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
@@ -4101,17 +4262,16 @@ validate_activation_request (NMManager *self,
                              NMConnection *connection,
                              const char *device_path,
                              NMDevice **out_device,
-                             gboolean *out_vpn,
+                             gboolean *out_is_vpn,
                              GError **error)
 {
 	NMDevice *device = NULL;
-	gboolean vpn = FALSE;
-	NMAuthSubject *subject = NULL;
-	char *error_desc = NULL;
+	gboolean is_vpn = FALSE;
+	gs_unref_object NMAuthSubject *subject = NULL;
 
-	g_assert (connection);
-	g_assert (out_device);
-	g_assert (out_vpn);
+	nm_assert (NM_IS_CONNECTION (connection));
+	nm_assert (out_device);
+	nm_assert (out_is_vpn);
 
 	/* Validate the caller */
 	subject = nm_auth_subject_new_unix_process_from_context (context);
@@ -4123,76 +4283,80 @@ validate_activation_request (NMManager *self,
 		return NULL;
 	}
 
-	/* Ensure the subject has permissions for this connection */
-	if (!nm_auth_is_subject_in_acl (connection,
-	                                subject,
-	                                &error_desc)) {
-		g_set_error_literal (error,
-		                     NM_MANAGER_ERROR,
-		                     NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                     error_desc);
-		g_free (error_desc);
-		goto error;
-	}
-
-	/* Check whether it's a VPN or not */
-	if (   nm_connection_get_setting_vpn (connection)
-	    || nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME))
-		vpn = TRUE;
+	if (!nm_auth_is_subject_in_acl_set_error (connection,
+	                                          subject,
+	                                          NM_MANAGER_ERROR,
+	                                          NM_MANAGER_ERROR_PERMISSION_DENIED,
+	                                          error))
+		return NULL;
 
-	/* Normalize device path */
-	if (device_path && g_strcmp0 (device_path, "/") == 0)
-		device_path = NULL;
+	is_vpn = _connection_is_vpn (connection);
 
-	/* And validate it */
-	if (device_path) {
+	if (*out_device) {
+		device = *out_device;
+		nm_assert (NM_IS_DEVICE (device));
+		nm_assert (device_path);
+		nm_assert (nm_streq0 (device_path, nm_dbus_object_get_path (NM_DBUS_OBJECT (device))));
+		nm_assert (device == nm_manager_get_device_by_path (self, device_path));
+	} else if (device_path) {
 		device = nm_manager_get_device_by_path (self, device_path);
 		if (!device) {
 			g_set_error_literal (error,
 			                     NM_MANAGER_ERROR,
 			                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
 			                     "Device not found");
-			goto error;
+			return NULL;
 		}
-	} else
+	} else if (!is_vpn) {
 		device = nm_manager_get_best_device_for_connection (self, connection, TRUE, NULL);
+		if (!device) {
+			gs_free char *iface = NULL;
+
+			/* VPN and software-device connections don't need a device yet,
+			 * but non-virtual connections do ... */
+			if (!nm_connection_is_virtual (connection)) {
+				g_set_error_literal (error,
+				                     NM_MANAGER_ERROR,
+				                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+				                     "No suitable device found for this connection.");
+				return NULL;
+			}
 
-	if (!device && !vpn) {
-		gs_free char *iface = NULL;
+			/* Look for an existing device with the connection's interface name */
+			iface = nm_manager_get_connection_iface (self, connection, NULL, error);
+			if (!iface)
+				return NULL;
 
-		/* VPN and software-device connections don't need a device yet,
-		 * but non-virtual connections do ... */
-		if (!nm_connection_is_virtual (connection)) {
-			g_set_error_literal (error,
-			                     NM_MANAGER_ERROR,
-			                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-			                     "No suitable device found for this connection.");
-			goto error;
+			device = find_device_by_iface (self, iface, connection, NULL);
+			if (!device) {
+				g_set_error_literal (error,
+				                     NM_MANAGER_ERROR,
+				                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+				                     "Failed to find a compatible device for this connection");
+				return NULL;
+			}
 		}
-
-		/* Look for an existing device with the connection's interface name */
-		iface = nm_manager_get_connection_iface (self, connection, NULL, error);
-		if (!iface)
-			goto error;
-
-		device = find_device_by_iface (self, iface, connection, NULL);
 	}
 
-	if ((!vpn || device_path) && !device) {
+	if (is_vpn && device) {
+		/* VPN's are treated specially. Maybe the should accept a device as well,
+		 * however, later on during activation, we don't handle the device.
+		 *
+		 * Maybe we should, and maybe it makes sense to specify a device
+		 * when activating a VPN. But for now, just error out.  */
 		g_set_error_literal (error,
 		                     NM_MANAGER_ERROR,
 		                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-		                     "Failed to find a compatible device for this connection");
-		goto error;
+		                     "Cannot specify device when activating VPN");
+		return NULL;
 	}
 
-	*out_device = device;
-	*out_vpn = vpn;
-	return subject;
+	nm_assert (   ( is_vpn && !device)
+	           || (!is_vpn && NM_IS_DEVICE (device)));
 
-error:
-	g_object_unref (subject);
-	return NULL;
+	*out_device = device;
+	*out_is_vpn = is_vpn;
+	return g_steal_pointer (&subject);
 }
 
 /*****************************************************************************/
@@ -4214,24 +4378,27 @@ _activation_auth_done (NMActiveConnection *active,
 	subject = nm_active_connection_get_subject (active);
 	connection = nm_active_connection_get_settings_connection (active);
 
-	if (success) {
-		if (_internal_activate_generic (self, active, &error)) {
-			nm_settings_connection_autoconnect_blocked_reason_set (connection,
-			                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST,
-			                                                       FALSE);
-			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, NULL,
-			                            subject, NULL);
-			return;
-		}
-	} else {
+	if (!success) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                             error_desc);
+		goto fail;
 	}
 
+	if (!_internal_activate_generic (self, active, &error))
+		goto fail;
+
+	nm_settings_connection_autoconnect_blocked_reason_set (connection,
+	                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST,
+	                                                       FALSE);
+	g_dbus_method_invocation_return_value (context,
+	                                       g_variant_new ("(o)",
+	                                       nm_dbus_object_get_path (NM_DBUS_OBJECT (active))));
+	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, TRUE, NULL,
+	                            subject, NULL);
+	return;
+
+fail:
 	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE, NULL,
 	                            subject, error->message);
 	nm_active_connection_set_state_fail (active,
@@ -4242,12 +4409,15 @@ _activation_auth_done (NMActiveConnection *active,
 }
 
 static void
-impl_manager_activate_connection (NMManager *self,
-                                  GDBusMethodInvocation *context,
-                                  const char *connection_path,
-                                  const char *device_path,
-                                  const char *specific_object_path)
-{
+impl_manager_activate_connection (NMDBusObject *obj,
+                                  const NMDBusInterfaceInfoExtended *interface_info,
+                                  const NMDBusMethodInfoExtended *method_info,
+                                  GDBusConnection *dbus_connection,
+                                  const char *sender,
+                                  GDBusMethodInvocation *invocation,
+                                  GVariant *parameters)
+{
+	NMManager *self = NM_MANAGER (obj);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	gs_unref_object NMActiveConnection *active = NULL;
 	gs_unref_object NMAuthSubject *subject = NULL;
@@ -4255,14 +4425,15 @@ impl_manager_activate_connection (NMManager *self,
 	NMDevice *device = NULL;
 	gboolean is_vpn = FALSE;
 	GError *error = NULL;
+	const char *connection_path;
+	const char *device_path;
+	const char *specific_object_path;
+
+	g_variant_get (parameters, "(&o&o&o)", &connection_path, &device_path, &specific_object_path);
 
-	/* Normalize object paths */
-	if (g_strcmp0 (connection_path, "/") == 0)
-		connection_path = NULL;
-	if (g_strcmp0 (specific_object_path, "/") == 0)
-		specific_object_path = NULL;
-	if (g_strcmp0 (device_path, "/") == 0)
-		device_path = NULL;
+	connection_path = nm_utils_dbus_normalize_object_path (connection_path);
+	specific_object_path = nm_utils_dbus_normalize_object_path (specific_object_path);
+	device_path = nm_utils_dbus_normalize_object_path (device_path);
 
 	/* If the connection path is given and valid, that connection is activated.
 	 * Otherwise the "best" connection for the device is chosen and activated,
@@ -4297,7 +4468,7 @@ impl_manager_activate_connection (NMManager *self,
 	}
 
 	subject = validate_activation_request (self,
-	                                       context,
+	                                       invocation,
 	                                       NM_CONNECTION (connection),
 	                                       device_path,
 	                                       &device,
@@ -4307,12 +4478,14 @@ impl_manager_activate_connection (NMManager *self,
 		goto error;
 
 	active = _new_active_connection (self,
+	                                 is_vpn,
 	                                 NM_CONNECTION (connection),
 	                                 NULL,
 	                                 specific_object_path,
 	                                 device,
 	                                 subject,
 	                                 NM_ACTIVATION_TYPE_MANAGED,
+	                                 NM_ACTIVATION_REASON_USER_REQUEST,
 	                                 &error);
 	if (!active)
 		goto error;
@@ -4324,7 +4497,7 @@ impl_manager_activate_connection (NMManager *self,
 	                                NULL,
 	                                _activation_auth_done,
 	                                self,
-	                                context);
+	                                invocation);
 	return;
 
 error:
@@ -4332,7 +4505,7 @@ error:
 		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE, NULL,
 		                            subject, error->message);
 	}
-	g_dbus_method_invocation_take_error (context, error);
+	g_dbus_method_invocation_take_error (invocation, error);
 }
 
 /*****************************************************************************/
@@ -4353,7 +4526,7 @@ activation_add_done (NMSettings *settings,
 	AddAndActivateInfo *info = user_data;
 	NMManager *self;
 	gs_unref_object NMActiveConnection *active = NULL;
-	GError *local = NULL;
+	gs_free_error GError *local = NULL;
 
 	self = info->manager;
 	active = info->active;
@@ -4372,8 +4545,8 @@ activation_add_done (NMSettings *settings,
 			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_dbus_object_get_path (NM_DBUS_OBJECT (new_connection)),
+			                   nm_dbus_object_get_path (NM_DBUS_OBJECT (active))));
 			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE,
 			                            nm_active_connection_get_settings_connection (active),
 			                            TRUE,
@@ -4386,6 +4559,7 @@ activation_add_done (NMSettings *settings,
 	}
 
 	nm_assert (error);
+
 	nm_active_connection_set_state_fail (active,
 	                                     NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN,
 	                                     error->message);
@@ -4398,7 +4572,6 @@ activation_add_done (NMSettings *settings,
 	                            NULL,
 	                            nm_active_connection_get_subject (active),
 	                            error->message);
-	g_clear_error (&local);
 }
 
 static void
@@ -4413,27 +4586,12 @@ _add_and_activate_auth_done (NMActiveConnection *active,
 	GDBusMethodInvocation *context = user_data2;
 	AddAndActivateInfo *info;
 	GError *error = NULL;
+	gs_unref_object NMConnection *connection = NULL;
 
-	if (success) {
-		NMConnection *connection;
-
-		connection = g_object_steal_qdata (G_OBJECT (active),
-		                                   active_connection_add_and_activate_quark ());
-
-		info = g_slice_new (AddAndActivateInfo);
-		info->manager = self;
-		info->active = g_object_ref (active);
+	connection = g_object_steal_qdata (G_OBJECT (active),
+	                                   active_connection_add_and_activate_quark ());
 
-		/* Basic sender auth checks performed; try to add the connection */
-		nm_settings_add_connection_dbus (priv->settings,
-		                                 connection,
-		                                 FALSE,
-		                                 context,
-		                                 activation_add_done,
-		                                 info);
-		g_object_unref (connection);
-	} else {
-		g_assert (error_desc);
+	if (!success) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                             error_desc);
@@ -4444,32 +4602,50 @@ _add_and_activate_auth_done (NMActiveConnection *active,
 		                            nm_active_connection_get_subject (active),
 		                            error->message);
 		g_dbus_method_invocation_take_error (context, error);
+		g_object_unref (active);
+		return;
 	}
 
-	g_object_unref (active);
+	info = g_slice_new (AddAndActivateInfo);
+	info->manager = self;
+
+	/* we pass on the reference to @active. */
+	info->active = active;
+
+	/* Basic sender auth checks performed; try to add the connection */
+	nm_settings_add_connection_dbus (priv->settings,
+	                                 connection,
+	                                 FALSE,
+	                                 context,
+	                                 activation_add_done,
+	                                 info);
 }
 
 static void
-impl_manager_add_and_activate_connection (NMManager *self,
-                                          GDBusMethodInvocation *context,
-                                          GVariant *settings,
-                                          const char *device_path,
-                                          const char *specific_object_path)
-{
+impl_manager_add_and_activate_connection (NMDBusObject *obj,
+                                          const NMDBusInterfaceInfoExtended *interface_info,
+                                          const NMDBusMethodInfoExtended *method_info,
+                                          GDBusConnection *dbus_connection,
+                                          const char *sender,
+                                          GDBusMethodInvocation *invocation,
+                                          GVariant *parameters)
+{
+	NMManager *self = NM_MANAGER (obj);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMConnection *connection = NULL;
-	GSList *all_connections = NULL;
+	gs_unref_object NMConnection *connection = NULL;
 	NMActiveConnection *active = NULL;
-	NMAuthSubject *subject = NULL;
+	gs_unref_object NMAuthSubject *subject = NULL;
 	GError *error = NULL;
 	NMDevice *device = NULL;
-	gboolean vpn = FALSE;
+	gboolean is_vpn = FALSE;
+	gs_unref_variant GVariant *settings = NULL;
+	const char *device_path;
+	const char *specific_object_path;
 
-	/* Normalize object paths */
-	if (g_strcmp0 (specific_object_path, "/") == 0)
-		specific_object_path = NULL;
-	if (g_strcmp0 (device_path, "/") == 0)
-		device_path = NULL;
+	g_variant_get (parameters, "(@a{sa{sv}}&o&o)", &settings, &device_path, &specific_object_path);
+
+	specific_object_path = nm_utils_dbus_normalize_object_path (specific_object_path);
+	device_path = nm_utils_dbus_normalize_object_path (device_path);
 
 	/* Try to create a new connection with the given settings.
 	 * We allow empty settings for AddAndActivateConnection(). In that case,
@@ -4483,29 +4659,16 @@ impl_manager_add_and_activate_connection (NMManager *self,
 		_nm_connection_replace_settings (connection, settings, NM_SETTING_PARSE_FLAGS_STRICT, NULL);
 
 	subject = validate_activation_request (self,
-	                                       context,
+	                                       invocation,
 	                                       connection,
 	                                       device_path,
 	                                       &device,
-	                                       &vpn,
+	                                       &is_vpn,
 	                                       &error);
 	if (!subject)
 		goto error;
 
-	{
-		gs_free NMSettingsConnection **connections = NULL;
-		guint i, len;
-
-		connections = nm_settings_get_connections_clone (priv->settings, &len,
-		                                                 NULL, NULL,
-		                                                 nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
-		all_connections = NULL;
-		for (i = len; i > 0; ) {
-			i--;
-			all_connections = g_slist_prepend (all_connections, connections[i]);
-		}
-	}
-	if (vpn) {
+	if (is_vpn) {
 		/* Try to fill the VPN's connection setting and name at least */
 		if (!nm_connection_get_setting_vpn (connection)) {
 			error = g_error_new_literal (NM_CONNECTION_ERROR,
@@ -4518,7 +4681,7 @@ impl_manager_add_and_activate_connection (NMManager *self,
 		nm_utils_complete_generic (priv->platform,
 		                           connection,
 		                           NM_SETTING_VPN_SETTING_NAME,
-		                           all_connections,
+		                           (NMConnection *const*) nm_settings_get_connections (priv->settings, NULL),
 		                           NULL,
 		                           _("VPN connection"),
 		                           NULL,
@@ -4528,42 +4691,44 @@ impl_manager_add_and_activate_connection (NMManager *self,
 		if (!nm_device_complete_connection (device,
 		                                    connection,
 		                                    specific_object_path,
-		                                    all_connections,
+		                                    (NMConnection *const*) nm_settings_get_connections (priv->settings, NULL),
 		                                    &error))
 			goto error;
 	}
-	g_slist_free (all_connections);
-	all_connections = NULL;
 
 	active = _new_active_connection (self,
+	                                 is_vpn,
 	                                 connection,
 	                                 NULL,
 	                                 specific_object_path,
 	                                 device,
 	                                 subject,
 	                                 NM_ACTIVATION_TYPE_MANAGED,
+	                                 NM_ACTIVATION_REASON_USER_REQUEST,
 	                                 &error);
 	if (!active)
 		goto error;
 
+	/* FIXME: nm_active_connection_authorize() already has two user-data pointers
+	 *        to piggyback additional data. Instead of attaching the third argument to
+	 *        @active's user-data, add a third paramter.
+	 *        Or alternatively, allocate a data structure to pass on additional data.
+	 *        Then we don't need two user-data pointers. */
 	g_object_set_qdata_full (G_OBJECT (active),
 	                         active_connection_add_and_activate_quark (),
 	                         connection,
 	                         g_object_unref);
 
-	nm_active_connection_authorize (active, connection, _add_and_activate_auth_done, self, context);
-	g_object_unref (subject);
+	nm_active_connection_authorize (active, connection, _add_and_activate_auth_done, self, invocation);
+
+	/* we passed the pointers on to the callback of authorize. */
+	g_steal_pointer (&connection);
+	g_steal_pointer (&active);
 	return;
 
 error:
 	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE, NULL, FALSE, NULL, subject, error->message);
-	g_clear_object (&connection);
-	g_slist_free (all_connections);
-	g_clear_object (&subject);
-	g_clear_object (&active);
-
-	g_assert (error);
-	g_dbus_method_invocation_take_error (context, error);
+	g_dbus_method_invocation_take_error (invocation, error);
 }
 
 /*****************************************************************************/
@@ -4659,21 +4824,28 @@ deactivate_net_auth_done_cb (NMAuthChain *chain,
 	else
 		g_dbus_method_invocation_return_value (context, NULL);
 
-	nm_auth_chain_unref (chain);
+	nm_auth_chain_destroy (chain);
 }
 
 static void
-impl_manager_deactivate_connection (NMManager *self,
-                                    GDBusMethodInvocation *context,
-                                    const char *active_path)
-{
+impl_manager_deactivate_connection (NMDBusObject *obj,
+                                    const NMDBusInterfaceInfoExtended *interface_info,
+                                    const NMDBusMethodInfoExtended *method_info,
+                                    GDBusConnection *dbus_connection,
+                                    const char *sender,
+                                    GDBusMethodInvocation *invocation,
+                                    GVariant *parameters)
+{
+	NMManager *self = NM_MANAGER (obj);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMActiveConnection *ac;
 	NMSettingsConnection *connection = NULL;
 	GError *error = NULL;
 	NMAuthSubject *subject = NULL;
 	NMAuthChain *chain;
-	char *error_desc = NULL;
+	const char *active_path;
+
+	g_variant_get (parameters, "(&o)", &active_path);
 
 	/* Find the connection by its object path */
 	ac = active_connection_get_by_path (self, active_path);
@@ -4688,7 +4860,7 @@ impl_manager_deactivate_connection (NMManager *self,
 	}
 
 	/* Validate the caller */
-	subject = nm_auth_subject_new_unix_process_from_context (context);
+	subject = nm_auth_subject_new_unix_process_from_context (invocation);
 	if (!subject) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
@@ -4696,19 +4868,15 @@ impl_manager_deactivate_connection (NMManager *self,
 		goto done;
 	}
 
-	/* Ensure the subject has permissions for this connection */
-	if (!nm_auth_is_subject_in_acl (NM_CONNECTION (connection),
-	                                subject,
-	                                &error_desc)) {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             error_desc);
-		g_free (error_desc);
+	if (!nm_auth_is_subject_in_acl_set_error (NM_CONNECTION (connection),
+	                                          subject,
+	                                          NM_MANAGER_ERROR,
+	                                          NM_MANAGER_ERROR_PERMISSION_DENIED,
+	                                          &error))
 		goto done;
-	}
 
 	/* Validate the user request */
-	chain = nm_auth_chain_new_subject (subject, context, deactivate_net_auth_done_cb, self);
+	chain = nm_auth_chain_new_subject (subject, invocation, deactivate_net_auth_done_cb, self);
 	if (!chain) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
@@ -4726,7 +4894,7 @@ done:
 			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DEACTIVATE, connection, FALSE, NULL,
 			                            subject, error->message);
 		}
-		g_dbus_method_invocation_take_error (context, error);
+		g_dbus_method_invocation_take_error (invocation, error);
 	}
 	g_clear_object (&subject);
 }
@@ -4834,7 +5002,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	gboolean suspending, waking_from_suspend;
-	GSList *iter;
+	NMDevice *device;
 
 	suspending = sleeping_changed && priv->sleeping;
 	waking_from_suspend = sleeping_changed && !priv->sleeping;
@@ -4845,9 +5013,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 		/* FIXME: are there still hardware devices that need to be disabled around
 		 * suspend/resume?
 		 */
-		for (iter = priv->devices; iter; iter = iter->next) {
-			NMDevice *device = iter->data;
-
+		c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
 			/* FIXME: shouldn't we be unmanaging software devices if !suspending? */
 			if (nm_device_is_software (device))
 				continue;
@@ -4875,9 +5041,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 
 		if (waking_from_suspend) {
 			sleep_devices_clear (self);
-			for (iter = priv->devices; iter; iter = iter->next) {
-				NMDevice *device = iter->data;
-
+			c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
 				if (nm_device_is_software (device))
 					continue;
 
@@ -4904,8 +5068,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 		nm_manager_rfkill_update (self, RFKILL_TYPE_UNKNOWN);
 
 		/* Re-manage managed devices */
-		for (iter = priv->devices; iter; iter = iter->next) {
-			NMDevice *device = NM_DEVICE (iter->data);
+		c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
 			guint i;
 
 			if (nm_device_is_software (device)) {
@@ -4998,27 +5161,28 @@ sleep_auth_done_cb (NMAuthChain *chain,
 		g_dbus_method_invocation_return_value (context, NULL);
 	}
 
-	nm_auth_chain_unref (chain);
+	nm_auth_chain_destroy (chain);
 }
 #endif
 
 static void
-impl_manager_sleep (NMManager *self,
-                    GDBusMethodInvocation *context,
-                    gboolean do_sleep)
-{
-	NMManagerPrivate *priv;
+impl_manager_sleep (NMDBusObject *obj,
+                    const NMDBusInterfaceInfoExtended *interface_info,
+                    const NMDBusMethodInfoExtended *method_info,
+                    GDBusConnection *connection,
+                    const char *sender,
+                    GDBusMethodInvocation *invocation,
+                    GVariant *parameters)
+{
+	NMManager *self = NM_MANAGER (obj);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GError *error = NULL;
 	gs_unref_object NMAuthSubject *subject = NULL;
-#if 0
-	NMAuthChain *chain;
-	const char *error_desc = NULL;
-#endif
+	gboolean do_sleep;
 
-	g_return_if_fail (NM_IS_MANAGER (self));
+	g_variant_get (parameters, "(b)", &do_sleep);
 
-	priv = NM_MANAGER_GET_PRIVATE (self);
-	subject = nm_auth_subject_new_unix_process_from_context (context);
+	subject = nm_auth_subject_new_unix_process_from_context (invocation);
 
 	if (priv->sleeping == do_sleep) {
 		error = g_error_new (NM_MANAGER_ERROR,
@@ -5026,7 +5190,7 @@ impl_manager_sleep (NMManager *self,
 		                     "Already %s", do_sleep ? "asleep" : "awake");
 		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);
+		g_dbus_method_invocation_take_error (invocation, error);
 		return;
 	}
 
@@ -5040,22 +5204,8 @@ impl_manager_sleep (NMManager *self,
 	 */
 	_internal_sleep (self, do_sleep);
 	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);
+	g_dbus_method_invocation_return_value (invocation, NULL);
 	return;
-
-#if 0
-	chain = nm_auth_chain_new (context, sleep_auth_done_cb, self, &error_desc);
-	if (chain) {
-		priv->auth_chains = g_slist_append (priv->auth_chains, chain);
-		nm_auth_chain_set_data (chain, "sleep", GUINT_TO_POINTER (do_sleep), NULL);
-		nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SLEEP_WAKE, TRUE);
-	} else {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             error_desc);
-		g_dbus_method_invocation_take_error (context, error);
-	}
-#endif
 }
 
 static void
@@ -5131,21 +5281,25 @@ enable_net_done_cb (NMAuthChain *chain,
 		g_dbus_method_invocation_take_error (context, ret_error);
 	}
 
-	nm_auth_chain_unref (chain);
+	nm_auth_chain_destroy (chain);
 }
 
 static void
-impl_manager_enable (NMManager *self,
-                     GDBusMethodInvocation *context,
-                     gboolean enable)
-{
-	NMManagerPrivate *priv;
+impl_manager_enable (NMDBusObject *obj,
+                     const NMDBusInterfaceInfoExtended *interface_info,
+                     const NMDBusMethodInfoExtended *method_info,
+                     GDBusConnection *connection,
+                     const char *sender,
+                     GDBusMethodInvocation *invocation,
+                     GVariant *parameters)
+{
+	NMManager *self = NM_MANAGER (obj);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMAuthChain *chain;
 	GError *error = NULL;
+	gboolean enable;
 
-	g_return_if_fail (NM_IS_MANAGER (self));
-
-	priv = NM_MANAGER_GET_PRIVATE (self);
+	g_variant_get (parameters, "(b)", &enable);
 
 	if (priv->net_enabled == enable) {
 		error = g_error_new (NM_MANAGER_ERROR,
@@ -5154,7 +5308,7 @@ impl_manager_enable (NMManager *self,
 		goto done;
 	}
 
-	chain = nm_auth_chain_new_context (context, enable_net_done_cb, self);
+	chain = nm_auth_chain_new_context (invocation, enable_net_done_cb, self);
 	if (!chain) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
@@ -5168,7 +5322,7 @@ impl_manager_enable (NMManager *self,
 
 done:
 	if (error)
-		g_dbus_method_invocation_take_error (context, error);
+		g_dbus_method_invocation_take_error (invocation, error);
 }
 
 /* Permissions */
@@ -5235,23 +5389,28 @@ get_permissions_done_cb (NMAuthChain *chain,
 		                                       g_variant_new ("(a{ss})", &results));
 	}
 
-	nm_auth_chain_unref (chain);
+	nm_auth_chain_destroy (chain);
 }
 
 static void
-impl_manager_get_permissions (NMManager *self,
-                              GDBusMethodInvocation *context)
-{
+impl_manager_get_permissions (NMDBusObject *obj,
+                              const NMDBusInterfaceInfoExtended *interface_info,
+                              const NMDBusMethodInfoExtended *method_info,
+                              GDBusConnection *connection,
+                              const char *sender,
+                              GDBusMethodInvocation *invocation,
+                              GVariant *parameters)
+{
+	NMManager *self = NM_MANAGER (obj);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMAuthChain *chain;
-	GError *error = NULL;
 
-	chain = nm_auth_chain_new_context (context, get_permissions_done_cb, self);
+	chain = nm_auth_chain_new_context (invocation, get_permissions_done_cb, self);
 	if (!chain) {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             "Unable to authenticate request.");
-		g_dbus_method_invocation_take_error (context, error);
+		g_dbus_method_invocation_return_error_literal (invocation,
+		                                               NM_MANAGER_ERROR,
+		                                               NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                                               "Unable to authenticate request.");
 		return;
 	}
 
@@ -5275,83 +5434,123 @@ impl_manager_get_permissions (NMManager *self,
 }
 
 static void
-impl_manager_get_state (NMManager *self,
-                        GDBusMethodInvocation *context)
+impl_manager_state (NMDBusObject *obj,
+                    const NMDBusInterfaceInfoExtended *interface_info,
+                    const NMDBusMethodInfoExtended *method_info,
+                    GDBusConnection *connection,
+                    const char *sender,
+                    GDBusMethodInvocation *invocation,
+                    GVariant *parameters)
 {
+	NMManager *self = NM_MANAGER (obj);
+
 	nm_manager_update_state (self);
-	g_dbus_method_invocation_return_value (context,
+	g_dbus_method_invocation_return_value (invocation,
 	                                       g_variant_new ("(u)", NM_MANAGER_GET_PRIVATE (self)->state));
 }
 
 static void
-impl_manager_set_logging (NMManager *self,
-                          GDBusMethodInvocation *context,
-                          const char *level,
-                          const char *domains)
-{
+impl_manager_set_logging (NMDBusObject *obj,
+                          const NMDBusInterfaceInfoExtended *interface_info,
+                          const NMDBusMethodInfoExtended *method_info,
+                          GDBusConnection *connection,
+                          const char *sender,
+                          GDBusMethodInvocation *invocation,
+                          GVariant *parameters)
+{
+	NMManager *self = NM_MANAGER (obj);
 	GError *error = NULL;
+	const char *level;
+	const char *domains;
 
 	/* The permission is already enforced by the D-Bus daemon, but we ensure
 	 * that the caller is still alive so that clients are forced to wait and
 	 * we'll be able to switch to polkit without breaking behavior.
 	 */
-	if (!nm_bus_manager_ensure_uid (nm_bus_manager_get (),
-	                                context,
-	                                G_MAXULONG,
-	                                NM_MANAGER_ERROR,
-	                                NM_MANAGER_ERROR_PERMISSION_DENIED))
+	if (!nm_dbus_manager_ensure_uid (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)),
+	                                 invocation,
+	                                 G_MAXULONG,
+	                                 NM_MANAGER_ERROR,
+	                                 NM_MANAGER_ERROR_PERMISSION_DENIED))
 		return;
 
+	g_variant_get (parameters, "(&s&s)", &level, &domains);
+
 	if (nm_logging_setup (level, domains, NULL, &error)) {
 		_LOGI (LOGD_CORE, "logging: level '%s' domains '%s'",
 		       nm_logging_level_to_string (), nm_logging_domains_to_string ());
 	}
 
 	if (error)
-		g_dbus_method_invocation_take_error (context, error);
+		g_dbus_method_invocation_take_error (invocation, error);
 	else
-		g_dbus_method_invocation_return_value (context, NULL);
+		g_dbus_method_invocation_return_value (invocation, NULL);
 }
 
 static void
-impl_manager_get_logging (NMManager *manager,
-                          GDBusMethodInvocation *context)
-{
-	g_dbus_method_invocation_return_value (context,
+impl_manager_get_logging (NMDBusObject *obj,
+                          const NMDBusInterfaceInfoExtended *interface_info,
+                          const NMDBusMethodInfoExtended *method_info,
+                          GDBusConnection *connection,
+                          const char *sender,
+                          GDBusMethodInvocation *invocation,
+                          GVariant *parameters)
+{
+	g_dbus_method_invocation_return_value (invocation,
 	                                       g_variant_new ("(ss)",
 	                                                      nm_logging_level_to_string (),
 	                                                      nm_logging_domains_to_string ()));
 }
 
 typedef struct {
-	guint remaining;
+	NMManager *self;
 	GDBusMethodInvocation *context;
-	NMConnectivityState state;
+	guint remaining;
 } ConnectivityCheckData;
 
 static void
-device_connectivity_done (NMDevice *device, NMConnectivityState state, gpointer user_data)
+device_connectivity_done (NMDevice *device,
+                          NMDeviceConnectivityHandle *handle,
+                          NMConnectivityState state,
+                          GError *error,
+                          gpointer user_data)
 {
 	ConnectivityCheckData *data = user_data;
+	NMManager *self;
+	NMManagerPrivate *priv;
 
-	data->remaining--;
+	nm_assert (data);
+	nm_assert (data->remaining > 0);
+	nm_assert (NM_IS_MANAGER (data->self));
 
-	/* We check if the state is already FULL so that we can provide the
-	 * response without waiting for slower devices that are not going to
-	 * affect the overall state anyway. */
+	data->remaining--;
 
-	if (data->state != NM_CONNECTIVITY_FULL) {
-		if (state > data->state)
-			data->state = state;
+	self = data->self;
+	priv = NM_MANAGER_GET_PRIVATE (self);
 
-		if (data->state == NM_CONNECTIVITY_FULL || !data->remaining) {
-			g_dbus_method_invocation_return_value (data->context,
-			                                       g_variant_new ("(u)", data->state));
-		}
+	if (   data->context
+	    && (   data->remaining == 0
+	        || (   state == NM_CONNECTIVITY_FULL
+	            && priv->connectivity_state == NM_CONNECTIVITY_FULL))) {
+		/* despite having a @handle and @state returned by the requests, we always
+		 * return the current connectivity_state. That is, because the connectivity_state
+		 * and the answer to the connectivity check shall agree.
+		 *
+		 * However, if one of the requests (early) returns full connectivity and agrees with
+		 * the accumulated connectivity state, we no longer have to wait. The result is set.
+		 *
+		 * This also works well, because NMDevice first emits change signals to its own
+		 * connectivity state, which is then taken into account for the accumulated global
+		 * state. All this happens, before the callback is invoked. */
+		g_dbus_method_invocation_return_value (g_steal_pointer (&data->context),
+		                                       g_variant_new ("(u)",
+		                                                      (guint) priv->connectivity_state));
 	}
 
-	if (!data->remaining)
+	if (data->remaining == 0) {
+		g_object_unref (self);
 		g_slice_free (ConnectivityCheckData, data);
+	}
 }
 
 static void
@@ -5365,7 +5564,7 @@ check_connectivity_auth_done_cb (NMAuthChain *chain,
 	GError *error = NULL;
 	NMAuthCallResult result;
 	ConnectivityCheckData *data;
-	const GSList *devices;
+	NMDevice *device;
 
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
 
@@ -5381,39 +5580,59 @@ check_connectivity_auth_done_cb (NMAuthChain *chain,
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                             "Not authorized to recheck connectivity");
-	} else {
-		/* it's allowed */
-		data = g_slice_new0 (ConnectivityCheckData);
-		data->context = context;
+	}
 
-		for (devices = priv->devices; devices; devices = devices->next) {
+	if (error) {
+		g_dbus_method_invocation_take_error (context, error);
+		goto out;
+	}
+
+	data = g_slice_new (ConnectivityCheckData);
+	data->self = g_object_ref (self);
+	data->context = context;
+	data->remaining = 0;
+
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+		if (nm_device_check_connectivity (device,
+		                                  device_connectivity_done,
+		                                  data))
 			data->remaining++;
-			nm_device_check_connectivity (NM_DEVICE (devices->data),
-			                              device_connectivity_done,
-			                              data);
-		}
 	}
 
-	if (error)
-		g_dbus_method_invocation_take_error (context, error);
-	nm_auth_chain_unref (chain);
+	if (data->remaining == 0) {
+		/* call the handler at least once. */
+		data->remaining = 1;
+		device_connectivity_done (NULL,
+		                          NULL,
+		                          NM_CONNECTIVITY_UNKNOWN,
+		                          NULL,
+		                          data);
+		/* @data got destroyed. */
+	}
+
+out:
+	nm_auth_chain_destroy (chain);
 }
 
 static void
-impl_manager_check_connectivity (NMManager *self,
-                                 GDBusMethodInvocation *context)
-{
+impl_manager_check_connectivity (NMDBusObject *obj,
+                                 const NMDBusInterfaceInfoExtended *interface_info,
+                                 const NMDBusMethodInfoExtended *method_info,
+                                 GDBusConnection *connection,
+                                 const char *sender,
+                                 GDBusMethodInvocation *invocation,
+                                 GVariant *parameters)
+{
+	NMManager *self = NM_MANAGER (obj);
 	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, self);
+	chain = nm_auth_chain_new_context (invocation, 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.");
-		g_dbus_method_invocation_take_error (context, error);
+		g_dbus_method_invocation_return_error_literal(invocation,
+		                                              NM_MANAGER_ERROR,
+		                                              NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                                              "Unable to authenticate request.");
 		return;
 	}
 
@@ -5430,15 +5649,14 @@ start_factory (NMDeviceFactory *factory, gpointer user_data)
 void
 nm_manager_write_device_state (NMManager *self)
 {
-	const GSList *devices;
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDevice *device;
 	gs_unref_hashtable GHashTable *seen_ifindexes = NULL;
 	gint nm_owned;
 
-	seen_ifindexes = g_hash_table_new (NULL, NULL);
+	seen_ifindexes = g_hash_table_new (nm_direct_hash, NULL);
 
-	for (devices = priv->devices; devices; devices = devices->next) {
-		NMDevice *device = NM_DEVICE (devices->data);
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
 		int ifindex;
 		gboolean managed;
 		NMConfigDeviceStateManagedType managed_type;
@@ -5579,10 +5797,10 @@ void
 nm_manager_stop (NMManager *self)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDevice *device;
 
-	/* Remove all devices */
-	while (priv->devices)
-		remove_device (self, NM_DEVICE (priv->devices->data), TRUE, TRUE);
+	while ((device = c_list_first_entry (&priv->devices_lst_head, NMDevice, devices_lst)))
+		remove_device (self, device, TRUE, TRUE);
 
 	_active_connection_cleanup (self);
 
@@ -5594,21 +5812,20 @@ handle_firmware_changed (gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GSList *iter;
+	NMDevice *device;
 
 	priv->fw_changed_id = 0;
 
 	/* Try to re-enable devices with missing firmware */
-	for (iter = priv->devices; iter; iter = iter->next) {
-		NMDevice *candidate = NM_DEVICE (iter->data);
-		NMDeviceState state = nm_device_get_state (candidate);
+	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+		NMDeviceState state = nm_device_get_state (device);
 
-		if (   nm_device_get_firmware_missing (candidate)
+		if (   nm_device_get_firmware_missing (device)
 		    && (state == NM_DEVICE_STATE_UNAVAILABLE)) {
-			_LOG2I (LOGD_CORE, candidate, "firmware may now be available");
+			_LOG2I (LOGD_CORE, device, "firmware may now be available");
 
 			/* Re-set unavailable state to try bringing the device up again */
-			nm_device_state_changed (candidate,
+			nm_device_state_changed (device,
 			                         NM_DEVICE_STATE_UNAVAILABLE,
 			                         NM_DEVICE_STATE_REASON_NONE);
 		}
@@ -5727,318 +5944,133 @@ policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer u
 	}
 }
 
-#define NM_PERM_DENIED_ERROR "org.freedesktop.NetworkManager.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;
+	NMDBusObject *obj;
+	const NMDBusInterfaceInfoExtended *interface_info;
+	const NMDBusPropertyInfoExtended *property_info;
+	GVariant *value;
+	guint64 export_version_id;
+} DBusSetPropertyHandle;
 
-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);
-}
+#define NM_PERM_DENIED_ERROR "org.freedesktop.NetworkManager.PermissionDenied"
 
 static void
-prop_set_auth_done_cb (NMAuthChain *chain,
-                       GError *error,
-                       GDBusMethodInvocation *context, /* NULL */
-                       gpointer user_data)
+_dbus_set_property_auth_cb (NMAuthChain *chain,
+                            GError *error,
+                            GDBusMethodInvocation *invocation,
+                            gpointer user_data)
 {
-	PropertyFilterData *pfd = user_data;
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (pfd->self);
+	DBusSetPropertyHandle *handle_data = user_data;
+	gs_unref_object NMDBusObject *obj = handle_data->obj;
+	const NMDBusInterfaceInfoExtended *interface_info = handle_data->interface_info;
+	const NMDBusPropertyInfoExtended *property_info = handle_data->property_info;
+	gs_unref_variant GVariant *value = handle_data->value;
+	guint64 export_version_id = handle_data->export_version_id;
+	gs_unref_object NMManager *self = handle_data->self;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMAuthCallResult result;
-	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;
+	const char *error_name = NULL;
+	const char *error_message = NULL;
+	GValue gvalue;
+
+	g_slice_free (DBusSetPropertyHandle, handle_data);
 
 	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_literal (pfd->message,
-		                                                 NM_PERM_DENIED_ERROR,
-		                                                 (error_message = "Not authorized to perform this operation"));
-		if (error)
-			error_message = error->message;
-		goto done;
-	}
+	result = nm_auth_chain_get_result (chain, property_info->writable.permission);
 
-	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_literal (pfd->message,
-		                                                 "org.freedesktop.DBus.Error.UnknownObject",
-		                                                 (error_message = "Object doesn't exist."));
-		goto done;
+	if (   error
+	    || result != NM_AUTH_CALL_RESULT_YES) {
+		error_name = NM_PERM_DENIED_ERROR;
+		error_message = error ? error->message : "Not authorized to perform this operation";
+		goto out;
 	}
 
-	/* do some extra type checking... */
-	if (!nm_exported_object_get_interface_by_type (object, pfd->interface_type)) {
-		reply = g_dbus_message_new_method_error_literal (pfd->message,
-		                                                 "org.freedesktop.DBus.Error.InvalidArgs",
-		                                                 (error_message = "Object is of unexpected type."));
-		goto done;
+	if (export_version_id != nm_dbus_object_get_export_version_id (obj)) {
+		error_name = "org.freedesktop.DBus.Error.UnknownObject";
+		error_message = "Object was deleted while authenticating";
+		goto out;
 	}
 
-	args = g_dbus_message_get_body (pfd->message);
-	g_variant_get (args, "(&s&sv)", NULL, NULL, &value);
-	g_assert (pfd->glib_propname);
+	/* Handle some properties specially *sigh* */
+	if (   interface_info == &interface_info_manager
+	    && nm_streq (property_info->property_name, NM_MANAGER_GLOBAL_DNS_CONFIGURATION)) {
+		const NMGlobalDnsConfig *global_dns;
 
-	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_literal (pfd->message,
-			                                                 NM_PERM_DENIED_ERROR,
-			                                                 (error_message = "Global DNS configuration already set via configuration file"));
-			goto done;
+		if (   global_dns
+		    && !nm_global_dns_config_is_internal (global_dns)) {
+			error_name = NM_PERM_DENIED_ERROR;
+			error_message = "Global DNS configuration already set via configuration file";
+			goto out;
 		}
-		/* ... 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 if (!strcmp (pfd->glib_propname, NM_DEVICE_STATISTICS_REFRESH_RATE_MS)) {
-		g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_UINT32));
-		/* the same here */
-		g_object_set (object, pfd->glib_propname, (guint) g_variant_get_uint32 (value), NULL);
-	} else {
-		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);
 	}
 
-	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);
+	g_dbus_gvariant_to_gvalue (value, &gvalue);
+	g_object_set_property (G_OBJECT (obj), property_info->property_name, &gvalue);
+	g_value_unset (&gvalue);
 
-	free_property_filter_data (pfd);
+out:
+	nm_audit_log_control_op (property_info->writable.audit_op,
+	                         property_info->property_name,
+	                         !error_message,
+	                         nm_auth_chain_get_subject (chain),
+	                         error_message);
+	if (error_message)
+		g_dbus_method_invocation_return_dbus_error (invocation, error_name, error_message);
+	else
+		g_dbus_method_invocation_return_value (invocation, NULL);
+	nm_auth_chain_destroy (chain);
 }
 
-static gboolean
-do_set_property_check (gpointer user_data)
+void
+nm_manager_dbus_set_property_handle (NMDBusObject *obj,
+                                     const NMDBusInterfaceInfoExtended *interface_info,
+                                     const NMDBusPropertyInfoExtended *property_info,
+                                     GDBusConnection *connection,
+                                     const char *sender,
+                                     GDBusMethodInvocation *invocation,
+                                     GVariant *value,
+                                     gpointer user_data)
 {
-	PropertyFilterData *pfd = user_data;
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (pfd->self);
-	GDBusMessage *reply = NULL;
+	NMManager *self = user_data;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMAuthChain *chain;
 	const char *error_message = NULL;
+	gs_unref_object NMAuthSubject *subject = NULL;
+	DBusSetPropertyHandle *handle_data;
 
-	pfd->subject = nm_auth_subject_new_unix_process_from_message (pfd->connection, pfd->message);
-	if (!pfd->subject) {
-		reply = g_dbus_message_new_method_error_literal (pfd->message,
-		                                                 NM_PERM_DENIED_ERROR,
-		                                                 (error_message = "Could not determine request UID."));
-		goto out;
+	subject = nm_auth_subject_new_unix_process_from_context (invocation);
+	if (!subject) {
+		error_message = "Could not determine request UID";
+		goto err;
 	}
 
-	/* Validate the user request */
-	chain = nm_auth_chain_new_subject (pfd->subject, NULL, prop_set_auth_done_cb, pfd);
-	if (!chain) {
-		reply = g_dbus_message_new_method_error_literal (pfd->message,
-		                                                 NM_PERM_DENIED_ERROR,
-		                                                 (error_message = "Could not authenticate request."));
-		goto out;
-	}
+	handle_data = g_slice_new0 (DBusSetPropertyHandle);
+	handle_data->self = g_object_ref (self);
+	handle_data->obj = g_object_ref (obj);
+	handle_data->interface_info = interface_info;
+	handle_data->property_info = property_info;
+	handle_data->value = g_variant_ref (value);
+	handle_data->export_version_id = nm_dbus_object_get_export_version_id (obj);
 
+	chain = nm_auth_chain_new_subject (subject, invocation, _dbus_set_property_auth_cb, handle_data);
 	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
-	nm_auth_chain_add_call (chain, pfd->permission, TRUE);
-
-out:
-	if (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);
-	}
-
-	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 if (!strcmp (propname, "ConnectivityCheckEnabled")) {
-			glib_propname = NM_MANAGER_CONNECTIVITY_CHECK_ENABLED;
-			permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK;
-			audit_op = NM_AUDIT_OP_NET_CONTROL;
-		} 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 if (!strcmp (propiface, NM_DBUS_INTERFACE_DEVICE_STATISTICS)) {
-		if (!strcmp (propname, "RefreshRateMs")) {
-			glib_propname = NM_DEVICE_STATISTICS_REFRESH_RATE_MS;
-			permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS;
-			audit_op = NM_AUDIT_OP_STATISTICS;
-			expected_type = G_VARIANT_TYPE ("u");
-		} 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;
+	nm_auth_chain_add_call (chain, property_info->writable.permission, TRUE);
+	return;
 
-		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);
-	}
+err:
+	nm_audit_log_control_op (property_info->writable.audit_op,
+	                         property_info->property_name,
+	                         FALSE,
+	                         invocation,
+	                         error_message);
+	g_dbus_method_invocation_return_error_literal (invocation,
+	                                               G_DBUS_ERROR,
+	                                               G_DBUS_ERROR_AUTH_FAILED,
+	                                               error_message);
 }
 
 /*****************************************************************************/
@@ -6049,7 +6081,7 @@ _checkpoint_mgr_get (NMManager *self, gboolean create_as_needed)
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 
 	if (G_UNLIKELY (!priv->checkpoint_mgr) && create_as_needed)
-		priv->checkpoint_mgr = nm_checkpoint_manager_new (self);
+		priv->checkpoint_mgr = nm_checkpoint_manager_new (self, obj_properties[PROP_CHECKPOINTS]);
 	return priv->checkpoint_mgr;
 }
 
@@ -6068,13 +6100,15 @@ checkpoint_auth_done_cb (NMAuthChain *chain,
 	GVariant *variant = NULL;
 	GError *error = NULL;
 	const char *arg = NULL;
+	guint32 add_timeout;
 
 	op = nm_auth_chain_get_data (chain, "audit-op");
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
 	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK);
 
-	if (   nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_DESTROY)
-	    || nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ROLLBACK))
+	if (NM_IN_STRSET (op, NM_AUDIT_OP_CHECKPOINT_DESTROY,
+	                      NM_AUDIT_OP_CHECKPOINT_ROLLBACK,
+	                      NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT))
 		arg = checkpoint_path = nm_auth_chain_get_data (chain, "checkpoint_path");
 
 	if (auth_error) {
@@ -6098,7 +6132,7 @@ checkpoint_auth_done_cb (NMAuthChain *chain,
 			                                           (NMCheckpointCreateFlags) flags,
 			                                           &error);
 			if (checkpoint) {
-				arg = nm_exported_object_get_path (NM_EXPORTED_OBJECT (checkpoint));
+				arg = nm_dbus_object_get_path (NM_DBUS_OBJECT (checkpoint));
 				variant = g_variant_new ("(o)", arg);
 			}
 		} else if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_DESTROY)) {
@@ -6107,6 +6141,10 @@ checkpoint_auth_done_cb (NMAuthChain *chain,
 		} else if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ROLLBACK)) {
 			nm_checkpoint_manager_rollback (_checkpoint_mgr_get (self, TRUE),
 			                                checkpoint_path, &variant, &error);
+		} else if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT)) {
+			add_timeout = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "add_timeout"));
+			nm_checkpoint_manager_adjust_rollback_timeout (_checkpoint_mgr_get (self, TRUE),
+			                                               checkpoint_path, add_timeout, &error);
 		} else
 			g_return_if_reached ();
 	}
@@ -6119,63 +6157,71 @@ checkpoint_auth_done_cb (NMAuthChain *chain,
 	else
 		g_dbus_method_invocation_return_value (context, variant);
 
-
-	nm_auth_chain_unref (chain);
+	nm_auth_chain_destroy (chain);
 }
 
 static void
-impl_manager_checkpoint_create (NMManager *self,
-                                GDBusMethodInvocation *context,
-                                const char *const *devices,
-                                guint32 rollback_timeout,
-                                guint32 flags)
-{
-	NMManagerPrivate *priv;
+impl_manager_checkpoint_create (NMDBusObject *obj,
+                                const NMDBusInterfaceInfoExtended *interface_info,
+                                const NMDBusMethodInfoExtended *method_info,
+                                GDBusConnection *connection,
+                                const char *sender,
+                                GDBusMethodInvocation *invocation,
+                                GVariant *parameters)
+{
+	NMManager *self = NM_MANAGER (obj);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMAuthChain *chain;
-	GError *error = NULL;
+	char **devices;
+	guint32 rollback_timeout;
+	guint32 flags;
 
 	G_STATIC_ASSERT_EXPR (sizeof (flags) <= sizeof (NMCheckpointCreateFlags));
-	g_return_if_fail (NM_IS_MANAGER (self));
-	priv = NM_MANAGER_GET_PRIVATE (self);
 
-	chain = nm_auth_chain_new_context (context, checkpoint_auth_done_cb, self);
+	chain = nm_auth_chain_new_context (invocation, checkpoint_auth_done_cb, self);
 	if (!chain) {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             "Unable to authenticate request.");
-		g_dbus_method_invocation_take_error (context, error);
+		g_dbus_method_invocation_return_error_literal (invocation,
+		                                               NM_MANAGER_ERROR,
+		                                               NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                                               "Unable to authenticate request.");
 		return;
 	}
 
+	g_variant_get (parameters, "(^aouu)", &devices, &rollback_timeout, &flags);
+
 	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
 	nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_CREATE, NULL);
-	nm_auth_chain_set_data (chain, "devices", g_strdupv ((char **) devices), (GDestroyNotify) g_strfreev);
+	nm_auth_chain_set_data (chain, "devices", devices, (GDestroyNotify) g_strfreev);
 	nm_auth_chain_set_data (chain, "flags",  GUINT_TO_POINTER (flags), NULL);
 	nm_auth_chain_set_data (chain, "timeout", GUINT_TO_POINTER (rollback_timeout), NULL);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE);
 }
 
 static void
-impl_manager_checkpoint_destroy (NMManager *self,
-                                 GDBusMethodInvocation *context,
-                                 const char *checkpoint_path)
-{
-	NMManagerPrivate *priv;
-	GError *error = NULL;
+impl_manager_checkpoint_destroy (NMDBusObject *obj,
+                                 const NMDBusInterfaceInfoExtended *interface_info,
+                                 const NMDBusMethodInfoExtended *method_info,
+                                 GDBusConnection *connection,
+                                 const char *sender,
+                                 GDBusMethodInvocation *invocation,
+                                 GVariant *parameters)
+{
+	NMManager *self = NM_MANAGER (obj);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMAuthChain *chain;
+	const char *checkpoint_path;
 
-	g_return_if_fail (NM_IS_MANAGER (self));
-	priv = NM_MANAGER_GET_PRIVATE (self);
-
-	chain = nm_auth_chain_new_context (context, checkpoint_auth_done_cb, self);
+	chain = nm_auth_chain_new_context (invocation, checkpoint_auth_done_cb, self);
 	if (!chain) {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             "Unable to authenticate request.");
-		g_dbus_method_invocation_take_error (context, error);
+		g_dbus_method_invocation_return_error_literal (invocation,
+		                                               NM_MANAGER_ERROR,
+		                                               NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                                               "Unable to authenticate request.");
 		return;
 	}
 
+	g_variant_get (parameters, "(&o)", &checkpoint_path);
+
 	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
 	nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_DESTROY, NULL);
 	nm_auth_chain_set_data (chain, "checkpoint_path", g_strdup (checkpoint_path), g_free);
@@ -6183,39 +6229,79 @@ impl_manager_checkpoint_destroy (NMManager *self,
 }
 
 static void
-impl_manager_checkpoint_rollback (NMManager *self,
-                                  GDBusMethodInvocation *context,
-                                  const char *checkpoint_path)
-{
-	NMManagerPrivate *priv;
-	GError *error = NULL;
+impl_manager_checkpoint_rollback (NMDBusObject *obj,
+                                  const NMDBusInterfaceInfoExtended *interface_info,
+                                  const NMDBusMethodInfoExtended *method_info,
+                                  GDBusConnection *connection,
+                                  const char *sender,
+                                  GDBusMethodInvocation *invocation,
+                                  GVariant *parameters)
+{
+	NMManager *self = NM_MANAGER (obj);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMAuthChain *chain;
+	const char *checkpoint_path;
 
-	g_return_if_fail (NM_IS_MANAGER (self));
-	priv = NM_MANAGER_GET_PRIVATE (self);
-
-	chain = nm_auth_chain_new_context (context, checkpoint_auth_done_cb, self);
+	chain = nm_auth_chain_new_context (invocation, checkpoint_auth_done_cb, self);
 	if (!chain) {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             "Unable to authenticate request.");
-		g_dbus_method_invocation_take_error (context, error);
+		g_dbus_method_invocation_return_error_literal (invocation,
+		                                               NM_MANAGER_ERROR,
+		                                               NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                                               "Unable to authenticate request.");
 		return;
 	}
 
+	g_variant_get (parameters, "(&o)", &checkpoint_path);
+
 	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
 	nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_ROLLBACK, NULL);
 	nm_auth_chain_set_data (chain, "checkpoint_path", g_strdup (checkpoint_path), g_free);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE);
 }
 
+static void
+impl_manager_checkpoint_adjust_rollback_timeout (NMDBusObject *obj,
+                                                 const NMDBusInterfaceInfoExtended *interface_info,
+                                                 const NMDBusMethodInfoExtended *method_info,
+                                                 GDBusConnection *connection,
+                                                 const char *sender,
+                                                 GDBusMethodInvocation *invocation,
+                                                 GVariant *parameters)
+{
+	NMManager *self = NM_MANAGER (obj);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMAuthChain *chain;
+	const char *checkpoint_path;
+	guint32 add_timeout;
+
+	chain = nm_auth_chain_new_context (invocation, checkpoint_auth_done_cb, self);
+	if (!chain) {
+		g_dbus_method_invocation_return_error_literal (invocation,
+		                                               NM_MANAGER_ERROR,
+		                                               NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                                               "Unable to authenticate request.");
+		return;
+	}
+
+	g_variant_get (parameters, "(&ou)", &checkpoint_path, &add_timeout);
+
+	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+	nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT, NULL);
+	nm_auth_chain_set_data (chain, "checkpoint_path", g_strdup (checkpoint_path), g_free);
+	nm_auth_chain_set_data (chain, "add_timeout", GUINT_TO_POINTER (add_timeout), NULL);
+	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE);
+}
+
 /*****************************************************************************/
 
 static void
 auth_mgr_changed (NMAuthManager *auth_manager, gpointer user_data)
 {
 	/* Let clients know they should re-check their authorization */
-	g_signal_emit (NM_MANAGER (user_data), signals[CHECK_PERMISSIONS], 0);
+	nm_dbus_object_emit_signal (user_data,
+	                            &interface_info_manager,
+	                            &signal_info_check_permissions,
+	                            "()");
 }
 
 #define KERN_RFKILL_OP_CHANGE_ALL 3
@@ -6340,14 +6426,6 @@ periodic_update_active_connection_timestamps (gpointer user_data)
 	return G_SOURCE_CONTINUE;
 }
 
-static void
-dbus_connection_changed_cb (NMBusManager *dbus_mgr,
-                            GDBusConnection *connection,
-                            gpointer user_data)
-{
-	_set_prop_filter (NM_MANAGER (user_data), connection);
-}
-
 /*****************************************************************************/
 
 void
@@ -6414,8 +6492,7 @@ nm_manager_setup (void)
 	nm_singleton_instance_register ();
 	_LOGD (LOGD_CORE, "setup %s singleton (%p)", "NMManager", singleton_instance);
 
-	nm_exported_object_export ((NMExportedObject *) self);
-
+	nm_dbus_object_export (NM_DBUS_OBJECT (self));
 	return self;
 }
 
@@ -6428,11 +6505,9 @@ constructed (GObject *object)
 
 	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 ();
 
-	nm_exported_object_export (NM_EXPORTED_OBJECT (priv->settings));
+	nm_dbus_object_export (NM_DBUS_OBJECT (priv->settings));
 
 	g_signal_connect (priv->settings, "notify::" NM_SETTINGS_STARTUP_COMPLETE,
 	                  G_CALLBACK (settings_startup_complete_changed), self);
@@ -6500,6 +6575,7 @@ nm_manager_init (NMManager *self)
 	GFile *file;
 
 	c_list_init (&priv->link_cb_lst);
+	c_list_init (&priv->devices_lst_head);
 	c_list_init (&priv->active_connections_lst_head);
 	c_list_init (&priv->delete_volatile_connection_lst_head);
 
@@ -6531,12 +6607,6 @@ nm_manager_init (NMManager *self)
 	priv->state = NM_STATE_DISCONNECTED;
 	priv->startup = TRUE;
 
-	priv->dbus_mgr = g_object_ref (nm_bus_manager_get ());
-	g_signal_connect (priv->dbus_mgr,
-	                  NM_BUS_MANAGER_DBUS_CONNECTION_CHANGED,
-	                  G_CALLBACK (dbus_connection_changed_cb),
-	                  self);
-
 	/* sleep/wake handling */
 	priv->sleep_monitor = nm_sleep_monitor_new ();
 	g_signal_connect (priv->sleep_monitor, NM_SLEEP_MONITOR_SLEEPING,
@@ -6571,13 +6641,7 @@ nm_manager_init (NMManager *self)
 	priv->timestamp_update_id = g_timeout_add_seconds (300, (GSourceFunc) periodic_update_active_connection_timestamps, self);
 
 	priv->metered = NM_METERED_UNKNOWN;
-	priv->sleep_devices = g_hash_table_new (g_direct_hash, g_direct_equal);
-}
-
-static gboolean
-device_is_real (GObject *device, gpointer user_data)
-{
-	return nm_device_is_real (NM_DEVICE (device));
+	priv->sleep_devices = g_hash_table_new (nm_direct_hash, NULL);
 }
 
 static void
@@ -6592,7 +6656,6 @@ get_property (GObject *object, guint prop_id,
 	const char *path;
 	NMActiveConnection *ac;
 	GPtrArray *ptrarr;
-	gboolean vbool;
 
 	switch (prop_id) {
 	case PROP_VERSION:
@@ -6634,7 +6697,7 @@ get_property (GObject *object, guint prop_id,
 	case PROP_ACTIVE_CONNECTIONS:
 		ptrarr = g_ptr_array_new ();
 		c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) {
-			path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (ac));
+			path = nm_dbus_object_get_path (NM_DBUS_OBJECT (ac));
 			if (path)
 				g_ptr_array_add (ptrarr, g_strdup (path));
 		}
@@ -6649,15 +6712,10 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_boolean (value, nm_config_data_get_connectivity_uri (config_data) != NULL);
 		break;
 	case PROP_CONNECTIVITY_CHECK_ENABLED:
-#if WITH_CONCHECK
-		vbool = nm_connectivity_check_enabled (nm_connectivity_get ());
-#else
-		vbool = FALSE;
-#endif
-		g_value_set_boolean (value, vbool);
+		g_value_set_boolean (value, concheck_enabled (self, NULL));
 		break;
 	case PROP_PRIMARY_CONNECTION:
-		nm_utils_g_value_set_object_path (value, priv->primary_connection);
+		nm_dbus_utils_g_value_set_object_path (value, priv->primary_connection);
 		break;
 	case PROP_PRIMARY_CONNECTION_TYPE:
 		type = NULL;
@@ -6671,13 +6729,15 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_string (value, type ? type : "");
 		break;
 	case PROP_ACTIVATING_CONNECTION:
-		nm_utils_g_value_set_object_path (value, priv->activating_connection);
+		nm_dbus_utils_g_value_set_object_path (value, priv->activating_connection);
 		break;
 	case PROP_SLEEPING:
 		g_value_set_boolean (value, priv->sleeping);
 		break;
 	case PROP_DEVICES:
-		nm_utils_g_value_set_object_path_array (value, priv->devices, device_is_real, NULL);
+		g_value_take_boxed (value,
+		                    nm_utils_strv_make_deep_copied (_get_devices_paths (self,
+		                                                                        FALSE)));
 		break;
 	case PROP_METERED:
 		g_value_set_uint (value, priv->metered);
@@ -6688,7 +6748,16 @@ get_property (GObject *object, guint prop_id,
 		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);
+		g_value_take_boxed (value,
+		                    nm_utils_strv_make_deep_copied (_get_devices_paths (self,
+		                                                                        TRUE)));
+		break;
+	case PROP_CHECKPOINTS:
+		g_value_take_boxed (value,
+		                    priv->checkpoint_mgr
+		                    ? nm_utils_strv_make_deep_copied (nm_checkpoint_manager_get_checkpoint_paths (priv->checkpoint_mgr,
+		                                                                                                  NULL))
+		                    : NULL);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -6755,11 +6824,6 @@ dispose (GObject *object)
 	CList *iter, *iter_safe;
 	NMActiveConnection *ac, *ac_safe;
 
-	nm_clear_g_source (&priv->delete_volatile_connection_idle_id);
-	_delete_volatile_connection_all (self, FALSE);
-	nm_assert (!priv->delete_volatile_connection_idle_id);
-	nm_assert (c_list_is_empty (&priv->delete_volatile_connection_lst_head));
-
 	g_signal_handlers_disconnect_by_func (priv->platform,
 	                                      G_CALLBACK (platform_link_cb),
 	                                      self);
@@ -6771,14 +6835,18 @@ dispose (GObject *object)
 		g_slice_free (PlatformLinkCbData, data);
 	}
 
-	g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_unref);
+	g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_destroy);
 	priv->auth_chains = NULL;
 
 	nm_clear_g_source (&priv->devices_inited_id);
 
-	if (priv->checkpoint_mgr) {
-		nm_checkpoint_manager_destroy_all (priv->checkpoint_mgr, NULL);
-		g_clear_pointer (&priv->checkpoint_mgr, nm_checkpoint_manager_unref);
+	g_clear_pointer (&priv->checkpoint_mgr, nm_checkpoint_manager_free);
+
+	if (priv->concheck_mgr) {
+		g_signal_handlers_disconnect_by_func (priv->concheck_mgr,
+		                                      G_CALLBACK (concheck_config_changed_cb),
+		                                      self);
+		g_clear_object (&priv->concheck_mgr);
 	}
 
 	if (priv->auth_mgr) {
@@ -6788,7 +6856,7 @@ dispose (GObject *object)
 		g_clear_object (&priv->auth_mgr);
 	}
 
-	g_assert (priv->devices == NULL);
+	nm_assert (c_list_is_empty (&priv->devices_lst_head));
 
 	nm_clear_g_source (&priv->ac_cleanup_id);
 
@@ -6825,13 +6893,6 @@ dispose (GObject *object)
 
 	g_clear_object (&priv->vpn_manager);
 
-	/* Unregister property filter */
-	if (priv->dbus_mgr) {
-		g_signal_handlers_disconnect_by_func (priv->dbus_mgr, dbus_connection_changed_cb, self);
-		g_clear_object (&priv->dbus_mgr);
-	}
-	_set_prop_filter (self, NULL);
-
 	sleep_devices_clear (self);
 	g_clear_pointer (&priv->sleep_devices, g_hash_table_unref);
 
@@ -6854,6 +6915,11 @@ dispose (GObject *object)
 		g_clear_object (&priv->rfkill_mgr);
 	}
 
+	nm_clear_g_source (&priv->delete_volatile_connection_idle_id);
+	_delete_volatile_connection_all (self, FALSE);
+	nm_assert (!priv->delete_volatile_connection_idle_id);
+	nm_assert (c_list_is_empty (&priv->delete_volatile_connection_lst_head));
+
 	nm_device_factory_manager_for_each_factory (_deinit_device_factory, self);
 
 	nm_clear_g_source (&priv->timestamp_update_id);
@@ -6875,22 +6941,274 @@ finalize (GObject *object)
 	g_object_unref (priv->platform);
 }
 
+static const GDBusSignalInfo signal_info_check_permissions = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
+	"CheckPermissions",
+);
+
+static const GDBusSignalInfo signal_info_state_changed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
+	"StateChanged",
+	.args = NM_DEFINE_GDBUS_ARG_INFOS (
+		NM_DEFINE_GDBUS_ARG_INFO ("state", "u"),
+	),
+);
+
+static const GDBusSignalInfo signal_info_device_added = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
+	"DeviceAdded",
+	.args = NM_DEFINE_GDBUS_ARG_INFOS (
+		NM_DEFINE_GDBUS_ARG_INFO ("device_path", "o"),
+	),
+);
+
+static const GDBusSignalInfo signal_info_device_removed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
+	"DeviceRemoved",
+	.args = NM_DEFINE_GDBUS_ARG_INFOS (
+		NM_DEFINE_GDBUS_ARG_INFO ("device_path", "o"),
+	),
+);
+
+static const NMDBusInterfaceInfoExtended interface_info_manager = {
+	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
+		NM_DBUS_INTERFACE,
+		.methods = NM_DEFINE_GDBUS_METHOD_INFOS (
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"Reload",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("flags", "u"),
+					),
+				),
+				.handle = impl_manager_reload,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"GetDevices",
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("devices", "ao"),
+					),
+				),
+				.handle = impl_manager_get_devices,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"GetAllDevices",
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("devices", "ao"),
+					),
+				),
+				.handle = impl_manager_get_all_devices,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"GetDeviceByIpIface",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("iface", "s"),
+					),
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("device", "o"),
+					),
+				),
+				.handle = impl_manager_get_device_by_ip_iface,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"ActivateConnection",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("connection",      "o"),
+						NM_DEFINE_GDBUS_ARG_INFO ("device",          "o"),
+						NM_DEFINE_GDBUS_ARG_INFO ("specific_object", "o"),
+					),
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("active_connection", "o"),
+					),
+				),
+				.handle = impl_manager_activate_connection,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"AddAndActivateConnection",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("connection",      "a{sa{sv}}"),
+						NM_DEFINE_GDBUS_ARG_INFO ("device",          "o"),
+						NM_DEFINE_GDBUS_ARG_INFO ("specific_object", "o"),
+					),
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("path",              "o"),
+						NM_DEFINE_GDBUS_ARG_INFO ("active_connection", "o"),
+					),
+				),
+				.handle = impl_manager_add_and_activate_connection,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"DeactivateConnection",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("active_connection", "o"),
+					),
+				),
+				.handle = impl_manager_deactivate_connection,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"Sleep",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("sleep", "b"),
+					),
+				),
+				.handle = impl_manager_sleep,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"Enable",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("enable", "b"),
+					),
+				),
+				.handle = impl_manager_enable,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"GetPermissions",
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("permissions", "a{ss}"),
+					),
+				),
+				.handle = impl_manager_get_permissions,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"SetLogging",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("level",   "s"),
+						NM_DEFINE_GDBUS_ARG_INFO ("domains", "s"),
+					),
+				),
+				.handle = impl_manager_set_logging,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"GetLogging",
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("level",   "s"),
+						NM_DEFINE_GDBUS_ARG_INFO ("domains", "s"),
+					),
+				),
+				.handle = impl_manager_get_logging,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"CheckConnectivity",
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("connectivity", "u"),
+					),
+				),
+				.handle = impl_manager_check_connectivity,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"state",
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("state", "u"),
+					),
+				),
+				.handle = impl_manager_state,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"CheckpointCreate",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("devices",          "ao"),
+						NM_DEFINE_GDBUS_ARG_INFO ("rollback_timeout", "u"),
+						NM_DEFINE_GDBUS_ARG_INFO ("flags",            "u"),
+					),
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("checkpoint", "o"),
+					),
+				),
+				.handle = impl_manager_checkpoint_create,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"CheckpointDestroy",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("checkpoint", "o"),
+					),
+				),
+				.handle = impl_manager_checkpoint_destroy,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"CheckpointRollback",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("checkpoint", "o"),
+					),
+					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("result", "a{su}"),
+					),
+				),
+				.handle = impl_manager_checkpoint_rollback,
+			),
+			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+					"CheckpointAdjustRollbackTimeout",
+					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+						NM_DEFINE_GDBUS_ARG_INFO ("checkpoint", "o"),
+						NM_DEFINE_GDBUS_ARG_INFO ("add_timeout", "u"),
+					),
+				),
+				.handle = impl_manager_checkpoint_adjust_rollback_timeout,
+			),
+		),
+		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
+			&nm_signal_info_property_changed_legacy,
+			&signal_info_check_permissions,
+			&signal_info_state_changed,
+			&signal_info_device_added,
+			&signal_info_device_removed,
+		),
+		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Devices",                    "ao",    NM_MANAGER_DEVICES),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("AllDevices",                 "ao",    NM_MANAGER_ALL_DEVICES),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Checkpoints",                "ao",    NM_MANAGER_CHECKPOINTS),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("NetworkingEnabled",          "b",     NM_MANAGER_NETWORKING_ENABLED),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("WirelessEnabled",            "b",     NM_MANAGER_WIRELESS_ENABLED,              NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI,               NM_AUDIT_OP_RADIO_CONTROL),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("WirelessHardwareEnabled",    "b",     NM_MANAGER_WIRELESS_HARDWARE_ENABLED),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("WwanEnabled",                "b",     NM_MANAGER_WWAN_ENABLED,                  NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN,               NM_AUDIT_OP_RADIO_CONTROL),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("WwanHardwareEnabled",        "b",     NM_MANAGER_WWAN_HARDWARE_ENABLED),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("WimaxEnabled",               "b",     NM_MANAGER_WIMAX_ENABLED,                 NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX,              NM_AUDIT_OP_RADIO_CONTROL),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("WimaxHardwareEnabled",       "b",     NM_MANAGER_WIMAX_HARDWARE_ENABLED),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("ActiveConnections",          "ao",    NM_MANAGER_ACTIVE_CONNECTIONS),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("PrimaryConnection",          "o",     NM_MANAGER_PRIMARY_CONNECTION),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("PrimartConnectionType",      "s",     NM_MANAGER_PRIMARY_CONNECTION_TYPE),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Metered",                    "u",     NM_MANAGER_METERED),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("ActivatingConnection",       "o",     NM_MANAGER_ACTIVATING_CONNECTION),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Startup",                    "b",     NM_MANAGER_STARTUP),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Version",                    "s",     NM_MANAGER_VERSION),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Capabilities",               "u",     NM_MANAGER_CAPABILITIES),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("State",                      "u",     NM_MANAGER_STATE),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Connectivity",               "u",     NM_MANAGER_CONNECTIVITY),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("ConnectivityCheckAvailable", "b",     NM_MANAGER_CONNECTIVITY_CHECK_AVAILABLE),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("ConnectivityCheckEnabled",   "b",     NM_MANAGER_CONNECTIVITY_CHECK_ENABLED,    NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK, NM_AUDIT_OP_NET_CONTROL),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("GlobalDnsConfiguration",     "a{sv}", NM_MANAGER_GLOBAL_DNS_CONFIGURATION,      NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS,        NM_AUDIT_OP_NET_CONTROL),
+		),
+	),
+	.legacy_property_changed = TRUE,
+};
+
 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);
+	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (manager_class);
 
-	exported_object_class->export_path = NM_DBUS_PATH;
+	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_STATIC (NM_DBUS_PATH);
+	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_manager);
 
-	/* virtual methods */
 	object_class->constructed = constructed;
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 
-	/* properties */
 	obj_properties[PROP_VERSION] =
 	    g_param_spec_string (NM_MANAGER_VERSION, "", "",
 	                         NULL,
@@ -6898,11 +7216,11 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	                         G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_CAPABILITIES] =
-		g_param_spec_variant (NM_MANAGER_CAPABILITIES, "", "",
-		                      G_VARIANT_TYPE ("au"),
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS);
+	    g_param_spec_variant (NM_MANAGER_CAPABILITIES, "", "",
+	                          G_VARIANT_TYPE ("au"),
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_STATE] =
 	    g_param_spec_uint (NM_MANAGER_STATE, "", "",
@@ -7053,11 +7371,17 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	                        G_PARAM_READABLE |
 	                        G_PARAM_STATIC_STRINGS);
 
+	obj_properties[PROP_CHECKPOINTS] =
+	    g_param_spec_boxed (NM_MANAGER_CHECKPOINTS, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READABLE |
+	                        G_PARAM_STATIC_STRINGS);
+
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	/* signals */
 
-	/* D-Bus exported; emitted only for realized devices */
+	/* emitted only for realized devices */
 	signals[DEVICE_ADDED] =
 	    g_signal_new (NM_MANAGER_DEVICE_ADDED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
@@ -7073,7 +7397,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	                  NULL, NULL, NULL,
 	                  G_TYPE_NONE, 1, G_TYPE_OBJECT);
 
-	/* D-Bus exported; emitted only for realized devices when a device
+	/* emitted only for realized devices when a device
 	 * becomes unrealized or removed */
 	signals[DEVICE_REMOVED] =
 	    g_signal_new (NM_MANAGER_DEVICE_REMOVED,
@@ -7090,20 +7414,6 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	                  NULL, NULL, NULL,
 	                  G_TYPE_NONE, 1, G_TYPE_OBJECT);
 
-	signals[STATE_CHANGED] =
-	    g_signal_new (NM_MANAGER_STATE_CHANGED,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_FIRST,
-	                  0, NULL, NULL, NULL,
-	                  G_TYPE_NONE, 1, G_TYPE_UINT);
-
-	signals[CHECK_PERMISSIONS] =
-	    g_signal_new (NM_MANAGER_CHECK_PERMISSIONS,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_FIRST,
-	                  0, NULL, NULL, NULL,
-	                  G_TYPE_NONE, 0);
-
 	signals[ACTIVE_CONNECTION_ADDED] =
 	    g_signal_new (NM_MANAGER_ACTIVE_CONNECTION_ADDED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
@@ -7124,25 +7434,4 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	                  G_SIGNAL_RUN_FIRST,
 	                  0, NULL, NULL, NULL,
 	                  G_TYPE_NONE, 0);
-
-	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (manager_class),
-	                                        NMDBUS_TYPE_MANAGER_SKELETON,
-	                                        "Reload", impl_manager_reload,
-	                                        "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,
-	                                        "CheckpointCreate", impl_manager_checkpoint_create,
-	                                        "CheckpointDestroy", impl_manager_checkpoint_destroy,
-	                                        "CheckpointRollback", impl_manager_checkpoint_rollback,
-	                                        NULL);
 }