summary refs log tree commit diff
path: root/src/devices/wifi
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/wifi')
-rw-r--r--src/devices/wifi/meson.build46
-rw-r--r--src/devices/wifi/nm-device-iwd.c248
-rw-r--r--src/devices/wifi/nm-device-iwd.h2
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.c53
-rw-r--r--src/devices/wifi/nm-device-wifi-p2p.c187
-rw-r--r--src/devices/wifi/nm-device-wifi.c782
-rw-r--r--src/devices/wifi/nm-device-wifi.h9
-rw-r--r--src/devices/wifi/nm-iwd-manager.c3
-rw-r--r--src/devices/wifi/nm-wifi-ap.c651
-rw-r--r--src/devices/wifi/nm-wifi-ap.h26
-rw-r--r--src/devices/wifi/nm-wifi-factory.c4
-rw-r--r--src/devices/wifi/nm-wifi-p2p-peer.c221
-rw-r--r--src/devices/wifi/nm-wifi-p2p-peer.h11
-rw-r--r--src/devices/wifi/nm-wifi-utils.c25
-rw-r--r--src/devices/wifi/nm-wifi-utils.h2
-rw-r--r--src/devices/wifi/tests/test-devices-wifi.c57
16 files changed, 899 insertions, 1428 deletions
diff --git a/src/devices/wifi/meson.build b/src/devices/wifi/meson.build
index 6566f201..00404c71 100644
--- a/src/devices/wifi/meson.build
+++ b/src/devices/wifi/meson.build
@@ -1,28 +1,38 @@
-common_sources = files(
-  'nm-wifi-ap.c',
-  'nm-wifi-p2p-peer.c',
-  'nm-wifi-utils.c',
-)
-
-sources = common_sources + files(
-  'nm-device-olpc-mesh.c',
-  'nm-device-wifi-p2p.c',
-  'nm-device-wifi.c',
-  'nm-wifi-common.c',
-  'nm-wifi-factory.c',
-)
+# SPDX-License-Identifier: LGPL-2.1+
 
+iwd_sources = files()
 if enable_iwd
-  sources += files(
+  iwd_sources += files(
     'nm-device-iwd.c',
     'nm-iwd-manager.c',
   )
 endif
 
+libnm_wifi_base = static_library(
+  'nm-wifi-base',
+  sources: files(
+    'nm-device-olpc-mesh.c',
+    'nm-device-wifi-p2p.c',
+    'nm-device-wifi.c',
+    'nm-wifi-ap.c',
+    'nm-wifi-common.c',
+    'nm-wifi-p2p-peer.c',
+    'nm-wifi-utils.c',
+  ) + iwd_sources,
+  dependencies: daemon_nm_default_dep,
+  c_args: daemon_c_flags,
+)
+
+libnm_wifi_base_dep = declare_dependency(
+  link_with: libnm_wifi_base,
+)
+
 libnm_device_plugin_wifi = shared_module(
   'nm-device-plugin-wifi',
-  sources: sources,
-  dependencies: daemon_nm_default_dep,
+  sources: files(
+    'nm-wifi-factory.c',
+  ),
+  dependencies: [ daemon_nm_default_dep, libnm_wifi_base_dep ],
   c_args: daemon_c_flags,
   link_args: ldflags_linker_script_devices,
   link_depends: linker_script_devices,
@@ -43,8 +53,8 @@ if enable_tests
 
   exe = executable(
     test_unit,
-    ['tests/' + test_unit + '.c'] + common_sources,
-    dependencies: libnetwork_manager_test_dep,
+    'tests/' + test_unit + '.c',
+    dependencies: [ libnetwork_manager_test_dep, libnm_wifi_base_dep ],
     c_args: test_c_flags,
   )
 
diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c
index 6b587e3f..3c1d5b22 100644
--- a/src/devices/wifi/nm-device-iwd.c
+++ b/src/devices/wifi/nm-device-iwd.c
@@ -7,24 +7,26 @@
 
 #include "nm-device-iwd.h"
 
-#include "nm-libnm-core-intern/nm-common-macros.h"
-#include "devices/nm-device.h"
 #include "devices/nm-device-private.h"
-#include "nm-utils.h"
+#include "devices/nm-device.h"
 #include "nm-act-request.h"
+#include "nm-config.h"
+#include "nm-core-internal.h"
+#include "nm-dbus-manager.h"
+#include "nm-glib-aux/nm-ref-string.h"
+#include "nm-iwd-manager.h"
+#include "nm-libnm-core-intern/nm-common-macros.h"
+#include "nm-setting-8021x.h"
 #include "nm-setting-connection.h"
-#include "nm-setting-wireless.h"
 #include "nm-setting-wireless-security.h"
-#include "nm-setting-8021x.h"
+#include "nm-setting-wireless.h"
+#include "nm-std-aux/nm-dbus-compat.h"
+#include "nm-utils.h"
+#include "nm-wifi-common.h"
+#include "nm-wifi-utils.h"
 #include "settings/nm-settings-connection.h"
 #include "settings/nm-settings.h"
-#include "nm-wifi-utils.h"
-#include "nm-wifi-common.h"
-#include "nm-core-internal.h"
-#include "nm-config.h"
-#include "nm-iwd-manager.h"
-#include "nm-dbus-manager.h"
-#include "nm-std-aux/nm-dbus-compat.h"
+#include "supplicant/nm-supplicant-types.h"
 
 #include "devices/nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceIwd);
@@ -41,14 +43,6 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceIwd,
 	PROP_LAST_SCAN,
 );
 
-enum {
-	SCANNING_PROHIBITED,
-
-	LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
 typedef struct {
 	GDBusObject *   dbus_obj;
 	GDBusProxy *    dbus_device_proxy;
@@ -77,37 +71,35 @@ struct _NMDeviceIwd {
 
 struct _NMDeviceIwdClass {
 	NMDeviceClass parent;
-
-	/* Signals */
-	gboolean (*scanning_prohibited) (NMDeviceIwd *device, gboolean periodic);
 };
 
 /*****************************************************************************/
 
 G_DEFINE_TYPE (NMDeviceIwd, nm_device_iwd, NM_TYPE_DEVICE)
 
-#define NM_DEVICE_IWD_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDeviceIwd, NM_IS_DEVICE_IWD)
+#define NM_DEVICE_IWD_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDeviceIwd, NM_IS_DEVICE_IWD, NMDevice)
 
 /*****************************************************************************/
 
 static void schedule_periodic_scan (NMDeviceIwd *self,
                                     gboolean initial_scan);
 
+static gboolean check_scanning_prohibited (NMDeviceIwd *self, gboolean periodic);
+
 /*****************************************************************************/
 
 static void
 _ap_dump (NMDeviceIwd *self,
           NMLogLevel log_level,
           const NMWifiAP *ap,
-          const char *prefix,
-          gint32 now_s)
+          const char *prefix)
 {
 	char buf[1024];
 
 	buf[0] = '\0';
 	_NMLOG (log_level, LOGD_WIFI_SCAN, "wifi-ap: %-7s %s",
 	        prefix,
-	        nm_wifi_ap_to_string (ap, buf, sizeof (buf), now_s));
+	        nm_wifi_ap_to_string (ap, buf, sizeof (buf), 0));
 }
 
 /* Callers ensure we're not removing current_ap */
@@ -124,12 +116,12 @@ ap_add_remove (NMDeviceIwd *self,
 		ap->wifi_device = NM_DEVICE (self);
 		c_list_link_tail (&priv->aps_lst_head, &ap->aps_lst);
 		nm_dbus_object_export (NM_DBUS_OBJECT (ap));
-		_ap_dump (self, LOGL_DEBUG, ap, "added", 0);
+		_ap_dump (self, LOGL_DEBUG, ap, "added");
 		nm_device_wifi_emit_signal_access_point (NM_DEVICE (self), ap, TRUE);
 	} else {
 		ap->wifi_device = NULL;
 		c_list_unlink (&ap->aps_lst);
-		_ap_dump (self, LOGL_DEBUG, ap, "removed", 0);
+		_ap_dump (self, LOGL_DEBUG, ap, "removed");
 	}
 
 	_notify (self, PROP_ACCESS_POINTS);
@@ -191,45 +183,43 @@ remove_all_aps (NMDeviceIwd *self)
 	nm_device_recheck_available_connections (NM_DEVICE (self));
 }
 
-static GVariant *
-vardict_from_network_type (const char *type)
+static NM80211ApSecurityFlags
+ap_security_flags_from_network_type (const char *type)
 {
-	GVariantBuilder builder;
-	const char *key_mgmt = "";
-	const char *pairwise = "ccmp";
+	NM80211ApSecurityFlags flags;
 
-	if (!strcmp (type, "psk"))
-		key_mgmt = "wpa-psk";
-	else if (!strcmp (type, "8021x"))
-		key_mgmt = "wpa-eap";
+	if (nm_streq (type, "psk"))
+		flags = NM_802_11_AP_SEC_KEY_MGMT_PSK;
+	else if (nm_streq (type, "8021x"))
+		flags = NM_802_11_AP_SEC_KEY_MGMT_802_1X;
 	else
-		return NULL;
+		return NM_802_11_AP_SEC_NONE;
 
-	g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
-	g_variant_builder_add (&builder, "{sv}", "KeyMgmt",
-	                       g_variant_new_strv (&key_mgmt, 1));
-	g_variant_builder_add (&builder, "{sv}", "Pairwise",
-	                       g_variant_new_strv (&pairwise, 1));
-	g_variant_builder_add (&builder, "{sv}", "Group",
-	                       g_variant_new_string ("ccmp"));
-	return g_variant_new ("a{sv}", &builder);
+	flags |= NM_802_11_AP_SEC_PAIR_CCMP;
+	flags |= NM_802_11_AP_SEC_GROUP_CCMP;
+	return flags;
 }
 
 static void
 insert_ap_from_network (NMDeviceIwd *self,
                         GHashTable *aps,
                         const char *path,
+                        gint64 last_seen_msec,
                         int16_t signal,
                         uint32_t ap_id)
 {
 	gs_unref_object GDBusProxy *network_proxy = NULL;
-	gs_unref_variant GVariant *name_value = NULL, *type_value = NULL;
-	const char *name, *type;
-	GVariantBuilder builder;
-	gs_unref_variant GVariant *props = NULL;
-	GVariant *rsn;
+	gs_unref_variant GVariant *name_value = NULL;
+	gs_unref_variant GVariant *type_value = NULL;
+	nm_auto_ref_string NMRefString *bss_path = NULL;
+	const char *name;
+	const char *type;
+	NMSupplicantBssInfo bss_info;
 	uint8_t bssid[6];
 	NMWifiAP *ap;
+	gs_unref_bytes GBytes *ssid = NULL;
+
+	bss_path = nm_ref_string_new (path);
 
 	if (g_hash_table_lookup (aps, path)) {
 		_LOGD (LOGD_WIFI, "Duplicate network at %s", path);
@@ -253,6 +243,11 @@ insert_ap_from_network (NMDeviceIwd *self,
 	name = g_variant_get_string (name_value, NULL);
 	type = g_variant_get_string (type_value, NULL);
 
+	if (nm_streq (type, "wep")) {
+		/* WEP not supported */
+		return;
+	}
+
 	/* What we get from IWD are networks, or ESSs, that may contain
 	 * multiple APs, or BSSs, each.  We don't get information about any
 	 * specific BSSs within an ESS but we can safely present each ESS
@@ -268,32 +263,26 @@ insert_ap_from_network (NMDeviceIwd *self,
 	bssid[4] = ap_id >> 8;
 	bssid[5] = ap_id;
 
-	/* WEP not supported */
-	if (nm_streq (type, "wep"))
-		return;
-
-	g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
-	g_variant_builder_add (&builder, "{sv}", "BSSID",
-	                       g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, bssid, 6, 1));
-	g_variant_builder_add (&builder, "{sv}", "Mode",
-	                       g_variant_new_string ("infrastructure"));
-
-	rsn = vardict_from_network_type (type);
-	if (rsn)
-		g_variant_builder_add (&builder, "{sv}", "RSN", rsn);
+	ssid = g_bytes_new (name, NM_MIN (32u, strlen (name)));
 
-	props = g_variant_new ("a{sv}", &builder);
+	bss_info = (NMSupplicantBssInfo) {
+		.bss_path       = bss_path,
+		.last_seen_msec = last_seen_msec,
+		.bssid_valid    = TRUE,
+		.mode           = NM_802_11_MODE_INFRA,
+		.rsn_flags      = ap_security_flags_from_network_type (type),
+		.ssid           = ssid,
+		.signal_percent = nm_wifi_utils_level_to_quality (signal / 100),
+		.frequency      = 2417,
+		.max_rate       = 65000,
+	};
+	memcpy (bss_info.bssid, bssid, sizeof (bssid));
 
-	ap = nm_wifi_ap_new_from_properties (path, props);
+	ap = nm_wifi_ap_new_from_properties (&bss_info);
 
-	nm_wifi_ap_set_ssid_arr (ap,
-	                         (const guint8 *) name,
-	                         NM_MIN (32, strlen (name)));
+	nm_assert (bss_path == nm_wifi_ap_get_supplicant_path (ap));
 
-	nm_wifi_ap_set_strength (ap, nm_wifi_utils_level_to_quality (signal / 100));
-	nm_wifi_ap_set_freq (ap, 2417);
-	nm_wifi_ap_set_max_bitrate (ap, 65000);
-	g_hash_table_insert (aps, (gpointer) nm_wifi_ap_get_supplicant_path (ap), ap);
+	g_hash_table_insert (aps, bss_path, ap);
 }
 
 static void
@@ -313,6 +302,7 @@ get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 	gboolean compat;
 	const char *return_sig;
 	static uint32_t ap_id = 0;
+	gint64 last_seen_msec;
 
 	variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
 	if (!variant) {
@@ -336,16 +326,17 @@ get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 		return;
 	}
 
-	new_aps = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref);
+	new_aps = g_hash_table_new_full (nm_direct_hash, NULL, NULL, g_object_unref);
 
 	g_variant_get (variant, return_sig, &networks);
 
+	last_seen_msec = nm_utils_get_monotonic_timestamp_msec ();
 	if (compat) {
 		while (g_variant_iter_next (networks, "(&o&sn&s)", &path, &name, &signal, &type))
-			insert_ap_from_network (self, new_aps, path, signal, ap_id++);
+			insert_ap_from_network (self, new_aps, path, last_seen_msec, signal, ap_id++);
 	} else {
 		while (g_variant_iter_next (networks, "(&on)", &path, &signal))
-			insert_ap_from_network (self, new_aps, path, signal, ap_id++);
+			insert_ap_from_network (self, new_aps, path, last_seen_msec, signal, ap_id++);
 	}
 
 	g_variant_iter_free (networks);
@@ -356,7 +347,7 @@ get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 		                              nm_wifi_ap_get_supplicant_path (ap));
 		if (new_ap) {
 			if (nm_wifi_ap_set_strength (ap, nm_wifi_ap_get_strength (new_ap))) {
-				_ap_dump (self, LOGL_TRACE, ap, "updated", 0);
+				_ap_dump (self, LOGL_TRACE, ap, "updated");
 				changed = TRUE;
 			}
 			g_hash_table_remove (new_aps,
@@ -566,20 +557,17 @@ is_connection_known_network (NMConnection *connection)
 static gboolean
 is_ap_known_network (NMWifiAP *ap)
 {
-	GDBusProxy *network_proxy;
+	gs_unref_object GDBusProxy *network_proxy = NULL;
 	gs_unref_variant GVariant *known_network = NULL;
 
 	network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (),
-	                                                   nm_wifi_ap_get_supplicant_path (ap),
+	                                                   nm_ref_string_get_str (nm_wifi_ap_get_supplicant_path (ap)),
 	                                                   NM_IWD_NETWORK_INTERFACE);
 	if (!network_proxy)
 		return FALSE;
 
 	known_network = g_dbus_proxy_get_cached_property (network_proxy, "KnownNetwork");
-	g_object_unref (network_proxy);
-
-	return    known_network
-	       && g_variant_is_of_type (known_network, G_VARIANT_TYPE_OBJECT_PATH);
+	return nm_g_variant_is_of_type (known_network, G_VARIANT_TYPE_OBJECT_PATH);
 }
 
 static gboolean
@@ -975,15 +963,6 @@ _nm_device_iwd_get_aps (NMDeviceIwd *self)
 	return &NM_DEVICE_IWD_GET_PRIVATE (self)->aps_lst_head;
 }
 
-static gboolean
-check_scanning_prohibited (NMDeviceIwd *self, gboolean periodic)
-{
-	gboolean prohibited = FALSE;
-
-	g_signal_emit (self, signals[SCANNING_PROHIBITED], 0, periodic, &prohibited);
-	return prohibited;
-}
-
 static void
 scan_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 {
@@ -993,12 +972,12 @@ scan_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 	gs_free_error GError *error = NULL;
 
 	variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
-	if (!variant && nm_utils_error_is_cancelled (error, FALSE))
+	if (!variant && nm_utils_error_is_cancelled (error))
 		return;
 
 	priv = NM_DEVICE_IWD_GET_PRIVATE (self);
 	priv->scan_requested = FALSE;
-	priv->last_scan = nm_utils_get_monotonic_timestamp_ms ();
+	priv->last_scan = nm_utils_get_monotonic_timestamp_msec ();
 	_notify (self, PROP_LAST_SCAN);
 
 	/* On success, priv->scanning becomes true right before or right
@@ -1093,7 +1072,7 @@ _nm_device_iwd_request_scan (NMDeviceIwd *self,
 }
 
 static gboolean
-scanning_prohibited (NMDeviceIwd *self, gboolean periodic)
+check_scanning_prohibited (NMDeviceIwd *self, gboolean periodic)
 {
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
 
@@ -1148,7 +1127,7 @@ try_reply_agent_request (NMDeviceIwd *self,
 
 	*replied = FALSE;
 
-	if (!strcmp (method_name, "RequestPassphrase")) {
+	if (nm_streq (method_name, "RequestPassphrase")) {
 		const char *psk;
 
 		if (!s_wireless_sec)
@@ -1168,7 +1147,7 @@ try_reply_agent_request (NMDeviceIwd *self,
 		*setting_name = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
 		*setting_key = NM_SETTING_WIRELESS_SECURITY_PSK;
 		return TRUE;
-	} else if (!strcmp (method_name, "RequestPrivateKeyPassphrase")) {
+	} else if (nm_streq (method_name, "RequestPrivateKeyPassphrase")) {
 		const char *password;
 
 		if (!s_8021x)
@@ -1188,7 +1167,7 @@ try_reply_agent_request (NMDeviceIwd *self,
 		*setting_name = NM_SETTING_802_1X_SETTING_NAME;
 		*setting_key = NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD;
 		return TRUE;
-	} else if (!strcmp (method_name, "RequestUserNameAndPassword")) {
+	} else if (nm_streq (method_name, "RequestUserNameAndPassword")) {
 		const char *identity, *password;
 
 		if (!s_8021x)
@@ -1212,7 +1191,7 @@ try_reply_agent_request (NMDeviceIwd *self,
 		else
 			*setting_key = NM_SETTING_802_1X_PASSWORD;
 		return TRUE;
-	} else if (!strcmp (method_name, "RequestUserPassword")) {
+	} else if (nm_streq (method_name, "RequestUserPassword")) {
 		const char *password;
 
 		if (!s_8021x)
@@ -1270,7 +1249,7 @@ wifi_secrets_cb (NMActRequest *req,
 
 	priv->wifi_secrets_id = NULL;
 
-	if (nm_utils_error_is_cancelled (error, FALSE)) {
+	if (nm_utils_error_is_cancelled (error)) {
 		g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR,
 		                                               NM_DEVICE_ERROR_INVALID_CONNECTION,
 		                                               "NM secrets request cancelled");
@@ -1366,7 +1345,7 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 		       "Activation: (wifi) Network.Connect failed: %s",
 		       error->message);
 
-		if (nm_utils_error_is_cancelled (error, FALSE))
+		if (nm_utils_error_is_cancelled (error))
 			return;
 
 		if (!NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_NEED_AUTH))
@@ -1438,7 +1417,7 @@ act_failed_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 	gs_free_error GError *error = NULL;
 
 	variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
-	if (!variant && nm_utils_error_is_cancelled (error, FALSE))
+	if (!variant && nm_utils_error_is_cancelled (error))
 		return;
 
 	/* Change state to FAILED unless already done by state_changed
@@ -1467,7 +1446,7 @@ act_start_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 		       "Activation: (wifi) Network.Connect failed: %s",
 		       error->message);
 
-		if (nm_utils_error_is_cancelled (error, FALSE))
+		if (nm_utils_error_is_cancelled (error))
 			return;
 
 		if (!NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG))
@@ -1587,7 +1566,7 @@ act_set_mode_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 		       "Activation: (wifi) Setting Device.Mode failed: %s",
 		       error->message);
 
-		if (nm_utils_error_is_cancelled (error, FALSE))
+		if (nm_utils_error_is_cancelled (error))
 			return;
 
 		if (   !NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG)
@@ -1647,7 +1626,7 @@ act_psk_cb (NMActRequest *req,
 	NMDeviceIwdPrivate *priv;
 	NMDevice *device;
 
-	if (nm_utils_error_is_cancelled (error, FALSE))
+	if (nm_utils_error_is_cancelled (error))
 		return;
 
 	priv = NM_DEVICE_IWD_GET_PRIVATE (self);
@@ -1772,29 +1751,25 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceIwd *self = NM_DEVICE_IWD (device);
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 	NMActRequest *req;
 	NMConnection *connection;
 	NMSettingWireless *s_wireless;
 	const char *mode;
 
 	req = nm_device_get_act_request (device);
-	g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
-
 	connection = nm_act_request_get_applied_connection (req);
-	g_assert (connection);
-
 	s_wireless = nm_connection_get_setting_wireless (connection);
 	g_return_val_if_fail (s_wireless, NM_ACT_STAGE_RETURN_FAILURE);
 
 	mode = nm_setting_wireless_get_mode (s_wireless);
+
 	if (NM_IN_STRSET (mode, NULL, NM_SETTING_WIRELESS_MODE_INFRA)) {
-		GDBusProxy *network_proxy;
+		gs_unref_object GDBusProxy *network_proxy = NULL;
 		NMWifiAP *ap = priv->current_ap;
 
 		if (!ap) {
 			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
-			goto out;
+			goto out_fail;
 		}
 
 		/* 802.1x networks that are not IWD Known Networks will definitely
@@ -1808,18 +1783,18 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 			       nm_connection_get_id (connection));
 
 			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS);
-			goto out;
+			goto out_fail;
 		}
 
 		network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (),
-		                                                   nm_wifi_ap_get_supplicant_path (ap),
+		                                                   nm_ref_string_get_str (nm_wifi_ap_get_supplicant_path (ap)),
 		                                                   NM_IWD_NETWORK_INTERFACE);
 		if (!network_proxy) {
 			_LOGE (LOGD_DEVICE | LOGD_WIFI,
 			       "Activation: (wifi) could not get Network interface proxy for %s",
-			       nm_wifi_ap_get_supplicant_path (ap));
+			       nm_ref_string_get_str (nm_wifi_ap_get_supplicant_path (ap)));
 			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
-			goto out;
+			goto out_fail;
 		}
 
 		if (!priv->cancellable)
@@ -1832,12 +1807,15 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 		                   NULL, G_DBUS_CALL_FLAGS_NONE, G_MAXINT,
 		                   priv->cancellable, network_connect_cb, self);
 
-		g_object_unref (network_proxy);
-	} else if (NM_IN_STRSET (mode, NM_SETTING_WIRELESS_MODE_AP, NM_SETTING_WIRELESS_MODE_ADHOC)) {
+		return NM_ACT_STAGE_RETURN_POSTPONE;
+	}
+
+	if (NM_IN_STRSET (mode, NM_SETTING_WIRELESS_MODE_AP, NM_SETTING_WIRELESS_MODE_ADHOC)) {
 		NMSettingWirelessSecurity *s_wireless_sec;
 
 		s_wireless_sec = nm_connection_get_setting_wireless_security (connection);
-		if (s_wireless_sec && !nm_setting_wireless_security_get_psk (s_wireless_sec)) {
+		if (   s_wireless_sec
+		    && !nm_setting_wireless_security_get_psk (s_wireless_sec)) {
 			/* PSK is missing from the settings, have to request it */
 
 			wifi_secrets_cancel (self);
@@ -1852,16 +1830,18 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 			nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE);
 		} else
 			act_set_mode (self);
-	}
 
-	/* We'll get stage3 started when the supplicant connects */
-	ret = NM_ACT_STAGE_RETURN_POSTPONE;
+		return NM_ACT_STAGE_RETURN_POSTPONE;
+	}
 
-out:
-	if (ret == NM_ACT_STAGE_RETURN_FAILURE)
-		cleanup_association_attempt (self, FALSE);
+	_LOGW (LOGD_DEVICE | LOGD_WIFI,
+	       "Activation: (wifi) iwd cannot handle mode %s",
+	       mode);
+	NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
 
-	return ret;
+out_fail:
+	cleanup_association_attempt (self, FALSE);
+	return NM_ACT_STAGE_RETURN_FAILURE;
 }
 
 static guint32
@@ -1987,7 +1967,7 @@ device_state_changed (NMDevice *device,
 static gboolean
 get_enabled (NMDevice *device)
 {
-	return NM_DEVICE_IWD_GET_PRIVATE ((NMDeviceIwd *) device)->enabled;
+	return NM_DEVICE_IWD_GET_PRIVATE (device)->enabled;
 }
 
 static void
@@ -2099,7 +2079,7 @@ get_property (GObject *object, guint prop_id,
 	case PROP_LAST_SCAN:
 		g_value_set_int64 (value,
 		                   priv->last_scan > 0
-		                       ? nm_utils_monotonic_timestamp_as_boottime (priv->last_scan, NM_UTILS_NS_PER_MSEC)
+		                       ? nm_utils_monotonic_timestamp_as_boottime (priv->last_scan, NM_UTILS_NSEC_PER_MSEC)
 		                       : (gint64) -1);
 		break;
 	default:
@@ -2582,8 +2562,6 @@ nm_device_iwd_class_init (NMDeviceIwdClass *klass)
 
 	device_class->state_changed = device_state_changed;
 
-	klass->scanning_prohibited = scanning_prohibited;
-
 	obj_properties[PROP_MODE] =
 	    g_param_spec_uint (NM_DEVICE_IWD_MODE, "", "",
 	                       NM_802_11_MODE_UNKNOWN,
@@ -2628,12 +2606,4 @@ nm_device_iwd_class_init (NMDeviceIwdClass *klass)
 	                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
-
-	signals[SCANNING_PROHIBITED] =
-	    g_signal_new (NM_DEVICE_IWD_SCANNING_PROHIBITED,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_LAST,
-	                  G_STRUCT_OFFSET (NMDeviceIwdClass, scanning_prohibited),
-	                  NULL, NULL, NULL,
-	                  G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN);
 }
diff --git a/src/devices/wifi/nm-device-iwd.h b/src/devices/wifi/nm-device-iwd.h
index 586e02f4..1f15d3f4 100644
--- a/src/devices/wifi/nm-device-iwd.h
+++ b/src/devices/wifi/nm-device-iwd.h
@@ -25,8 +25,6 @@
 #define NM_DEVICE_IWD_SCANNING            NM_DEVICE_WIFI_SCANNING
 #define NM_DEVICE_IWD_LAST_SCAN           NM_DEVICE_WIFI_LAST_SCAN
 
-#define NM_DEVICE_IWD_SCANNING_PROHIBITED  NM_DEVICE_WIFI_SCANNING_PROHIBITED
-
 typedef struct _NMDeviceIwd NMDeviceIwd;
 typedef struct _NMDeviceIwdClass NMDeviceIwdClass;
 
diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c
index 516dc78b..770b53f6 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.c
+++ b/src/devices/wifi/nm-device-olpc-mesh.c
@@ -57,7 +57,7 @@ struct _NMDeviceOlpcMeshClass {
 
 G_DEFINE_TYPE (NMDeviceOlpcMesh, nm_device_olpc_mesh, NM_TYPE_DEVICE)
 
-#define NM_DEVICE_OLPC_MESH_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceOlpcMesh, NM_IS_DEVICE_OLPC_MESH)
+#define NM_DEVICE_OLPC_MESH_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceOlpcMesh, NM_IS_DEVICE_OLPC_MESH, NMDevice)
 
 /*****************************************************************************/
 
@@ -130,7 +130,6 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device);
 	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
-	gboolean scanning;
 
 	/* disconnect companion device, if it is connected */
 	if (nm_device_get_act_request (NM_DEVICE (priv->companion))) {
@@ -145,8 +144,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	}
 
 	/* wait with continuing configuration until the companion device is done scanning */
-	g_object_get (priv->companion, NM_DEVICE_WIFI_SCANNING, &scanning, NULL);
-	if (scanning) {
+	if (nm_device_wifi_get_scanning (NM_DEVICE_WIFI (priv->companion))) {
 		priv->stage1_waiting = TRUE;
 		return NM_ACT_STAGE_RETURN_POSTPONE;
 	}
@@ -189,10 +187,10 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	gboolean success;
 
 	s_mesh = nm_device_get_applied_setting (device, NM_TYPE_SETTING_OLPC_MESH);
-
 	g_return_val_if_fail (s_mesh, NM_ACT_STAGE_RETURN_FAILURE);
 
 	ssid = nm_setting_olpc_mesh_get_ssid (s_mesh);
+
 	nm_device_take_down (NM_DEVICE (self), TRUE);
 	success = nm_platform_mesh_set_ssid (nm_device_get_platform (device),
 	                                     nm_device_get_ifindex (device),
@@ -237,6 +235,9 @@ companion_cleanup (NMDeviceOlpcMesh *self)
 	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
 
 	if (priv->companion) {
+		nm_device_wifi_scanning_prohibited_track (NM_DEVICE_WIFI (priv->companion),
+		                                          self,
+		                                          FALSE);
 		g_signal_handlers_disconnect_by_data (priv->companion, self);
 		g_clear_object (&priv->companion);
 	}
@@ -248,15 +249,16 @@ companion_notify_cb (NMDeviceWifi *companion, GParamSpec *pspec, gpointer user_d
 {
 	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
 	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
-	gboolean scanning;
+
+	nm_assert (NM_IS_DEVICE_WIFI (companion));
+	nm_assert (priv->companion == (gpointer) companion);
 
 	if (!priv->stage1_waiting)
 		return;
 
-	g_object_get (companion, NM_DEVICE_WIFI_SCANNING, &scanning, NULL);
-	if (!scanning) {
+	if (!nm_device_wifi_get_scanning (NM_DEVICE_WIFI (companion))) {
 		priv->stage1_waiting = FALSE;
-		nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
+		nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self), FALSE);
 	}
 }
 
@@ -290,16 +292,6 @@ companion_state_changed_cb (NMDeviceWifi *companion,
 }
 
 static gboolean
-companion_scan_prohibited_cb (NMDeviceWifi *companion, gboolean periodic, gpointer user_data)
-{
-	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
-	NMDeviceState state = nm_device_get_state (NM_DEVICE (self));
-
-	/* Don't allow the companion to scan while configuring the mesh interface */
-	return (state >= NM_DEVICE_STATE_PREPARE) && (state <= NM_DEVICE_STATE_IP_CONFIG);
-}
-
-static gboolean
 companion_autoconnect_allowed_cb (NMDeviceWifi *companion, gpointer user_data)
 {
 	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
@@ -324,7 +316,7 @@ check_companion (NMDeviceOlpcMesh *self, NMDevice *other)
 	if (!nm_utils_hwaddr_matches (my_addr, -1, their_addr, -1))
 		return FALSE;
 
-	g_assert (priv->companion == NULL);
+	nm_assert (priv->companion == NULL);
 	priv->companion = g_object_ref (other);
 
 	_LOGI (LOGD_OLPC, "found companion Wi-Fi device %s",
@@ -336,9 +328,6 @@ check_companion (NMDeviceOlpcMesh *self, NMDevice *other)
 	g_signal_connect (G_OBJECT (other), "notify::" NM_DEVICE_WIFI_SCANNING,
 	                  G_CALLBACK (companion_notify_cb), self);
 
-	g_signal_connect (G_OBJECT (other), NM_DEVICE_WIFI_SCANNING_PROHIBITED,
-	                  G_CALLBACK (companion_scan_prohibited_cb), self);
-
 	g_signal_connect (G_OBJECT (other), NM_DEVICE_AUTOCONNECT_ALLOWED,
 	                  G_CALLBACK (companion_autoconnect_allowed_cb), self);
 
@@ -400,8 +389,24 @@ state_changed (NMDevice *device,
                NMDeviceState old_state,
                NMDeviceStateReason reason)
 {
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device);
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+
 	if (new_state == NM_DEVICE_STATE_UNAVAILABLE)
-		find_companion (NM_DEVICE_OLPC_MESH (device));
+		find_companion (self);
+
+	if (priv->companion) {
+		gboolean temporarily_prohibited = FALSE;
+
+		if (   new_state >= NM_DEVICE_STATE_PREPARE
+		    && new_state <= NM_DEVICE_STATE_IP_CONFIG) {
+			/* Don't allow the companion to scan while configuring the mesh interface */
+			temporarily_prohibited = TRUE;
+		}
+		nm_device_wifi_scanning_prohibited_track (NM_DEVICE_WIFI (priv->companion),
+		                                          self,
+		                                          temporarily_prohibited);
+	}
 }
 
 static guint32
diff --git a/src/devices/wifi/nm-device-wifi-p2p.c b/src/devices/wifi/nm-device-wifi-p2p.c
index 34ff70fa..497bc436 100644
--- a/src/devices/wifi/nm-device-wifi-p2p.c
+++ b/src/devices/wifi/nm-device-wifi-p2p.c
@@ -12,19 +12,20 @@
 #include "supplicant/nm-supplicant-manager.h"
 #include "supplicant/nm-supplicant-interface.h"
 
-#include "nm-manager.h"
-#include "nm-utils.h"
-#include "nm-wifi-p2p-peer.h"
 #include "NetworkManagerUtils.h"
 #include "devices/nm-device-private.h"
-#include "settings/nm-settings.h"
-#include "nm-setting-wifi-p2p.h"
 #include "nm-act-request.h"
+#include "nm-core-internal.h"
+#include "nm-glib-aux/nm-ref-string.h"
 #include "nm-ip4-config.h"
-#include "platform/nm-platform.h"
 #include "nm-manager.h"
-#include "nm-core-internal.h"
+#include "nm-manager.h"
+#include "nm-setting-wifi-p2p.h"
+#include "nm-utils.h"
+#include "nm-wifi-p2p-peer.h"
+#include "platform/nm-platform.h"
 #include "platform/nmp-object.h"
+#include "settings/nm-settings.h"
 
 #include "devices/nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceWifiP2P);
@@ -104,7 +105,7 @@ peer_list_dump (gpointer user_data)
 
 	if (_LOGD_ENABLED (LOGD_WIFI_SCAN)) {
 		NMWifiP2PPeer *peer;
-		gint32 now_s = nm_utils_get_monotonic_timestamp_s ();
+		gint32 now_s = nm_utils_get_monotonic_timestamp_sec ();
 
 		_LOGD (LOGD_WIFI_SCAN, "P2P Peers: [now:%u]", now_s);
 		c_list_for_each_entry (peer, &priv->peers_lst_head, peers_lst)
@@ -227,11 +228,7 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 		return FALSE;
 
 	supplicant_state = nm_supplicant_interface_get_state (priv->mgmt_iface);
-	if (   supplicant_state < NM_SUPPLICANT_INTERFACE_STATE_READY
-	    || supplicant_state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED)
-		return FALSE;
-
-	return TRUE;
+	return nm_supplicant_interface_state_is_operational (supplicant_state);
 }
 
 static gboolean
@@ -432,7 +429,6 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 
 	connection = nm_device_get_applied_connection (device);
 	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
-
 	nm_assert (NM_IS_SETTING_WIFI_P2P (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIFI_P2P)));
 
 	/* The prepare stage ensures that the peer has been found */
@@ -452,7 +448,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	/* TODO: Fix "pbc" being hardcoded here! */
 	nm_supplicant_interface_p2p_connect (priv->mgmt_iface,
 	                                     nm_wifi_p2p_peer_get_supplicant_path (peer),
-	                                     "pbc", NULL);
+	                                     "pbc",
+	                                     NULL);
 
 	/* Set up a timeout on the connect attempt */
 	if (priv->sup_timeout_id == 0) {
@@ -526,7 +523,7 @@ peer_add_remove (NMDeviceWifiP2P *self,
 			if (peer) {
 				/* A peer for the connection was found, cancel the timeout and go to configure state. */
 				nm_clear_g_source (&priv->find_peer_timeout_id);
-				nm_device_activate_schedule_stage1_device_prepare (device);
+				nm_device_activate_schedule_stage1_device_prepare (device, FALSE);
 			}
 		}
 
@@ -649,52 +646,49 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 	NMSupplicantInterfaceState new_state = new_state_i;
 	NMSupplicantInterfaceState old_state = old_state_i;
 
-	if (new_state == old_state)
-		return;
-
 	_LOGI (LOGD_DEVICE | LOGD_WIFI,
 	       "supplicant management interface state: %s -> %s",
 	       nm_supplicant_interface_state_to_string (old_state),
 	       nm_supplicant_interface_state_to_string (new_state));
 
-	switch (new_state) {
-	case NM_SUPPLICANT_INTERFACE_STATE_READY:
-		_LOGD (LOGD_WIFI, "supplicant ready");
+	if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
+		supplicant_interfaces_release (self, TRUE);
 		nm_device_queue_recheck_available (device,
 		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
 		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+		return;
+	}
 
-		if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY)
-			_set_is_waiting_for_supplicant (self, FALSE);
-		break;
-	case NM_SUPPLICANT_INTERFACE_STATE_DOWN:
-		supplicant_interfaces_release (self, TRUE);
+	if (old_state == NM_SUPPLICANT_INTERFACE_STATE_STARTING) {
+		_LOGD (LOGD_WIFI, "supplicant ready");
 		nm_device_queue_recheck_available (device,
 		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
 		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
-		break;
-	default:
-		break;
+		_set_is_waiting_for_supplicant (self, FALSE);
 	}
 }
 
 static void
-supplicant_iface_peer_updated_cb (NMSupplicantInterface *iface,
-                                  const char *object_path,
-                                  GVariant *properties,
+supplicant_iface_peer_changed_cb (NMSupplicantInterface *iface,
+                                  NMSupplicantPeerInfo *peer_info,
+                                  gboolean is_present,
                                   NMDeviceWifiP2P *self)
 {
-	NMDeviceWifiP2PPrivate *priv;
+	NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self);
 	NMWifiP2PPeer *found_peer;
 
-	g_return_if_fail (self != NULL);
-	g_return_if_fail (object_path != NULL);
+	found_peer = nm_wifi_p2p_peers_find_by_supplicant_path (&priv->peers_lst_head, peer_info->peer_path->str);
 
-	priv  = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self);
+	if (!is_present) {
+		if (!found_peer)
+			return;
+
+		peer_add_remove (self, FALSE, found_peer, TRUE);
+		goto out;
+	}
 
-	found_peer = nm_wifi_p2p_peers_find_by_supplicant_path (&priv->peers_lst_head, object_path);
 	if (found_peer) {
-		if (!nm_wifi_p2p_peer_update_from_properties (found_peer, object_path, properties))
+		if (!nm_wifi_p2p_peer_update_from_properties (found_peer, peer_info))
 			return;
 
 		update_disconnect_on_connection_peer_missing (self);
@@ -702,35 +696,11 @@ supplicant_iface_peer_updated_cb (NMSupplicantInterface *iface,
 	} else {
 		gs_unref_object NMWifiP2PPeer *peer = NULL;
 
-		peer = nm_wifi_p2p_peer_new_from_properties (object_path, properties);
-		if (!peer) {
-			_LOGD (LOGD_WIFI, "invalid P2P peer properties received for %s", object_path);
-			return;
-		}
-
+		peer = nm_wifi_p2p_peer_new_from_properties (peer_info);
 		peer_add_remove (self, TRUE, peer, TRUE);
 	}
 
-	schedule_peer_list_dump (self);
-}
-
-static void
-supplicant_iface_peer_removed_cb (NMSupplicantInterface *iface,
-                                  const char *object_path,
-                                  NMDeviceWifiP2P *self)
-{
-	NMDeviceWifiP2PPrivate *priv;
-	NMWifiP2PPeer *peer;
-
-	g_return_if_fail (self != NULL);
-	g_return_if_fail (object_path != NULL);
-
-	priv  = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self);
-	peer = nm_wifi_p2p_peers_find_by_supplicant_path (&priv->peers_lst_head, object_path);
-	if (!peer)
-		return;
-
-	peer_add_remove (self, FALSE, peer, TRUE);
+out:
 	schedule_peer_list_dump (self);
 }
 
@@ -742,7 +712,7 @@ check_group_iface_ready (NMDeviceWifiP2P *self)
 	if (!priv->group_iface)
 		return;
 
-	if (nm_supplicant_interface_get_state (priv->group_iface) < NM_SUPPLICANT_INTERFACE_STATE_READY)
+	if (!nm_supplicant_interface_state_is_operational (nm_supplicant_interface_get_state (priv->group_iface)))
 		return;
 
 	if (!nm_supplicant_interface_get_p2p_group_joined (priv->group_iface))
@@ -755,6 +725,24 @@ check_group_iface_ready (NMDeviceWifiP2P *self)
 }
 
 static void
+supplicant_group_iface_is_ready (NMDeviceWifiP2P *self)
+{
+	NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self);
+
+	_LOGD (LOGD_WIFI, "P2P Group supplicant ready");
+
+	if (!nm_device_set_ip_iface (NM_DEVICE (self), nm_supplicant_interface_get_ifname (priv->group_iface))) {
+		nm_device_state_changed (NM_DEVICE (self),
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+		return;
+	}
+
+	_set_is_waiting_for_supplicant (self, FALSE);
+	check_group_iface_ready (self);
+}
+
+static void
 supplicant_group_iface_state_cb (NMSupplicantInterface *iface,
                                  int new_state_i,
                                  int old_state_i,
@@ -762,44 +750,26 @@ supplicant_group_iface_state_cb (NMSupplicantInterface *iface,
                                  gpointer user_data)
 {
 	NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data);
-	NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self);
-	NMDevice *device = NM_DEVICE (self);
 	NMSupplicantInterfaceState new_state = new_state_i;
 	NMSupplicantInterfaceState old_state = old_state_i;
 
-	if (new_state == old_state)
-		return;
-
 	_LOGI (LOGD_DEVICE | LOGD_WIFI,
 	       "P2P Group supplicant interface state: %s -> %s",
 	       nm_supplicant_interface_state_to_string (old_state),
 	       nm_supplicant_interface_state_to_string (new_state));
 
-	switch (new_state) {
-	case NM_SUPPLICANT_INTERFACE_STATE_READY:
-		_LOGD (LOGD_WIFI, "P2P Group supplicant ready");
-
-		if (!nm_device_set_ip_iface (device, nm_supplicant_interface_get_ifname (priv->group_iface))) {
-			nm_device_state_changed (device,
-			                         NM_DEVICE_STATE_FAILED,
-			                         NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
-			break;
-		}
-
-		if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY)
-			_set_is_waiting_for_supplicant (self, FALSE);
-
-		check_group_iface_ready (self);
-		break;
-	case NM_SUPPLICANT_INTERFACE_STATE_DOWN:
+	if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
 		supplicant_group_interface_release (self);
 
-		nm_device_state_changed (device,
+		nm_device_state_changed (NM_DEVICE (self),
 		                         NM_DEVICE_STATE_DISCONNECTED,
 		                         NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
-		break;
-	default:
-		break;
+		return;
+	}
+
+	if (old_state == NM_SUPPLICANT_INTERFACE_STATE_STARTING) {
+		supplicant_group_iface_is_ready (self);
+		return;
 	}
 }
 
@@ -833,8 +803,9 @@ supplicant_iface_group_started_cb (NMSupplicantInterface *iface,
                                    NMDeviceWifiP2P *self)
 {
 	NMDeviceWifiP2PPrivate *priv;
+	NMSupplicantInterfaceState state;
 
-	g_return_if_fail (self != NULL);
+	g_return_if_fail (self);
 
 	if (!nm_device_is_activating (NM_DEVICE (self))) {
 		_LOGW (LOGD_DEVICE | LOGD_WIFI, "P2P: WPA supplicant notified a group start but we are not trying to connect! Ignoring the event.");
@@ -844,6 +815,7 @@ supplicant_iface_group_started_cb (NMSupplicantInterface *iface,
 	priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self);
 
 	supplicant_group_interface_release (self);
+
 	priv->group_iface = g_object_ref (group_iface);
 
 	/* We need to wait for the interface to be ready and the group
@@ -862,10 +834,13 @@ supplicant_iface_group_started_cb (NMSupplicantInterface *iface,
 	                  G_CALLBACK (supplicant_group_iface_group_finished_cb),
 	                  self);
 
-	if (nm_supplicant_interface_get_state (priv->group_iface) < NM_SUPPLICANT_INTERFACE_STATE_READY)
+	state = nm_supplicant_interface_get_state (priv->group_iface);
+	if (state == NM_SUPPLICANT_INTERFACE_STATE_STARTING) {
 		_set_is_waiting_for_supplicant (self, TRUE);
+		return;
+	}
 
-	check_group_iface_ready (self);
+	supplicant_group_iface_is_ready (self);
 }
 
 static void
@@ -935,9 +910,8 @@ device_state_changed (NMDevice *device,
 		break;
 	case NM_DEVICE_STATE_UNAVAILABLE:
 		if (   !priv->mgmt_iface
-		    || nm_supplicant_interface_get_state (priv->mgmt_iface) < NM_SUPPLICANT_INTERFACE_STATE_READY)
+		    || !nm_supplicant_interface_state_is_operational (nm_supplicant_interface_get_state (priv->mgmt_iface)))
 			_set_is_waiting_for_supplicant (self, TRUE);
-
 		break;
 	case NM_DEVICE_STATE_NEED_AUTH:
 		/* Disconnect? */
@@ -1084,20 +1058,20 @@ nm_device_wifi_p2p_set_mgmt_iface (NMDeviceWifiP2P *self,
 		goto done;
 
 	_LOGD (LOGD_DEVICE | LOGD_WIFI, "P2P: WPA supplicant management interface changed to %s.",
-	       nm_supplicant_interface_get_object_path (iface));
+	       nm_ref_string_get_str (nm_supplicant_interface_get_object_path (iface)));
 
 	priv->mgmt_iface = g_object_ref (iface);
 
-	g_signal_connect (priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_STATE,
+	g_signal_connect (priv->mgmt_iface,
+	                  NM_SUPPLICANT_INTERFACE_STATE,
 	                  G_CALLBACK (supplicant_iface_state_cb),
 	                  self);
-	g_signal_connect (priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_PEER_UPDATED,
-	                  G_CALLBACK (supplicant_iface_peer_updated_cb),
-	                  self);
-	g_signal_connect (priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_PEER_REMOVED,
-	                  G_CALLBACK (supplicant_iface_peer_removed_cb),
+	g_signal_connect (priv->mgmt_iface,
+	                  NM_SUPPLICANT_INTERFACE_PEER_CHANGED,
+	                  G_CALLBACK (supplicant_iface_peer_changed_cb),
 	                  self);
-	g_signal_connect (priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_GROUP_STARTED,
+	g_signal_connect (priv->mgmt_iface,
+	                  NM_SUPPLICANT_INTERFACE_GROUP_STARTED,
 	                  G_CALLBACK (supplicant_iface_group_started_cb),
 	                  self);
 done:
@@ -1106,8 +1080,7 @@ done:
 	                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
 	_set_is_waiting_for_supplicant (self,
 	                                   !priv->mgmt_iface
-	                                || (  nm_supplicant_interface_get_state (priv->mgmt_iface)
-	                                    < NM_SUPPLICANT_INTERFACE_STATE_READY));
+	                                || !nm_supplicant_interface_state_is_operational (nm_supplicant_interface_get_state (priv->mgmt_iface)));
 }
 
 void
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index 98f9df25..266a8425 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -11,6 +11,8 @@
 #include <netinet/in.h>
 #include <unistd.h>
 
+#include "nm-glib-aux/nm-ref-string.h"
+#include "nm-glib-aux/nm-c-list.h"
 #include "nm-device-wifi-p2p.h"
 #include "nm-wifi-ap.h"
 #include "nm-libnm-core-intern/nm-common-macros.h"
@@ -42,10 +44,9 @@
 #include "devices/nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceWifi);
 
-/* All of these are in seconds */
-#define SCAN_INTERVAL_MIN 3
-#define SCAN_INTERVAL_STEP 20
-#define SCAN_INTERVAL_MAX 120
+#define SCAN_INTERVAL_SEC_MIN   3
+#define SCAN_INTERVAL_SEC_STEP 20
+#define SCAN_INTERVAL_SEC_MAX 120
 
 #define SCAN_RAND_MAC_ADDRESS_EXPIRE_MIN 5
 
@@ -62,7 +63,6 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWifi,
 );
 
 enum {
-	SCANNING_PROHIBITED,
 	P2P_DEVICE_CREATED,
 
 	LAST_SIGNAL
@@ -71,9 +71,10 @@ enum {
 static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct {
-	gint8             invalid_strength_counter;
-
 	CList             aps_lst_head;
+	GHashTable       *aps_idx_by_supplicant_path;
+
+	CList             scanning_prohibited_lst_head;
 
 	NMWifiAP *        current_ap;
 	guint32           rate;
@@ -83,13 +84,15 @@ typedef struct {
 	bool              is_scanning:1;
 	bool              hidden_probe_scan_warn:1;
 
-	gint64            last_scan; /* milliseconds */
+	gint64            last_scan_msec;
+
 	gint32            scheduled_scan_time; /* seconds */
-	guint8            scan_interval; /* seconds */
+	guint8            scan_interval_sec;
 	guint             pending_scan_id;
 	guint             ap_dump_id;
 
 	NMSupplicantManager   *sup_mgr;
+	NMSupplMgrCreateIfaceHandle *sup_create_handle;
 	NMSupplicantInterface *sup_iface;
 	guint                  sup_timeout_id; /* supplicant association timeout */
 
@@ -97,7 +100,7 @@ typedef struct {
 
 	NMActRequestGetSecretsCallId *wifi_secrets_id;
 
-	guint             periodic_source_id;
+	guint             periodic_update_id;
 	guint             link_timeout_id;
 	guint32           failed_iface_count;
 	guint             reacquire_iface_id;
@@ -122,53 +125,46 @@ struct _NMDeviceWifi
 struct _NMDeviceWifiClass
 {
 	NMDeviceClass parent;
-
-	/* Signals */
-	gboolean (*scanning_prohibited) (NMDeviceWifi *device, gboolean periodic);
 };
 
 /*****************************************************************************/
 
 G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE)
 
-#define NM_DEVICE_WIFI_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDeviceWifi, NM_IS_DEVICE_WIFI)
+#define NM_DEVICE_WIFI_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDeviceWifi, NM_IS_DEVICE_WIFI, NMDevice)
 
 /*****************************************************************************/
 
 static gboolean check_scanning_prohibited (NMDeviceWifi *self, gboolean periodic);
 
+static void supplicant_iface_state_down (NMDeviceWifi *self);
+
 static void schedule_scan (NMDeviceWifi *self, gboolean backoff);
 
 static void cleanup_association_attempt (NMDeviceWifi * self,
                                          gboolean disconnect);
 
+static void supplicant_iface_state (NMDeviceWifi *self,
+                                    NMSupplicantInterfaceState new_state,
+                                    NMSupplicantInterfaceState old_state,
+                                    int disconnect_reason,
+                                    gboolean is_real_signal);
+
 static void supplicant_iface_state_cb (NMSupplicantInterface *iface,
                                        int new_state_i,
                                        int old_state_i,
                                        int disconnect_reason,
                                        gpointer user_data);
 
-static void supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
-                                             const char *object_path,
-                                             GVariant *properties,
-                                             NMDeviceWifi *self);
-
-static void supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
-                                             const char *object_path,
+static void supplicant_iface_bss_changed_cb (NMSupplicantInterface *iface,
+                                             NMSupplicantBssInfo *bss_info,
+                                             gboolean is_present,
                                              NMDeviceWifi *self);
 
-static void supplicant_iface_scan_done_cb (NMSupplicantInterface * iface,
-                                           gboolean success,
-                                           NMDeviceWifi * self);
-
 static void supplicant_iface_wps_credentials_cb (NMSupplicantInterface *iface,
                                                  GVariant *credentials,
                                                  NMDeviceWifi *self);
 
-static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface,
-                                                 GParamSpec * pspec,
-                                                 NMDeviceWifi * self);
-
 static void supplicant_iface_notify_current_bss (NMSupplicantInterface *iface,
                                                  GParamSpec *pspec,
                                                  NMDeviceWifi *self);
@@ -177,6 +173,10 @@ static void supplicant_iface_notify_p2p_available (NMSupplicantInterface *iface,
                                                    GParamSpec *pspec,
                                                    NMDeviceWifi *self);
 
+static void _requested_scan_set (NMDeviceWifi *self, gboolean value);
+
+static void periodic_update (NMDeviceWifi *self);
+
 static void request_wireless_scan (NMDeviceWifi *self,
                                    gboolean periodic,
                                    gboolean force_if_scanning,
@@ -193,19 +193,66 @@ static void recheck_p2p_availability (NMDeviceWifi *self);
 
 /*****************************************************************************/
 
+void
+nm_device_wifi_scanning_prohibited_track (NMDeviceWifi *self,
+                                          gpointer tag,
+                                          gboolean temporarily_prohibited)
+{
+	NMDeviceWifiPrivate *priv;
+	NMCListElem *elem;
+
+	g_return_if_fail (NM_IS_DEVICE_WIFI (self));
+	nm_assert (tag);
+
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	/* We track these with a simple CList. This would be not efficient, if
+	 * there would be many users that need to be tracked at the same time (there
+	 * aren't). In fact, most of the time there is no NMDeviceOlpcMesh and
+	 * nobody tracks itself here. Optimize for that and simplicity. */
+
+	elem = nm_c_list_elem_find_first (&priv->scanning_prohibited_lst_head,
+	                                  iter,
+	                                  iter == tag);
+
+	if (!temporarily_prohibited) {
+		if (!elem)
+			return;
+
+		nm_c_list_elem_free (elem);
+		return;
+	}
+
+	if (elem)
+		return;
+
+	c_list_link_tail (&priv->scanning_prohibited_lst_head,
+	                  &nm_c_list_elem_new_stale (tag)->lst);
+}
+
+/*****************************************************************************/
+
 static void
 _ap_dump (NMDeviceWifi *self,
           NMLogLevel log_level,
           const NMWifiAP *ap,
           const char *prefix,
-          gint32 now_s)
+          gint64 now_msec)
 {
 	char buf[1024];
 
 	buf[0] = '\0';
 	_NMLOG (log_level, LOGD_WIFI_SCAN, "wifi-ap: %-7s %s",
 	        prefix,
-	        nm_wifi_ap_to_string (ap, buf, sizeof (buf), now_s));
+	        nm_wifi_ap_to_string (ap, buf, sizeof (buf), now_msec));
+}
+
+gboolean
+nm_device_wifi_get_scanning (NMDeviceWifi *self)
+{
+	g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), FALSE);
+
+	return NM_DEVICE_WIFI_GET_PRIVATE (self)->is_scanning;
 }
 
 static void
@@ -213,6 +260,7 @@ _notify_scanning (NMDeviceWifi *self)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	gboolean scanning;
+	gboolean last_scan_changed = FALSE;
 
 	scanning =    priv->sup_iface
 	           && nm_supplicant_interface_get_scanning (priv->sup_iface);
@@ -220,9 +268,42 @@ _notify_scanning (NMDeviceWifi *self)
 	if (scanning == priv->is_scanning)
 		return;
 
-	_LOGD (LOGD_WIFI, "wifi-scan: scanning-state: %s", scanning ? "scanning" : "idle");
 	priv->is_scanning = scanning;
-	_notify (self, PROP_SCANNING);
+
+	if (   !scanning
+	    || priv->last_scan_msec == 0) {
+		last_scan_changed = TRUE;
+		priv->last_scan_msec = nm_utils_get_monotonic_timestamp_msec ();
+	}
+
+	_LOGD (LOGD_WIFI,
+	       "wifi-scan: scanning-state: %s%s",
+	       scanning ? "scanning" : "idle",
+	       last_scan_changed ? " (notify last-scan)" : "");
+
+	schedule_scan (self, TRUE);
+
+	nm_gobject_notify_together (self,
+	                            PROP_SCANNING,
+	                              last_scan_changed
+	                            ? PROP_LAST_SCAN
+	                            : PROP_0);
+
+	if (!priv->is_scanning) {
+		_requested_scan_set (self, FALSE);
+		if (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED) {
+			/* Run a quick update of current AP when coming out of a scan */
+			periodic_update (self);
+		}
+	}
+}
+
+static void
+supplicant_iface_notify_scanning_cb (NMSupplicantInterface *iface,
+                                     GParamSpec *pspec,
+                                     NMDeviceWifi *self)
+{
+	_notify_scanning (self);
 }
 
 static gboolean
@@ -237,40 +318,40 @@ unmanaged_on_quit (NMDevice *self)
 	return TRUE;
 }
 
-static gboolean
-supplicant_interface_acquire (NMDeviceWifi *self)
+static void
+supplicant_interface_acquire_cb (NMSupplicantManager *supplicant_manager,
+                                 NMSupplMgrCreateIfaceHandle *handle,
+                                 NMSupplicantInterface *iface,
+                                 GError *error,
+                                 gpointer user_data)
 {
+	NMDeviceWifi *self = user_data;
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
-	g_return_val_if_fail (self != NULL, FALSE);
-	g_return_val_if_fail (!priv->sup_iface, TRUE);
+	if (nm_utils_error_is_cancelled (error))
+		return;
 
-	priv->sup_iface = nm_supplicant_manager_create_interface (priv->sup_mgr,
-	                                                          nm_device_get_iface (NM_DEVICE (self)),
-	                                                          NM_SUPPLICANT_DRIVER_WIRELESS);
-	if (!priv->sup_iface) {
-		_LOGE (LOGD_WIFI, "Couldn't initialize supplicant interface");
-		return FALSE;
+	nm_assert (priv->sup_create_handle == handle);
+
+	priv->sup_create_handle = NULL;
+
+	if (error) {
+		_LOGE (LOGD_WIFI, "Couldn't initialize supplicant interface: %s",
+		       error->message);
+		supplicant_iface_state_down (self);
+		nm_device_remove_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE);
+		return;
 	}
 
-	if (nm_supplicant_interface_get_state (priv->sup_iface) < NM_SUPPLICANT_INTERFACE_STATE_READY)
-		nm_device_add_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, FALSE);
+	priv->sup_iface = g_object_ref (iface);
 
 	g_signal_connect (priv->sup_iface,
 	                  NM_SUPPLICANT_INTERFACE_STATE,
 	                  G_CALLBACK (supplicant_iface_state_cb),
 	                  self);
 	g_signal_connect (priv->sup_iface,
-	                  NM_SUPPLICANT_INTERFACE_BSS_UPDATED,
-	                  G_CALLBACK (supplicant_iface_bss_updated_cb),
-	                  self);
-	g_signal_connect (priv->sup_iface,
-	                  NM_SUPPLICANT_INTERFACE_BSS_REMOVED,
-	                  G_CALLBACK (supplicant_iface_bss_removed_cb),
-	                  self);
-	g_signal_connect (priv->sup_iface,
-	                  NM_SUPPLICANT_INTERFACE_SCAN_DONE,
-	                  G_CALLBACK (supplicant_iface_scan_done_cb),
+	                  NM_SUPPLICANT_INTERFACE_BSS_CHANGED,
+	                  G_CALLBACK (supplicant_iface_bss_changed_cb),
 	                  self);
 	g_signal_connect (priv->sup_iface,
 	                  NM_SUPPLICANT_INTERFACE_WPS_CREDENTIALS,
@@ -291,7 +372,30 @@ supplicant_interface_acquire (NMDeviceWifi *self)
 
 	_notify_scanning (self);
 
-	return TRUE;
+	if (nm_supplicant_interface_get_state (priv->sup_iface) != NM_SUPPLICANT_INTERFACE_STATE_STARTING) {
+		/* fake an initial state change. */
+		supplicant_iface_state (user_data,
+		                        NM_SUPPLICANT_INTERFACE_STATE_STARTING,
+		                        nm_supplicant_interface_get_state (priv->sup_iface),
+		                        0,
+		                        FALSE);
+	}
+}
+
+static void
+supplicant_interface_acquire (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	nm_assert (!priv->sup_iface);
+	nm_assert (!priv->sup_create_handle);
+
+	priv->sup_create_handle = nm_supplicant_manager_create_interface (priv->sup_mgr,
+	                                                                  nm_device_get_ifindex (NM_DEVICE (self)),
+	                                                                  NM_SUPPLICANT_DRIVER_WIRELESS,
+	                                                                  supplicant_interface_acquire_cb,
+	                                                                  self);
+	nm_device_add_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE);
 }
 
 static void
@@ -310,27 +414,26 @@ _requested_scan_set (NMDeviceWifi *self, gboolean value)
 		nm_device_add_pending_action ((NMDevice *) self, NM_PENDING_ACTION_WIFI_SCAN, TRUE);
 	else {
 		nm_device_emit_recheck_auto_activate (NM_DEVICE (self));
-		nm_device_remove_pending_action ((NMDevice *) self, NM_PENDING_ACTION_WIFI_SCAN, TRUE);
+		nm_device_remove_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WIFI_SCAN, TRUE);
 	}
 }
 
 static void
 supplicant_interface_release (NMDeviceWifi *self)
 {
-	NMDeviceWifiPrivate *priv;
-
-	g_return_if_fail (self != NULL);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
-	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	if (nm_clear_pointer (&priv->sup_create_handle, nm_supplicant_manager_create_interface_cancel))
+		nm_device_remove_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE);
 
 	_requested_scan_set (self, FALSE);
 
 	nm_clear_g_source (&priv->pending_scan_id);
 
 	/* Reset the scan interval to be pretty frequent when disconnected */
-	priv->scan_interval = SCAN_INTERVAL_MIN + SCAN_INTERVAL_STEP;
+	priv->scan_interval_sec = SCAN_INTERVAL_SEC_MIN + SCAN_INTERVAL_SEC_STEP;
 	_LOGD (LOGD_WIFI, "wifi-scan: reset interval to %u seconds",
-	       (unsigned) priv->scan_interval);
+	       (unsigned) priv->scan_interval_sec);
 
 	nm_clear_g_source (&priv->ap_dump_id);
 
@@ -410,44 +513,48 @@ set_current_ap (NMDeviceWifi *self, NMWifiAP *new_ap, gboolean recheck_available
 static void
 periodic_update (NMDeviceWifi *self)
 {
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	int ifindex = nm_device_get_ifindex (NM_DEVICE (self));
+	NMDeviceWifiPrivate *priv;
+	int ifindex;
 	guint32 new_rate;
-	int percent;
-	NMDeviceState state;
-	NMSupplicantInterfaceState supplicant_state;
 
-	/* BSSID and signal strength have meaningful values only if the device
-	 * is activated and not scanning.
-	 */
-	state = nm_device_get_state (NM_DEVICE (self));
-	if (state != NM_DEVICE_STATE_ACTIVATED)
+	if (nm_device_get_state (NM_DEVICE (self)) != NM_DEVICE_STATE_ACTIVATED) {
+		/* BSSID and signal strength have meaningful values only if the device
+		 * is activated and not scanning.
+		 */
 		return;
+	}
 
-	/* Only update current AP if we're actually talking to something, otherwise
-	 * assume the old one (if any) is still valid until we're told otherwise or
-	 * the connection fails.
-	 */
-	supplicant_state = nm_supplicant_interface_get_state (priv->sup_iface);
-	if (   supplicant_state < NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING
-	    || supplicant_state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED
-	    || nm_supplicant_interface_get_scanning (priv->sup_iface))
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	if (   !nm_supplicant_interface_state_is_associated (nm_supplicant_interface_get_state (priv->sup_iface))
+	    || nm_supplicant_interface_get_scanning (priv->sup_iface)) {
+		/* Only update current AP if we're actually talking to something, otherwise
+		 * assume the old one (if any) is still valid until we're told otherwise or
+		 * the connection fails.
+		 */
 		return;
+	}
 
-	/* In AP mode we currently have nothing to do. */
-	if (priv->mode == NM_802_11_MODE_AP)
+	if (priv->mode == NM_802_11_MODE_AP) {
+		/* In AP mode we currently have nothing to do. */
 		return;
+	}
+
+	ifindex = nm_device_get_ifindex (NM_DEVICE (self));
+	if (ifindex <= 0)
+		g_return_if_reached ();
 
 	if (priv->current_ap) {
-		/* Smooth out the strength to work around crappy drivers */
+		int percent;
+
 		percent = nm_platform_wifi_get_quality (nm_device_get_platform (NM_DEVICE (self)), ifindex);
-		if (percent >= 0 || ++priv->invalid_strength_counter > 3) {
+		if (   percent >= 0
+		    && percent <= 100) {
 			if (nm_wifi_ap_set_strength (priv->current_ap, (gint8) percent)) {
 #if NM_MORE_LOGGING
 				_ap_dump (self, LOGL_TRACE, priv->current_ap, "updated", 0);
 #endif
 			}
-			priv->invalid_strength_counter = 0;
 		}
 	}
 
@@ -461,7 +568,7 @@ periodic_update (NMDeviceWifi *self)
 static gboolean
 periodic_update_cb (gpointer user_data)
 {
-	periodic_update (NM_DEVICE_WIFI (user_data));
+	periodic_update (user_data);
 	return TRUE;
 }
 
@@ -477,12 +584,16 @@ ap_add_remove (NMDeviceWifi *self,
 		g_object_ref (ap);
 		ap->wifi_device = NM_DEVICE (self);
 		c_list_link_tail (&priv->aps_lst_head, &ap->aps_lst);
+		if (!g_hash_table_insert (priv->aps_idx_by_supplicant_path, nm_wifi_ap_get_supplicant_path (ap), ap))
+			nm_assert_not_reached ();
 		nm_dbus_object_export (NM_DBUS_OBJECT (ap));
 		_ap_dump (self, LOGL_DEBUG, ap, "added", 0);
 		nm_device_wifi_emit_signal_access_point (NM_DEVICE (self), ap, TRUE);
 	} else {
 		ap->wifi_device = NULL;
 		c_list_unlink (&ap->aps_lst);
+		if (!g_hash_table_remove (priv->aps_idx_by_supplicant_path, nm_wifi_ap_get_supplicant_path (ap)))
+			nm_assert_not_reached ();
 		_ap_dump (self, LOGL_DEBUG, ap, "removed", 0);
 	}
 
@@ -593,7 +704,7 @@ deactivate (NMDevice *device)
 	int ifindex = nm_device_get_ifindex (device);
 	NM80211Mode old_mode = priv->mode;
 
-	nm_clear_g_source (&priv->periodic_source_id);
+	nm_clear_g_source (&priv->periodic_update_id);
 
 	cleanup_association_attempt (self, TRUE);
 
@@ -694,7 +805,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection, GError
 		}
 
 		if (priv->sup_iface) {
-			if (nm_supplicant_interface_get_ap_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_NO) {
+			if (nm_supplicant_interface_get_capability (priv->sup_iface, NM_SUPPL_CAP_TYPE_AP) == NM_TERNARY_FALSE) {
 				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
 				                            "wpa_supplicant does not support Access Point mode");
 				return FALSE;
@@ -708,7 +819,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection, GError
 		}
 
 		if (priv->sup_iface) {
-			if (nm_supplicant_interface_get_mesh_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_NO) {
+			if (nm_supplicant_interface_get_capability (priv->sup_iface, NM_SUPPL_CAP_TYPE_MESH) == NM_TERNARY_FALSE) {
 				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
 				                            "wpa_supplicant does not support Mesh mode");
 				return FALSE;
@@ -936,7 +1047,7 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 		return FALSE;
 
 	supplicant_state = nm_supplicant_interface_get_state (priv->sup_iface);
-	if (   supplicant_state < NM_SUPPLICANT_INTERFACE_STATE_READY
+	if (   supplicant_state <= NM_SUPPLICANT_INTERFACE_STATE_STARTING
 	    || supplicant_state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED)
 		return FALSE;
 
@@ -1047,7 +1158,7 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset)
 		return;
 	}
 
-	now = nm_utils_get_monotonic_timestamp_s ();
+	now = nm_utils_get_monotonic_timestamp_sec ();
 
 	if (now >= priv->hw_addr_scan_expire) {
 		gs_free char *generate_mac_address_mask = NULL;
@@ -1074,16 +1185,17 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset)
 static GPtrArray *
 ssids_options_to_ptrarray (GVariant *value, GError **error)
 {
-	GPtrArray *ssids = NULL;
-	const guint8 *bytes;
-	gsize len;
-	int num_ssids, i;
+	gs_unref_ptrarray GPtrArray *ssids = NULL;
+	gsize num_ssids;
+	gsize i;
+
+	nm_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("aay")));
 
 	num_ssids = g_variant_n_children (value);
 	if (num_ssids > 32) {
 		g_set_error_literal (error,
 		                     NM_DEVICE_ERROR,
-		                     NM_DEVICE_ERROR_NOT_ALLOWED,
+		                     NM_DEVICE_ERROR_INVALID_ARGUMENT,
 		                     "too many SSIDs requested to scan");
 		return NULL;
 	}
@@ -1092,22 +1204,30 @@ ssids_options_to_ptrarray (GVariant *value, GError **error)
 		ssids = g_ptr_array_new_full (num_ssids, (GDestroyNotify) g_bytes_unref);
 		for (i = 0; i < num_ssids; i++) {
 			gs_unref_variant GVariant *v = NULL;
+			gsize len;
+			const guint8 *bytes;
 
 			v = g_variant_get_child_value (value, i);
 			bytes = g_variant_get_fixed_array (v, &len, sizeof (guint8));
 			if (len > 32) {
 				g_set_error (error,
 				             NM_DEVICE_ERROR,
-				             NM_DEVICE_ERROR_NOT_ALLOWED,
-				             "SSID at index %d more than 32 bytes", i);
-				g_ptr_array_unref (ssids);
+				             NM_DEVICE_ERROR_INVALID_ARGUMENT,
+				             "SSID at index %d more than 32 bytes", (int) i);
 				return NULL;
 			}
 
 			g_ptr_array_add (ssids, g_bytes_new (bytes, len));
 		}
 	}
-	return ssids;
+
+	return g_steal_pointer (&ssids);
+}
+
+GPtrArray *
+nmtst_ssids_options_to_ptrarray (GVariant *value, GError **error)
+{
+	return ssids_options_to_ptrarray (value, error);
 }
 
 static void
@@ -1118,8 +1238,7 @@ dbus_request_scan_cb (NMDevice *device,
                       gpointer user_data)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
-	gs_unref_variant GVariant *scan_options = user_data;
-	gs_unref_ptrarray GPtrArray *ssids = NULL;
+	gs_unref_ptrarray GPtrArray *ssids = user_data;
 
 	if (error) {
 		g_dbus_method_invocation_return_gerror (context, error);
@@ -1134,41 +1253,42 @@ dbus_request_scan_cb (NMDevice *device,
 		return;
 	}
 
-	if (scan_options) {
-		gs_unref_variant GVariant *val = g_variant_lookup_value (scan_options, "ssids", NULL);
+	request_wireless_scan (self, FALSE, FALSE, ssids);
+	g_dbus_method_invocation_return_value (context, NULL);
+}
+
+void
+_nm_device_wifi_request_scan (NMDeviceWifi *self,
+                              GVariant *options,
+                              GDBusMethodInvocation *invocation)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMDevice *device = NM_DEVICE (self);
+	gint64 last_scan;
+	gs_unref_ptrarray GPtrArray *ssids = NULL;
+
+	if (options) {
+		gs_unref_variant GVariant *val = g_variant_lookup_value (options, "ssids", NULL);
 
 		if (val) {
 			gs_free_error GError *ssid_error = NULL;
 
 			if (!g_variant_is_of_type (val, G_VARIANT_TYPE ("aay"))) {
-				g_dbus_method_invocation_return_error_literal (context,
+				g_dbus_method_invocation_return_error_literal (invocation,
 				                                               NM_DEVICE_ERROR,
-				                                               NM_DEVICE_ERROR_NOT_ALLOWED,
+				                                               NM_DEVICE_ERROR_INVALID_ARGUMENT,
 				                                               "Invalid 'ssid' scan option");
 				return;
 			}
 
 			ssids = ssids_options_to_ptrarray (val, &ssid_error);
 			if (ssid_error) {
-				g_dbus_method_invocation_return_gerror (context, ssid_error);
+				g_dbus_method_invocation_return_gerror (invocation, ssid_error);
 				return;
 			}
 		}
 	}
 
-	request_wireless_scan (self, FALSE, FALSE, ssids);
-	g_dbus_method_invocation_return_value (context, NULL);
-}
-
-void
-_nm_device_wifi_request_scan (NMDeviceWifi *self,
-                              GVariant *options,
-                              GDBusMethodInvocation *invocation)
-{
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	NMDevice *device = NM_DEVICE (self);
-	gint64 last_scan;
-
 	if (   !priv->enabled
 	    || !priv->sup_iface
 	    || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED
@@ -1189,7 +1309,13 @@ _nm_device_wifi_request_scan (NMDeviceWifi *self,
 	}
 
 	last_scan = nm_supplicant_interface_get_last_scan (priv->sup_iface);
-	if (last_scan && (nm_utils_get_monotonic_timestamp_ms () - last_scan) < 10 * NM_UTILS_MSEC_PER_SECOND) {
+	if (   last_scan > 0
+	    && nm_utils_get_monotonic_timestamp_msec () < last_scan + (10 * NM_UTILS_MSEC_PER_SEC)) {
+		/* FIXME: we really should not outright reject a scan request in this case. We should
+		 * ensure to start a scan request soon, possibly with rate limiting. And there is no
+		 * need to tell the caller that we aren't going to scan...
+		 *
+		 * Same above, if we are currently scanning... */
 		g_dbus_method_invocation_return_error_literal (invocation,
 		                                               NM_DEVICE_ERROR,
 		                                               NM_DEVICE_ERROR_NOT_ALLOWED,
@@ -1204,16 +1330,19 @@ _nm_device_wifi_request_scan (NMDeviceWifi *self,
 	                       NM_AUTH_PERMISSION_WIFI_SCAN,
 	                       TRUE,
 	                       dbus_request_scan_cb,
-	                       options ? g_variant_ref (options) : NULL);
+	                       g_steal_pointer (&ssids));
 }
 
 static gboolean
-scanning_prohibited (NMDeviceWifi *self, gboolean periodic)
+check_scanning_prohibited (NMDeviceWifi *self,
+                           gboolean periodic)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	NMSupplicantInterfaceState supplicant_state;
 
-	g_return_val_if_fail (priv->sup_iface != NULL, TRUE);
+	nm_assert (NM_IS_SUPPLICANT_INTERFACE (priv->sup_iface));
+
+	if (!c_list_is_empty (&priv->scanning_prohibited_lst_head))
+		return TRUE;
 
 	/* Don't scan when a an AP or Ad-Hoc connection is active as it will
 	 * disrupt connected clients or peers.
@@ -1250,11 +1379,11 @@ scanning_prohibited (NMDeviceWifi *self, gboolean periodic)
 	}
 
 	/* Prohibit scans if the supplicant is busy */
-	supplicant_state = nm_supplicant_interface_get_state (priv->sup_iface);
-	if (   supplicant_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING
-	    || supplicant_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED
-	    || supplicant_state == NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE
-	    || supplicant_state == NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE
+	if (   NM_IN_SET (nm_supplicant_interface_get_state (priv->sup_iface),
+	                  NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING,
+	                  NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED,
+	                  NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE,
+	                  NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE)
 	    || nm_supplicant_interface_get_scanning (priv->sup_iface))
 		return TRUE;
 
@@ -1263,15 +1392,6 @@ scanning_prohibited (NMDeviceWifi *self, gboolean periodic)
 }
 
 static gboolean
-check_scanning_prohibited (NMDeviceWifi *self, gboolean periodic)
-{
-	gboolean prohibited = FALSE;
-
-	g_signal_emit (self, signals[SCANNING_PROHIBITED], 0, periodic, &prohibited);
-	return prohibited;
-}
-
-static gboolean
 hidden_filter_func (NMSettings *settings,
                     NMSettingsConnection *set_con,
                     gpointer user_data)
@@ -1420,16 +1540,17 @@ static void
 schedule_scan (NMDeviceWifi *self, gboolean backoff)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	gint32 now = nm_utils_get_monotonic_timestamp_s ();
+	gint32 now = nm_utils_get_monotonic_timestamp_sec ();
 
-	/* Cancel the pending scan if it would happen later than (now + the scan_interval) */
+	/* Cancel the pending scan if it would happen later than (now + the scan_interval_sec) */
 	if (priv->pending_scan_id) {
-		if (now + priv->scan_interval < priv->scheduled_scan_time)
+		if (now + priv->scan_interval_sec < priv->scheduled_scan_time)
 			nm_clear_g_source (&priv->pending_scan_id);
 	}
 
 	if (!priv->pending_scan_id) {
-		guint factor = 2, next_scan = priv->scan_interval;
+		guint factor = 2;
+		guint next_scan = priv->scan_interval_sec;
 
 		if (    nm_device_is_activating (NM_DEVICE (self))
 		    || (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED))
@@ -1439,41 +1560,25 @@ schedule_scan (NMDeviceWifi *self, gboolean backoff)
 		                                               request_wireless_scan_periodic,
 		                                               self);
 
-		priv->scheduled_scan_time = now + priv->scan_interval;
-		if (backoff && (priv->scan_interval < (SCAN_INTERVAL_MAX / factor))) {
-				priv->scan_interval += (SCAN_INTERVAL_STEP / factor);
+		priv->scheduled_scan_time = now + priv->scan_interval_sec;
+		if (backoff && (priv->scan_interval_sec < (SCAN_INTERVAL_SEC_MAX / factor))) {
+				priv->scan_interval_sec += (SCAN_INTERVAL_SEC_STEP / factor);
 				/* Ensure the scan interval will never be less than 20s... */
-				priv->scan_interval = MAX(priv->scan_interval, SCAN_INTERVAL_MIN + SCAN_INTERVAL_STEP);
+				priv->scan_interval_sec = MAX(priv->scan_interval_sec, SCAN_INTERVAL_SEC_MIN + SCAN_INTERVAL_SEC_STEP);
 				/* ... or more than 120s */
-				priv->scan_interval = MIN(priv->scan_interval, SCAN_INTERVAL_MAX);
-		} else if (!backoff && (priv->scan_interval == 0)) {
+				priv->scan_interval_sec = MIN(priv->scan_interval_sec, SCAN_INTERVAL_SEC_MAX);
+		} else if (!backoff && (priv->scan_interval_sec == 0)) {
 			/* Invalid combination; would cause continual rescheduling of
 			 * the scan and hog CPU.  Reset to something minimally sane.
 			 */
-			priv->scan_interval = 5;
+			priv->scan_interval_sec = 5;
 		}
 
 		_LOGD (LOGD_WIFI, "wifi-scan: scheduled in %d seconds (interval now %d seconds)",
-		       next_scan, priv->scan_interval);
+		       next_scan, priv->scan_interval_sec);
 	}
 }
 
-static void
-supplicant_iface_scan_done_cb (NMSupplicantInterface *iface,
-                               gboolean success,
-                               NMDeviceWifi *self)
-{
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-
-	_LOGD (LOGD_WIFI, "wifi-scan: scan-done callback: %s", success ? "successful" : "failed");
-
-	priv->last_scan = nm_utils_get_monotonic_timestamp_ms ();
-	_notify (self, PROP_LAST_SCAN);
-	schedule_scan (self, success);
-
-	_requested_scan_set (self, FALSE);
-}
-
 /****************************************************************************
  * WPA Supplicant control stuff
  *
@@ -1489,14 +1594,21 @@ ap_list_dump (gpointer user_data)
 
 	if (_LOGD_ENABLED (LOGD_WIFI_SCAN)) {
 		NMWifiAP *ap;
-		gint32 now_s = nm_utils_get_monotonic_timestamp_s ();
-
-		_LOGD (LOGD_WIFI_SCAN, "APs: [now:%u last:%" G_GINT64_FORMAT " next:%u]",
-		       now_s,
-		       priv->last_scan / NM_UTILS_MSEC_PER_SECOND,
+		gint64 now_msec = nm_utils_get_monotonic_timestamp_msec ();
+		char str_buf[100];
+
+		_LOGD (LOGD_WIFI_SCAN, "APs: [now:%u.%03u, last:%s, next:%u]",
+		       (guint) (now_msec / NM_UTILS_MSEC_PER_SEC),
+		       (guint) (now_msec % NM_UTILS_MSEC_PER_SEC),
+		         priv->last_scan_msec > 0
+		       ? nm_sprintf_buf (str_buf,
+		                         "%u.%03u",
+		                         (guint) (priv->last_scan_msec / NM_UTILS_MSEC_PER_SEC),
+		                         (guint) (priv->last_scan_msec % NM_UTILS_MSEC_PER_SEC))
+		       : "-1",
 		       priv->scheduled_scan_time);
 		c_list_for_each_entry (ap, &priv->aps_lst_head, aps_lst)
-			_ap_dump (self, LOGL_DEBUG, ap, "dump", now_s);
+			_ap_dump (self, LOGL_DEBUG, ap, "dump", now_msec);
 	}
 	return G_SOURCE_REMOVE;
 }
@@ -1543,40 +1655,43 @@ try_fill_ssid_for_hidden_ap (NMDeviceWifi *self,
 }
 
 static void
-supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
-                                 const char *object_path,
-                                 GVariant *properties,
+supplicant_iface_bss_changed_cb (NMSupplicantInterface *iface,
+                                 NMSupplicantBssInfo *bss_info,
+                                 gboolean is_present,
                                  NMDeviceWifi *self)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	NMDeviceState state;
-	NMWifiAP *found_ap = NULL;
+	NMWifiAP *found_ap;
 	GBytes *ssid;
 
-	g_return_if_fail (self != NULL);
-	g_return_if_fail (properties != NULL);
-	g_return_if_fail (iface != NULL);
+	found_ap = g_hash_table_lookup (priv->aps_idx_by_supplicant_path, bss_info->bss_path);
 
-	/* Ignore new APs when unavailable, unmanaged, or in AP mode */
-	state = nm_device_get_state (NM_DEVICE (self));
-	if (state <= NM_DEVICE_STATE_UNAVAILABLE)
-		return;
-	if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP)
+	if (!is_present) {
+		if (!found_ap)
+			return;
+		if (found_ap == priv->current_ap) {
+			/* The current AP cannot be removed (to prevent NM indicating that
+			 * it is connected, but to nothing), but it must be removed later
+			 * when the current AP is changed or cleared.  Set 'fake' to
+			 * indicate that this AP is now unknown to the supplicant.
+			 */
+			if (nm_wifi_ap_set_fake (found_ap, TRUE))
+				_ap_dump (self, LOGL_DEBUG, found_ap, "updated", 0);
+		} else {
+			ap_add_remove (self, FALSE, found_ap, TRUE);
+			schedule_ap_list_dump (self);
+		}
 		return;
+	}
 
-	found_ap = nm_wifi_aps_find_by_supplicant_path (&priv->aps_lst_head, object_path);
 	if (found_ap) {
-		if (!nm_wifi_ap_update_from_properties (found_ap, object_path, properties))
+		if (!nm_wifi_ap_update_from_properties (found_ap, bss_info))
 			return;
 		_ap_dump (self, LOGL_DEBUG, found_ap, "updated", 0);
 	} else {
 		gs_unref_object NMWifiAP *ap = NULL;
 
-		ap = nm_wifi_ap_new_from_properties (object_path, properties);
-		if (!ap) {
-			_LOGD (LOGD_WIFI, "invalid AP properties received for %s", object_path);
-			return;
-		}
+		ap = nm_wifi_ap_new_from_properties (bss_info);
 
 		/* Let the manager try to fill in the SSID from seen-bssids lists */
 		ssid = nm_wifi_ap_get_ssid (ap);
@@ -1606,43 +1721,13 @@ supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
 	/* Update the current AP if the supplicant notified a current BSS change
 	 * before it sent the current BSS's scan result.
 	 */
-	if (g_strcmp0 (nm_supplicant_interface_get_current_bss (iface), object_path) == 0)
+	if (nm_supplicant_interface_get_current_bss (iface) == bss_info->bss_path)
 		supplicant_iface_notify_current_bss (priv->sup_iface, NULL, self);
 
 	schedule_ap_list_dump (self);
 }
 
 static void
-supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
-                                 const char *object_path,
-                                 NMDeviceWifi *self)
-{
-	NMDeviceWifiPrivate *priv;
-	NMWifiAP *ap;
-
-	g_return_if_fail (self != NULL);
-	g_return_if_fail (object_path != NULL);
-
-	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	ap = nm_wifi_aps_find_by_supplicant_path (&priv->aps_lst_head, object_path);
-	if (!ap)
-		return;
-
-	if (ap == priv->current_ap) {
-		/* The current AP cannot be removed (to prevent NM indicating that
-		 * it is connected, but to nothing), but it must be removed later
-		 * when the current AP is changed or cleared.  Set 'fake' to
-		 * indicate that this AP is now unknown to the supplicant.
-		 */
-		if (nm_wifi_ap_set_fake (ap, TRUE))
-			_ap_dump (self, LOGL_DEBUG, ap, "updated", 0);
-	} else {
-		ap_add_remove (self, FALSE, ap, TRUE);
-		schedule_ap_list_dump (self);
-	}
-}
-
-static void
 cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
@@ -1725,7 +1810,7 @@ wifi_secrets_cb (NMActRequest *req,
 		return;
 	}
 
-	nm_device_activate_schedule_stage1_device_prepare (device);
+	nm_device_activate_schedule_stage1_device_prepare (device, FALSE);
 }
 
 static void
@@ -1792,7 +1877,7 @@ supplicant_iface_wps_credentials_cb (NMSupplicantInterface *iface,
 	}
 
 	wifi_secrets_cancel (self);
-	nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
+	nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self), FALSE);
 }
 
 static gboolean
@@ -2020,50 +2105,71 @@ reacquire_interface_cb (gpointer user_data)
 }
 
 static void
-supplicant_iface_state_cb (NMSupplicantInterface *iface,
-                           int new_state_i,
-                           int old_state_i,
-                           int disconnect_reason,
-                           gpointer user_data)
+supplicant_iface_state_down (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMDevice *device = NM_DEVICE (self);
+
+	nm_device_queue_recheck_available (device,
+	                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
+	                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+	cleanup_association_attempt (self, FALSE);
+
+	/* If the device is already in UNAVAILABLE state then the state change
+	 * is a NOP and the interface won't be re-acquired in the device state
+	 * change handler.  So ensure we have a new one here so that we're
+	 * ready if the supplicant comes back.
+	 */
+	supplicant_interface_release (self);
+	if (priv->failed_iface_count < 5)
+		priv->reacquire_iface_id = g_timeout_add_seconds (10, reacquire_interface_cb, self);
+	else
+		_LOGI (LOGD_DEVICE | LOGD_WIFI, "supplicant interface keeps failing, giving up");
+}
+
+static void
+supplicant_iface_state (NMDeviceWifi *self,
+                        NMSupplicantInterfaceState new_state,
+                        NMSupplicantInterfaceState old_state,
+                        int disconnect_reason,
+                        gboolean is_real_signal)
 {
-	NMDeviceWifi *self = NM_DEVICE_WIFI (user_data);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (self);
 	NMDeviceState devstate;
 	gboolean scanning;
-	NMSupplicantInterfaceState new_state = new_state_i;
-	NMSupplicantInterfaceState old_state = old_state_i;
-
-	if (new_state == old_state)
-		return;
 
 	_LOGI (LOGD_DEVICE | LOGD_WIFI,
-	       "supplicant interface state: %s -> %s",
+	       "supplicant interface state: %s -> %s%s",
 	       nm_supplicant_interface_state_to_string (old_state),
-	       nm_supplicant_interface_state_to_string (new_state));
+	       nm_supplicant_interface_state_to_string (new_state),
+	       is_real_signal ? "" : " (simulated signal)");
+
+	if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
+		supplicant_iface_state_down (self);
+		goto out;
+	}
 
 	devstate = nm_device_get_state (device);
-	scanning = nm_supplicant_interface_get_scanning (iface);
+	scanning = nm_supplicant_interface_get_scanning (priv->sup_iface);
+
+	if (old_state == NM_SUPPLICANT_INTERFACE_STATE_STARTING) {
+		_LOGD (LOGD_WIFI, "supplicant ready");
+		nm_device_queue_recheck_available (NM_DEVICE (device),
+		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
+		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+		priv->scan_interval_sec = SCAN_INTERVAL_SEC_MIN;
+	}
 
 	/* In these states we know the supplicant is actually talking to something */
 	if (   new_state >= NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING
 	    && new_state <= NM_SUPPLICANT_INTERFACE_STATE_COMPLETED)
 		priv->ssid_found = TRUE;
 
-	if (   old_state < NM_SUPPLICANT_INTERFACE_STATE_READY
-	    && new_state >= NM_SUPPLICANT_INTERFACE_STATE_READY)
+	if (old_state == NM_SUPPLICANT_INTERFACE_STATE_STARTING)
 		recheck_p2p_availability (self);
 
 	switch (new_state) {
-	case NM_SUPPLICANT_INTERFACE_STATE_READY:
-		_LOGD (LOGD_WIFI, "supplicant ready");
-		nm_device_queue_recheck_available (NM_DEVICE (device),
-		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
-		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
-		priv->scan_interval = SCAN_INTERVAL_MIN;
-		if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY)
-			nm_device_remove_pending_action (device, NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE);
-		break;
 	case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED:
 		nm_clear_g_source (&priv->sup_timeout_id);
 		nm_clear_g_source (&priv->link_timeout_id);
@@ -2118,26 +2224,6 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 			}
 		}
 		break;
-	case NM_SUPPLICANT_INTERFACE_STATE_DOWN:
-		nm_device_queue_recheck_available (NM_DEVICE (device),
-		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
-		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
-		cleanup_association_attempt (self, FALSE);
-
-		if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY)
-			nm_device_remove_pending_action (device, NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE);
-
-		/* If the device is already in UNAVAILABLE state then the state change
-		 * is a NOP and the interface won't be re-acquired in the device state
-		 * change handler.  So ensure we have a new one here so that we're
-		 * ready if the supplicant comes back.
-		 */
-		supplicant_interface_release (self);
-		if (priv->failed_iface_count < 5)
-			priv->reacquire_iface_id = g_timeout_add_seconds (10, reacquire_interface_cb, self);
-		else
-			_LOGI (LOGD_DEVICE | LOGD_WIFI, "supplicant interface keeps failing, giving up");
-		break;
 	case NM_SUPPLICANT_INTERFACE_STATE_INACTIVE:
 		/* we would clear _requested_scan_set() and trigger a new scan.
 		 * However, we don't want to cancel the current pending action, so force
@@ -2148,10 +2234,25 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 		break;
 	}
 
-	/* Signal scanning state changes */
-	if (   new_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING
-	    || old_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING)
-		_notify_scanning (self);
+out:
+	_notify_scanning (self);
+
+	if (old_state == NM_SUPPLICANT_INTERFACE_STATE_STARTING)
+		nm_device_remove_pending_action (device, NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE);
+}
+
+static void
+supplicant_iface_state_cb (NMSupplicantInterface *iface,
+                           int new_state_i,
+                           int old_state_i,
+                           int disconnect_reason,
+                           gpointer user_data)
+{
+	supplicant_iface_state (user_data,
+	                        new_state_i,
+	                        old_state_i,
+	                        disconnect_reason,
+	                        TRUE);
 }
 
 static void
@@ -2162,7 +2263,7 @@ supplicant_iface_assoc_cb (NMSupplicantInterface *iface,
 	NMDeviceWifi *self = NM_DEVICE_WIFI (user_data);
 	NMDevice *device = NM_DEVICE (self);
 
-	if (   error && !nm_utils_error_is_cancelled (error, TRUE)
+	if (   error && !nm_utils_error_is_cancelled_or_disposing (error)
 	    && nm_device_is_activating (device)) {
 		cleanup_association_attempt (self, TRUE);
 		nm_device_queue_state (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
@@ -2170,30 +2271,17 @@ supplicant_iface_assoc_cb (NMSupplicantInterface *iface,
 }
 
 static void
-supplicant_iface_notify_scanning_cb (NMSupplicantInterface *iface,
-                                     GParamSpec *pspec,
-                                     NMDeviceWifi *self)
-{
-	_notify_scanning (self);
-
-	/* Run a quick update of current AP when coming out of a scan */
-	if (   !NM_DEVICE_WIFI_GET_PRIVATE (self)->is_scanning
-	    && nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED)
-		periodic_update (self);
-}
-
-static void
 supplicant_iface_notify_current_bss (NMSupplicantInterface *iface,
                                      GParamSpec *pspec,
                                      NMDeviceWifi *self)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	const char *current_bss;
+	NMRefString *current_bss;
 	NMWifiAP *new_ap = NULL;
 
 	current_bss = nm_supplicant_interface_get_current_bss (iface);
 	if (current_bss)
-		new_ap = nm_wifi_aps_find_by_supplicant_path (&priv->aps_lst_head, current_bss);
+		new_ap = g_hash_table_lookup (priv->aps_idx_by_supplicant_path, current_bss);
 
 	if (new_ap != priv->current_ap) {
 		const char *new_bssid = NULL;
@@ -2284,11 +2372,8 @@ supplicant_iface_notify_p2p_available (NMSupplicantInterface *iface,
                                        GParamSpec *pspec,
                                        NMDeviceWifi *self)
 {
-	/* Do not update when the interface is still initializing. */
-	if (nm_supplicant_interface_get_state (iface) < NM_SUPPLICANT_INTERFACE_STATE_READY)
-		return;
-
-	recheck_p2p_availability (self);
+	if (nm_supplicant_interface_get_state (iface) > NM_SUPPLICANT_INTERFACE_STATE_STARTING)
+		recheck_p2p_availability (self);
 }
 
 static gboolean
@@ -2472,15 +2557,11 @@ build_supplicant_config (NMDeviceWifi *self,
 	s_wireless = nm_connection_get_setting_wireless (connection);
 	g_return_val_if_fail (s_wireless != NULL, NULL);
 
-	config = nm_supplicant_config_new (
-		nm_supplicant_interface_get_pmf_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES,
-		nm_supplicant_interface_get_fils_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES,
-		nm_supplicant_interface_get_ft_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES,
-		nm_supplicant_interface_get_sha384_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES);
+	config = nm_supplicant_config_new (nm_supplicant_interface_get_capabilities (priv->sup_iface));
 
 	/* Warn if AP mode may not be supported */
-	if (   g_strcmp0 (nm_setting_wireless_get_mode (s_wireless), NM_SETTING_WIRELESS_MODE_AP) == 0
-	    && nm_supplicant_interface_get_ap_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_UNKNOWN) {
+	if (   nm_streq0 (nm_setting_wireless_get_mode (s_wireless), NM_SETTING_WIRELESS_MODE_AP)
+	    && nm_supplicant_interface_get_capability (priv->sup_iface, NM_SUPPL_CAP_TYPE_AP) != NM_TERNARY_TRUE) {
 		_LOGW (LOGD_WIFI, "Supplicant may not support AP mode; connection may time out.");
 	}
 
@@ -2753,8 +2834,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
-	NMSupplicantConfig *config = NULL;
+	gs_unref_object NMSupplicantConfig *config = NULL;
 	NM80211Mode ap_mode;
 	NMActRequest *req;
 	NMWifiAP *ap;
@@ -2774,15 +2854,14 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	ap = priv->current_ap;
 	if (!ap) {
 		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
-		goto out;
+		goto out_fail;
 	}
+
 	ap_mode = nm_wifi_ap_get_mode (ap);
 
 	connection = nm_act_request_get_applied_connection (req);
-	g_assert (connection);
-
 	s_wireless = nm_connection_get_setting_wireless (connection);
-	g_assert (s_wireless);
+	nm_assert (s_wireless);
 
 	/* If we need secrets, get them */
 	setting_name = nm_connection_need_secrets (connection, NULL);
@@ -2791,13 +2870,12 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 		       "Activation: (wifi) access point '%s' has security, but secrets are required.",
 		       nm_connection_get_id (connection));
 
-		if (handle_auth_or_fail (self, req, FALSE))
-			ret = NM_ACT_STAGE_RETURN_POSTPONE;
-		else {
+		if (!handle_auth_or_fail (self, req, FALSE)) {
 			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS);
-			ret = NM_ACT_STAGE_RETURN_FAILURE;
+			goto out_fail;
 		}
-		goto out;
+
+		return NM_ACT_STAGE_RETURN_POSTPONE;
 	}
 
 	if (!wake_on_wlan_enable (self))
@@ -2830,17 +2908,19 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 
 	/* Build up the supplicant configuration */
 	config = build_supplicant_config (self, connection, nm_wifi_ap_get_freq (ap), &error);
-	if (config == NULL) {
+	if (!config) {
 		_LOGE (LOGD_DEVICE | LOGD_WIFI,
 		       "Activation: (wifi) couldn't build wireless configuration: %s",
 		       error->message);
 		g_clear_error (&error);
 		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
-		goto out;
+		goto out_fail;
 	}
 
-	nm_supplicant_interface_assoc (priv->sup_iface, config,
-	                               supplicant_iface_assoc_cb, self);
+	nm_supplicant_interface_assoc (priv->sup_iface,
+	                               config,
+	                               supplicant_iface_assoc_cb,
+	                               self);
 
 	/* Set up a timeout on the association attempt */
 	timeout = nm_device_get_supplicant_timeout (NM_DEVICE (self));
@@ -2848,25 +2928,16 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	                                              supplicant_connection_timeout_cb,
 	                                              self);
 
-	if (!priv->periodic_source_id)
-		priv->periodic_source_id = g_timeout_add_seconds (6, periodic_update_cb, self);
+	if (!priv->periodic_update_id)
+		priv->periodic_update_id = g_timeout_add_seconds (6, periodic_update_cb, self);
 
 	/* We'll get stage3 started when the supplicant connects */
-	ret = NM_ACT_STAGE_RETURN_POSTPONE;
-
-out:
-	if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
-		cleanup_association_attempt (self, TRUE);
-		wake_on_wlan_restore (self);
-	}
+	return NM_ACT_STAGE_RETURN_POSTPONE;
 
-	if (config) {
-		/* Supplicant interface object refs the config; we no longer care about
-		 * it after this function.
-		 */
-		g_object_unref (config);
-	}
-	return ret;
+out_fail:
+	cleanup_association_attempt (self, TRUE);
+	wake_on_wlan_restore (self);
+	return NM_ACT_STAGE_RETURN_FAILURE;
 }
 
 static NMActStageReturn
@@ -3033,7 +3104,7 @@ activation_success_handler (NMDevice *device)
 	update_seen_bssids_cache (self, priv->current_ap);
 
 	/* Reset scan interval to something reasonable */
-	priv->scan_interval = SCAN_INTERVAL_MIN + (SCAN_INTERVAL_STEP * 2);
+	priv->scan_interval_sec = SCAN_INTERVAL_SEC_MIN + (SCAN_INTERVAL_SEC_STEP * 2);
 }
 
 static void
@@ -3053,10 +3124,9 @@ device_state_changed (NMDevice *device,
 		/* Clean up the supplicant interface because in these states the
 		 * device cannot be used.
 		 */
-		if (priv->sup_iface)
-			supplicant_interface_release (self);
+		supplicant_interface_release (self);
 
-		nm_clear_g_source (&priv->periodic_source_id);
+		nm_clear_g_source (&priv->periodic_update_id);
 
 		cleanup_association_attempt (self, TRUE);
 		cleanup_supplicant_failures (self);
@@ -3095,7 +3165,7 @@ device_state_changed (NMDevice *device,
 		break;
 	case NM_DEVICE_STATE_DISCONNECTED:
 		/* Kick off a scan to get latest results */
-		priv->scan_interval = SCAN_INTERVAL_MIN;
+		priv->scan_interval_sec = SCAN_INTERVAL_SEC_MIN;
 		request_wireless_scan (self, FALSE, FALSE, NULL);
 		break;
 	default:
@@ -3109,7 +3179,7 @@ device_state_changed (NMDevice *device,
 static gboolean
 get_enabled (NMDevice *device)
 {
-	return NM_DEVICE_WIFI_GET_PRIVATE ((NMDeviceWifi *) device)->enabled;
+	return NM_DEVICE_WIFI_GET_PRIVATE (device)->enabled;
 }
 
 static void
@@ -3155,8 +3225,7 @@ set_enabled (NMDevice *device, gboolean enabled)
 
 		/* Re-initialize the supplicant interface and wait for it to be ready */
 		cleanup_supplicant_failures (self);
-		if (priv->sup_iface)
-			supplicant_interface_release (self);
+		supplicant_interface_release (self);
 		supplicant_interface_acquire (self);
 
 		_LOGD (LOGD_WIFI, "enable waiting on supplicant state");
@@ -3251,13 +3320,13 @@ get_property (GObject *object, guint prop_id,
 		nm_dbus_utils_g_value_set_object_path (value, priv->current_ap);
 		break;
 	case PROP_SCANNING:
-		g_value_set_boolean (value, priv->is_scanning);
+		g_value_set_boolean (value, nm_device_wifi_get_scanning (self));
 		break;
 	case PROP_LAST_SCAN:
 		g_value_set_int64 (value,
-		                   priv->last_scan > 0
-		                       ? nm_utils_monotonic_timestamp_as_boottime (priv->last_scan, NM_UTILS_NS_PER_MSEC)
-		                       : (gint64) -1);
+		                     priv->last_scan_msec > 0
+		                   ? nm_utils_monotonic_timestamp_as_boottime (priv->last_scan_msec, NM_UTILS_NSEC_PER_MSEC)
+		                   : (gint64) -1);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -3291,6 +3360,8 @@ nm_device_wifi_init (NMDeviceWifi *self)
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
 	c_list_init (&priv->aps_lst_head);
+	c_list_init (&priv->scanning_prohibited_lst_head);
+	priv->aps_idx_by_supplicant_path = g_hash_table_new (nm_direct_hash, NULL);
 
 	priv->hidden_probe_scan_warn = TRUE;
 	priv->mode = NM_802_11_MODE_INFRA;
@@ -3331,7 +3402,9 @@ dispose (GObject *object)
 	NMDeviceWifi *self = NM_DEVICE_WIFI (object);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
-	nm_clear_g_source (&priv->periodic_source_id);
+	nm_assert (c_list_is_empty (&priv->scanning_prohibited_lst_head));
+
+	nm_clear_g_source (&priv->periodic_update_id);
 
 	wifi_secrets_cancel (self);
 
@@ -3359,6 +3432,9 @@ finalize (GObject *object)
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
 	nm_assert (c_list_is_empty (&priv->aps_lst_head));
+	nm_assert (g_hash_table_size (priv->aps_idx_by_supplicant_path) == 0);
+
+	g_hash_table_unref (priv->aps_idx_by_supplicant_path);
 
 	G_OBJECT_CLASS (nm_device_wifi_parent_class)->finalize (object);
 }
@@ -3406,8 +3482,6 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 
 	device_class->state_changed = device_state_changed;
 
-	klass->scanning_prohibited = scanning_prohibited;
-
 	obj_properties[PROP_MODE] =
 	    g_param_spec_uint (NM_DEVICE_WIFI_MODE, "", "",
 	                       NM_802_11_MODE_UNKNOWN,
@@ -3454,14 +3528,6 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-	signals[SCANNING_PROHIBITED] =
-	    g_signal_new (NM_DEVICE_WIFI_SCANNING_PROHIBITED,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_LAST,
-	                  G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_prohibited),
-	                  NULL, NULL, NULL,
-	                  G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN);
-
 	signals[P2P_DEVICE_CREATED] =
 	    g_signal_new (NM_DEVICE_WIFI_P2P_DEVICE_CREATED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
diff --git a/src/devices/wifi/nm-device-wifi.h b/src/devices/wifi/nm-device-wifi.h
index aaf47143..c9fce4e2 100644
--- a/src/devices/wifi/nm-device-wifi.h
+++ b/src/devices/wifi/nm-device-wifi.h
@@ -24,7 +24,6 @@
 #define NM_DEVICE_WIFI_SCANNING            "scanning"
 #define NM_DEVICE_WIFI_LAST_SCAN           "last-scan"
 
-#define NM_DEVICE_WIFI_SCANNING_PROHIBITED    "scanning-prohibited"
 #define NM_DEVICE_WIFI_P2P_DEVICE_CREATED     "p2p-device-created"
 
 typedef struct _NMDeviceWifi NMDeviceWifi;
@@ -40,4 +39,12 @@ void _nm_device_wifi_request_scan (NMDeviceWifi *self,
                                    GVariant *options,
                                    GDBusMethodInvocation *invocation);
 
+GPtrArray *nmtst_ssids_options_to_ptrarray (GVariant *value, GError **error);
+
+gboolean nm_device_wifi_get_scanning (NMDeviceWifi *self);
+
+void nm_device_wifi_scanning_prohibited_track (NMDeviceWifi *self,
+                                               gpointer tag,
+                                               gboolean temporarily_prohibited);
+
 #endif /* __NETWORKMANAGER_DEVICE_WIFI_H__ */
diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c
index 470cb1c9..39c863a3 100644
--- a/src/devices/wifi/nm-iwd-manager.c
+++ b/src/devices/wifi/nm-iwd-manager.c
@@ -874,7 +874,8 @@ nm_iwd_manager_is_known_network (NMIwdManager *self, const char *name,
 }
 
 GDBusProxy *
-nm_iwd_manager_get_dbus_interface (NMIwdManager *self, const char *path,
+nm_iwd_manager_get_dbus_interface (NMIwdManager *self,
+                                   const char *path,
                                    const char *name)
 {
 	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
index ee7dc236..e427c86f 100644
--- a/src/devices/wifi/nm-wifi-ap.c
+++ b/src/devices/wifi/nm-wifi-ap.c
@@ -10,15 +10,16 @@
 
 #include <stdlib.h>
 
-#include "nm-setting-wireless.h"
-
-#include "nm-wifi-utils.h"
 #include "NetworkManagerUtils.h"
-#include "nm-utils.h"
-#include "nm-core-internal.h"
-#include "platform/nm-platform.h"
 #include "devices/nm-device.h"
+#include "nm-core-internal.h"
 #include "nm-dbus-manager.h"
+#include "nm-glib-aux/nm-ref-string.h"
+#include "nm-setting-wireless.h"
+#include "nm-utils.h"
+#include "nm-wifi-utils.h"
+#include "platform/nm-platform.h"
+#include "supplicant/nm-supplicant-interface.h"
 
 #define PROTO_WPA "wpa"
 #define PROTO_RSN "rsn"
@@ -39,8 +40,6 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMWifiAP,
 );
 
 struct _NMWifiAPPrivate {
-	char *supplicant_path;   /* D-Bus object path of this AP from wpa_supplicant */
-
 	/* Scanned or cached values */
 	GBytes *           ssid;
 	char *             address;
@@ -49,6 +48,9 @@ struct _NMWifiAPPrivate {
 	guint32            freq;        /* Frequency in MHz; ie 2412 (== 2.412 GHz) */
 	guint32            max_bitrate; /* Maximum bitrate of the AP in Kbit/s (ie 54000 Kb/s == 54Mbit/s) */
 
+	gint64             last_seen_msec; /* Timestamp when the AP was seen lastly (in nm_utils_get_monotonic_timestamp_*() scale).
+	                                    * Note that this value might be negative! */
+
 	NM80211ApFlags         flags;      /* General flags */
 	NM80211ApSecurityFlags wpa_flags;  /* WPA-related flags */
 	NM80211ApSecurityFlags rsn_flags;  /* RSN (WPA2) -related flags */
@@ -58,7 +60,6 @@ struct _NMWifiAPPrivate {
 	/* Non-scanned attributes */
 	bool               fake:1;       /* Whether or not the AP is from a scan */
 	bool               hotspot:1;    /* Whether the AP is a local device's hotspot network */
-	gint32             last_seen;    /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */
 };
 
 typedef struct _NMWifiAPPrivate NMWifiAPPrivate;
@@ -73,14 +74,6 @@ G_DEFINE_TYPE (NMWifiAP, nm_wifi_ap, NM_TYPE_DBUS_OBJECT)
 
 /*****************************************************************************/
 
-const char *
-nm_wifi_ap_get_supplicant_path (NMWifiAP *ap)
-{
-	g_return_val_if_fail (NM_IS_WIFI_AP (ap), NULL);
-
-	return NM_WIFI_AP_GET_PRIVATE (ap)->supplicant_path;
-}
-
 GBytes *
 nm_wifi_ap_get_ssid (const NMWifiAP *ap)
 {
@@ -148,11 +141,7 @@ nm_wifi_ap_set_ssid (NMWifiAP *ap, GBytes *ssid)
 static gboolean
 nm_wifi_ap_set_flags (NMWifiAP *ap, NM80211ApFlags flags)
 {
-	NMWifiAPPrivate *priv;
-
-	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
-
-	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+	NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
 	if (priv->flags != flags) {
 		priv->flags = flags;
@@ -165,11 +154,8 @@ nm_wifi_ap_set_flags (NMWifiAP *ap, NM80211ApFlags flags)
 static gboolean
 nm_wifi_ap_set_wpa_flags (NMWifiAP *ap, NM80211ApSecurityFlags flags)
 {
-	NMWifiAPPrivate *priv;
-
-	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+	NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
-	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 	if (priv->wpa_flags != flags) {
 		priv->wpa_flags = flags;
 		_notify (ap, PROP_WPA_FLAGS);
@@ -181,11 +167,8 @@ nm_wifi_ap_set_wpa_flags (NMWifiAP *ap, NM80211ApSecurityFlags flags)
 static gboolean
 nm_wifi_ap_set_rsn_flags (NMWifiAP *ap, NM80211ApSecurityFlags flags)
 {
-	NMWifiAPPrivate *priv;
-
-	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+	NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
-	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 	if (priv->rsn_flags != flags) {
 		priv->rsn_flags = flags;
 		_notify (ap, PROP_RSN_FLAGS);
@@ -203,11 +186,9 @@ nm_wifi_ap_get_address (const NMWifiAP *ap)
 }
 
 static gboolean
-nm_wifi_ap_set_address_bin (NMWifiAP *ap, const guint8 *addr /* ETH_ALEN bytes */)
+nm_wifi_ap_set_address_bin (NMWifiAP *ap, const guint8 addr[static 6 /* ETH_ALEN */])
 {
-	NMWifiAPPrivate *priv;
-
-	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+	NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
 	if (   !priv->address
 	    || !nm_utils_hwaddr_matches (addr, ETH_ALEN, priv->address, -1)) {
@@ -241,16 +222,14 @@ nm_wifi_ap_get_mode (NMWifiAP *ap)
 }
 
 static gboolean
-nm_wifi_ap_set_mode (NMWifiAP *ap, const NM80211Mode mode)
+nm_wifi_ap_set_mode (NMWifiAP *ap, NM80211Mode mode)
 {
-	NMWifiAPPrivate *priv;
-
-	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
-	g_return_val_if_fail (   mode == NM_802_11_MODE_ADHOC
-	                      || mode == NM_802_11_MODE_INFRA
-	                      || mode == NM_802_11_MODE_MESH, FALSE);
+	NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
-	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+	nm_assert (NM_IN_SET (mode, NM_802_11_MODE_UNKNOWN,
+	                            NM_802_11_MODE_ADHOC,
+	                            NM_802_11_MODE_INFRA,
+	                            NM_802_11_MODE_MESH));
 
 	if (priv->mode != mode) {
 		priv->mode = mode;
@@ -277,13 +256,9 @@ nm_wifi_ap_get_strength (NMWifiAP *ap)
 }
 
 gboolean
-nm_wifi_ap_set_strength (NMWifiAP *ap, const gint8 strength)
+nm_wifi_ap_set_strength (NMWifiAP *ap, gint8 strength)
 {
-	NMWifiAPPrivate *priv;
-
-	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
-
-	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+	NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
 	if (priv->strength != strength) {
 		priv->strength = strength;
@@ -303,13 +278,9 @@ nm_wifi_ap_get_freq (NMWifiAP *ap)
 
 gboolean
 nm_wifi_ap_set_freq (NMWifiAP *ap,
-                     const guint32 freq)
+                     guint32 freq)
 {
-	NMWifiAPPrivate *priv;
-
-	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
-
-	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+	NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
 	if (priv->freq != freq) {
 		priv->freq = freq;
@@ -378,16 +349,12 @@ nm_wifi_ap_get_flags (const NMWifiAP *ap)
 }
 
 static gboolean
-nm_wifi_ap_set_last_seen (NMWifiAP *ap, gint32 last_seen)
+nm_wifi_ap_set_last_seen (NMWifiAP *ap, gint32 last_seen_msec)
 {
-	NMWifiAPPrivate *priv;
-
-	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
-
-	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+	NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
-	if (priv->last_seen != last_seen) {
-		priv->last_seen = last_seen;
+	if (priv->last_seen_msec != last_seen_msec) {
+		priv->last_seen_msec = last_seen_msec;
 		_notify (ap, PROP_LAST_SEEN);
 		return TRUE;
 	}
@@ -402,501 +369,53 @@ nm_wifi_ap_get_metered (const NMWifiAP *self)
 
 /*****************************************************************************/
 
-static NM80211ApSecurityFlags
-security_from_vardict (GVariant *security)
-{
-	NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE;
-	const char **array, *tmp;
-
-	g_return_val_if_fail (g_variant_is_of_type (security, G_VARIANT_TYPE_VARDICT), NM_802_11_AP_SEC_NONE);
-
-	if (   g_variant_lookup (security, "KeyMgmt", "^a&s", &array)
-	    && array) {
-		if (g_strv_contains (array, "wpa-psk") ||
-		    g_strv_contains (array, "wpa-ft-psk"))
-			flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK;
-		if (g_strv_contains (array, "wpa-eap") ||
-		    g_strv_contains (array, "wpa-ft-eap") ||
-		    g_strv_contains (array, "wpa-fils-sha256") ||
-		    g_strv_contains (array, "wpa-fils-sha384"))
-			flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X;
-		if (g_strv_contains (array, "sae"))
-			flags |= NM_802_11_AP_SEC_KEY_MGMT_SAE;
-		g_free (array);
-	}
-
-	if (   g_variant_lookup (security, "Pairwise", "^a&s", &array)
-	    && array) {
-		if (g_strv_contains (array, "tkip"))
-			flags |= NM_802_11_AP_SEC_PAIR_TKIP;
-		if (g_strv_contains (array, "ccmp"))
-			flags |= NM_802_11_AP_SEC_PAIR_CCMP;
-		g_free (array);
-	}
-
-	if (g_variant_lookup (security, "Group", "&s", &tmp)) {
-		if (strcmp (tmp, "wep40") == 0)
-			flags |= NM_802_11_AP_SEC_GROUP_WEP40;
-		if (strcmp (tmp, "wep104") == 0)
-			flags |= NM_802_11_AP_SEC_GROUP_WEP104;
-		if (strcmp (tmp, "tkip") == 0)
-			flags |= NM_802_11_AP_SEC_GROUP_TKIP;
-		if (strcmp (tmp, "ccmp") == 0)
-			flags |= NM_802_11_AP_SEC_GROUP_CCMP;
-	}
-
-	return flags;
-}
-
-/*****************************************************************************/
-
-static guint32
-get_max_rate_ht_20 (int mcs)
-{
-	switch (mcs) {
-	case 0:  return 6500000;
-	case 1:
-	case 8:  return 13000000;
-	case 2:
-	case 16: return 19500000;
-	case 3:
-	case 9:
-	case 24: return 26000000;
-	case 4:
-	case 10:
-	case 17: return 39000000;
-	case 5:
-	case 11:
-	case 25: return 52000000;
-	case 6:
-	case 18: return 58500000;
-	case 7:  return 65000000;
-	case 12:
-	case 19:
-	case 26: return 78000000;
-	case 13:
-	case 27: return 104000000;
-	case 14:
-	case 20: return 117000000;
-	case 15: return 130000000;
-	case 21:
-	case 28: return 156000000;
-	case 22: return 175500000;
-	case 23: return 195000000;
-	case 29: return 208000000;
-	case 30: return 234000000;
-	case 31: return 260000000;
-	}
-	return 0;
-}
-
-static guint32
-get_max_rate_ht_40 (int mcs)
-{
-	switch (mcs) {
-	case 0:  return 13500000;
-	case 1:
-	case 8:  return 27000000;
-	case 2:  return 40500000;
-	case 3:
-	case 9:
-	case 24: return 54000000;
-	case 4:
-	case 10:
-	case 17: return 81000000;
-	case 5:
-	case 11:
-	case 25: return 108000000;
-	case 6:
-	case 18: return 121500000;
-	case 7:  return 135000000;
-	case 12:
-	case 19:
-	case 26: return 162000000;
-	case 13:
-	case 27: return 216000000;
-	case 14:
-	case 20: return 243000000;
-	case 15: return 270000000;
-	case 16: return 40500000;
-	case 21:
-	case 28: return 324000000;
-	case 22: return 364500000;
-	case 23: return 405000000;
-	case 29: return 432000000;
-	case 30: return 486000000;
-	case 31: return 540000000;
-	}
-	return 0;
-}
-
-static guint32
-get_max_rate_vht_80_ss1 (int mcs)
-{
-	switch (mcs) {
-	case 0:  return 29300000;
-	case 1:  return 58500000;
-	case 2:  return 87800000;
-	case 3:  return 117000000;
-	case 4:  return 175500000;
-	case 5:  return 234000000;
-	case 6:  return 263300000;
-	case 7:  return 292500000;
-	case 8:  return 351000000;
-	case 9:  return 390000000;
-	}
-	return 0;
-}
-
-static guint32
-get_max_rate_vht_80_ss2 (int mcs)
-{
-	switch (mcs) {
-	case 0:  return 58500000;
-	case 1:  return 117000000;
-	case 2:  return 175500000;
-	case 3:  return 234000000;
-	case 4:  return 351000000;
-	case 5:  return 468000000;
-	case 6:  return 526500000;
-	case 7:  return 585000000;
-	case 8:  return 702000000;
-	case 9:  return 780000000;
-	}
-	return 0;
-}
-
-static guint32
-get_max_rate_vht_80_ss3 (int mcs)
-{
-	switch (mcs) {
-	case 0:  return 87800000;
-	case 1:  return 175500000;
-	case 2:  return 263300000;
-	case 3:  return 351000000;
-	case 4:  return 526500000;
-	case 5:  return 702000000;
-	case 6:  return 0;
-	case 7:  return 877500000;
-	case 8:  return 105300000;
-	case 9:  return 117000000;
-	}
-	return 0;
-}
-
-static guint32
-get_max_rate_vht_160_ss1 (int mcs)
-{
-	switch (mcs) {
-	case 0:  return 58500000;
-	case 1:  return 117000000;
-	case 2:  return 175500000;
-	case 3:  return 234000000;
-	case 4:  return 351000000;
-	case 5:  return 468000000;
-	case 6:  return 526500000;
-	case 7:  return 585000000;
-	case 8:  return 702000000;
-	case 9:  return 780000000;
-	}
-	return 0;
-}
-
-static guint32
-get_max_rate_vht_160_ss2 (int mcs)
-{
-	switch (mcs) {
-	case 0:  return 117000000;
-	case 1:  return 234000000;
-	case 2:  return 351000000;
-	case 3:  return 468000000;
-	case 4:  return 702000000;
-	case 5:  return 936000000;
-	case 6:  return 1053000000;
-	case 7:  return 1170000000;
-	case 8:  return 1404000000;
-	case 9:  return 1560000000;
-	}
-	return 0;
-}
-
-static guint32
-get_max_rate_vht_160_ss3 (int mcs)
-{
-	switch (mcs) {
-	case 0:  return 175500000;
-	case 1:  return 351000000;
-	case 2:  return 526500000;
-	case 3:  return 702000000;
-	case 4:  return 1053000000;
-	case 5:  return 1404000000;
-	case 6:  return 1579500000;
-	case 7:  return 1755000000;
-	case 8:  return 2106000000;
-	case 9:  return 0;
-	}
-	return 0;
-}
-
-static gboolean
-get_max_rate_ht (const guint8 *bytes, guint len, guint32 *out_maxrate)
-{
-	guint32 i;
-	guint8 ht_cap_info;
-	const guint8 *supported_mcs_set;
-	guint32 rate;
-
-	/* http://standards.ieee.org/getieee802/download/802.11-2012.pdf
-	 * https://mrncciew.com/2014/10/19/cwap-ht-capabilities-ie/
-	 */
-
-	if (len != 26)
-		return FALSE;
-
-	ht_cap_info = bytes[0];
-	supported_mcs_set = &bytes[3];
-	*out_maxrate = 0;
-
-	/* Find the maximum supported mcs rate */
-	for (i = 0; i <= 76; i++) {
-		unsigned int mcs_octet = i / 8;
-		unsigned int MCS_RATE_BIT = 1 << i % 8;
-
-		if (supported_mcs_set[mcs_octet] & MCS_RATE_BIT) {
-			/* Check for 40Mhz wide channel support */
-			if (ht_cap_info & (1 << 1))
-				rate = get_max_rate_ht_40 (i);
-			else
-				rate = get_max_rate_ht_20 (i);
-
-			if (rate > *out_maxrate)
-				*out_maxrate = rate;
-		}
-	}
-
-	return TRUE;
-}
-
-static gboolean
-get_max_rate_vht (const guint8 *bytes, guint len, guint32 *out_maxrate)
-{
-	guint32 mcs, m;
-	guint8 vht_cap, tx_map;
-
-	/* https://tda802dot11.blogspot.it/2014/10/vht-capabilities-element-vht.html
-	 * http://chimera.labs.oreilly.com/books/1234000001739/ch03.html#management_frames */
-
-	if (len != 12)
-		return FALSE;
-
-	vht_cap = bytes[0];
-	tx_map = bytes[8];
-
-	/* Check for mcs rates 8 and 9 support */
-	if (tx_map & 0x2a)
-		mcs = 9;
-	else if (tx_map & 0x15)
-		mcs = 8;
-	else
-		mcs = 7;
-
-	/* Check for 160Mhz wide channel support and
-	 * spatial stream support */
-	if (vht_cap & (1 << 2)) {
-		if (tx_map & 0x30)
-			m = get_max_rate_vht_160_ss3 (mcs);
-		else if (tx_map & 0x0C)
-			m = get_max_rate_vht_160_ss2 (mcs);
-		else
-			m = get_max_rate_vht_160_ss1 (mcs);
-	} else {
-		if (tx_map & 0x30)
-			m = get_max_rate_vht_80_ss3 (mcs);
-		else if (tx_map & 0x0C)
-			m = get_max_rate_vht_80_ss2 (mcs);
-		else
-			m = get_max_rate_vht_80_ss1 (mcs);
-	}
-
-	*out_maxrate = m;
-	return TRUE;
-}
-
-/* Management Frame Information Element IDs, ieee80211_eid */
-#define WLAN_EID_HT_CAPABILITY       45
-#define WLAN_EID_VHT_CAPABILITY     191
-#define WLAN_EID_VENDOR_SPECIFIC    221
-
-static void
-parse_ies (const guint8 *bytes, gsize len, guint32 *out_max_rate, gboolean *out_metered)
-{
-	guint8 id, elem_len;
-	guint32 m;
-
-	*out_max_rate = 0;
-	*out_metered = FALSE;
-
-	while (len) {
-		if (len < 2)
-			break;
-
-		id = *bytes++;
-		elem_len = *bytes++;
-		len -= 2;
-
-		if (elem_len > len)
-			break;
-
-		switch (id) {
-		case WLAN_EID_HT_CAPABILITY:
-			if (get_max_rate_ht (bytes, elem_len, &m))
-				*out_max_rate = NM_MAX (*out_max_rate, m);
-			break;
-		case WLAN_EID_VHT_CAPABILITY:
-			if (get_max_rate_vht (bytes, elem_len, &m))
-				*out_max_rate = NM_MAX (*out_max_rate, m);
-			break;
-		case WLAN_EID_VENDOR_SPECIFIC:
-			if (   len == 8
-			    && bytes[0] == 0x00            /* OUI: Microsoft */
-			    && bytes[1] == 0x50
-			    && bytes[2] == 0xf2
-			    && bytes[3] == 0x11)           /* OUI type: Network cost */
-				*out_metered = (bytes[7] > 1); /* Cost level > 1 */
-			break;
-		}
-
-		len -= elem_len;
-		bytes += elem_len;
-	}
-}
-
-/*****************************************************************************/
-
 gboolean
 nm_wifi_ap_update_from_properties (NMWifiAP *ap,
-                                   const char *supplicant_path,
-                                   GVariant *properties)
+                                   const NMSupplicantBssInfo *bss_info)
 {
 	NMWifiAPPrivate *priv;
-	const guint8 *bytes;
-	GVariant *v;
-	gsize len;
-	gsize i;
-	gboolean b = FALSE;
-	const char *s;
-	gint16 i16;
-	guint16 u16;
 	gboolean changed = FALSE;
-	gboolean metered;
-	guint32 max_rate, rate;
 
 	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
-	g_return_val_if_fail (properties, FALSE);
+	g_return_val_if_fail (bss_info, FALSE);
+	nm_assert (NM_IS_REF_STRING (bss_info->bss_path));
 
 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
-	g_object_freeze_notify (G_OBJECT (ap));
-
-	if (g_variant_lookup (properties, "Privacy", "b", &b) && b)
-		changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_PRIVACY);
-
-	v = g_variant_lookup_value (properties, "WPS", G_VARIANT_TYPE_VARDICT);
-	if (v) {
-		if (g_variant_lookup (v, "Type", "&s", &s)) {
-			changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_WPS);
-			if (strcmp (s, "pbc") == 0)
-				changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_WPS_PBC);
-			else if (strcmp (s, "pin") == 0)
-				changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_WPS_PIN);
-		}
-		g_variant_unref (v);
-	}
-
-	if (g_variant_lookup (properties, "Mode", "&s", &s)) {
-		if (!g_strcmp0 (s, "infrastructure"))
-			changed |= nm_wifi_ap_set_mode (ap, NM_802_11_MODE_INFRA);
-		else if (!g_strcmp0 (s, "ad-hoc"))
-			changed |= nm_wifi_ap_set_mode (ap, NM_802_11_MODE_ADHOC);
-		else if (!g_strcmp0 (s, "mesh"))
-			changed |= nm_wifi_ap_set_mode (ap, NM_802_11_MODE_MESH);
-	}
-
-	if (g_variant_lookup (properties, "Signal", "n", &i16))
-		changed |= nm_wifi_ap_set_strength (ap, nm_wifi_utils_level_to_quality (i16));
-
-	if (g_variant_lookup (properties, "Frequency", "q", &u16))
-		changed |= nm_wifi_ap_set_freq (ap, u16);
-
-	v = g_variant_lookup_value (properties, "SSID", G_VARIANT_TYPE_BYTESTRING);
-	if (v) {
-		bytes = g_variant_get_fixed_array (v, &len, 1);
-		len = MIN (32, len);
+	nm_assert (   !ap->_supplicant_path
+	           || ap->_supplicant_path == bss_info->bss_path);
 
-		/* Stupid ieee80211 layer uses <hidden> */
-		if (   bytes
-		    && len
-		    && !(   NM_IN_SET (len, 8, 9)
-		         && memcmp (bytes, "<hidden>", len) == 0)
-		    && !nm_utils_is_empty_ssid (bytes, len)) {
-			/* good */
-		} else
-			len = 0;
-
-		changed |= nm_wifi_ap_set_ssid_arr (ap, bytes, len);
-
-		g_variant_unref (v);
-	}
+	g_object_freeze_notify (G_OBJECT (ap));
 
-	v = g_variant_lookup_value (properties, "BSSID", G_VARIANT_TYPE_BYTESTRING);
-	if (v) {
-		bytes = g_variant_get_fixed_array (v, &len, 1);
-		if (   len == ETH_ALEN
-		    && memcmp (bytes, nm_ip_addr_zero.addr_eth, ETH_ALEN) != 0
-		    && memcmp (bytes, (char[ETH_ALEN]) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, ETH_ALEN) != 0)
-			changed |= nm_wifi_ap_set_address_bin (ap, bytes);
-		g_variant_unref (v);
+	if (!ap->_supplicant_path) {
+		ap->_supplicant_path = nm_ref_string_ref (bss_info->bss_path);
+		changed = TRUE;
 	}
 
-	max_rate = 0;
-	v = g_variant_lookup_value (properties, "Rates", G_VARIANT_TYPE ("au"));
-	if (v) {
-		const guint32 *rates = g_variant_get_fixed_array (v, &len, sizeof (guint32));
+	changed |= nm_wifi_ap_set_flags (ap, bss_info->ap_flags);
+	changed |= nm_wifi_ap_set_mode (ap, bss_info->mode);
+	changed |= nm_wifi_ap_set_strength (ap, bss_info->signal_percent);
+	changed |= nm_wifi_ap_set_freq (ap, bss_info->frequency);
+	changed |= nm_wifi_ap_set_ssid (ap, bss_info->ssid);
 
-		for (i = 0; i < len; i++)
-			max_rate = NM_MAX (max_rate, rates[i]);
-		g_variant_unref (v);
-	}
-	v = g_variant_lookup_value (properties, "IEs", G_VARIANT_TYPE_BYTESTRING);
-	if (v) {
-		bytes = g_variant_get_fixed_array (v, &len, 1);
-		parse_ies (bytes, len, &rate, &metered);
-		max_rate = NM_MAX (max_rate, rate);
-		g_variant_unref (v);
-		priv->metered = metered;
+	if (bss_info->bssid_valid)
+		changed |= nm_wifi_ap_set_address_bin (ap, bss_info->bssid);
+	else {
+		/* we don't actually clear the value. */
 	}
 
-	if (max_rate)
-		changed |= nm_wifi_ap_set_max_bitrate (ap, max_rate / 1000);
+	changed |= nm_wifi_ap_set_max_bitrate (ap, bss_info->max_rate);
 
-	v = g_variant_lookup_value (properties, "WPA", G_VARIANT_TYPE_VARDICT);
-	if (v) {
-		changed |= nm_wifi_ap_set_wpa_flags (ap, priv->wpa_flags | security_from_vardict (v));
-		g_variant_unref (v);
+	if (priv->metered != bss_info->metered) {
+		priv->metered = bss_info->metered;
+		changed = TRUE;
 	}
 
-	v = g_variant_lookup_value (properties, "RSN", G_VARIANT_TYPE_VARDICT);
-	if (v) {
-		changed |= nm_wifi_ap_set_rsn_flags (ap, priv->rsn_flags | security_from_vardict (v));
-		g_variant_unref (v);
-	}
+	changed |= nm_wifi_ap_set_wpa_flags (ap, bss_info->wpa_flags);
+	changed |= nm_wifi_ap_set_rsn_flags (ap, bss_info->rsn_flags);
 
-	if (!priv->supplicant_path) {
-		priv->supplicant_path = g_strdup (supplicant_path);
-		changed = TRUE;
-	}
+	changed |= nm_wifi_ap_set_last_seen (ap, bss_info->last_seen_msec);
 
-	changed |= nm_wifi_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_s ());
 	changed |= nm_wifi_ap_set_fake (ap, FALSE);
 
 	g_object_thaw_notify (G_OBJECT (ap));
@@ -984,20 +503,22 @@ const char *
 nm_wifi_ap_to_string (const NMWifiAP *self,
                       char *str_buf,
                       gulong buf_len,
-                      gint32 now_s)
+                      gint64 now_msec)
 {
 	const NMWifiAPPrivate *priv;
 	const char *supplicant_id = "-";
 	const char *export_path;
 	guint32 chan;
 	gs_free char *ssid_to_free = NULL;
+	char str_buf_ts[100];
 
 	g_return_val_if_fail (NM_IS_WIFI_AP (self), NULL);
 
 	priv = NM_WIFI_AP_GET_PRIVATE (self);
+
 	chan = nm_utils_wifi_freq_to_channel (priv->freq);
-	if (priv->supplicant_path)
-		supplicant_id = strrchr (priv->supplicant_path, '/') ?: supplicant_id;
+	if (self->_supplicant_path)
+		supplicant_id = strrchr (self->_supplicant_path->str, '/') ?: supplicant_id;
 
 	export_path = nm_dbus_object_get_path (NM_DBUS_OBJECT (self));
 	if (export_path)
@@ -1005,8 +526,10 @@ nm_wifi_ap_to_string (const NMWifiAP *self,
 	else
 		export_path = "/";
 
+	nm_utils_get_monotonic_timestamp_msec_cached (&now_msec);
+
 	g_snprintf (str_buf, buf_len,
-	            "%17s %-35s [ %c %3u %3u%% %c%c W:%04X R:%04X ] %3us sup:%s [nm:%s]",
+	            "%17s %-35s [ %c %3u %3u%% %c%c W:%04X R:%04X ] %s sup:%s [nm:%s]",
 	            priv->address ?: "(none)",
 	            (ssid_to_free = _nm_utils_ssid_to_string (priv->ssid)),
 	            (priv->mode == NM_802_11_MODE_ADHOC
@@ -1024,7 +547,12 @@ nm_wifi_ap_to_string (const NMWifiAP *self,
 	            priv->metered ? 'M' : '_',
 	            priv->wpa_flags & 0xFFFF,
 	            priv->rsn_flags & 0xFFFF,
-	            priv->last_seen > 0 ? ((now_s > 0 ? now_s : nm_utils_get_monotonic_timestamp_s ()) - priv->last_seen) : -1,
+	              priv->last_seen_msec != G_MININT64
+	            ? nm_sprintf_buf (str_buf_ts,
+	                              "%3u.%03us",
+	                              (guint) ((now_msec - priv->last_seen_msec) / 1000),
+	                              (guint) ((now_msec - priv->last_seen_msec) % 1000))
+	            : "        ",
 	            supplicant_id,
 	            export_path);
 	return str_buf;
@@ -1177,9 +705,9 @@ get_property (GObject *object, guint prop_id,
 		break;
 	case PROP_LAST_SEEN:
 		g_value_set_int (value,
-		                 priv->last_seen > 0
-		                     ? (int) nm_utils_monotonic_timestamp_as_boottime (priv->last_seen, NM_UTILS_NS_PER_SECOND)
-		                     : -1);
+		                   priv->last_seen_msec != G_MININT64
+		                 ? (int) NM_MAX (nm_utils_monotonic_timestamp_as_boottime (priv->last_seen_msec, NM_UTILS_NSEC_PER_MSEC) / 1000, 1)
+		                 : -1);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1204,26 +732,16 @@ nm_wifi_ap_init (NMWifiAP *self)
 	priv->flags = NM_802_11_AP_FLAGS_NONE;
 	priv->wpa_flags = NM_802_11_AP_SEC_NONE;
 	priv->rsn_flags = NM_802_11_AP_SEC_NONE;
-	priv->last_seen = -1;
+	priv->last_seen_msec = G_MININT64;
 }
 
 NMWifiAP *
-nm_wifi_ap_new_from_properties (const char *supplicant_path, GVariant *properties)
+nm_wifi_ap_new_from_properties (const NMSupplicantBssInfo *bss_info)
 {
 	NMWifiAP *ap;
 
-	g_return_val_if_fail (supplicant_path != NULL, NULL);
-	g_return_val_if_fail (properties != NULL, NULL);
-
-	ap = (NMWifiAP *) g_object_new (NM_TYPE_WIFI_AP, NULL);
-	nm_wifi_ap_update_from_properties (ap, supplicant_path, properties);
-
-	/* ignore APs with invalid or missing BSSIDs */
-	if (!nm_wifi_ap_get_address (ap)) {
-		g_object_unref (ap);
-		return NULL;
-	}
-
+	ap = g_object_new (NM_TYPE_WIFI_AP, NULL);
+	nm_wifi_ap_update_from_properties (ap, bss_info);
 	return ap;
 }
 
@@ -1349,7 +867,7 @@ finalize (GObject *object)
 	nm_assert (!self->wifi_device);
 	nm_assert (c_list_is_empty (&self->aps_lst));
 
-	g_free (priv->supplicant_path);
+	nm_ref_string_unref (self->_supplicant_path);
 	if (priv->ssid)
 		g_bytes_unref (priv->ssid);
 	g_free (priv->address);
@@ -1394,7 +912,8 @@ nm_wifi_ap_class_init (NMWifiAPClass *ap_class)
 	| NM_802_11_AP_SEC_GROUP_CCMP \
 	| NM_802_11_AP_SEC_KEY_MGMT_PSK \
 	| NM_802_11_AP_SEC_KEY_MGMT_802_1X \
-	| NM_802_11_AP_SEC_KEY_MGMT_SAE )
+	| NM_802_11_AP_SEC_KEY_MGMT_SAE \
+	| NM_802_11_AP_SEC_KEY_MGMT_OWE )
 
 	GObjectClass *object_class = G_OBJECT_CLASS (ap_class);
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (ap_class);
@@ -1515,20 +1034,6 @@ nm_wifi_aps_find_first_compatible (const CList *aps_lst_head,
 	return NULL;
 }
 
-NMWifiAP *
-nm_wifi_aps_find_by_supplicant_path (const CList *aps_lst_head, const char *path)
-{
-	NMWifiAP *ap;
-
-	g_return_val_if_fail (path != NULL, NULL);
-
-	c_list_for_each_entry (ap, aps_lst_head, aps_lst) {
-		if (nm_streq0 (path, nm_wifi_ap_get_supplicant_path (ap)))
-			return ap;
-	}
-	return NULL;
-}
-
 /*****************************************************************************/
 
 NMWifiAP *
diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h
index 472dfdf9..1bf4e604 100644
--- a/src/devices/wifi/nm-wifi-ap.h
+++ b/src/devices/wifi/nm-wifi-ap.h
@@ -33,20 +33,21 @@ typedef struct {
 	NMDBusObject parent;
 	NMDevice *wifi_device;
 	CList aps_lst;
+	NMRefString *_supplicant_path;
 	struct _NMWifiAPPrivate *_priv;
 } NMWifiAP;
 
+struct _NMSupplicantBssInfo;
+
 typedef struct _NMWifiAPClass NMWifiAPClass;
 
 GType nm_wifi_ap_get_type (void);
 
-NMWifiAP *   nm_wifi_ap_new_from_properties      (const char *supplicant_path,
-                                                  GVariant *properties);
-NMWifiAP *   nm_wifi_ap_new_fake_from_connection (NMConnection *connection);
+NMWifiAP *nm_wifi_ap_new_from_properties (const struct _NMSupplicantBssInfo *bss_info);
+NMWifiAP *nm_wifi_ap_new_fake_from_connection (NMConnection *connection);
 
-gboolean          nm_wifi_ap_update_from_properties   (NMWifiAP *ap,
-                                                       const char *supplicant_path,
-                                                       GVariant *properties);
+gboolean nm_wifi_ap_update_from_properties (NMWifiAP *ap,
+                                            const struct _NMSupplicantBssInfo *bss_info);
 
 gboolean          nm_wifi_ap_check_compatible         (NMWifiAP *self,
                                                        NMConnection *connection);
@@ -56,7 +57,14 @@ gboolean          nm_wifi_ap_complete_connection      (NMWifiAP *self,
                                                        gboolean lock_bssid,
                                                        GError **error);
 
-const char *      nm_wifi_ap_get_supplicant_path      (NMWifiAP *ap);
+static inline NMRefString *
+nm_wifi_ap_get_supplicant_path (NMWifiAP *ap)
+{
+	g_return_val_if_fail (NM_IS_WIFI_AP (ap), NULL);
+
+	return ap->_supplicant_path;
+}
+
 GBytes           *nm_wifi_ap_get_ssid                 (const NMWifiAP *ap);
 gboolean          nm_wifi_ap_set_ssid_arr             (NMWifiAP *ap,
                                                        const guint8 *ssid,
@@ -86,7 +94,7 @@ gboolean          nm_wifi_ap_get_metered              (const NMWifiAP *self);
 const char       *nm_wifi_ap_to_string                (const NMWifiAP *self,
                                                        char *str_buf,
                                                        gulong buf_len,
-                                                       gint32 now_s);
+                                                       gint64 now_msec);
 
 const char      **nm_wifi_aps_get_paths        (const CList *aps_lst_head,
                                                 gboolean include_without_ssid);
@@ -94,8 +102,6 @@ const char      **nm_wifi_aps_get_paths        (const CList *aps_lst_head,
 NMWifiAP         *nm_wifi_aps_find_first_compatible (const CList *aps_lst_head,
                                                      NMConnection *connection);
 
-NMWifiAP         *nm_wifi_aps_find_by_supplicant_path (const CList *aps_lst_head, const char *path);
-
 NMWifiAP         *nm_wifi_ap_lookup_for_device (NMDevice *device, const char *exported_path);
 
 #endif /* __NM_WIFI_AP_H__ */
diff --git a/src/devices/wifi/nm-wifi-factory.c b/src/devices/wifi/nm-wifi-factory.c
index 821460a5..9b87bbbf 100644
--- a/src/devices/wifi/nm-wifi-factory.c
+++ b/src/devices/wifi/nm-wifi-factory.c
@@ -95,7 +95,7 @@ create_device (NMDeviceFactory *factory,
 	            iface,
 	            NM_PRINT_FMT_QUOTE_STRING (backend),
 	            WITH_IWD ? " (iwd support enabled)" : "");
-	if (!backend || !strcasecmp (backend, "wpa_supplicant")) {
+	if (!backend || !g_ascii_strcasecmp (backend, "wpa_supplicant")) {
 		NMDevice *device;
 		NMDeviceWifiCapabilities capabilities;
 		NM80211Mode mode;
@@ -129,7 +129,7 @@ create_device (NMDeviceFactory *factory,
 		return device;
 	}
 #if WITH_IWD
-	else if (!strcasecmp (backend, "iwd"))
+	else if (!g_ascii_strcasecmp (backend, "iwd"))
 		return nm_device_iwd_new (iface);
 #endif
 
diff --git a/src/devices/wifi/nm-wifi-p2p-peer.c b/src/devices/wifi/nm-wifi-p2p-peer.c
index f8da0046..c58bd95e 100644
--- a/src/devices/wifi/nm-wifi-p2p-peer.c
+++ b/src/devices/wifi/nm-wifi-p2p-peer.c
@@ -9,15 +9,16 @@
 
 #include <stdlib.h>
 
-#include "nm-setting-wireless.h"
-
-#include "nm-wifi-utils.h"
 #include "NetworkManagerUtils.h"
-#include "nm-utils.h"
-#include "nm-core-internal.h"
-#include "platform/nm-platform.h"
 #include "devices/nm-device.h"
+#include "nm-core-internal.h"
 #include "nm-dbus-manager.h"
+#include "nm-glib-aux/nm-ref-string.h"
+#include "nm-setting-wireless.h"
+#include "nm-utils.h"
+#include "nm-wifi-utils.h"
+#include "platform/nm-platform.h"
+#include "supplicant/nm-supplicant-types.h"
 
 /*****************************************************************************/
 
@@ -28,7 +29,6 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMWifiP2PPeer,
 	PROP_MODEL_NUMBER,
 	PROP_SERIAL,
 	PROP_WFD_IES,
-	PROP_GROUPS,
 	PROP_HW_ADDRESS,
 	PROP_STRENGTH,
 	PROP_LAST_SEEN,
@@ -36,7 +36,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMWifiP2PPeer,
 );
 
 struct _NMWifiP2PPeerPrivate {
-	char *supplicant_path;   /* D-Bus object path of this Peer from wpa_supplicant */
+	NMRefString *supplicant_path;   /* D-Bus object path of this Peer from wpa_supplicant */
 
 	/* Scanned or cached values */
 	char *             name;
@@ -55,7 +55,7 @@ struct _NMWifiP2PPeerPrivate {
 	NM80211ApFlags     flags;      /* General flags */
 
 	/* Non-scanned attributes */
-	gint32             last_seen;    /* Timestamp when the Peer was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */
+	gint32             last_seen;    /* Timestamp when the Peer was seen lastly (obtained via nm_utils_get_monotonic_timestamp_sec()) */
 };
 
 typedef struct _NMWifiP2PPeerPrivate NMWifiP2PPeerPrivate;
@@ -151,7 +151,7 @@ nm_wifi_p2p_peer_get_supplicant_path (NMWifiP2PPeer *peer)
 {
 	g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL);
 
-	return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->supplicant_path;
+	return nm_ref_string_get_str (NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->supplicant_path);
 }
 
 const char *
@@ -163,18 +163,12 @@ nm_wifi_p2p_peer_get_name (const NMWifiP2PPeer *peer)
 }
 
 gboolean
-nm_wifi_p2p_peer_set_name (NMWifiP2PPeer *peer, const char *name)
+nm_wifi_p2p_peer_set_name (NMWifiP2PPeer *peer, const char *str)
 {
 	NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer);
-	g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE);
 
-	if (g_strcmp0 (name, priv->name) == 0)
+	if (!nm_utils_strdup_reset (&priv->name, str))
 		return FALSE;
-
-	g_clear_pointer (&priv->name, g_free);
-	if (name)
-		priv->name = g_strdup (name);
-
 	_notify (peer, PROP_NAME);
 	return TRUE;
 }
@@ -188,18 +182,12 @@ nm_wifi_p2p_peer_get_manufacturer (const NMWifiP2PPeer *peer)
 }
 
 gboolean
-nm_wifi_p2p_peer_set_manufacturer (NMWifiP2PPeer *peer, const char *manufacturer)
+nm_wifi_p2p_peer_set_manufacturer (NMWifiP2PPeer *peer, const char *str)
 {
 	NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer);
-	g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE);
 
-	if (g_strcmp0 (manufacturer, priv->manufacturer) == 0)
+	if (!nm_utils_strdup_reset (&priv->manufacturer, str))
 		return FALSE;
-
-	g_clear_pointer (&priv->manufacturer, g_free);
-	if (manufacturer)
-		priv->manufacturer = g_strdup (manufacturer);
-
 	_notify (peer, PROP_MANUFACTURER);
 	return TRUE;
 }
@@ -213,18 +201,12 @@ nm_wifi_p2p_peer_get_model (const NMWifiP2PPeer *peer)
 }
 
 gboolean
-nm_wifi_p2p_peer_set_model (NMWifiP2PPeer *peer, const char *model)
+nm_wifi_p2p_peer_set_model (NMWifiP2PPeer *peer, const char *str)
 {
 	NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer);
-	g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE);
 
-	if (g_strcmp0 (model, priv->model) == 0)
+	if (!nm_utils_strdup_reset (&priv->model, str))
 		return FALSE;
-
-	g_clear_pointer (&priv->model, g_free);
-	if (model)
-		priv->model = g_strdup (model);
-
 	_notify (peer, PROP_MODEL);
 	return TRUE;
 }
@@ -238,18 +220,12 @@ nm_wifi_p2p_peer_get_model_number (const NMWifiP2PPeer *peer)
 }
 
 gboolean
-nm_wifi_p2p_peer_set_model_number (NMWifiP2PPeer *peer, const char *model_number)
+nm_wifi_p2p_peer_set_model_number (NMWifiP2PPeer *peer, const char *str)
 {
 	NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer);
-	g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE);
 
-	if (g_strcmp0 (model_number, priv->model_number) == 0)
+	if (!nm_utils_strdup_reset (&priv->model_number, str))
 		return FALSE;
-
-	g_clear_pointer (&priv->model_number, g_free);
-	if (model_number)
-		priv->model_number = g_strdup (model_number);
-
 	_notify (peer, PROP_MODEL_NUMBER);
 	return TRUE;
 }
@@ -263,18 +239,12 @@ nm_wifi_p2p_peer_get_serial (const NMWifiP2PPeer *peer)
 }
 
 gboolean
-nm_wifi_p2p_peer_set_serial (NMWifiP2PPeer *peer, const char *serial)
+nm_wifi_p2p_peer_set_serial (NMWifiP2PPeer *peer, const char *str)
 {
 	NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer);
-	g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE);
 
-	if (g_strcmp0 (serial, priv->serial) == 0)
+	if (!nm_utils_strdup_reset (&priv->serial, str))
 		return FALSE;
-
-	g_clear_pointer (&priv->serial, g_free);
-	if (serial)
-		priv->serial = g_strdup (serial);
-
 	_notify (peer, PROP_SERIAL);
 	return TRUE;
 }
@@ -291,6 +261,7 @@ gboolean
 nm_wifi_p2p_peer_set_wfd_ies (NMWifiP2PPeer *peer, GBytes *wfd_ies)
 {
 	NMWifiP2PPeerPrivate *priv;
+	gs_unref_bytes GBytes *wfd_ies_old = NULL;
 
 	g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE);
 
@@ -299,7 +270,7 @@ nm_wifi_p2p_peer_set_wfd_ies (NMWifiP2PPeer *peer, GBytes *wfd_ies)
 	if (nm_gbytes_equal0 (priv->wfd_ies, wfd_ies))
 		return FALSE;
 
-	g_bytes_unref (priv->wfd_ies);
+	wfd_ies_old = g_steal_pointer (&priv->wfd_ies);
 	priv->wfd_ies = wfd_ies ? g_bytes_ref (wfd_ies) : NULL;
 
 	_notify (peer, PROP_WFD_IES);
@@ -311,27 +282,7 @@ nm_wifi_p2p_peer_get_groups (const NMWifiP2PPeer *peer)
 {
 	g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL);
 
-	return (const char * const*) NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->groups;
-}
-
-static gboolean
-nm_wifi_p2p_peer_set_groups (NMWifiP2PPeer *peer, const char** groups)
-{
-	NMWifiP2PPeerPrivate *priv;
-
-	g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE);
-	g_return_val_if_fail (groups != NULL, FALSE);
-
-	priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer);
-
-	if (_nm_utils_strv_equal (priv->groups, (char **) groups))
-		return FALSE;
-
-	g_strfreev (priv->groups);
-	priv->groups = g_strdupv ((char**) groups);
-
-	_notify (peer, PROP_GROUPS);
-	return TRUE;
+	return (const char *const*) NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->groups;
 }
 
 const char *
@@ -382,11 +333,7 @@ nm_wifi_p2p_peer_get_strength (NMWifiP2PPeer *peer)
 gboolean
 nm_wifi_p2p_peer_set_strength (NMWifiP2PPeer *peer, const gint8 strength)
 {
-	NMWifiP2PPeerPrivate *priv;
-
-	g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE);
-
-	priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer);
+	NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer);
 
 	if (priv->strength != strength) {
 		priv->strength = strength;
@@ -426,80 +373,42 @@ nm_wifi_p2p_peer_set_last_seen (NMWifiP2PPeer *peer, gint32 last_seen)
 
 gboolean
 nm_wifi_p2p_peer_update_from_properties (NMWifiP2PPeer *peer,
-                                         const char *supplicant_path,
-                                         GVariant *properties)
+                                         const NMSupplicantPeerInfo *peer_info)
 {
 	NMWifiP2PPeerPrivate *priv;
-	const guint8 *bytes;
-	GVariant *v;
-	gsize len;
-	const char *s;
-	const char **sv;
-	gint32 i32;
 	gboolean changed = FALSE;
 
 	g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE);
-	g_return_val_if_fail (properties, FALSE);
+	g_return_val_if_fail (peer_info, FALSE);
+	nm_assert (NM_IS_REF_STRING (peer_info->peer_path));
 
 	priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer);
 
-	g_object_freeze_notify (G_OBJECT (peer));
-
-	if (g_variant_lookup (properties, "level", "i", &i32))
-		changed |= nm_wifi_p2p_peer_set_strength (peer, nm_wifi_utils_level_to_quality (i32));
-
-	if (g_variant_lookup (properties, "DeviceName", "&s", &s))
-		changed |= nm_wifi_p2p_peer_set_name (peer, s);
-
-	if (g_variant_lookup (properties, "Manufacturer", "&s", &s))
-		changed |= nm_wifi_p2p_peer_set_manufacturer (peer, s);
-
-	if (g_variant_lookup (properties, "Model", "&s", &s))
-		changed |= nm_wifi_p2p_peer_set_model (peer, s);
-
-	if (g_variant_lookup (properties, "ModelNumber", "&s", &s))
-		changed |= nm_wifi_p2p_peer_set_model_number (peer, s);
-
-	if (g_variant_lookup (properties, "Serial", "&s", &s))
-		changed |= nm_wifi_p2p_peer_set_serial (peer, s);
+	nm_assert (   !priv->supplicant_path
+	           || priv->supplicant_path == peer_info->peer_path);
 
-	v = g_variant_lookup_value (properties, "DeviceAddress", G_VARIANT_TYPE_BYTESTRING);
-	if (v) {
-		bytes = g_variant_get_fixed_array (v, &len, 1);
-		if (   len == ETH_ALEN
-		    && memcmp (bytes, nm_ip_addr_zero.addr_eth, ETH_ALEN) != 0
-		    && memcmp (bytes, (char[ETH_ALEN]) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, ETH_ALEN) != 0)
-			changed |= nm_wifi_p2p_peer_set_address_bin (peer, bytes);
-		g_variant_unref (v);
-	}
-
-	/* The IEs property contains the WFD R1 subelements */
-	v = g_variant_lookup_value (properties, "IEs", G_VARIANT_TYPE_BYTESTRING);
-	if (v) {
-		gs_unref_bytes GBytes *b = NULL;
-
-		bytes = g_variant_get_fixed_array (v, &len, 1);
-		b = g_bytes_new (bytes, len);
-		changed |= nm_wifi_p2p_peer_set_wfd_ies (peer, b);
-		g_variant_unref (v);
-	}
-
-	v = g_variant_lookup_value (properties, "Groups", G_VARIANT_TYPE_OBJECT_PATH_ARRAY);
-	if (v) {
-		sv = g_variant_get_objv (v, NULL);
-		changed |= nm_wifi_p2p_peer_set_groups (peer, sv);
-		g_free (sv);
-	}
-
-	/*if (max_rate)
-		changed |= nm_wifi_p2p_peer_set_max_bitrate (peer, max_rate / 1000);*/
+	g_object_freeze_notify (G_OBJECT (peer));
 
 	if (!priv->supplicant_path) {
-		priv->supplicant_path = g_strdup (supplicant_path);
+		priv->supplicant_path = nm_ref_string_ref (peer_info->peer_path);
 		changed = TRUE;
 	}
 
-	changed |= nm_wifi_p2p_peer_set_last_seen (peer, nm_utils_get_monotonic_timestamp_s ());
+	changed |= nm_wifi_p2p_peer_set_strength (peer, peer_info->signal_percent);
+	changed |= nm_wifi_p2p_peer_set_name (peer, peer_info->device_name);
+	changed |= nm_wifi_p2p_peer_set_manufacturer (peer, peer_info->manufacturer);
+	changed |= nm_wifi_p2p_peer_set_model (peer, peer_info->model);
+	changed |= nm_wifi_p2p_peer_set_model_number (peer, peer_info->model_number);
+	changed |= nm_wifi_p2p_peer_set_serial (peer, peer_info->serial);
+
+	if (peer_info->address_valid)
+		changed |= nm_wifi_p2p_peer_set_address_bin (peer, peer_info->address);
+	else {
+		/* we don't reset the address. */
+	}
+
+	changed |= nm_wifi_p2p_peer_set_wfd_ies (peer, peer_info->ies);
+	changed |= nm_wifi_p2p_peer_set_last_seen (peer, peer_info->last_seen_msec / 1000u);
 
 	g_object_thaw_notify (G_OBJECT (peer));
 
@@ -521,7 +430,7 @@ nm_wifi_p2p_peer_to_string (const NMWifiP2PPeer *self,
 	priv = NM_WIFI_P2P_PEER_GET_PRIVATE (self);
 
 	if (priv->supplicant_path)
-		supplicant_id = strrchr (priv->supplicant_path, '/') ?: supplicant_id;
+		supplicant_id = strrchr (priv->supplicant_path->str, '/') ?: supplicant_id;
 
 	export_path = nm_dbus_object_get_path (NM_DBUS_OBJECT (self));
 	if (export_path)
@@ -537,7 +446,7 @@ nm_wifi_p2p_peer_to_string (const NMWifiP2PPeer *self,
 	            priv->model,
 	            priv->model_number,
 	            priv->serial,
-	            priv->last_seen > 0 ? ((now_s > 0 ? now_s : nm_utils_get_monotonic_timestamp_s ()) - priv->last_seen) : -1,
+	            priv->last_seen > 0 ? ((now_s > 0 ? now_s : nm_utils_get_monotonic_timestamp_sec ()) - priv->last_seen) : -1,
 	            supplicant_id,
 	            export_path);
 
@@ -601,12 +510,6 @@ get_property (GObject *object, guint prop_id,
 	case PROP_WFD_IES:
 		g_value_take_variant (value, nm_utils_gbytes_to_variant_ay (priv->wfd_ies));
 		break;
-	case PROP_GROUPS:
-		g_value_set_variant (value,
-		                      g_variant_new_strv (   (const char*const*) priv->groups
-		                                          ?: NM_PTRARRAY_EMPTY (const char *),
-		                                          -1));
-		break;
 	case PROP_HW_ADDRESS:
 		g_value_set_string (value, priv->address);
 		break;
@@ -616,7 +519,7 @@ get_property (GObject *object, guint prop_id,
 	case PROP_LAST_SEEN:
 		g_value_set_int (value,
 		                 priv->last_seen > 0
-		                     ? (int) nm_utils_monotonic_timestamp_as_boottime (priv->last_seen, NM_UTILS_NS_PER_SECOND)
+		                     ? (int) nm_utils_monotonic_timestamp_as_boottime (priv->last_seen, NM_UTILS_NSEC_PER_SEC)
 		                     : -1);
 		break;
 	default:
@@ -643,22 +546,14 @@ nm_wifi_p2p_peer_init (NMWifiP2PPeer *self)
 }
 
 NMWifiP2PPeer *
-nm_wifi_p2p_peer_new_from_properties (const char *supplicant_path, GVariant *properties)
+nm_wifi_p2p_peer_new_from_properties (const NMSupplicantPeerInfo *peer_info)
 {
 	NMWifiP2PPeer *peer;
 
-	g_return_val_if_fail (supplicant_path != NULL, NULL);
-	g_return_val_if_fail (properties != NULL, NULL);
-
-	peer = (NMWifiP2PPeer *) g_object_new (NM_TYPE_WIFI_P2P_PEER, NULL);
-	nm_wifi_p2p_peer_update_from_properties (peer, supplicant_path, properties);
-
-	/* ignore peers with invalid or missing address */
-	if (!nm_wifi_p2p_peer_get_address (peer)) {
-		g_object_unref (peer);
-		return NULL;
-	}
+	g_return_val_if_fail (peer_info, NULL);
 
+	peer = g_object_new (NM_TYPE_WIFI_P2P_PEER, NULL);
+	nm_wifi_p2p_peer_update_from_properties (peer, peer_info);
 	return peer;
 }
 
@@ -671,7 +566,7 @@ finalize (GObject *object)
 	nm_assert (!self->wifi_device);
 	nm_assert (c_list_is_empty (&self->peers_lst));
 
-	g_free (priv->supplicant_path);
+	nm_ref_string_unref (priv->supplicant_path);
 	g_free (priv->name);
 	g_free (priv->manufacturer);
 	g_free (priv->model);
@@ -688,6 +583,7 @@ static const NMDBusInterfaceInfoExtended interface_info_p2p_peer = {
 	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
 		NM_DBUS_INTERFACE_WIFI_P2P_PEER,
 		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
+			/* Before 1.24, we wrongly exposed a property "Groups" of type "as". Don't reuse that property name. */
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Flags",        "u",  NM_WIFI_P2P_PEER_FLAGS),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Name",         "s",  NM_WIFI_P2P_PEER_NAME),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Manufacturer", "s",  NM_WIFI_P2P_PEER_MANUFACTURER),
@@ -695,7 +591,6 @@ static const NMDBusInterfaceInfoExtended interface_info_p2p_peer = {
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("ModelNumber",  "s",  NM_WIFI_P2P_PEER_MODEL_NUMBER),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Serial",       "s",  NM_WIFI_P2P_PEER_SERIAL),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("WfdIEs",       "ay", NM_WIFI_P2P_PEER_WFD_IES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Groups",       "as", NM_WIFI_P2P_PEER_GROUPS),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("HwAddress",    "s",  NM_WIFI_P2P_PEER_HW_ADDRESS),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Strength",     "y",  NM_WIFI_P2P_PEER_STRENGTH),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("LastSeen",     "i",  NM_WIFI_P2P_PEER_LAST_SEEN),
@@ -756,12 +651,6 @@ nm_wifi_p2p_peer_class_init (NMWifiP2PPeerClass *klass)
 	                          NULL,
 	                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
-	obj_properties[PROP_GROUPS] =
-	    g_param_spec_variant (NM_WIFI_P2P_PEER_GROUPS, "", "",
-	                          G_VARIANT_TYPE ("as"),
-	                          NULL,
-	                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
-
 	obj_properties[PROP_HW_ADDRESS] =
 	    g_param_spec_string (NM_WIFI_P2P_PEER_HW_ADDRESS, "", "",
 	                         NULL,
diff --git a/src/devices/wifi/nm-wifi-p2p-peer.h b/src/devices/wifi/nm-wifi-p2p-peer.h
index 07f25cc1..59f9c5d2 100644
--- a/src/devices/wifi/nm-wifi-p2p-peer.h
+++ b/src/devices/wifi/nm-wifi-p2p-peer.h
@@ -24,7 +24,6 @@
 #define NM_WIFI_P2P_PEER_MODEL_NUMBER         "model-number"
 #define NM_WIFI_P2P_PEER_SERIAL               "serial"
 #define NM_WIFI_P2P_PEER_WFD_IES              "wfd-ies"
-#define NM_WIFI_P2P_PEER_GROUPS               "groups"
 #define NM_WIFI_P2P_PEER_HW_ADDRESS           "hw-address"
 #define NM_WIFI_P2P_PEER_STRENGTH             "strength"
 #define NM_WIFI_P2P_PEER_LAST_SEEN            "last-seen"
@@ -38,14 +37,14 @@ typedef struct {
 
 typedef struct _NMWifiP2PPeerClass NMWifiP2PPeerClass;
 
+struct _NMSupplicantPeerInfo;
+
 GType nm_wifi_p2p_peer_get_type (void);
 
-NMWifiP2PPeer *   nm_wifi_p2p_peer_new_from_properties      (const char *supplicant_path,
-                                                             GVariant *properties);
+NMWifiP2PPeer *nm_wifi_p2p_peer_new_from_properties (const struct _NMSupplicantPeerInfo *peer_info);
 
-gboolean          nm_wifi_p2p_peer_update_from_properties   (NMWifiP2PPeer *peer,
-                                                             const char *supplicant_path,
-                                                             GVariant *properties);
+gboolean nm_wifi_p2p_peer_update_from_properties (NMWifiP2PPeer *peer,
+                                                  const struct _NMSupplicantPeerInfo *peer_info);
 
 gboolean          nm_wifi_p2p_peer_check_compatible         (NMWifiP2PPeer *self,
                                                              NMConnection *connection);
diff --git a/src/devices/wifi/nm-wifi-utils.c b/src/devices/wifi/nm-wifi-utils.c
index b9b7ec42..0903dc8b 100644
--- a/src/devices/wifi/nm-wifi-utils.c
+++ b/src/devices/wifi/nm-wifi-utils.c
@@ -759,6 +759,12 @@ nm_wifi_utils_complete_connection (GBytes *ap_ssid,
 		              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "sae",
 		              NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open",
 		              NULL);
+	} else if (   (key_mgmt && !strcmp (key_mgmt, "owe"))
+	           || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_OWE)) {
+		g_object_set (s_wsec,
+		              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "owe",
+		              NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open",
+		              NULL);
 	} else if (   (key_mgmt && !strcmp (key_mgmt, "wpa-psk"))
 	           || (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)
 	           || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) {
@@ -780,25 +786,6 @@ nm_wifi_utils_complete_connection (GBytes *ap_ssid,
 	return TRUE;
 }
 
-guint32
-nm_wifi_utils_level_to_quality (int val)
-{
-	if (val < 0) {
-		/* Assume dBm already; rough conversion: best = -40, worst = -100 */
-		val = abs (CLAMP (val, -100, -40) + 40);  /* normalize to 0 */
-		val = 100 - (int) ((100.0 * (double) val) / 60.0);
-	} else if (val > 110 && val < 256) {
-		/* assume old-style WEXT 8-bit unsigned signal level */
-		val -= 256;  /* subtract 256 to convert to dBm */
-		val = abs (CLAMP (val, -100, -40) + 40);  /* normalize to 0 */
-		val = 100 - (int) ((100.0 * (double) val) / 60.0);
-	} else {
-		/* Assume signal is a "quality" percentage */
-	}
-
-	return CLAMP (val, 0, 100);
-}
-
 gboolean
 nm_wifi_utils_is_manf_default_ssid (GBytes *ssid)
 {
diff --git a/src/devices/wifi/nm-wifi-utils.h b/src/devices/wifi/nm-wifi-utils.h
index 982080b9..505baa84 100644
--- a/src/devices/wifi/nm-wifi-utils.h
+++ b/src/devices/wifi/nm-wifi-utils.h
@@ -30,8 +30,6 @@ gboolean nm_wifi_utils_complete_connection (GBytes *ssid,
                                             gboolean lock_bssid,
                                             GError **error);
 
-guint32 nm_wifi_utils_level_to_quality (int val);
-
 gboolean nm_wifi_utils_is_manf_default_ssid (GBytes *ssid);
 
 NMIwdNetworkSecurity nm_wifi_connection_get_iwd_security (NMConnection *connection,
diff --git a/src/devices/wifi/tests/test-devices-wifi.c b/src/devices/wifi/tests/test-devices-wifi.c
index a960e7a2..bde36307 100644
--- a/src/devices/wifi/tests/test-devices-wifi.c
+++ b/src/devices/wifi/tests/test-devices-wifi.c
@@ -6,6 +6,7 @@
 #include "nm-default.h"
 
 #include "devices/wifi/nm-wifi-utils.h"
+#include "devices/wifi/nm-device-wifi.h"
 #include "nm-core-internal.h"
 
 #include "nm-test-utils-core.h"
@@ -1337,6 +1338,60 @@ test_strength_all (void)
 
 /*****************************************************************************/
 
+static void
+do_test_ssids_options_to_ptrarray (const char *const*ssids)
+{
+	GVariantBuilder builder;
+	gs_unref_variant GVariant *variant = NULL;
+	gs_unref_ptrarray GPtrArray *ssids_arr = NULL;
+	gs_free_error GError *error = NULL;
+	gsize len;
+	gsize i;
+
+	g_assert (ssids);
+
+	len = NM_PTRARRAY_LEN (ssids);
+
+	g_variant_builder_init (&builder, G_VARIANT_TYPE ("aay"));
+	for (i = 0; i < len; i++) {
+		const char *ssid = ssids[i];
+
+		g_variant_builder_add (&builder,
+		                       "@ay",
+		                       g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, ssid, strlen (ssid), 1));
+	}
+	variant = g_variant_builder_end (&builder);
+
+	if (nmtst_get_rand_bool ())
+		g_variant_ref_sink (variant);
+
+	ssids_arr = nmtst_ssids_options_to_ptrarray (variant, &error);
+	g_assert (!error);
+	if (len == 0) {
+		g_assert (!ssids_arr);
+		return;
+	}
+	g_assert_cmpint (len, ==, ssids_arr->len);
+	for (i = 0; i < len; i++) {
+		const char *ssid = ssids[i];
+		GBytes *bytes = ssids_arr->pdata[i];
+
+		g_assert (nm_utils_gbytes_equal_mem (bytes,
+		                                     ssid,
+		                                     strlen (ssid)));
+	}
+}
+
+static void
+test_ssids_options_to_ptrarray (void)
+{
+	do_test_ssids_options_to_ptrarray (NM_PTRARRAY_EMPTY (const char *));
+	do_test_ssids_options_to_ptrarray (NM_MAKE_STRV ("ab"));
+	do_test_ssids_options_to_ptrarray (NM_MAKE_STRV ("ab", "cd", "fsdfdsf"));
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int
@@ -1501,5 +1556,7 @@ main (int argc, char **argv)
 	g_test_add_func ("/wifi/strength/all",
 	                 test_strength_all);
 
+	g_test_add_func ("/wifi/ssids_options_to_ptrarray", test_ssids_options_to_ptrarray);
+
 	return g_test_run ();
 }