about summary refs log tree commit diff
path: root/src/settings/nm-settings-connection.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2019-07-31 10:51:42 +0200
committerMichael Biebl <biebl@debian.org>2019-07-31 10:51:42 +0200
commit2e5fa45ddfbb5cffa1e78221f1cea706e2f298af (patch)
tree86f69d36c56de3074280456eddc854a780b8e04b /src/settings/nm-settings-connection.c
parent85563b7fc7ec2cd21e38debb9b28db342e2e8e7c (diff)
New upstream version 1.19.90 upstream/1.19.90
Diffstat (limited to 'src/settings/nm-settings-connection.c')
-rw-r--r--src/settings/nm-settings-connection.c1693
1 files changed, 671 insertions, 1022 deletions
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index 3fdaa598..25a27e68 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -1,4 +1,3 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager system settings service
  *
  * This program is free software; you can redistribute it and/or modify
@@ -25,6 +24,7 @@
 
 #include "c-list/src/c-list.h"
 
+#include "nm-glib-aux/nm-keyfile-aux.h"
 #include "nm-libnm-core-intern/nm-common-macros.h"
 #include "nm-config.h"
 #include "nm-config-data.h"
@@ -37,14 +37,18 @@
 #include "NetworkManagerUtils.h"
 #include "nm-core-internal.h"
 #include "nm-audit-manager.h"
-
-#define SETTINGS_TIMESTAMPS_FILE  NMSTATEDIR "/timestamps"
-#define SETTINGS_SEEN_BSSIDS_FILE NMSTATEDIR "/seen-bssids"
+#include "nm-settings.h"
+#include "settings/plugins/keyfile/nms-keyfile-storage.h"
 
 #define AUTOCONNECT_RETRIES_UNSET        -2
 #define AUTOCONNECT_RETRIES_FOREVER      -1
 #define AUTOCONNECT_RESET_RETRIES_TIMER 300
 
+#define _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES ((NMSettingsUpdate2Flags) (  NM_SETTINGS_UPDATE2_FLAG_TO_DISK \
+                                                                               | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY \
+                                                                               | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED \
+                                                                               | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY))
+
 /*****************************************************************************/
 
 NMConnection **
@@ -70,13 +74,11 @@ nm_settings_connections_array_to_connections (NMSettingsConnection *const*connec
 
 NM_GOBJECT_PROPERTIES_DEFINE (NMSettingsConnection,
 	PROP_UNSAVED,
-	PROP_READY,
 	PROP_FLAGS,
 	PROP_FILENAME,
 );
 
 enum {
-	REMOVED,
 	UPDATED_INTERNAL,
 	FLAGS_CHANGED,
 	LAST_SIGNAL
@@ -86,18 +88,12 @@ static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct _NMSettingsConnectionPrivate {
 
-	NMAgentManager *agent_mgr;
-	NMSessionMonitor *session_monitor;
-	gulong session_changed_id;
-
-	NMSettingsConnectionIntFlags flags:5;
-
-	bool removed:1;
-	bool ready:1;
+	NMSettings *settings;
 
-	bool timestamp_set:1;
+	NMKeyFileDB *kf_db_timestamps;
+	NMKeyFileDB *kf_db_seen_bssids;
 
-	NMSettingsAutoconnectBlockedReason autoconnect_blocked_reason:4;
+	NMAgentManager *agent_mgr;
 
 	/* List of pending authentication requests */
 	CList auth_lst_head;
@@ -106,12 +102,18 @@ typedef struct _NMSettingsConnectionPrivate {
 
 	NMConnection *connection;
 
+	NMSettingsStorage *storage;
+
+	char *filename;
+
+	NMDevice *default_wired_device;
+
 	/* Caches secrets from on-disk connections; were they not cached any
 	 * call to nm_connection_clear_secrets() wipes them out and we'd have
 	 * to re-read them from disk which defeats the purpose of having the
 	 * connection in-memory at all.
 	 */
-	NMConnection *system_secrets;
+	GVariant *system_secrets;
 
 	/* Caches secrets from agents during the activation process; if new system
 	 * secrets are returned from an agent, they get written out to disk,
@@ -119,9 +121,7 @@ typedef struct _NMSettingsConnectionPrivate {
 	 * secrets, and would wipe out any agent-owned or not-saved secrets the
 	 * agent also returned.
 	 */
-	NMConnection *agent_secrets;
-
-	char *filename;
+	GVariant *agent_secrets;
 
 	GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */
 
@@ -130,10 +130,21 @@ typedef struct _NMSettingsConnectionPrivate {
 	guint64 last_secret_agent_version_id;
 
 	int autoconnect_retries;
+
 	gint32 autoconnect_retries_blocked_until;
 
+	bool timestamp_set:1;
+
+	NMSettingsAutoconnectBlockedReason autoconnect_blocked_reason:4;
+
+	NMSettingsConnectionIntFlags flags:5;
+
 } NMSettingsConnectionPrivate;
 
+struct _NMSettingsConnectionClass {
+	NMDBusObjectClass parent;
+};
+
 G_DEFINE_TYPE (NMSettingsConnection, nm_settings_connection, NM_TYPE_DBUS_OBJECT)
 
 #define NM_SETTINGS_CONNECTION_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMSettingsConnection, NM_IS_SETTINGS_CONNECTION)
@@ -152,7 +163,7 @@ G_DEFINE_TYPE (NMSettingsConnection, nm_settings_connection, NM_TYPE_DBUS_OBJECT
             const char *__uuid = (self) ? nm_settings_connection_get_uuid (self) : NULL; \
             \
             if (self) { \
-                g_snprintf (__prefix, sizeof (__prefix), "%s[%p%s%s]", _NMLOG_PREFIX_NAME, self, __uuid ? "," : "", __uuid ?: ""); \
+                g_snprintf (__prefix, sizeof (__prefix), "%s["NM_HASH_OBFUSCATE_PTR_FMT"%s%s]", _NMLOG_PREFIX_NAME, NM_HASH_OBFUSCATE_PTR (self), __uuid ? "," : "", __uuid ?: ""); \
                 __p_prefix = __prefix; \
             } \
             _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, __uuid, \
@@ -167,6 +178,92 @@ static const GDBusSignalInfo signal_info_updated;
 static const GDBusSignalInfo signal_info_removed;
 static const NMDBusInterfaceInfoExtended interface_info_settings_connection;
 
+static void update_system_secrets_cache (NMSettingsConnection *self, NMConnection *new);
+static void update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new);
+
+/*****************************************************************************/
+
+NMDevice *
+nm_settings_connection_default_wired_get_device (NMSettingsConnection *self)
+{
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
+	nm_assert (!priv->default_wired_device || NM_IS_DEVICE (priv->default_wired_device));
+
+	return priv->default_wired_device;
+}
+
+void
+nm_settings_connection_default_wired_set_device (NMSettingsConnection *self,
+                                                 NMDevice *device)
+{
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
+	nm_assert (!priv->default_wired_device || NM_IS_DEVICE (priv->default_wired_device));
+	nm_assert (!device || NM_IS_DEVICE (device));
+
+	nm_assert ((!!priv->default_wired_device) != (!!device));
+
+	priv->default_wired_device = device;
+}
+
+/*****************************************************************************/
+
+NMSettingsStorage *
+nm_settings_connection_get_storage (NMSettingsConnection *self)
+{
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
+
+	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->storage;
+}
+
+void
+_nm_settings_connection_set_storage (NMSettingsConnection *self,
+                                     NMSettingsStorage *storage)
+{
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+	const char *filename;
+
+	nm_assert (NM_IS_SETTINGS_STORAGE (storage));
+	nm_assert (   !priv->storage
+	           || nm_streq (nm_settings_storage_get_uuid (storage),
+	                        nm_settings_storage_get_uuid (priv->storage)));
+
+	nm_g_object_ref_set (&priv->storage, storage);
+
+	filename = nm_settings_storage_get_filename (priv->storage);
+
+	if (!nm_streq0 (priv->filename, filename)) {
+		g_free (priv->filename);
+		priv->filename = g_strdup (filename);
+		_notify (self, PROP_FILENAME);
+	}
+}
+
+/*****************************************************************************/
+
+gboolean
+nm_settings_connection_still_valid (NMSettingsConnection *self)
+{
+	gboolean valid;
+
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
+
+	valid = !c_list_is_empty (&self->_connections_lst);
+
+	nm_assert (valid == nm_settings_has_connection (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->settings, self));
+
+	return valid;
+}
+
+/*****************************************************************************/
+
+static GHashTable *
+_seen_bssids_hash_new (void)
+{
+	return g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
+}
+
 /*****************************************************************************/
 
 NMConnection *
@@ -177,6 +274,50 @@ nm_settings_connection_get_connection (NMSettingsConnection *self)
 	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->connection;
 }
 
+void
+_nm_settings_connection_set_connection (NMSettingsConnection *self,
+                                        NMConnection *new_connection,
+                                        NMConnection **out_connection_old,
+                                        NMSettingsConnectionUpdateReason update_reason)
+{
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+	gs_unref_object NMConnection *connection_old = NULL;
+
+	nm_assert (NM_IS_CONNECTION (new_connection));
+	nm_assert (NM_IS_SETTINGS_STORAGE (priv->storage));
+	nm_assert (nm_streq0 (nm_settings_storage_get_uuid (priv->storage), nm_connection_get_uuid (new_connection)));
+	nm_assert (!out_connection_old || !*out_connection_old);
+
+	if (   !priv->connection
+	    || !nm_connection_compare (priv->connection,
+	                               new_connection,
+	                               NM_SETTING_COMPARE_FLAG_EXACT)) {
+		connection_old = priv->connection;
+		priv->connection = g_object_ref (new_connection);
+		nmtst_connection_assert_unchanging (priv->connection);
+
+		/* note that we only return @connection_old if the new connection actually differs from
+		 * before.
+		 *
+		 * So, there are three cases:
+		 *
+		 *  - return %NULL when setting the connection the first time.
+		 *  - return %NULL if setting a profile with the same content that we already have.
+		 *  - return the previous pointer if the connection changed. */
+		NM_SET_OUT (out_connection_old, g_steal_pointer (&connection_old));
+	}
+
+	if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS))
+		update_system_secrets_cache (self, NULL);
+	else if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS))
+		update_system_secrets_cache (self, priv->connection);
+
+	if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS))
+		update_agent_secrets_cache (self, NULL);
+	else if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS))
+		update_agent_secrets_cache (self, priv->connection);
+}
+
 /*****************************************************************************/
 
 gboolean
@@ -206,34 +347,23 @@ nm_settings_connection_get_last_secret_agent_version_id (NMSettingsConnection *s
 
 /*****************************************************************************/
 
-static void
-set_visible (NMSettingsConnection *self, gboolean new_visible)
-{
-	nm_settings_connection_set_flags (self,
-	                                  NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE,
-	                                  new_visible);
-}
-
-void
-nm_settings_connection_recheck_visibility (NMSettingsConnection *self)
+gboolean
+nm_settings_connection_check_visibility (NMSettingsConnection *self,
+                                         NMSessionMonitor *session_monitor)
 {
-	NMSettingsConnectionPrivate *priv;
 	NMSettingConnection *s_con;
 	guint32 num, i;
 
-	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
 
-	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+	nm_assert (NM_IS_SESSION_MONITOR (session_monitor));
 
 	s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (self));
 
 	/* Check every user in the ACL for a session */
 	num = nm_setting_connection_get_num_permissions (s_con);
-	if (num == 0) {
-		/* Visible to all */
-		set_visible (self, TRUE);
-		return;
-	}
+	if (num == 0)
+		return TRUE;
 
 	for (i = 0; i < num; i++) {
 		const char *user;
@@ -243,20 +373,13 @@ nm_settings_connection_recheck_visibility (NMSettingsConnection *self)
 			continue;
 		if (!nm_session_monitor_user_to_uid (user, &uid))
 			continue;
-		if (!nm_session_monitor_session_exists (priv->session_monitor, uid, FALSE))
+		if (!nm_session_monitor_session_exists (session_monitor, uid, FALSE))
 			continue;
 
-		set_visible (self, TRUE);
-		return;
+		return TRUE;
 	}
 
-	set_visible (self, FALSE);
-}
-
-static void
-session_changed_cb (NMSessionMonitor *self, NMSettingsConnection *sett_conn)
-{
-	nm_settings_connection_recheck_visibility (sett_conn);
+	return FALSE;
 }
 
 /*****************************************************************************/
@@ -302,7 +425,8 @@ nm_settings_connection_check_permission (NMSettingsConnection *self,
 		if (nm_setting_connection_get_permission (s_con, i, NULL, &puser, NULL)) {
 			NMSecretAgent *agent = nm_agent_manager_get_agent_by_user (priv->agent_mgr, puser);
 
-			if (agent && nm_secret_agent_has_permission (agent, permission))
+			if (   agent
+			    && nm_secret_agent_has_permission (agent, permission))
 				return TRUE;
 		}
 	}
@@ -312,421 +436,196 @@ nm_settings_connection_check_permission (NMSettingsConnection *self,
 
 /*****************************************************************************/
 
-static gboolean
-secrets_filter_cb (NMSetting *setting,
-                   const char *secret,
-                   NMSettingSecretFlags flags,
-                   gpointer user_data)
+static void
+update_system_secrets_cache (NMSettingsConnection *self, NMConnection *new)
 {
-	NMSettingSecretFlags filter_flags = GPOINTER_TO_UINT (user_data);
-
-	/* Returns TRUE to remove the secret */
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+	gs_unref_object NMConnection *connection_cloned = NULL;
+	gs_unref_variant GVariant *old_secrets = NULL;
 
-	/* Can't use bitops with SECRET_FLAG_NONE so handle that specifically */
-	if (   (flags == NM_SETTING_SECRET_FLAG_NONE)
-	    && (filter_flags == NM_SETTING_SECRET_FLAG_NONE))
-		return FALSE;
+	old_secrets = g_steal_pointer (&priv->system_secrets);
 
-	/* Otherwise if the secret has at least one of the desired flags keep it */
-	return (flags & filter_flags) ? FALSE : TRUE;
-}
+	if (!new)
+		goto out;
 
-static void
-update_system_secrets_cache (NMSettingsConnection *self)
-{
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+	/* FIXME: improve NMConnection API so we can avoid the overhead of cloning the connection,
+	 *   in particular if there are no secrets to begin with. */
 
-	if (priv->system_secrets)
-		g_object_unref (priv->system_secrets);
-	priv->system_secrets = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
+	connection_cloned = nm_simple_connection_new_clone (new);
 
 	/* Clear out non-system-owned and not-saved secrets */
-	nm_connection_clear_secrets_with_flags (priv->system_secrets,
-	                                        secrets_filter_cb,
-	                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_NONE));
+	_nm_connection_clear_secrets_by_secret_flags (connection_cloned,
+	                                              NM_SETTING_SECRET_FLAG_NONE);
+
+	priv->system_secrets = nm_g_variant_ref_sink (nm_connection_to_dbus (connection_cloned, NM_CONNECTION_SERIALIZE_ONLY_SECRETS));
+
+out:
+	if (_LOGT_ENABLED ()) {
+		if ((!!old_secrets) != (!!priv->system_secrets)) {
+			_LOGT ("update system secrets: secrets %s",
+			       old_secrets ? "cleared" : "set");
+		} else if (   priv->system_secrets
+		           && !g_variant_equal (old_secrets, priv->system_secrets))
+			_LOGT ("update system secrets: secrets updated");
+	}
 }
 
 static void
 update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new)
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-	NMSettingSecretFlags filter_flags = NM_SETTING_SECRET_FLAG_NOT_SAVED | NM_SETTING_SECRET_FLAG_AGENT_OWNED;
+	gs_unref_object NMConnection *connection_cloned = NULL;
+	gs_unref_variant GVariant *old_secrets = NULL;
 
-	if (priv->agent_secrets)
-		g_object_unref (priv->agent_secrets);
-	priv->agent_secrets = nm_simple_connection_new_clone (   new
-	                                                      ?: nm_settings_connection_get_connection (self));
+	old_secrets = g_steal_pointer (&priv->agent_secrets);
 
-	/* Clear out non-system-owned secrets */
-	nm_connection_clear_secrets_with_flags (priv->agent_secrets,
-	                                        secrets_filter_cb,
-	                                        GUINT_TO_POINTER (filter_flags));
-}
+	if (!new)
+		goto out;
 
-static void
-secrets_cleared_cb (NMConnection *connection, NMSettingsConnection *self)
-{
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+	/* FIXME: improve NMConnection API so we can avoid the overhead of cloning the connection,
+	 *   in particular if there are no secrets to begin with. */
 
-	/* Clear agent secrets when connection's secrets are cleared since agent
-	 * secrets are transient.
-	 */
-	if (priv->agent_secrets)
-		g_object_unref (priv->agent_secrets);
-	priv->agent_secrets = NULL;
-}
+	connection_cloned = nm_simple_connection_new_clone (new);
 
-static void
-set_persist_mode (NMSettingsConnection *self, NMSettingsConnectionPersistMode persist_mode)
-{
-	NMSettingsConnectionIntFlags flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE;
-	const NMSettingsConnectionIntFlags ALL =   NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED
-	                                         | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
-	                                         | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE;
+	/* Clear out non-system-owned secrets */
+	_nm_connection_clear_secrets_by_secret_flags (connection_cloned,
+	                                                NM_SETTING_SECRET_FLAG_NOT_SAVED
+	                                              | NM_SETTING_SECRET_FLAG_AGENT_OWNED);
 
-	switch (persist_mode) {
-	case NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK:
-		flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE;
-		break;
-	case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY:
-	case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED:
-	case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY:
-		flags = NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED;
-		break;
-	case NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED:
-	case NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY:
-		flags = NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED |
-		        NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE;
-		break;
-	case NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED:
-		/* only set the connection as unsaved, but preserve the nm-generated
-		 * and volatile flag. */
-		nm_settings_connection_set_flags (self,
-		                                  NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED,
-		                                  TRUE);
-		return;
-	case NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP:
-	case NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED:
-		/* Nothing to do */
-		return;
-	}
+	priv->agent_secrets = nm_g_variant_ref_sink (nm_connection_to_dbus (connection_cloned, NM_CONNECTION_SERIALIZE_ONLY_SECRETS));
 
-	nm_settings_connection_set_flags_full (self, ALL, flags);
+out:
+	if (_LOGT_ENABLED ()) {
+		if ((!!old_secrets) != (!!priv->agent_secrets)) {
+			_LOGT ("update agent secrets: secrets %s",
+			       old_secrets ? "cleared" : "set");
+		} else if (   priv->agent_secrets
+		           && !g_variant_equal (old_secrets, priv->agent_secrets))
+			_LOGT ("update agent secrets: secrets updated");
+	}
 }
 
-static void
-_emit_updated (NMSettingsConnection *self, gboolean by_user)
+void
+nm_settings_connection_clear_secrets (NMSettingsConnection *self,
+                                      gboolean clear_cached_system_secrets,
+                                      gboolean persist)
 {
-	nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
-	                            &interface_info_settings_connection,
-	                            &signal_info_updated,
-	                            "()");
-	g_signal_emit (self, signals[UPDATED_INTERNAL], 0, by_user);
-}
+	gs_unref_object NMConnection *connection_cloned = NULL;
 
-static void
-connection_changed_cb (NMConnection *connection, NMSettingsConnection *self)
-{
-	set_persist_mode (self, NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED);
-	_emit_updated (self, FALSE);
-}
+	if (!nm_settings_connection_still_valid (self))
+		return;
 
-static gboolean
-_delete (NMSettingsConnection *self, GError **error)
-{
-	NMSettingsConnectionClass *klass;
-	GError *local = NULL;
-	const char *filename;
+	/* FIXME: add API to NMConnection so that we can clone a profile without secrets. */
 
-	nm_assert (NM_IS_SETTINGS_CONNECTION (self));
+	connection_cloned = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
 
-	klass = NM_SETTINGS_CONNECTION_GET_CLASS (self);
-	if (!klass->delete) {
-		g_set_error (&local,
-		             NM_SETTINGS_ERROR,
-		             NM_SETTINGS_ERROR_FAILED,
-		             "delete not supported");
-		goto fail;
-	}
-	if (!klass->delete (self,
-	                    &local))
-		goto fail;
-
-	filename = nm_settings_connection_get_filename (self);
-	if (filename) {
-		_LOGD ("delete: success deleting connection (\"%s\")", filename);
-		nm_settings_connection_set_filename (self, NULL);
-	} else
-		_LOGT ("delete: success deleting connection (no-file)");
-	return TRUE;
-fail:
-	_LOGD ("delete: failure deleting connection: %s", local->message);
-	g_propagate_error (error, local);
-	return FALSE;
+	nm_connection_clear_secrets (connection_cloned);
+
+	if (!nm_settings_connection_update (self,
+	                                    connection_cloned,
+	                                       persist
+	                                    ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP
+	                                    : NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST,
+	                                    NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+	                                    NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+	                                      NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE
+	                                    | (clear_cached_system_secrets ? NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE)
+	                                    | NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS,
+	                                    "clear-secrets",
+	                                    NULL))
+		nm_assert_not_reached ();
 }
 
 static gboolean
-_update_prepare (NMSettingsConnection *self,
-                 NMConnection *new_connection,
+_secrets_update (NMConnection *connection,
+                 const char *setting_name,
+                 GVariant *secrets,
+                 NMConnection **out_new_connection,
                  GError **error)
 {
-	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
-	g_return_val_if_fail (NM_IS_CONNECTION (new_connection), FALSE);
+	gs_unref_variant GVariant *secrets_setting = NULL;
 
-	if (!nm_connection_normalize (new_connection, NULL, NULL, error))
-		return FALSE;
+	nm_assert (NM_IS_CONNECTION (connection));
 
-	if (   nm_dbus_object_get_path (NM_DBUS_OBJECT (self))
-	    && g_strcmp0 (nm_settings_connection_get_uuid (self), nm_connection_get_uuid (new_connection)) != 0) {
-		/* Updating the UUID is not allowed once the path is exported. */
-		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
-		             "connection %s cannot change the UUID from %s to %s", nm_settings_connection_get_id (self),
-		             nm_settings_connection_get_uuid (self), nm_connection_get_uuid (new_connection));
+	if (   setting_name
+	    && !nm_connection_get_setting_by_name (connection, setting_name)) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
+		                     setting_name);
 		return FALSE;
 	}
 
-	return TRUE;
-}
-
-gboolean
-nm_settings_connection_update (NMSettingsConnection *self,
-                               NMConnection *new_connection,
-                               NMSettingsConnectionPersistMode persist_mode,
-                               NMSettingsConnectionCommitReason commit_reason,
-                               const char *log_diff_name,
-                               GError **error)
-{
-	NMSettingsConnectionPrivate *priv;
-	NMSettingsConnectionClass *klass = NULL;
-	gs_unref_object NMConnection *reread_connection = NULL;
-	NMConnection *replace_connection;
-	gboolean replaced = FALSE;
-	gs_free char *logmsg_change = NULL;
-	GError *local = NULL;
-	gs_unref_object NMConnection *simple = NULL;
-	gs_unref_variant GVariant *con_agent_secrets = NULL;
-	gs_unref_variant GVariant *new_agent_secrets = NULL;
-
-	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
-
-	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-
-	if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK) {
-		klass = NM_SETTINGS_CONNECTION_GET_CLASS (self);
-		if (!klass->commit_changes) {
-			g_set_error (&local,
-			             NM_SETTINGS_ERROR,
-			             NM_SETTINGS_ERROR_FAILED,
-			             "writing settings not supported");
-			goto out;
-		}
-	}
-
-	if (   new_connection
-	    && !_update_prepare (self,
-	                         new_connection,
-	                         &local))
-		goto out;
-
-	if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK) {
-		if (!klass->commit_changes (self,
-		                            new_connection ?: nm_settings_connection_get_connection (self),
-		                            commit_reason,
-		                            &reread_connection,
-		                            &logmsg_change,
-		                            &local))
-			goto out;
-
-		if (   reread_connection
-		    && !_update_prepare (self,
-		                         reread_connection,
-		                         &local))
-			goto out;
-	}
-
-	replace_connection = reread_connection ?: new_connection;
-
-	/* Save agent-owned secrets from the new connection for later use */
-	if (new_connection) {
-		simple = nm_simple_connection_new_clone (new_connection);
-		nm_connection_clear_secrets_with_flags (simple,
-		                                        secrets_filter_cb,
-		                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
-		new_agent_secrets = nm_connection_to_dbus (simple, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
-		g_clear_object (&simple);
-	}
-
-	/* Disconnect the changed signal to ensure we don't set Unsaved when
-	 * it's not required.
-	 */
-	g_signal_handlers_block_by_func (priv->connection, G_CALLBACK (connection_changed_cb), self);
-
-	/* Do nothing if there's nothing to update */
-	if (   replace_connection
-	    && !nm_connection_compare (nm_settings_connection_get_connection (self),
-	                               replace_connection,
-	                               NM_SETTING_COMPARE_FLAG_EXACT)) {
-
-		if (log_diff_name) {
-			nm_utils_log_connection_diff (replace_connection, nm_settings_connection_get_connection (self), LOGL_DEBUG, LOGD_CORE, log_diff_name, "++ ",
-			                              nm_dbus_object_get_path (NM_DBUS_OBJECT (self)));
-		}
-
-		/* Make a copy of agent-owned secrets because they won't be present in
-		 * the connection returned by plugins, as plugins return only what was
-		 * reread from the file. */
-		simple = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
-		nm_connection_clear_secrets_with_flags (simple,
-		                                        secrets_filter_cb,
-		                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
-		con_agent_secrets = nm_connection_to_dbus (simple, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
-
-		nm_connection_replace_settings_from_connection (nm_settings_connection_get_connection (self), replace_connection);
-
-		replaced = TRUE;
-	}
+	if (!secrets)
+		return TRUE;
 
-	nm_settings_connection_set_flags (self,
-	                                  NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE,
-	                                  FALSE);
+	nm_assert (   g_variant_is_of_type (secrets, NM_VARIANT_TYPE_SETTING)
+	           || g_variant_is_of_type (secrets, NM_VARIANT_TYPE_CONNECTION));
 
-	if (replaced) {
-		/* Cache the just-updated system secrets in case something calls
-		 * nm_connection_clear_secrets() and clears them.
-		 */
-		update_system_secrets_cache (self);
-
-		/* Add agent and always-ask secrets back; they won't necessarily be
-		 * in the replacement connection data if it was eg reread from disk.
-		 */
-		if (priv->agent_secrets) {
-			GVariant *dict;
+	if (g_variant_n_children (secrets) == 0)
+		return TRUE;
 
-			dict = nm_connection_to_dbus (priv->agent_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
-			if (dict) {
-				(void) nm_connection_update_secrets (nm_settings_connection_get_connection (self), NULL, dict, NULL);
-				g_variant_unref (dict);
-			}
+	if (   setting_name
+	    && g_variant_is_of_type (secrets, NM_VARIANT_TYPE_CONNECTION)) {
+		secrets_setting = g_variant_lookup_value (secrets, setting_name, NM_VARIANT_TYPE_SETTING);
+		if (!secrets_setting) {
+			/* The connection dictionary didn't contain any secrets for
+			 * @setting_name; just return success.
+			 */
+			return TRUE;
 		}
-		if (con_agent_secrets)
-			(void) nm_connection_update_secrets (nm_settings_connection_get_connection (self), NULL, con_agent_secrets, NULL);
+		secrets = secrets_setting;
 	}
 
-	/* Apply agent-owned secrets from the new connection so that
-	 * they can be sent to agents */
-	if (new_agent_secrets) {
-		(void) nm_connection_update_secrets (nm_settings_connection_get_connection (self),
-		                                     NULL,
-		                                     new_agent_secrets,
-		                                     NULL);
+	/* if @out_new_connection is provided, we don't modify @connection but clone
+	 * and return it. Otherwise, we update @connection inplace. */
+	if (out_new_connection) {
+		nm_assert (!*out_new_connection);
+		connection = nm_simple_connection_new_clone (connection);
+		*out_new_connection = connection;
 	}
 
-	nm_settings_connection_recheck_visibility (self);
-
-	if (   replaced
-	    && persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP)
-		set_persist_mode (self, NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED);
-	else
-		set_persist_mode (self, persist_mode);
-
-	if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
-	                             NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY))
-		_delete (self, NULL);
-	else if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
-	                                  NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED))
-		nm_settings_connection_set_filename (self, NULL);
-
-	g_signal_handlers_unblock_by_func (priv->connection, G_CALLBACK (connection_changed_cb), self);
-
-	_emit_updated (self, TRUE);
-
-out:
-	if (local) {
-		_LOGI ("write: failure to update connection: %s", local->message);
-		g_propagate_error (error, local);
+	if (!nm_connection_update_secrets (connection,
+	                                   setting_name,
+	                                   secrets,
+	                                   error))
 		return FALSE;
-	}
 
-	if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK) {
-		if (reread_connection)
-			_LOGI ("write: successfully updated (%s), connection was modified in the process", logmsg_change);
-		else if (new_connection)
-			_LOGI ("write: successfully updated (%s)", logmsg_change);
-		else
-			_LOGI ("write: successfully committed (%s)", logmsg_change);
-	}
 	return TRUE;
 }
 
-static void
-remove_entry_from_db (NMSettingsConnection *self, const char* db_name)
-{
-	GKeyFile *key_file;
-	const char *db_file;
-
-	if (strcmp (db_name, "timestamps") == 0)
-		db_file = SETTINGS_TIMESTAMPS_FILE;
-	else if (strcmp (db_name, "seen-bssids") == 0)
-		db_file = SETTINGS_SEEN_BSSIDS_FILE;
-	else
-		return;
-
-	key_file = g_key_file_new ();
-	if (g_key_file_load_from_file (key_file, db_file, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
-		const char *connection_uuid;
-		char *data;
-		gsize len;
-		GError *error = NULL;
-
-		connection_uuid = nm_settings_connection_get_uuid (self);
-
-		g_key_file_remove_key (key_file, db_name, connection_uuid, NULL);
-		data = g_key_file_to_data (key_file, &len, &error);
-		if (data) {
-			g_file_set_contents (db_file, data, len, &error);
-			g_free (data);
-		}
-		if (error) {
-			_LOGW ("error writing %s file '%s': %s", db_name, db_file, error->message);
-			g_error_free (error);
-		}
-	}
-	g_key_file_free (key_file);
-}
-
 gboolean
-nm_settings_connection_delete (NMSettingsConnection *self,
+nm_settings_connection_update (NMSettingsConnection *self,
+                               NMConnection *new_connection,
+                               NMSettingsConnectionPersistMode persist_mode,
+                               NMSettingsConnectionIntFlags sett_flags,
+                               NMSettingsConnectionIntFlags sett_mask,
+                               NMSettingsConnectionUpdateReason update_reason,
+                               const char *log_context_name,
                                GError **error)
 {
-	gs_unref_object NMSettingsConnection *self_keep_alive = NULL;
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-	NMConnection *for_agents;
-
 	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
 
-	self_keep_alive = g_object_ref (self);
-
-	if (!_delete (self, error))
-		return FALSE;
-
-	set_visible (self, FALSE);
-
-	/* Tell agents to remove secrets for this connection */
-	for_agents = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
-	nm_connection_clear_secrets (for_agents);
-	nm_agent_manager_delete_secrets (priv->agent_mgr,
-	                                 nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
-	                                 for_agents);
-	g_object_unref (for_agents);
-
-	/* Remove timestamp from timestamps database file */
-	remove_entry_from_db (self, "timestamps");
+	return nm_settings_update_connection (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->settings,
+	                                      self,
+	                                      new_connection,
+	                                      persist_mode,
+	                                      sett_flags,
+	                                      sett_mask,
+	                                      update_reason,
+	                                      log_context_name,
+	                                      error);
+}
 
-	/* Remove connection from seen-bssids database file */
-	remove_entry_from_db (self, "seen-bssids");
+void
+nm_settings_connection_delete (NMSettingsConnection *self,
+                               gboolean allow_add_to_no_auto_default)
+{
+	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
 
-	nm_settings_connection_signal_remove (self);
-	return TRUE;
+	nm_settings_delete_connection (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->settings,
+	                               self,
+	                               allow_add_to_no_auto_default);
 }
 
 /*****************************************************************************/
@@ -788,13 +687,6 @@ _get_secrets_info_free (NMSettingsConnectionCallId *call_id)
 	g_slice_free (NMSettingsConnectionCallId, call_id);
 }
 
-static gboolean
-supports_secrets (NMSettingsConnection *self, const char *setting_name)
-{
-	/* All secrets supported */
-	return TRUE;
-}
-
 typedef struct {
 	NMSettingSecretFlags required;
 	NMSettingSecretFlags forbidden;
@@ -918,25 +810,37 @@ nm_settings_connection_new_secrets (NMSettingsConnection *self,
                                     GVariant *secrets,
                                     GError **error)
 {
-	if (!nm_settings_connection_has_unmodified_applied_connection (self, applied_connection,
-	                                                              NM_SETTING_COMPARE_FLAG_NONE)) {
+	gs_unref_object NMConnection *new_connection = NULL;
+	NMConnection *connection;
+
+	if (!nm_settings_connection_has_unmodified_applied_connection (self,
+	                                                               applied_connection,
+	                                                               NM_SETTING_COMPARE_FLAG_NONE)) {
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		                     "The connection was modified since activation");
 		return FALSE;
 	}
 
-	if (!nm_connection_update_secrets (nm_settings_connection_get_connection (self), setting_name, secrets, error))
-		return FALSE;
+	connection = nm_settings_connection_get_connection (self);
 
-	update_system_secrets_cache (self);
-	update_agent_secrets_cache (self, NULL);
+	if (!_secrets_update (connection,
+	                      setting_name,
+	                      secrets,
+	                      &new_connection,
+	                      error))
+		return FALSE;
 
-	nm_settings_connection_update (self,
-	                               NULL,
-	                               NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
-	                               NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
-	                               "new-secrets",
-	                               NULL);
+	if (!nm_settings_connection_update (self,
+	                                    new_connection ?: connection,
+	                                    NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+	                                    NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+	                                    NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+	                                      NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE
+	                                    | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+	                                    | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS,
+	                                    "new-secrets",
+	                                    NULL))
+		nm_assert_not_reached ();
 	return TRUE;
 }
 
@@ -957,9 +861,11 @@ get_secrets_done_cb (NMAgentManager *manager,
 	NMSettingsConnectionPrivate *priv;
 	NMConnection *applied_connection;
 	gs_free_error GError *local = NULL;
-	GVariant *dict = NULL;
+	gs_unref_variant GVariant *system_secrets = NULL;
+	gs_unref_object NMConnection *new_connection = NULL;
 	gboolean agent_had_system = FALSE;
 	ForEachSecretFlags cmp_flags = { NM_SETTING_SECRET_FLAG_NONE, NM_SETTING_SECRET_FLAG_NONE };
+	gs_unref_variant GVariant *filtered_secrets = NULL;
 
 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
@@ -1020,62 +926,69 @@ get_secrets_done_cb (NMAgentManager *manager,
 	       setting_name,
 	       call_id);
 
-	if (priv->system_secrets)
-		dict = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
+	system_secrets = nm_g_variant_ref (priv->system_secrets);
 
-	/* Update the connection with our existing secrets from backing storage */
-	nm_connection_clear_secrets (nm_settings_connection_get_connection (self));
-	if (!dict || nm_connection_update_secrets (nm_settings_connection_get_connection (self), setting_name, dict, &local)) {
-		gs_unref_variant GVariant *filtered_secrets = NULL;
-
-		/* Update the connection with the agent's secrets; by this point if any
-		 * system-owned secrets exist in 'secrets' the agent that provided them
-		 * will have been authenticated, so those secrets can replace the existing
-		 * system secrets.
-		 */
-		filtered_secrets = validate_secret_flags (nm_settings_connection_get_connection (self), secrets, &cmp_flags);
-		if (nm_connection_update_secrets (nm_settings_connection_get_connection (self), setting_name, filtered_secrets, &local)) {
-			/* Now that all secrets are updated, copy and cache new secrets,
-			 * then save them to backing storage.
-			 */
-			update_system_secrets_cache (self);
-			update_agent_secrets_cache (self, NULL);
+	new_connection = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
 
-			/* Only save secrets to backing storage if the agent returned any
-			 * new system secrets.  If it didn't, then the secrets are agent-
-			 * owned and there's no point to writing out the connection when
-			 * nothing has changed, since agent-owned secrets don't get saved here.
-			 */
-			if (agent_had_system) {
-				_LOGD ("(%s:%p) saving new secrets to backing storage",
-				       setting_name,
-				       call_id);
-
-				nm_settings_connection_update (self,
-				                               NULL,
-				                               NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
-				                               NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
-				                               "get-new-secrets",
-				                               NULL);
-			} else {
-				_LOGD ("(%s:%p) new agent secrets processed",
-				       setting_name,
-				       call_id);
-			}
+	nm_connection_clear_secrets (new_connection);
 
-		} else {
-			_LOGD ("(%s:%p) failed to update with agent secrets: %s",
-			       setting_name,
-			       call_id,
-			       local->message);
-		}
-	} else {
+	if (!_secrets_update (new_connection,
+	                      setting_name,
+	                      system_secrets,
+	                      NULL,
+	                      &local)) {
 		_LOGD ("(%s:%p) failed to update with existing secrets: %s",
 		       setting_name,
 		       call_id,
 		       local->message);
 	}
 
+	/* Update the connection with the agent's secrets; by this point if any
+	 * system-owned secrets exist in 'secrets' the agent that provided them
+	 * will have been authenticated, so those secrets can replace the existing
+	 * system secrets.
+	 */
+	filtered_secrets = validate_secret_flags (new_connection, secrets, &cmp_flags);
+
+	if (!_secrets_update (new_connection,
+	                      setting_name,
+	                      filtered_secrets,
+	                      NULL,
+	                      &local)) {
+		_LOGD ("(%s:%p) failed to update with agent secrets: %s",
+		       setting_name,
+		       call_id,
+		       local->message);
+	}
+
+	/* Only save secrets to backing storage if the agent returned any
+	 * new system secrets.  If it didn't, then the secrets are agent-
+	 * owned and there's no point to writing out the connection when
+	 * nothing has changed, since agent-owned secrets don't get saved here.
+	 */
+	if (agent_had_system) {
+		_LOGD ("(%s:%p) saving new secrets to backing storage",
+		       setting_name,
+		       call_id);
+	} else {
+		_LOGD ("(%s:%p) new agent secrets processed",
+		       setting_name,
+		       call_id);
+	}
+	if (!nm_settings_connection_update (self,
+	                                    new_connection,
+	                                      agent_had_system
+	                                    ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP
+	                                    : NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST,
+	                                    NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+	                                    NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+	                                      NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE
+	                                    | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+	                                    | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS,
+	                                    "get-new-secrets",
+	                                    NULL))
+		nm_assert_not_reached ();
+
 	applied_connection = call_id->applied_connection;
 	if (applied_connection) {
 		get_cmp_flags (self,
@@ -1091,18 +1004,17 @@ get_secrets_done_cb (NMAgentManager *manager,
 
 		nm_connection_clear_secrets (applied_connection);
 
-		if (!dict || nm_connection_update_secrets (applied_connection, setting_name, dict, NULL)) {
-			gs_unref_variant GVariant *filtered_secrets = NULL;
+		if (   !system_secrets
+		    || nm_connection_update_secrets (applied_connection, setting_name, system_secrets, NULL)) {
+			gs_unref_variant GVariant *filtered_secrets2 = NULL;
 
-			filtered_secrets = validate_secret_flags (applied_connection, secrets, &cmp_flags);
-			nm_connection_update_secrets (applied_connection, setting_name, filtered_secrets, NULL);
+			filtered_secrets2 = validate_secret_flags (applied_connection, secrets, &cmp_flags);
+			nm_connection_update_secrets (applied_connection, setting_name, filtered_secrets2, NULL);
 		}
 	}
 
 	_get_secrets_info_callback (call_id, agent_username, setting_name, local);
 	g_clear_error (&local);
-	if (dict)
-		g_variant_unref (dict);
 
 out:
 	_get_secrets_info_free (call_id);
@@ -1163,7 +1075,6 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
                                     gpointer callback_data)
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-	GVariant *existing_secrets = NULL;
 	NMAgentManagerCallId call_id_a;
 	gs_free char *joined_hints = NULL;
 	NMSettingsConnectionCallId *call_id;
@@ -1200,14 +1111,6 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 		goto schedule_dummy;
 	}
 
-	/* Use priv->system_secrets to work around the fact that nm_connection_clear_secrets()
-	 * will clear secrets on this object's settings.
-	 */
-	if (priv->system_secrets)
-		existing_secrets = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
-	if (existing_secrets)
-		g_variant_ref_sink (existing_secrets);
-
 	/* we remember the current version-id of the secret-agents. The version-id is strictly increasing,
 	 * as new agents register the number. We know hence, that this request was made against a certain
 	 * set of secret-agents.
@@ -1215,19 +1118,20 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 	 * Then we know that the this request probably did not yet include the latest secret-agent. */
 	priv->last_secret_agent_version_id = nm_agent_manager_get_agent_version_id (priv->agent_mgr);
 
+	/* Use priv->system_secrets to work around the fact that nm_connection_clear_secrets()
+	 * will clear secrets on this object's settings.
+	 */
 	call_id_a = nm_agent_manager_get_secrets (priv->agent_mgr,
 	                                          nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
 	                                          nm_settings_connection_get_connection (self),
 	                                          subject,
-	                                          existing_secrets,
+	                                          priv->system_secrets,
 	                                          setting_name,
 	                                          flags,
 	                                          hints,
 	                                          get_secrets_done_cb,
 	                                          call_id);
-	g_assert (call_id_a);
-	if (existing_secrets)
-		g_variant_unref (existing_secrets);
+	nm_assert (call_id_a);
 
 	_LOGD ("(%s:%p) secrets requested flags 0x%X hints '%s'",
 	       setting_name,
@@ -1420,37 +1324,6 @@ auth_start (NMSettingsConnection *self,
 
 /**** DBus method handlers ************************************/
 
-static gboolean
-check_writable (NMConnection *self, GError **error)
-{
-	NMSettingConnection *s_con;
-
-	g_return_val_if_fail (NM_IS_CONNECTION (self), FALSE);
-
-	s_con = nm_connection_get_setting_connection (self);
-	if (!s_con) {
-		g_set_error_literal (error,
-		                     NM_SETTINGS_ERROR,
-		                     NM_SETTINGS_ERROR_INVALID_CONNECTION,
-		                     "Connection did not have required 'connection' setting");
-		return FALSE;
-	}
-
-	/* If the connection is read-only, that has to be changed at the source of
-	 * the problem (ex a system settings plugin that can't write connections out)
-	 * instead of over D-Bus.
-	 */
-	if (nm_setting_connection_get_read_only (s_con)) {
-		g_set_error_literal (error,
-		                     NM_SETTINGS_ERROR,
-		                     NM_SETTINGS_ERROR_READ_ONLY_CONNECTION,
-		                     "Connection is read-only");
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
 static void
 get_settings_auth_cb (NMSettingsConnection *self,
                       GDBusMethodInvocation *context,
@@ -1458,46 +1331,41 @@ get_settings_auth_cb (NMSettingsConnection *self,
                       GError *error,
                       gpointer data)
 {
-	if (error)
+	gs_free const char **seen_bssids = NULL;
+	NMConnectionSerializationOptions options = {
+	};
+	GVariant *settings;
+
+	if (error) {
 		g_dbus_method_invocation_return_gerror (context, error);
-	else {
-		gs_unref_object NMConnection *dupl_con = NULL;
-		GVariant *settings;
-		NMSettingConnection *s_con;
-		NMSettingWireless *s_wifi;
-		guint64 timestamp = 0;
-		gs_free char **bssids = NULL;
-
-		dupl_con = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
-
-		/* Timestamp is not updated in connection's 'timestamp' property,
-		 * because it would force updating the connection and in turn
-		 * writing to /etc periodically, which we want to avoid. Rather real
-		 * timestamps are kept track of in a private variable. So, substitute
-		 * timestamp property with the real one here before returning the settings.
-		 */
-		nm_settings_connection_get_timestamp (self, &timestamp);
-		if (timestamp) {
-			s_con = nm_connection_get_setting_connection (dupl_con);
-			g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL);
-		}
-		/* Seen BSSIDs are not updated in 802-11-wireless 'seen-bssids' property
-		 * from the same reason as timestamp. Thus we put it here to GetSettings()
-		 * return settings too.
-		 */
-		bssids = nm_settings_connection_get_seen_bssids (self);
-		s_wifi = nm_connection_get_setting_wireless (dupl_con);
-		if (bssids && bssids[0] && s_wifi)
-			g_object_set (s_wifi, NM_SETTING_WIRELESS_SEEN_BSSIDS, bssids, NULL);
-
-		/* Secrets should *never* be returned by the GetSettings method, they
-		 * get returned by the GetSecrets method which can be better
-		 * protected against leakage of secrets to unprivileged callers.
-		 */
-		settings = nm_connection_to_dbus (dupl_con, NM_CONNECTION_SERIALIZE_NO_SECRETS);
-		g_dbus_method_invocation_return_value (context,
-		                                       g_variant_new ("(@a{sa{sv}})", settings));
+		return;
 	}
+
+	/* Timestamp is not updated in connection's 'timestamp' property,
+	 * because it would force updating the connection and in turn
+	 * writing to /etc periodically, which we want to avoid. Rather real
+	 * timestamps are kept track of in a private variable. So, substitute
+	 * timestamp property with the real one here before returning the settings.
+	 */
+	options.timestamp.has = TRUE;
+	nm_settings_connection_get_timestamp (self, &options.timestamp.val);
+
+	/* Seen BSSIDs are not updated in 802-11-wireless 'seen-bssids' property
+	 * from the same reason as timestamp. Thus we put it here to GetSettings()
+	 * return settings too.
+	 */
+	seen_bssids = nm_settings_connection_get_seen_bssids (self);
+	options.seen_bssids = seen_bssids;
+
+	/* Secrets should *never* be returned by the GetSettings method, they
+	 * get returned by the GetSecrets method which can be better
+	 * protected against leakage of secrets to unprivileged callers.
+	 */
+	settings = nm_connection_to_dbus_full (nm_settings_connection_get_connection (self),
+	                                       NM_CONNECTION_SERIALIZE_NO_SECRETS,
+	                                       &options);
+	g_dbus_method_invocation_return_value (context,
+	                                       g_variant_new ("(@a{sa{sv}})", settings));
 }
 
 static void
@@ -1533,28 +1401,6 @@ typedef struct {
 } UpdateInfo;
 
 static void
-cached_secrets_to_connection (NMSettingsConnection *self, NMConnection *connection)
-{
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-	GVariant *secrets_dict;
-
-	if (priv->agent_secrets) {
-		secrets_dict = nm_connection_to_dbus (priv->agent_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
-		if (secrets_dict) {
-			(void) nm_connection_update_secrets (connection, NULL, secrets_dict, NULL);
-			g_variant_unref (secrets_dict);
-		}
-	}
-	if (priv->system_secrets) {
-		secrets_dict = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
-		if (secrets_dict) {
-			(void) nm_connection_update_secrets (connection, NULL, secrets_dict, NULL);
-			g_variant_unref (secrets_dict);
-		}
-	}
-}
-
-static void
 update_complete (NMSettingsConnection *self,
                  UpdateInfo *info,
                  GError *error)
@@ -1566,7 +1412,7 @@ update_complete (NMSettingsConnection *self,
 
 		g_variant_builder_init (&result, G_VARIANT_TYPE ("a{sv}"));
 		g_dbus_method_invocation_return_value (info->context,
-		                                       g_variant_new ("(@a{sv})", g_variant_builder_end (&result)));
+		                                       g_variant_new ("(a{sv})", &result));
 	} else
 		g_dbus_method_invocation_return_value (info->context, NULL);
 
@@ -1587,24 +1433,28 @@ update_auth_cb (NMSettingsConnection *self,
                 GError *error,
                 gpointer data)
 {
+	NMSettingsConnectionPrivate *priv;
 	UpdateInfo *info = data;
-	NMSettingsConnectionCommitReason commit_reason;
 	gs_free_error GError *local = NULL;
 	NMSettingsConnectionPersistMode persist_mode;
-	const char *log_diff_name;
 
 	if (error) {
 		update_complete (self, info, error);
 		return;
 	}
 
+	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
 	if (info->new_settings) {
 		if (!_nm_connection_aggregate (info->new_settings, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL)) {
 			/* If the new connection has no secrets, we do not want to remove all
 			 * secrets, rather we keep all the existing ones. Do that by merging
 			 * them in to the new connection.
 			 */
-			cached_secrets_to_connection (self, info->new_settings);
+			if (priv->agent_secrets)
+				nm_connection_update_secrets (info->new_settings, NULL, priv->agent_secrets, NULL);
+			if (priv->system_secrets)
+				nm_connection_update_secrets (info->new_settings, NULL, priv->system_secrets, NULL);
 		} else {
 			/* Cache the new secrets from the agent, as stuff like inotify-triggered
 			 * changes to connection's backing config files will blow them away if
@@ -1628,45 +1478,38 @@ update_auth_cb (NMSettingsConnection *self,
 		}
 	}
 
-	commit_reason = NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION;
-	if (   info->new_settings
-	    && !nm_streq0 (nm_connection_get_id (nm_settings_connection_get_connection (self)),
-	                   nm_connection_get_id (info->new_settings)))
-		commit_reason |= NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED;
+	nm_assert (   !NM_FLAGS_ANY (info->flags, _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES)
+	           || nm_utils_is_power_of_two (info->flags & _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES));
 
 	if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_TO_DISK))
-		persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
-	else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY))
+		persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK;
+	else if (NM_FLAGS_ANY (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY))
 		persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY;
-	else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED)) {
-		persist_mode = NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
-		               ? NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED
-		               : NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED;
-	} else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY)) {
-		persist_mode = NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
-		               ? NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY
-		               : NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
+	else if (NM_FLAGS_ANY (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED))
+		persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED;
+	else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY)) {
+		persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
 	} else
 		persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP;
 
-	if (   persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK
-	    || (   persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP
-	        && !nm_settings_connection_get_unsaved (self)))
-		log_diff_name = info->new_settings ? "update-settings" : "write-out-to-disk";
-	else
-		log_diff_name = info->new_settings ? "update-unsaved" : "make-unsaved";
-
-	if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT)) {
-		nm_settings_connection_autoconnect_blocked_reason_set (self,
-		                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST,
-		                                                       TRUE);
-	}
-
 	nm_settings_connection_update (self,
 	                               info->new_settings,
 	                               persist_mode,
-	                               commit_reason,
-	                               log_diff_name,
+	                               (  NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
+	                                ? NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
+	                                : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE),
+	                                 NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
+	                               | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE,
+	                                 NM_SETTINGS_CONNECTION_UPDATE_REASON_FORCE_RENAME
+	                               | (  NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY)
+	                                  ? NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE
+	                                  : NM_SETTINGS_CONNECTION_UPDATE_REASON_REAPPLY_PARTIAL)
+	                               | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+	                               | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS
+	                               | (  NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT)
+	                                  ? NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT
+	                                  : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE),
+	                               "update-from-dbus",
 	                               &local);
 
 	if (!local) {
@@ -1677,15 +1520,17 @@ update_auth_cb (NMSettingsConnection *self,
 		 * Only send secrets to agents of the same UID that called update too.
 		 */
 		for_agent = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
-		nm_connection_clear_secrets_with_flags (for_agent,
-		                                        secrets_filter_cb,
-		                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
+		_nm_connection_clear_secrets_by_secret_flags (for_agent,
+		                                              NM_SETTING_SECRET_FLAG_AGENT_OWNED);
 		nm_agent_manager_save_secrets (info->agent_mgr,
 		                               nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
 		                               for_agent,
 		                               info->subject);
 	}
 
+	/* Reset auto retries back to default since connection was updated */
+	nm_settings_connection_autoconnect_retries_reset (self);
+
 	update_complete (self, info, local);
 }
 
@@ -1727,13 +1572,6 @@ settings_connection_update (NMSettingsConnection *self,
 	UpdateInfo *info;
 	const char *permission;
 
-	/* If the connection is read-only, that has to be changed at the source of
-	 * the problem (ex a system settings plugin that can't write connections out)
-	 * instead of over D-Bus.
-	 */
-	if (!check_writable (nm_settings_connection_get_connection (self), &error))
-		goto error;
-
 	/* Check if the settings are valid first */
 	if (new_settings) {
 		if (!g_variant_is_of_type (new_settings, NM_VARIANT_TYPE_CONNECTION)) {
@@ -1858,16 +1696,13 @@ impl_settings_connection_update2 (NMDBusObject *obj,
 	GVariantIter iter;
 	const char *args_name;
 	NMSettingsUpdate2Flags flags;
-	const NMSettingsUpdate2Flags ALL_PERSIST_MODES =   NM_SETTINGS_UPDATE2_FLAG_TO_DISK
-	                                                 | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY
-	                                                 | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED
-	                                                 | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY;
 
 	g_variant_get (parameters, "(@a{sa{sv}}u@a{sv})", &settings, &flags_u, &args);
 
-	if (NM_FLAGS_ANY (flags_u, ~((guint32) (ALL_PERSIST_MODES |
-	                                        NM_SETTINGS_UPDATE2_FLAG_VOLATILE |
-	                                        NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT)))) {
+	if (NM_FLAGS_ANY (flags_u, ~((guint32) (  _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES
+	                                        | NM_SETTINGS_UPDATE2_FLAG_VOLATILE
+	                                        | NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT
+	                                        | NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY)))) {
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
 		                             "Unknown flags");
@@ -1877,11 +1712,12 @@ impl_settings_connection_update2 (NMDBusObject *obj,
 
 	flags = (NMSettingsUpdate2Flags) flags_u;
 
-	if (   (   NM_FLAGS_ANY (flags, ALL_PERSIST_MODES)
-	        && !nm_utils_is_power_of_two (flags & ALL_PERSIST_MODES))
+	if (   (   NM_FLAGS_ANY (flags, _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES)
+	        && !nm_utils_is_power_of_two (flags & _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES))
 	    || (   NM_FLAGS_HAS (flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
-	        && !NM_FLAGS_ANY (flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED |
-	                                 NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY))) {
+	        && !NM_FLAGS_ANY (flags,   NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY
+	                                 | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED
+	                                 | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY))) {
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
 		                             "Conflicting flags");
@@ -1889,13 +1725,7 @@ impl_settings_connection_update2 (NMDBusObject *obj,
 		return;
 	}
 
-	if (!g_variant_is_of_type (args, G_VARIANT_TYPE ("a{sv}"))) {
-		error = g_error_new_literal (NM_SETTINGS_ERROR,
-		                             NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
-		                             "args is of invalid type");
-		g_dbus_method_invocation_take_error (invocation, error);
-		return;
-	}
+	nm_assert (g_variant_is_of_type (args, G_VARIANT_TYPE ("a{sv}")));
 
 	g_variant_iter_init (&iter, args);
 	while (g_variant_iter_next (&iter, "{&sv}", &args_name, NULL)) {
@@ -1921,7 +1751,6 @@ delete_auth_cb (NMSettingsConnection *self,
                 gpointer data)
 {
 	gs_unref_object NMSettingsConnection *self_keep_alive = NULL;
-	gs_free_error GError *local = NULL;
 
 	self_keep_alive = g_object_ref (self);
 
@@ -1932,15 +1761,11 @@ delete_auth_cb (NMSettingsConnection *self,
 		return;
 	}
 
-	nm_settings_connection_delete (self, &local);
+	nm_settings_connection_delete (self, TRUE);
 
 	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self,
-	                            !local, NULL, subject, local ? local->message : NULL);
-
-	if (local)
-		g_dbus_method_invocation_return_gerror (context, local);
-	else
-		g_dbus_method_invocation_return_value (context, NULL);
+	                            TRUE, NULL, subject, NULL);
+	g_dbus_method_invocation_return_value (context, NULL);
 }
 
 static const char *
@@ -1972,8 +1797,7 @@ impl_settings_connection_delete (NMDBusObject *obj,
 	gs_unref_object NMAuthSubject *subject = NULL;
 	GError *error = NULL;
 
-	if (!check_writable (nm_settings_connection_get_connection (self), &error))
-		goto err;
+	nm_assert (nm_settings_connection_still_valid (self));
 
 	subject = _new_auth_subject (invocation, &error);
 	if (!subject)
@@ -2088,25 +1912,13 @@ dbus_clear_secrets_auth_cb (NMSettingsConnection *self,
 		return;
 	}
 
-	/* Clear secrets in connection and caches */
-	nm_connection_clear_secrets (nm_settings_connection_get_connection (self));
-	if (priv->system_secrets)
-		nm_connection_clear_secrets (priv->system_secrets);
-	if (priv->agent_secrets)
-		nm_connection_clear_secrets (priv->agent_secrets);
+	nm_settings_connection_clear_secrets (self, TRUE, TRUE);
 
 	/* Tell agents to remove secrets for this connection */
 	nm_agent_manager_delete_secrets (priv->agent_mgr,
 	                                 nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
 	                                 nm_settings_connection_get_connection (self));
 
-	nm_settings_connection_update (self,
-	                               NULL,
-	                               NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
-	                               NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
-	                               "clear-secrets",
-	                               &local);
-
 	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self,
 	                            !local, NULL, subject, local ? local->message : NULL);
 
@@ -2147,40 +1959,28 @@ impl_settings_connection_clear_secrets (NMDBusObject *obj,
 /*****************************************************************************/
 
 void
-nm_settings_connection_added (NMSettingsConnection *self)
+_nm_settings_connection_emit_dbus_signal_updated (NMSettingsConnection *self)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-
-	/* FIXME: we should always dispose connections that are removed
-	 * and not reuse them, but currently plugins keep alive unmanaged
-	 * (e.g. NM_CONTROLLED=no) connections. */
-	priv->removed = FALSE;
+	nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
+	                            &interface_info_settings_connection,
+	                            &signal_info_updated,
+	                            "()");
 }
 
 void
-nm_settings_connection_signal_remove (NMSettingsConnection *self)
+_nm_settings_connection_emit_dbus_signal_removed (NMSettingsConnection *self)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-	AuthData *auth_data;
-
-	if (priv->removed)
-		return;
-	priv->removed = TRUE;
-
-	while ((auth_data = c_list_first_entry (&priv->auth_lst_head, AuthData, auth_lst)))
-		nm_auth_manager_check_authorization_cancel (auth_data->call_id);
-
 	nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
 	                            &interface_info_settings_connection,
 	                            &signal_info_removed,
 	                            "()");
-	g_signal_emit (self, signals[REMOVED], 0);
 }
 
-gboolean
-nm_settings_connection_get_unsaved (NMSettingsConnection *self)
+void
+_nm_settings_connection_emit_signal_updated_internal (NMSettingsConnection *self,
+                                                      NMSettingsConnectionUpdateReason update_reason)
 {
-	return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED);
+	g_signal_emit (self, signals[UPDATED_INTERNAL], 0, (guint) update_reason);
 }
 
 /*****************************************************************************/
@@ -2202,14 +2002,6 @@ nm_settings_connection_get_flags (NMSettingsConnection *self)
 }
 
 NMSettingsConnectionIntFlags
-nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionIntFlags flags, gboolean set)
-{
-	return nm_settings_connection_set_flags_full (self,
-	                                              flags,
-	                                              set ? flags : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE);
-}
-
-NMSettingsConnectionIntFlags
 nm_settings_connection_set_flags_full (NMSettingsConnection *self,
                                        NMSettingsConnectionIntFlags mask,
                                        NMSettingsConnectionIntFlags value)
@@ -2218,7 +2010,8 @@ nm_settings_connection_set_flags_full (NMSettingsConnection *self,
 	NMSettingsConnectionIntFlags old_flags;
 
 	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_INT_FLAGS_NONE);
-	nm_assert (mask && !NM_FLAGS_ANY (mask, ~NM_SETTINGS_CONNECTION_INT_FLAGS_ALL));
+
+	nm_assert (!NM_FLAGS_ANY (mask, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_ALL));
 	nm_assert (!NM_FLAGS_ANY (value, ~mask));
 
 	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
@@ -2338,11 +2131,13 @@ gboolean
 nm_settings_connection_get_timestamp (NMSettingsConnection *self,
                                       guint64 *out_timestamp)
 {
+	NMSettingsConnectionPrivate *priv;
+
 	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
 
-	if (out_timestamp)
-		*out_timestamp = NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->timestamp;
-	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->timestamp_set;
+	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+	NM_SET_OUT (out_timestamp, priv->timestamp);
+	return priv->timestamp_set;
 }
 
 /**
@@ -2350,98 +2145,113 @@ nm_settings_connection_get_timestamp (NMSettingsConnection *self,
  * @self: the #NMSettingsConnection
  * @timestamp: timestamp to set into the connection and to store into
  * the timestamps database
- * @flush_to_disk: if %TRUE, commit timestamp update to persistent storage
  *
  * Updates the connection and timestamps database with the provided timestamp.
  **/
 void
 nm_settings_connection_update_timestamp (NMSettingsConnection *self,
-                                         guint64 timestamp,
-                                         gboolean flush_to_disk)
+                                         guint64 timestamp)
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	const char *connection_uuid;
-	GKeyFile *timestamps_file;
-	char *data, *tmp;
-	gsize len;
-	GError *error = NULL;
+	char sbuf[60];
 
 	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
 
-	/* Update timestamp in private storage */
 	priv->timestamp = timestamp;
 	priv->timestamp_set = TRUE;
 
-	if (flush_to_disk == FALSE)
-		return;
-	if (nm_config_get_configure_and_quit (nm_config_get ()) == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD)
+	if (!priv->kf_db_timestamps)
 		return;
 
-	/* Save timestamp to timestamps database file */
-	timestamps_file = g_key_file_new ();
-	if (!g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) {
-		if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
-			_LOGW ("error parsing timestamps file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
-		g_clear_error (&error);
-	}
-
 	connection_uuid = nm_settings_connection_get_uuid (self);
-	tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp);
-	g_key_file_set_value (timestamps_file, "timestamps", connection_uuid, tmp);
-	g_free (tmp);
-
-	data = g_key_file_to_data (timestamps_file, &len, &error);
-	if (data) {
-		g_file_set_contents (SETTINGS_TIMESTAMPS_FILE, data, len, &error);
-		g_free (data);
-	}
-	if (error) {
-		_LOGW ("error saving timestamp to file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
-		g_error_free (error);
+	if (connection_uuid) {
+		nm_key_file_db_set_value (priv->kf_db_timestamps,
+		                          connection_uuid,
+		                          nm_sprintf_buf (sbuf, "%" G_GUINT64_FORMAT, timestamp));
 	}
-	g_key_file_free (timestamps_file);
 }
 
-/**
- * nm_settings_connection_read_and_fill_timestamp:
- * @self: the #NMSettingsConnection
- *
- * Retrieves timestamp of the connection's last usage from database file and
- * stores it into the connection private data.
- **/
 void
-nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *self)
+_nm_settings_connection_register_kf_dbs (NMSettingsConnection *self,
+                                         NMKeyFileDB *kf_db_timestamps,
+                                         NMKeyFileDB *kf_db_seen_bssids)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-	gs_unref_keyfile GKeyFile *timestamps_file = NULL;
-	gs_free_error GError *error = NULL;
-	gs_free char *tmp_str = NULL;
+	NMSettingsConnectionPrivate *priv;
 	const char *connection_uuid;
-	gint64 timestamp;
 
 	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
+	g_return_if_fail (kf_db_timestamps);
+	g_return_if_fail (kf_db_seen_bssids);
 
-	timestamps_file = g_key_file_new ();
-	if (!g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) {
-		_LOGD ("failed to read connection timestamp: %s", error->message);
-		return;
-	}
+	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
 	connection_uuid = nm_settings_connection_get_uuid (self);
-	tmp_str = g_key_file_get_value (timestamps_file, "timestamps", connection_uuid, &error);
-	if (!tmp_str) {
-		_LOGD ("failed to read connection timestamp: %s", error->message);
-		return;
-	}
 
-	timestamp = _nm_utils_ascii_str_to_int64 (tmp_str, 10, 0, G_MAXINT64, -1);
-	if (timestamp < 0) {
-		_LOGD ("failed to read connection timestamp: %s", "invalid number");
-		return;
+	if (priv->kf_db_timestamps != kf_db_timestamps) {
+		gs_free char *tmp_str = NULL;
+		guint64 timestamp;
+
+		nm_key_file_db_unref (priv->kf_db_timestamps);
+		priv->kf_db_timestamps = nm_key_file_db_ref (kf_db_timestamps);
+
+		tmp_str = nm_key_file_db_get_value (priv->kf_db_timestamps, connection_uuid);
+
+		timestamp = _nm_utils_ascii_str_to_uint64 (tmp_str, 10, 0, G_MAXUINT64, G_MAXUINT64);
+		if (timestamp != G_MAXUINT64) {
+			priv->timestamp = timestamp;
+			priv->timestamp_set = TRUE;
+			_LOGT ("read timestamp %"G_GUINT64_FORMAT" from keyfile database \"%s\"",
+			       timestamp, nm_key_file_db_get_filename (priv->kf_db_timestamps));
+		} else
+			_LOGT ("no timestamp from keyfile database \"%s\"",
+			       nm_key_file_db_get_filename (priv->kf_db_timestamps));
 	}
 
-	priv->timestamp = timestamp;
-	priv->timestamp_set = TRUE;
+	if (priv->kf_db_seen_bssids != kf_db_seen_bssids) {
+		gs_strfreev char **tmp_strv = NULL;
+		gsize i, len;
+
+		nm_key_file_db_unref (priv->kf_db_seen_bssids);
+		priv->kf_db_seen_bssids = nm_key_file_db_ref (kf_db_seen_bssids);
+
+		tmp_strv = nm_key_file_db_get_string_list (priv->kf_db_seen_bssids, connection_uuid, &len);
+
+		nm_clear_pointer (&priv->seen_bssids, g_hash_table_unref);
+
+		if (len > 0) {
+			_LOGT ("read %zu seen-bssids from keyfile database \"%s\"",
+			       len,
+			       nm_key_file_db_get_filename (priv->kf_db_seen_bssids));
+			priv->seen_bssids = _seen_bssids_hash_new ();
+			for (i = len; i > 0; )
+				g_hash_table_add (priv->seen_bssids, g_steal_pointer (&tmp_strv[--i]));
+			nm_clear_g_free (&tmp_strv);
+		} else {
+			NMSettingWireless *s_wifi;
+
+			_LOGT ("no seen-bssids from keyfile database \"%s\"",
+			       nm_key_file_db_get_filename (priv->kf_db_seen_bssids));
+
+			/* If this connection didn't have an entry in the seen-bssids database,
+			 * maybe this is the first time we've read it in, so populate the
+			 * seen-bssids list from the deprecated seen-bssids property of the
+			 * wifi setting.
+			 */
+			s_wifi = nm_connection_get_setting_wireless (nm_settings_connection_get_connection (self));
+			if (s_wifi) {
+				len = nm_setting_wireless_get_num_seen_bssids (s_wifi);
+				if (len > 0) {
+					priv->seen_bssids = _seen_bssids_hash_new ();
+					for (i = 0; i < len; i++) {
+						const char *bssid = nm_setting_wireless_get_seen_bssid (s_wifi, i);
+
+						g_hash_table_add (priv->seen_bssids, g_strdup (bssid));
+					}
+				}
+			}
+		}
+	}
 }
 
 /**
@@ -2453,25 +2263,14 @@ nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *self)
  * Returns: (transfer container) list of seen BSSIDs (in the standard hex-digits-and-colons notation).
  * The caller is responsible for freeing the list, but not the content.
  **/
-char **
+const char **
 nm_settings_connection_get_seen_bssids (NMSettingsConnection *self)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-	GHashTableIter iter;
-	char **bssids, *bssid;
-	int i;
-
 	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
 
-	bssids = g_new (char *, g_hash_table_size (priv->seen_bssids) + 1);
-
-	i = 0;
-	g_hash_table_iter_init (&iter, priv->seen_bssids);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid))
-		bssids[i++] = bssid;
-	bssids[i] = NULL;
-
-	return bssids;
+	return nm_utils_strdict_get_keys (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids,
+	                                  TRUE,
+	                                  NULL);
 }
 
 /**
@@ -2485,10 +2284,15 @@ gboolean
 nm_settings_connection_has_seen_bssid (NMSettingsConnection *self,
                                        const char *bssid)
 {
+	NMSettingsConnectionPrivate *priv;
+
 	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
-	g_return_val_if_fail (bssid != NULL, FALSE);
+	g_return_val_if_fail (bssid, FALSE);
+
+	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
-	return !!g_hash_table_lookup (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids, bssid);
+	return    priv->seen_bssids
+	       && g_hash_table_contains (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids, bssid);
 }
 
 /**
@@ -2504,108 +2308,29 @@ nm_settings_connection_add_seen_bssid (NMSettingsConnection *self,
                                        const char *seen_bssid)
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+	gs_free const char **strv = NULL;
 	const char *connection_uuid;
-	GKeyFile *seen_bssids_file;
-	char *data, *bssid_str;
-	const char **list;
-	gsize len;
-	GError *error = NULL;
-	GHashTableIter iter;
-	guint n;
 
 	g_return_if_fail (seen_bssid != NULL);
 
-	if (g_hash_table_lookup (priv->seen_bssids, seen_bssid))
-		return;  /* Already in the list */
-
-	/* Add the new BSSID; let the hash take ownership of the allocated BSSID string */
-	bssid_str = g_strdup (seen_bssid);
-	g_hash_table_insert (priv->seen_bssids, bssid_str, bssid_str);
-
-	/* Build up a list of all the BSSIDs in string form */
-	n = 0;
-	list = g_malloc0 (g_hash_table_size (priv->seen_bssids) * sizeof (char *));
-	g_hash_table_iter_init (&iter, priv->seen_bssids);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid_str))
-		list[n++] = bssid_str;
-
-	/* Save BSSID to seen-bssids file */
-	seen_bssids_file = g_key_file_new ();
-	g_key_file_set_list_separator (seen_bssids_file, ',');
-	if (!g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) {
-		if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
-			_LOGW ("error parsing seen-bssids file '%s': %s",
-			       SETTINGS_SEEN_BSSIDS_FILE, error->message);
-		}
-		g_clear_error (&error);
-	}
+	if (!priv->seen_bssids)
+		priv->seen_bssids = _seen_bssids_hash_new ();
 
-	connection_uuid = nm_settings_connection_get_uuid (self);
-	g_key_file_set_string_list (seen_bssids_file, "seen-bssids", connection_uuid, list, n);
-	g_free (list);
+	g_hash_table_add (priv->seen_bssids, g_strdup (seen_bssid));
 
-	data = g_key_file_to_data (seen_bssids_file, &len, &error);
-	if (data) {
-		g_file_set_contents (SETTINGS_SEEN_BSSIDS_FILE, data, len, &error);
-		g_free (data);
-	}
-	g_key_file_free (seen_bssids_file);
+	if (!priv->kf_db_seen_bssids)
+		return;
 
-	if (error) {
-		_LOGW ("error saving seen-bssids to file '%s': %s",
-		       SETTINGS_SEEN_BSSIDS_FILE, error->message);
-		g_error_free (error);
-	}
-}
+	connection_uuid = nm_settings_connection_get_uuid (self);
+	if (!connection_uuid)
+		return;
 
-/**
- * nm_settings_connection_read_and_fill_seen_bssids:
- * @self: the #NMSettingsConnection
- *
- * Retrieves seen BSSIDs of the connection from database file and stores then into the
- * connection private data.
- **/
-void
-nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self)
-{
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-	const char *connection_uuid;
-	GKeyFile *seen_bssids_file;
-	char **tmp_strv = NULL;
-	gsize i, len = 0;
-	NMSettingWireless *s_wifi;
-
-	/* Get seen BSSIDs from database file */
-	seen_bssids_file = g_key_file_new ();
-	g_key_file_set_list_separator (seen_bssids_file, ',');
-	if (g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
-		connection_uuid = nm_settings_connection_get_uuid (self);
-		tmp_strv = g_key_file_get_string_list (seen_bssids_file, "seen-bssids", connection_uuid, &len, NULL);
-	}
-	g_key_file_free (seen_bssids_file);
-
-	/* Update connection's seen-bssids */
-	if (tmp_strv) {
-		g_hash_table_remove_all (priv->seen_bssids);
-		for (i = 0; i < len; i++)
-			g_hash_table_insert (priv->seen_bssids, tmp_strv[i], tmp_strv[i]);
-		g_free (tmp_strv);
-	} else {
-		/* If this connection didn't have an entry in the seen-bssids database,
-		 * maybe this is the first time we've read it in, so populate the
-		 * seen-bssids list from the deprecated seen-bssids property of the
-		 * wifi setting.
-		 */
-		s_wifi = nm_connection_get_setting_wireless (nm_settings_connection_get_connection (self));
-		if (s_wifi) {
-			len = nm_setting_wireless_get_num_seen_bssids (s_wifi);
-			for (i = 0; i < len; i++) {
-				char *bssid_dup = g_strdup (nm_setting_wireless_get_seen_bssid (s_wifi, i));
+	strv = nm_utils_strdict_get_keys (priv->seen_bssids, TRUE, NULL);
 
-				g_hash_table_insert (priv->seen_bssids, bssid_dup, bssid_dup);
-			}
-		}
-	}
+	nm_key_file_db_set_string_list (priv->kf_db_seen_bssids,
+	                                connection_uuid,
+	                                strv ?: NM_PTRARRAY_EMPTY (const char *),
+	                                -1);
 }
 
 /*****************************************************************************/
@@ -2767,46 +2492,6 @@ nm_settings_connection_autoconnect_is_blocked (NMSettingsConnection *self)
 
 /*****************************************************************************/
 
-gboolean
-nm_settings_connection_get_ready (NMSettingsConnection *self)
-{
-	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->ready;
-}
-
-void
-nm_settings_connection_set_ready (NMSettingsConnection *self,
-                                  gboolean ready)
-{
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-
-	ready = !!ready;
-	if (priv->ready != ready) {
-		priv->ready = ready;
-		_notify (self, PROP_READY);
-	}
-}
-
-/**
- * nm_settings_connection_set_filename:
- * @self: an #NMSettingsConnection
- * @filename: @self's filename
- *
- * Called by a backend to sets the filename that @self is read
- * from/written to.
- */
-void
-nm_settings_connection_set_filename (NMSettingsConnection *self,
-                                     const char *filename)
-{
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-
-	if (g_strcmp0 (filename, priv->filename) != 0) {
-		g_free (priv->filename);
-		priv->filename = g_strdup (filename);
-		_notify (self, PROP_FILENAME);
-	}
-}
-
 /**
  * nm_settings_connection_get_filename:
  * @self: an #NMSettingsConnection
@@ -2820,9 +2505,9 @@ nm_settings_connection_set_filename (NMSettingsConnection *self,
 const char *
 nm_settings_connection_get_filename (NMSettingsConnection *self)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
 
-	return priv->filename;
+	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->filename;
 }
 
 const char *
@@ -2834,7 +2519,18 @@ nm_settings_connection_get_id (NMSettingsConnection *self)
 const char *
 nm_settings_connection_get_uuid (NMSettingsConnection *self)
 {
-	return nm_connection_get_uuid (nm_settings_connection_get_connection (self));
+	NMSettingsConnectionPrivate *priv;
+	const char *uuid;
+
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
+
+	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
+	uuid = nm_settings_storage_get_uuid (priv->storage);
+
+	nm_assert (uuid && nm_streq0 (uuid, nm_connection_get_uuid (nm_settings_connection_get_connection (self))));
+
+	return uuid;
 }
 
 const char *
@@ -2845,6 +2541,43 @@ nm_settings_connection_get_connection_type (NMSettingsConnection *self)
 
 /*****************************************************************************/
 
+void
+_nm_settings_connection_cleanup_after_remove (NMSettingsConnection *self)
+{
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+	AuthData *auth_data;
+
+	while ((auth_data = c_list_first_entry (&priv->auth_lst_head, AuthData, auth_lst)))
+		nm_auth_manager_check_authorization_cancel (auth_data->call_id);
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
+
+	switch (prop_id) {
+	case PROP_UNSAVED:
+		g_value_set_boolean (value, nm_settings_connection_get_unsaved (self));
+		break;
+	case PROP_FLAGS:
+		g_value_set_uint (value,
+		                  nm_settings_connection_get_flags (self) & _NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK);
+		break;
+	case PROP_FILENAME:
+		g_value_set_string (value, nm_settings_connection_get_filename (self));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/*****************************************************************************/
+
 static void
 nm_settings_connection_init (NMSettingsConnection *self)
 {
@@ -2855,35 +2588,19 @@ nm_settings_connection_init (NMSettingsConnection *self)
 
 	c_list_init (&self->_connections_lst);
 
-	priv->ready = TRUE;
 	c_list_init (&priv->call_ids_lst_head);
 	c_list_init (&priv->auth_lst_head);
 
-	priv->session_monitor = g_object_ref (nm_session_monitor_get ());
-	priv->session_changed_id = g_signal_connect (priv->session_monitor,
-	                                             NM_SESSION_MONITOR_CHANGED,
-	                                             G_CALLBACK (session_changed_cb), self);
-
 	priv->agent_mgr = g_object_ref (nm_agent_manager_get ());
-
-	priv->seen_bssids = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
+	priv->settings = g_object_ref (nm_settings_get ());
 
 	priv->autoconnect_retries = AUTOCONNECT_RETRIES_UNSET;
-
-	priv->connection = nm_simple_connection_new ();
-
-	g_signal_connect (priv->connection, NM_CONNECTION_SECRETS_CLEARED, G_CALLBACK (secrets_cleared_cb), self);
-	g_signal_connect (priv->connection, NM_CONNECTION_CHANGED, G_CALLBACK (connection_changed_cb), self);
 }
 
-static void
-constructed (GObject *object)
+NMSettingsConnection *
+nm_settings_connection_new (void)
 {
-	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
-
-	_LOGD ("constructed (%s)", G_OBJECT_TYPE_NAME (self));
-
-	G_OBJECT_CLASS (nm_settings_connection_parent_class)->constructed (object);
+	return g_object_new (NM_TYPE_SETTINGS_CONNECTION, NULL);
 }
 
 static void
@@ -2895,6 +2612,8 @@ dispose (GObject *object)
 
 	_LOGD ("disposing");
 
+	nm_assert (!priv->default_wired_device);
+
 	nm_assert (c_list_is_empty (&self->_connections_lst));
 	nm_assert (c_list_is_empty (&priv->auth_lst_head));
 
@@ -2904,80 +2623,29 @@ dispose (GObject *object)
 			_get_secrets_cancel (self, call_id, TRUE);
 	}
 
-	set_visible (self, FALSE);
-
-	if (priv->connection) {
-		/* Disconnect handlers.
-		 * connection_changed_cb() has to be disconnected *before* nm_connection_clear_secrets(),
-		 * because nm_connection_clear_secrets() emits NM_CONNECTION_CHANGED signal.
-		 */
-		g_signal_handlers_disconnect_by_func (priv->connection, G_CALLBACK (secrets_cleared_cb), self);
-		g_signal_handlers_disconnect_by_func (priv->connection, G_CALLBACK (connection_changed_cb), self);
-
-		/* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
-		nm_connection_clear_secrets (priv->connection);
-	}
-
-	g_clear_object (&priv->system_secrets);
-	g_clear_object (&priv->agent_secrets);
+	nm_clear_pointer (&priv->system_secrets, g_variant_unref);
+	nm_clear_pointer (&priv->agent_secrets, g_variant_unref);
 
 	g_clear_pointer (&priv->seen_bssids, g_hash_table_destroy);
 
-	nm_clear_g_signal_handler (priv->session_monitor, &priv->session_changed_id);
-	g_clear_object (&priv->session_monitor);
-
 	g_clear_object (&priv->agent_mgr);
 
 	g_clear_object (&priv->connection);
 
-	g_clear_pointer (&priv->filename, g_free);
+	g_clear_pointer (&priv->kf_db_timestamps, nm_key_file_db_unref);
+	g_clear_pointer (&priv->kf_db_seen_bssids, nm_key_file_db_unref);
 
 	G_OBJECT_CLASS (nm_settings_connection_parent_class)->dispose (object);
-}
 
-static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
-{
-	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
+	g_clear_object (&priv->storage);
 
-	switch (prop_id) {
-	case PROP_UNSAVED:
-		g_value_set_boolean (value, nm_settings_connection_get_unsaved (self));
-		break;
-	case PROP_READY:
-		g_value_set_boolean (value, nm_settings_connection_get_ready (self));
-		break;
-	case PROP_FLAGS:
-		g_value_set_uint (value,
-		                  nm_settings_connection_get_flags (self) & NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK);
-		break;
-	case PROP_FILENAME:
-		g_value_set_string (value, nm_settings_connection_get_filename (self));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
-{
-	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
+	nm_clear_g_free (&priv->filename);
 
-	switch (prop_id) {
-	case PROP_FILENAME:
-		/* construct-only */
-		nm_settings_connection_set_filename (self, g_value_get_string (value));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+	g_clear_object (&priv->settings);
 }
 
+/*****************************************************************************/
+
 static const GDBusSignalInfo signal_info_updated = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
 	"Updated",
 );
@@ -3087,12 +2755,8 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *klass)
 	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH_SETTINGS);
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_settings_connection);
 
-	object_class->constructed = constructed;
 	object_class->dispose = dispose;
 	object_class->get_property = get_property;
-	object_class->set_property = set_property;
-
-	klass->supports_secrets = supports_secrets;
 
 	obj_properties[PROP_UNSAVED] =
 	     g_param_spec_boolean (NM_SETTINGS_CONNECTION_UNSAVED, "", "",
@@ -3100,12 +2764,6 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *klass)
 	                           G_PARAM_READABLE |
 	                           G_PARAM_STATIC_STRINGS);
 
-	obj_properties[PROP_READY] =
-	     g_param_spec_boolean (NM_SETTINGS_CONNECTION_READY, "", "",
-	                           TRUE,
-	                           G_PARAM_READABLE |
-	                           G_PARAM_STATIC_STRINGS);
-
 	obj_properties[PROP_FLAGS] =
 	     g_param_spec_uint (NM_SETTINGS_CONNECTION_FLAGS, "", "",
 	                        0, G_MAXUINT32, 0,
@@ -3115,29 +2773,20 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *klass)
 	obj_properties[PROP_FILENAME] =
 	     g_param_spec_string (NM_SETTINGS_CONNECTION_FILENAME, "", "",
 	                          NULL,
-	                          G_PARAM_READWRITE |
-	                          G_PARAM_CONSTRUCT_ONLY |
+	                          G_PARAM_READABLE |
 	                          G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-	/* internal signal, with an argument (gboolean by_user). */
+	/* internal signal, with an argument (NMSettingsConnectionUpdateReason update_reason) as
+	 * guint. */
 	signals[UPDATED_INTERNAL] =
 	    g_signal_new (NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
 	                  G_TYPE_FROM_CLASS (klass),
 	                  G_SIGNAL_RUN_FIRST,
 	                  0, NULL, NULL,
-	                  g_cclosure_marshal_VOID__BOOLEAN,
-	                  G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
-
-	signals[REMOVED] =
-	    g_signal_new (NM_SETTINGS_CONNECTION_REMOVED,
-	                  G_TYPE_FROM_CLASS (klass),
-	                  G_SIGNAL_RUN_FIRST,
-	                  0,
-	                  NULL, NULL,
-	                  g_cclosure_marshal_VOID__VOID,
-	                  G_TYPE_NONE, 0);
+	                  g_cclosure_marshal_VOID__UINT,
+	                  G_TYPE_NONE, 1, G_TYPE_UINT);
 
 	signals[FLAGS_CHANGED] =
 	    g_signal_new (NM_SETTINGS_CONNECTION_FLAGS_CHANGED,