about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--debian/patches/device-add-get_autoconnect_allowed-virtual-function.patch86
-rw-r--r--debian/patches/device-separately-handle-NMDevice-s-autoconnect-by-user-a.patch232
-rw-r--r--debian/patches/device-wifi-block-autoconnect-while-scanning-is-in-progre.patch57
-rw-r--r--debian/patches/series14
-rw-r--r--debian/patches/supplicant-cleanup-network-when-cancelling-AddNetwork-req.patch153
-rw-r--r--debian/patches/supplicant-delay-SCAN_DONE-signal-until-all-BSS-are-initi.patch326
-rw-r--r--debian/patches/supplicant-merge-NEW_BSS-signal-with-BSS_UPDATED.patch274
-rw-r--r--debian/patches/supplicant-remove-unused-return-value-from-nm_supplicant_.patch107
-rw-r--r--debian/patches/supplicant-rework-nm_supplicant_interface_set_config-to-i.patch922
-rw-r--r--debian/patches/supplicant-trivial-move-code-around.patch167
-rw-r--r--debian/patches/supplicant-use-nm_clear_g_cancellable-helper.patch47
-rw-r--r--debian/patches/wifi-also-show-the-NM-D-Bus-path-for-the-Wi-Fi-AP-in-nm_w.patch51
-rw-r--r--debian/patches/wifi-check-for-invalid-BSSID-in-nm_wifi_ap_update_from_pr.patch120
-rw-r--r--debian/patches/wifi-introduce-enum-type-NMSupplicantInterfaceState-inste.patch375
-rw-r--r--debian/patches/wifi-only-react-on-AP-update-signal-when-there-are-actual.patch552
15 files changed, 3483 insertions, 0 deletions
diff --git a/debian/patches/device-add-get_autoconnect_allowed-virtual-function.patch b/debian/patches/device-add-get_autoconnect_allowed-virtual-function.patch
new file mode 100644
index 00000000..edeb7b02
--- /dev/null
+++ b/debian/patches/device-add-get_autoconnect_allowed-virtual-function.patch
@@ -0,0 +1,86 @@
+From: Thomas Haller <thaller@redhat.com>
+Date: Tue, 14 Feb 2017 12:45:38 +0100
+Subject: device: add get_autoconnect_allowed() virtual function
+
+It allows derived classes to override the autoconnect-allowed
+state.
+
+We already have
+
+- NM_DEVICE_AUTOCONNECT property, which is two parts:
+  - NMDevicePrivate::autoconnect_user, which is settable via
+    D-Bus by the use, to allow the device to autoconnect.
+  - NMDevicePrivate::autoconnect_intern, which is set by
+    internal decision.
+- NM_DEVICE_AUTOCONNECT_ALLOWED signal, where other devices can
+  subscribe to block autoconnect. Currently that is only used
+  by NMDeviceOlpcMesh.
+
+These two make up for nm_device_autoconnect_allowed().
+
+Add another way to allow derived classes to disable autoconnect
+temporarily. This could also be achieved by having the device
+subscribe to NM_DEVICE_AUTOCONNECT_ALLOWED of self, or by adding
+a signal slot. But a plain function pointer seems easier.
+
+(cherry picked from commit 6eaded9071fbf868476255adb8ee5f416e7ad134)
+---
+ src/devices/nm-device.c | 11 ++++++++++-
+ src/devices/nm-device.h |  5 +++++
+ 2 files changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
+index 7f015c3..96fa9af 100644
+--- a/src/devices/nm-device.c
++++ b/src/devices/nm-device.c
+@@ -3445,6 +3445,12 @@ nm_device_set_autoconnect_both (NMDevice *self, gboolean autoconnect)
+ }
+ 
+ static gboolean
++get_autoconnect_allowed (NMDevice *self)
++{
++	return TRUE;
++}
++
++static gboolean
+ autoconnect_allowed_accumulator (GSignalInvocationHint *ihint,
+                                  GValue *return_accu,
+                                  const GValue *handler_return, gpointer data)
+@@ -3466,10 +3472,12 @@ gboolean
+ nm_device_autoconnect_allowed (NMDevice *self)
+ {
+ 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
++	NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self);
+ 	GValue instance = G_VALUE_INIT;
+ 	GValue retval = G_VALUE_INIT;
+ 
+-	if (!nm_device_get_autoconnect (self))
++	if (   !nm_device_get_autoconnect (self)
++	    || !klass->get_autoconnect_allowed (self))
+ 		return FALSE;
+ 
+ 	/* Unrealized devices can always autoconnect. */
+@@ -13499,6 +13507,7 @@ nm_device_class_init (NMDeviceClass *klass)
+ 	klass->have_any_ready_slaves = have_any_ready_slaves;
+ 
+ 	klass->get_type_description = get_type_description;
++	klass->get_autoconnect_allowed = get_autoconnect_allowed;
+ 	klass->can_auto_connect = can_auto_connect;
+ 	klass->check_connection_compatible = check_connection_compatible;
+ 	klass->check_connection_available = check_connection_available;
+diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
+index 1b239c2..eb527cc 100644
+--- a/src/devices/nm-device.h
++++ b/src/devices/nm-device.h
+@@ -243,6 +243,11 @@ typedef struct {
+ 
+ 	void        (* set_enabled) (NMDevice *self, gboolean enabled);
+ 
++	/* allow derived classes to override the result of nm_device_autoconnect_allowed().
++	 * If the value changes, the class should call nm_device_emit_recheck_auto_activate(),
++	 * which emits NM_DEVICE_RECHECK_AUTO_ACTIVATE signal. */
++	gboolean    (* get_autoconnect_allowed) (NMDevice *self);
++
+ 	gboolean    (* can_auto_connect) (NMDevice *self,
+ 	                                  NMConnection *connection,
+ 	                                  char **specific_object);
diff --git a/debian/patches/device-separately-handle-NMDevice-s-autoconnect-by-user-a.patch b/debian/patches/device-separately-handle-NMDevice-s-autoconnect-by-user-a.patch
new file mode 100644
index 00000000..f2b52ebf
--- /dev/null
+++ b/debian/patches/device-separately-handle-NMDevice-s-autoconnect-by-user-a.patch
@@ -0,0 +1,232 @@
+From: Thomas Haller <thaller@redhat.com>
+Date: Tue, 14 Feb 2017 13:57:57 +0100
+Subject: device: separately handle NMDevice's autoconnect by user and
+ internal decision
+
+The NMDevice's autoconnect property is settable via D-Bus and is is
+also modified by internal decision, like when no PIN is available.
+
+Certain internal actions cause clearing the internal autoconnect flag,
+but they should not override the user desicion.
+
+For example, when NM awaks from sleep it would reenable autoconnect,
+but it should not reenable it for devices where the user explicitly
+said that autoconnect is to be disabled.
+
+Similarly, activating a device alone is not yet an instruction to
+re-enable autoconnect. If the user consciously disables autoconnect,
+it should stay enabled. On the other hand, activating a device should
+reenable autoconnect if it was blocked by internal decision.
+
+We need to track these two flags separately, and set them accordingly.
+
+(cherry picked from commit 2f9166e6b9c18af4801fd5cc6c01f7eaaeb9d538)
+---
+ src/devices/bluetooth/nm-device-bt.c |  2 +-
+ src/devices/nm-device.c              | 56 +++++++++++++++++++++++++-----------
+ src/devices/nm-device.h              |  2 +-
+ src/devices/wwan/nm-device-modem.c   |  2 +-
+ src/nm-manager.c                     |  2 +-
+ 5 files changed, 44 insertions(+), 20 deletions(-)
+
+diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c
+index 31b9bbf..1266070 100644
+--- a/src/devices/bluetooth/nm-device-bt.c
++++ b/src/devices/bluetooth/nm-device-bt.c
+@@ -483,8 +483,8 @@ modem_prepare_result (NMModem *modem,
+ 			 * the device to be auto-activated anymore, which would risk locking
+ 			 * the SIM if the incorrect PIN continues to be used.
+ 			 */
+-			nm_device_set_autoconnect (device, FALSE);
+ 			_LOGI (LOGD_MB, "disabling autoconnect due to failed SIM PIN");
++			nm_device_set_autoconnect_intern (device, FALSE);
+ 		}
+ 
+ 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
+index 2a2d276..7f015c3 100644
+--- a/src/devices/nm-device.c
++++ b/src/devices/nm-device.c
+@@ -416,7 +416,8 @@ typedef struct _NMDevicePrivate {
+ 	gboolean needs_ip6_subnet;
+ 
+ 	/* allow autoconnect feature */
+-	bool autoconnect;
++	bool autoconnect_intern:1;
++	bool autoconnect_user:1;
+ 
+ 	/* master interface for bridge/bond/team slave */
+ 	NMDevice *      master;
+@@ -482,6 +483,9 @@ static NMActStageReturn linklocal6_start (NMDevice *self);
+ 
+ static void _carrier_wait_check_queued_act_request (NMDevice *self);
+ 
++static void nm_device_set_autoconnect_both (NMDevice *self, gboolean autoconnect);
++static void nm_device_set_autoconnect_full (NMDevice *self, int autoconnect_intern, int autoconnect_user);
++
+ static const char *_activation_func_to_string (ActivationHandleFunc func);
+ static void activation_source_handle_cb (NMDevice *self, int family);
+ 
+@@ -2652,6 +2656,8 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
+ 	if (real_rate)
+ 		priv->stats.timeout_id = g_timeout_add (real_rate, _stats_timeout_cb, self);
+ 
++	nm_device_set_autoconnect_full (self, !!DEFAULT_AUTOCONNECT, TRUE);
++
+ 	klass->realize_start_notify (self, plink);
+ 
+ 	/* Do not manage externally created software devices until they are IFF_UP
+@@ -2846,7 +2852,7 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error)
+ 	priv->real = FALSE;
+ 	_notify (self, PROP_REAL);
+ 
+-	nm_device_set_autoconnect (self, DEFAULT_AUTOCONNECT);
++	nm_device_set_autoconnect_both (self, FALSE);
+ 
+ 	g_object_thaw_notify (G_OBJECT (self));
+ 
+@@ -3398,25 +3404,44 @@ nm_device_set_enabled (NMDevice *self, gboolean enabled)
+ gboolean
+ nm_device_get_autoconnect (NMDevice *self)
+ {
++	NMDevicePrivate *priv;
++
+ 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+ 
+-	return NM_DEVICE_GET_PRIVATE (self)->autoconnect;
++	priv = NM_DEVICE_GET_PRIVATE (self);
++	return priv->autoconnect_intern && priv->autoconnect_user;
+ }
+ 
+-void
+-nm_device_set_autoconnect (NMDevice *self, gboolean autoconnect)
++static void
++nm_device_set_autoconnect_full (NMDevice *self, int autoconnect_intern, int autoconnect_user)
+ {
+ 	NMDevicePrivate *priv;
++	gboolean old_value;
+ 
+ 	g_return_if_fail (NM_IS_DEVICE (self));
+ 
+-	autoconnect = !!autoconnect;
+-
+ 	priv = NM_DEVICE_GET_PRIVATE (self);
+-	if (priv->autoconnect != autoconnect) {
+-		priv->autoconnect = autoconnect;
++
++	old_value = nm_device_get_autoconnect (self);
++	if (autoconnect_intern != -1)
++		priv->autoconnect_intern = autoconnect_intern;
++	if (autoconnect_user != -1)
++		priv->autoconnect_user = autoconnect_user;
++	if (old_value != nm_device_get_autoconnect (self))
+ 		_notify (self, PROP_AUTOCONNECT);
+-	}
++}
++
++void
++nm_device_set_autoconnect_intern (NMDevice *self, gboolean autoconnect)
++{
++	nm_device_set_autoconnect_full (self, !!autoconnect, -1);
++}
++
++static void
++nm_device_set_autoconnect_both (NMDevice *self, gboolean autoconnect)
++{
++	autoconnect = !!autoconnect;
++	nm_device_set_autoconnect_full (self, autoconnect, autoconnect);
+ }
+ 
+ static gboolean
+@@ -3444,7 +3469,7 @@ nm_device_autoconnect_allowed (NMDevice *self)
+ 	GValue instance = G_VALUE_INIT;
+ 	GValue retval = G_VALUE_INIT;
+ 
+-	if (!priv->autoconnect)
++	if (!nm_device_get_autoconnect (self))
+ 		return FALSE;
+ 
+ 	/* Unrealized devices can always autoconnect. */
+@@ -8832,7 +8857,7 @@ disconnect_cb (NMDevice *self,
+ 		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DISCONNECT, self, FALSE, subject, local->message);
+ 		g_dbus_method_invocation_take_error (context, local);
+ 	} else {
+-		nm_device_set_autoconnect (self, FALSE);
++		nm_device_set_autoconnect_intern (self, FALSE);
+ 
+ 		nm_device_state_changed (self,
+ 		                         NM_DEVICE_STATE_DEACTIVATING,
+@@ -11996,7 +12021,7 @@ _set_state_full (NMDevice *self,
+ 	/* Reset autoconnect flag when the device is activating or connected. */
+ 	if (   state >= NM_DEVICE_STATE_PREPARE
+ 	    && state <= NM_DEVICE_STATE_ACTIVATED)
+-		nm_device_set_autoconnect (self, TRUE);
++		nm_device_set_autoconnect_intern  (self, TRUE);
+ 
+ 	_notify (self, PROP_STATE);
+ 	_notify (self, PROP_STATE_REASON);
+@@ -12950,7 +12975,6 @@ nm_device_init (NMDevice *self)
+ 	priv->state_reason = NM_DEVICE_STATE_REASON_NONE;
+ 	priv->dhcp_timeout = 0;
+ 	priv->rfkill_type = RFKILL_TYPE_UNKNOWN;
+-	priv->autoconnect = DEFAULT_AUTOCONNECT;
+ 	priv->unmanaged_flags = NM_UNMANAGED_PLATFORM_INIT;
+ 	priv->unmanaged_mask = priv->unmanaged_flags;
+ 	priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
+@@ -13222,7 +13246,7 @@ set_property (GObject *object, guint prop_id,
+ 		}
+ 		break;
+ 	case PROP_AUTOCONNECT:
+-		nm_device_set_autoconnect (self, g_value_get_boolean (value));
++		nm_device_set_autoconnect_both (self, g_value_get_boolean (value));
+ 		break;
+ 	case PROP_FIRMWARE_MISSING:
+ 		/* construct-only */
+@@ -13348,7 +13372,7 @@ get_property (GObject *object, guint prop_id,
+ 		g_value_set_boolean (value, nm_device_get_state (self) > NM_DEVICE_STATE_UNMANAGED);
+ 		break;
+ 	case PROP_AUTOCONNECT:
+-		g_value_set_boolean (value, priv->autoconnect);
++		g_value_set_boolean (value, nm_device_get_autoconnect (self));
+ 		break;
+ 	case PROP_FIRMWARE_MISSING:
+ 		g_value_set_boolean (value, priv->firmware_missing);
+diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
+index e512f2c..1b239c2 100644
+--- a/src/devices/nm-device.h
++++ b/src/devices/nm-device.h
+@@ -577,7 +577,7 @@ gboolean nm_device_unrealize          (NMDevice *device,
+                                        GError **error);
+ 
+ gboolean nm_device_get_autoconnect (NMDevice *device);
+-void nm_device_set_autoconnect (NMDevice *device, gboolean autoconnect);
++void nm_device_set_autoconnect_intern (NMDevice *device, gboolean autoconnect);
+ void nm_device_emit_recheck_auto_activate (NMDevice *device);
+ 
+ void nm_device_state_changed (NMDevice *device,
+diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
+index 7a70a0b..ee263d9 100644
+--- a/src/devices/wwan/nm-device-modem.c
++++ b/src/devices/wwan/nm-device-modem.c
+@@ -128,7 +128,7 @@ modem_prepare_result (NMModem *modem,
+ 			 * the device to be auto-activated anymore, which would risk locking
+ 			 * the SIM if the incorrect PIN continues to be used.
+ 			 */
+-			nm_device_set_autoconnect (device, FALSE);
++			nm_device_set_autoconnect_intern (device, FALSE);
+ 			_LOGI (LOGD_MB, "disabling autoconnect due to failed SIM PIN");
+ 		}
+ 
+diff --git a/src/nm-manager.c b/src/nm-manager.c
+index f433c32..cb3a679 100644
+--- a/src/nm-manager.c
++++ b/src/nm-manager.c
+@@ -4272,7 +4272,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
+ 					nm_device_set_enabled (device, enabled);
+ 			}
+ 
+-			nm_device_set_autoconnect (device, TRUE);
++			nm_device_set_autoconnect_intern (device, TRUE);
+ 
+ 			nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_SLEEPING, FALSE, NM_DEVICE_STATE_REASON_NOW_MANAGED);
+ 		}
diff --git a/debian/patches/device-wifi-block-autoconnect-while-scanning-is-in-progre.patch b/debian/patches/device-wifi-block-autoconnect-while-scanning-is-in-progre.patch
new file mode 100644
index 00000000..3f5a1cc3
--- /dev/null
+++ b/debian/patches/device-wifi-block-autoconnect-while-scanning-is-in-progre.patch
@@ -0,0 +1,57 @@
+From: Thomas Haller <thaller@redhat.com>
+Date: Tue, 14 Feb 2017 15:10:36 +0100
+Subject: device/wifi: block autoconnect while scanning is in progress
+
+We should only start autoconnecting after the scan is complete.
+Otherwise, we might activate a shared connection or pick a
+connection based on an incomplete scan list.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=770938
+(cherry picked from commit 2ab2254dd7336b9b7baa03ea1eb1f1c72f7ab6a8)
+---
+ src/devices/wifi/nm-device-wifi.c | 17 ++++++++++++++++-
+ 1 file changed, 16 insertions(+), 1 deletion(-)
+
+diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
+index 6085b37..944139f 100644
+--- a/src/devices/wifi/nm-device-wifi.c
++++ b/src/devices/wifi/nm-device-wifi.c
+@@ -295,8 +295,10 @@ _requested_scan_set (NMDeviceWifi *self, gboolean value)
+ 	priv->requested_scan = value;
+ 	if (value)
+ 		nm_device_add_pending_action ((NMDevice *) self, NM_PENDING_ACTION_WIFI_SCAN, TRUE);
+-	else
++	else {
++		nm_device_emit_recheck_auto_activate (NM_DEVICE (self));
+ 		nm_device_remove_pending_action ((NMDevice *) self, NM_PENDING_ACTION_WIFI_SCAN, TRUE);
++	}
+ }
+ 
+ static void
+@@ -964,6 +966,18 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
+ }
+ 
+ static gboolean
++get_autoconnect_allowed (NMDevice *device)
++{
++	NMDeviceWifiPrivate *priv;
++
++	if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->get_autoconnect_allowed (device))
++		return FALSE;
++
++	priv = NM_DEVICE_WIFI_GET_PRIVATE (NM_DEVICE_WIFI (device));
++	return !priv->requested_scan;
++}
++
++static gboolean
+ can_auto_connect (NMDevice *device,
+                   NMConnection *connection,
+                   char **specific_object)
+@@ -3184,6 +3198,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
+ 	object_class->finalize = finalize;
+ 
+ 	parent_class->can_auto_connect = can_auto_connect;
++	parent_class->get_autoconnect_allowed = get_autoconnect_allowed;
+ 	parent_class->is_available = is_available;
+ 	parent_class->check_connection_compatible = check_connection_compatible;
+ 	parent_class->check_connection_available = check_connection_available;
diff --git a/debian/patches/series b/debian/patches/series
index 534c7f63..7df065a4 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -6,3 +6,17 @@ systemd-Don-t-enable-NetworkManager-wait-online.serv.patch
 Fix-iscsiadm-path.patch
 libnm-disconnect-signal-from-D-Bus-proxies-on-dispose.patch
 dhcp-dhclient-parse-interface-statements.patch
+wifi-introduce-enum-type-NMSupplicantInterfaceState-inste.patch
+supplicant-rework-nm_supplicant_interface_set_config-to-i.patch
+supplicant-trivial-move-code-around.patch
+supplicant-use-nm_clear_g_cancellable-helper.patch
+supplicant-remove-unused-return-value-from-nm_supplicant_.patch
+supplicant-cleanup-network-when-cancelling-AddNetwork-req.patch
+supplicant-merge-NEW_BSS-signal-with-BSS_UPDATED.patch
+wifi-check-for-invalid-BSSID-in-nm_wifi_ap_update_from_pr.patch
+supplicant-delay-SCAN_DONE-signal-until-all-BSS-are-initi.patch
+wifi-only-react-on-AP-update-signal-when-there-are-actual.patch
+wifi-also-show-the-NM-D-Bus-path-for-the-Wi-Fi-AP-in-nm_w.patch
+device-separately-handle-NMDevice-s-autoconnect-by-user-a.patch
+device-add-get_autoconnect_allowed-virtual-function.patch
+device-wifi-block-autoconnect-while-scanning-is-in-progre.patch
diff --git a/debian/patches/supplicant-cleanup-network-when-cancelling-AddNetwork-req.patch b/debian/patches/supplicant-cleanup-network-when-cancelling-AddNetwork-req.patch
new file mode 100644
index 00000000..50ca92db
--- /dev/null
+++ b/debian/patches/supplicant-cleanup-network-when-cancelling-AddNetwork-req.patch
@@ -0,0 +1,153 @@
+From: Thomas Haller <thaller@redhat.com>
+Date: Wed, 15 Feb 2017 10:14:54 +0100
+Subject: supplicant: cleanup network when cancelling "AddNetwork" request
+
+If the assoc-request is cancelled while an "AddNetwork" request is
+pending, we must cleanup the added network when the request succeeds.
+
+The issue can also happen when NetworkManager shuts down and exits
+the mainloop. This scenario is unsolved as the cleanup action
+"RemoveNetwork" has no chance to run.
+
+"AddBlob" works differently in that blogs are added with a specific name,
+not like "AddNetwork", where a new D-Bus path is created. Maybe we should
+also cleanup blobs that were added by us, but currently we don't.
+
+(cherry picked from commit c47026715ec6a9612752f469358a7251aabbdee7)
+---
+ src/supplicant/nm-supplicant-interface.c | 58 ++++++++++++++++++++++++++++----
+ 1 file changed, 52 insertions(+), 6 deletions(-)
+
+diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c
+index ebedad6..44bd27a 100644
+--- a/src/supplicant/nm-supplicant-interface.c
++++ b/src/supplicant/nm-supplicant-interface.c
+@@ -39,15 +39,24 @@
+ 
+ /*****************************************************************************/
+ 
++struct _AddNetworkData;
++
+ typedef struct {
++	NMSupplicantInterface *self;
+ 	NMSupplicantConfig *cfg;
+ 	GCancellable *cancellable;
+ 	NMSupplicantInterfaceAssocCb callback;
+ 	gpointer user_data;
+ 	guint fail_on_idle_id;
+ 	guint blobs_left;
++	struct _AddNetworkData *add_network_data;
+ } AssocData;
+ 
++typedef struct _AddNetworkData {
++	/* the assoc_data at the time when doing the call. */
++	AssocData *assoc_data;
++} AddNetworkData;
++
+ enum {
+ 	STATE,               /* change in the interface's state */
+ 	REMOVED,             /* interface was removed by the supplicant */
+@@ -1032,6 +1041,11 @@ assoc_return (NMSupplicantInterface *self, GError *error, const char *message)
+ 	} else
+ 		_LOGD ("assoc[%p]: association request successful", assoc_data);
+ 
++	if (assoc_data->add_network_data) {
++		/* signal that this request already completed */
++		assoc_data->add_network_data->assoc_data = NULL;
++	}
++
+ 	nm_clear_g_source (&assoc_data->fail_on_idle_id);
+ 	nm_clear_g_cancellable (&assoc_data->cancellable);
+ 
+@@ -1153,6 +1167,8 @@ assoc_add_blob_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+ static void
+ assoc_add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+ {
++	AddNetworkData *add_network_data = user_data;
++	AssocData *assoc_data;
+ 	NMSupplicantInterface *self;
+ 	NMSupplicantInterfacePrivate *priv;
+ 	gs_unref_variant GVariant *reply = NULL;
+@@ -1162,17 +1178,40 @@ assoc_add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_dat
+ 	const char *blob_name;
+ 	GByteArray *blob_data;
+ 
++	assoc_data = add_network_data->assoc_data;
++	if (assoc_data)
++		assoc_data->add_network_data = NULL;
++	g_slice_free (AddNetworkData, add_network_data);
++
+ 	reply = _nm_dbus_proxy_call_finish (proxy, result,
+ 	                                    G_VARIANT_TYPE ("(o)"),
+ 	                                    &error);
+-	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
++
++	if (!assoc_data) {
++		if (!error) {
++			gs_free char *net_path = NULL;
++
++			/* the assoc-request was already cancelled, but the AddNetwork request succeeded.
++			 * Cleanup the created network.
++			 *
++			 * This cleanup action does not work when NetworkManager is about to exit
++			 * and leaves the mainloop. During program shutdown, we may orphan networks. */
++			g_variant_get (reply, "(o)", &net_path);
++			g_dbus_proxy_call (proxy,
++			                   "RemoveNetwork",
++			                   g_variant_new ("(o)", net_path),
++			                   G_DBUS_CALL_FLAGS_NONE,
++			                   -1,
++			                   NULL,
++			                   NULL,
++			                   NULL);
++		}
+ 		return;
++	}
+ 
+-	self = NM_SUPPLICANT_INTERFACE (user_data);
++	self = NM_SUPPLICANT_INTERFACE (assoc_data->self);
+ 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ 
+-	nm_clear_g_free (&priv->net_path);
+-
+ 	if (error) {
+ 		assoc_return (self, error, "failure to add network");
+ 		return;
+@@ -1213,6 +1252,7 @@ assoc_set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_dat
+ 	NMSupplicantInterfacePrivate *priv;
+ 	gs_unref_variant GVariant *reply = NULL;
+ 	gs_free_error GError *error = NULL;
++	AddNetworkData *add_network_data;
+ 
+ 	reply = g_dbus_proxy_call_finish (proxy, result, &error);
+ 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+@@ -1230,14 +1270,19 @@ assoc_set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_dat
+ 	       priv->assoc_data,
+ 	       nm_supplicant_config_get_ap_scan (priv->assoc_data->cfg));
+ 
++	add_network_data = g_slice_new0 (AddNetworkData);
++	priv->assoc_data->add_network_data = add_network_data;
++
++	add_network_data->assoc_data = priv->assoc_data;
++
+ 	g_dbus_proxy_call (priv->iface_proxy,
+ 	                   "AddNetwork",
+ 	                   g_variant_new ("(@a{sv})", nm_supplicant_config_to_variant (priv->assoc_data->cfg)),
+ 	                   G_DBUS_CALL_FLAGS_NONE,
+ 	                   -1,
+-	                   priv->assoc_data->cancellable,
++	                   NULL,
+ 	                   (GAsyncReadyCallback) assoc_add_network_cb,
+-	                   self);
++	                   add_network_data);
+ }
+ 
+ static gboolean
+@@ -1287,6 +1332,7 @@ nm_supplicant_interface_assoc (NMSupplicantInterface *self,
+ 	assoc_data = g_slice_new0 (AssocData);
+ 	priv->assoc_data = assoc_data;
+ 
++	assoc_data->self = self;
+ 	assoc_data->cfg = g_object_ref (cfg);
+ 	assoc_data->callback = callback;
+ 	assoc_data->user_data = user_data;
diff --git a/debian/patches/supplicant-delay-SCAN_DONE-signal-until-all-BSS-are-initi.patch b/debian/patches/supplicant-delay-SCAN_DONE-signal-until-all-BSS-are-initi.patch
new file mode 100644
index 00000000..8a83aadf
--- /dev/null
+++ b/debian/patches/supplicant-delay-SCAN_DONE-signal-until-all-BSS-are-initi.patch
@@ -0,0 +1,326 @@
+From: Thomas Haller <thaller@redhat.com>
+Date: Wed, 15 Feb 2017 11:05:47 +0100
+Subject: supplicant: delay SCAN_DONE signal until all BSS are initialized
+
+We initialize the BSS asyncronously. Don't declare SCAN_DONE
+until all BSS are up.
+
+Otherwise, especially during the very first scan we declare SCAN_DONE
+when having no BSS yet. This wrongly removes the pending action
+"wifi-scan", while "autoconnect" cannot happen as there are not BSS
+yet. Thus we declare "startup-complete" too early.
+
+Another issue is that we may start autoconnecting with an incomplete
+scan list, and thus pick a non-preferred connections.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=777831
+(cherry picked from commit e0f96770188eeaada70a299bd6dab7a50ec34a53)
+---
+ src/supplicant/nm-supplicant-interface.c | 166 +++++++++++++++++++++----------
+ 1 file changed, 113 insertions(+), 53 deletions(-)
+
+diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c
+index 7942bec..eb22733 100644
+--- a/src/supplicant/nm-supplicant-interface.c
++++ b/src/supplicant/nm-supplicant-interface.c
+@@ -39,6 +39,11 @@
+ 
+ /*****************************************************************************/
+ 
++typedef struct {
++	GDBusProxy *proxy;
++	gulong change_id;
++} BssData;
++
+ struct _AddNetworkData;
+ 
+ typedef struct {
+@@ -90,7 +95,10 @@ typedef struct {
+ 	NMSupplicantInterfaceState state;
+ 	int            disconnect_reason;
+ 
+-	gboolean       scanning;
++	gboolean       scanning:1;
++
++	bool           scan_done_pending:1;
++	bool           scan_done_success:1;
+ 
+ 	GDBusProxy *   wpas_proxy;
+ 	GCancellable * init_cancellable;
+@@ -142,6 +150,10 @@ G_DEFINE_TYPE (NMSupplicantInterface, nm_supplicant_interface, G_TYPE_OBJECT)
+ 
+ /*****************************************************************************/
+ 
++static void scan_done_emit_signal (NMSupplicantInterface *self);
++
++/*****************************************************************************/
++
+ NM_UTILS_LOOKUP_STR_DEFINE (nm_supplicant_interface_state_to_string, NMSupplicantInterfaceState,
+ 	NM_UTILS_LOOKUP_DEFAULT_WARN ("unknown"),
+ 	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INVALID,         "invalid"),
+@@ -164,10 +176,20 @@ NM_UTILS_LOOKUP_STR_DEFINE (nm_supplicant_interface_state_to_string, NMSupplican
+ /*****************************************************************************/
+ 
+ static void
+-bss_props_changed_cb (GDBusProxy *proxy,
+-                      GVariant *changed_properties,
+-                      char **invalidated_properties,
+-                      gpointer user_data)
++bss_data_destroy (gpointer user_data)
++{
++	BssData *bss_data = user_data;
++
++	nm_clear_g_signal_handler (bss_data->proxy, &bss_data->change_id);
++	g_object_unref (bss_data->proxy);
++	g_slice_free (BssData, bss_data);
++}
++
++static void
++bss_proxy_properties_changed_cb (GDBusProxy *proxy,
++                                 GVariant *changed_properties,
++                                 char **invalidated_properties,
++                                 gpointer user_data)
+ {
+ 	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
+ 	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+@@ -181,63 +203,75 @@ bss_props_changed_cb (GDBusProxy *proxy,
+ }
+ 
+ static GVariant *
+-_get_bss_proxy_properties (NMSupplicantInterface *self, GDBusProxy *proxy)
++bss_proxy_get_properties (NMSupplicantInterface *self, GDBusProxy *proxy)
+ {
+ 	gs_strfreev char **properties = NULL;
+ 	GVariantBuilder builder;
+ 	char **iter;
+ 
+ 	iter = properties = g_dbus_proxy_get_cached_property_names (proxy);
+-	if (!iter)
+-		return NULL;
+ 
+ 	g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+-	while (*iter) {
+-		GVariant *copy = g_dbus_proxy_get_cached_property (proxy, *iter);
++	if (iter) {
++		while (*iter) {
++			GVariant *copy = g_dbus_proxy_get_cached_property (proxy, *iter);
+ 
+-		g_variant_builder_add (&builder, "{sv}", *iter++, copy);
+-		g_variant_unref (copy);
++			g_variant_builder_add (&builder, "{sv}", *iter++, copy);
++			g_variant_unref (copy);
++		}
+ 	}
+-
+ 	return g_variant_builder_end (&builder);
+ }
+ 
+ #define BSS_PROXY_INITED "bss-proxy-inited"
+ 
+ static void
+-on_bss_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
++bss_proxy_acquired_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+ {
+ 	NMSupplicantInterface *self;
++	NMSupplicantInterfacePrivate *priv;
+ 	gs_free_error GError *error = NULL;
+-	gs_unref_variant GVariant *props = NULL;
++	GVariant *props = NULL;
++	const char *object_path;
++	BssData *bss_data;
+ 
+-	if (!g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error)) {
+-		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+-			self = NM_SUPPLICANT_INTERFACE (user_data);
+-			_LOGD ("failed to acquire BSS proxy: (%s)", error->message);
+-			g_hash_table_remove (NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->bss_proxies,
+-			                     g_dbus_proxy_get_object_path (proxy));
+-		}
++	g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error);
++	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ 		return;
+-	}
+ 
+ 	self = NM_SUPPLICANT_INTERFACE (user_data);
+-	props = _get_bss_proxy_properties (self, proxy);
+-	if (!props)
++	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
++
++	if (error) {
++		_LOGD ("failed to acquire BSS proxy: (%s)", error->message);
++		g_hash_table_remove (priv->bss_proxies,
++		                     g_dbus_proxy_get_object_path (proxy));
+ 		return;
++	}
+ 
+-	g_object_set_data (G_OBJECT (proxy), BSS_PROXY_INITED, GUINT_TO_POINTER (TRUE));
++	object_path = g_dbus_proxy_get_object_path (proxy);
++	bss_data = g_hash_table_lookup (priv->bss_proxies, object_path);
++	if (!bss_data)
++		return;
++
++	bss_data->change_id = g_signal_connect (proxy, "g-properties-changed", G_CALLBACK (bss_proxy_properties_changed_cb), self);
+ 
++	props = bss_proxy_get_properties (self, proxy);
+ 	g_signal_emit (self, signals[BSS_UPDATED], 0,
+ 	               g_dbus_proxy_get_object_path (proxy),
+ 	               g_variant_ref_sink (props));
++	g_variant_unref (props);
++
++	if (priv->scan_done_pending)
++		scan_done_emit_signal (self);
+ }
+ 
+ static void
+-handle_new_bss (NMSupplicantInterface *self, const char *object_path)
++bss_add_new (NMSupplicantInterface *self, const char *object_path)
+ {
+ 	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ 	GDBusProxy *bss_proxy;
++	BssData *bss_data;
+ 
+ 	g_return_if_fail (object_path != NULL);
+ 
+@@ -251,17 +285,20 @@ handle_new_bss (NMSupplicantInterface *self, const char *object_path)
+ 	                          "g-object-path", object_path,
+ 	                          "g-interface-name", WPAS_DBUS_IFACE_BSS,
+ 	                          NULL);
++	bss_data = g_slice_new0 (BssData);
++	bss_data->proxy = bss_proxy;
+ 	g_hash_table_insert (priv->bss_proxies,
+ 	                     (char *) g_dbus_proxy_get_object_path (bss_proxy),
+-	                     bss_proxy);
+-	g_signal_connect (bss_proxy, "g-properties-changed", G_CALLBACK (bss_props_changed_cb), self);
++	                     bss_data);
+ 	g_async_initable_init_async (G_ASYNC_INITABLE (bss_proxy),
+ 	                             G_PRIORITY_DEFAULT,
+ 	                             priv->other_cancellable,
+-	                             (GAsyncReadyCallback) on_bss_proxy_acquired,
++	                             (GAsyncReadyCallback) bss_proxy_acquired_cb,
+ 	                             self);
+ }
+ 
++/*****************************************************************************/
++
+ static void
+ set_state (NMSupplicantInterface *self, NMSupplicantInterfaceState new_state)
+ {
+@@ -562,38 +599,56 @@ iface_introspect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data
+ }
+ 
+ static void
+-wpas_iface_scan_done (GDBusProxy *proxy,
+-                      gboolean success,
+-                      gpointer user_data)
++scan_done_emit_signal (NMSupplicantInterface *self)
+ {
+-	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
+ 	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+-	GVariant *props;
++	const char *object_path;
++	BssData *bss_data;
++	gboolean success;
+ 	GHashTableIter iter;
+-	char *bss_path;
+-	GDBusProxy *bss_proxy;
+ 
+-	/* Cache last scan completed time */
+-	priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
++	g_hash_table_iter_init (&iter, priv->bss_proxies);
++	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &bss_data)) {
++		/* we have some BSS' that need to be initialized first. Delay
++		 * emitting signal. */
++		if (!bss_data->change_id) {
++			priv->scan_done_pending = TRUE;
++			return;
++		}
++	}
+ 
+ 	/* Emit BSS_UPDATED so that wifi device has the APs (in case it removed them) */
+ 	g_hash_table_iter_init (&iter, priv->bss_proxies);
+-	while (g_hash_table_iter_next (&iter, (gpointer) &bss_path, (gpointer) &bss_proxy)) {
+-		if (g_object_get_data (G_OBJECT (bss_proxy), BSS_PROXY_INITED)) {
+-			props = _get_bss_proxy_properties (self, bss_proxy);
+-			if (props) {
+-				g_signal_emit (self, signals[BSS_UPDATED], 0,
+-				               bss_path,
+-				               g_variant_ref_sink (props));
+-				g_variant_unref (props);
+-			}
+-		}
++	while (g_hash_table_iter_next (&iter, (gpointer *) &object_path, (gpointer *) &bss_data)) {
++		gs_unref_variant GVariant *props = NULL;
++
++		props = bss_proxy_get_properties (self, bss_data->proxy);
++		g_signal_emit (self, signals[BSS_UPDATED], 0,
++		               object_path,
++		               g_variant_ref_sink (props));
+ 	}
+ 
++	success = priv->scan_done_success;
++	priv->scan_done_success = FALSE;
++	priv->scan_done_pending = FALSE;
+ 	g_signal_emit (self, signals[SCAN_DONE], 0, success);
+ }
+ 
+ static void
++wpas_iface_scan_done (GDBusProxy *proxy,
++                      gboolean success,
++                      gpointer user_data)
++{
++	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
++	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
++
++	/* Cache last scan completed time */
++	priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
++	priv->scan_done_success |= success;
++	scan_done_emit_signal (self);
++}
++
++static void
+ wpas_iface_bss_added (GDBusProxy *proxy,
+                       const char *path,
+                       GVariant *props,
+@@ -605,7 +660,7 @@ wpas_iface_bss_added (GDBusProxy *proxy,
+ 	if (priv->scanning)
+ 		priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
+ 
+-	handle_new_bss (self, path);
++	bss_add_new (self, path);
+ }
+ 
+ static void
+@@ -615,9 +670,14 @@ wpas_iface_bss_removed (GDBusProxy *proxy,
+ {
+ 	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
+ 	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
++	BssData *bss_data;
+ 
++	bss_data = g_hash_table_lookup (priv->bss_proxies, path);
++	if (!bss_data)
++		return;
++	g_hash_table_steal (priv->bss_proxies, path);
+ 	g_signal_emit (self, signals[BSS_REMOVED], 0, path);
+-	g_hash_table_remove (priv->bss_proxies, path);
++	bss_data_destroy (bss_data);
+ }
+ 
+ static void
+@@ -665,7 +725,7 @@ props_changed_cb (GDBusProxy *proxy,
+ 	if (g_variant_lookup (changed_properties, "BSSs", "^a&o", &array)) {
+ 		iter = array;
+ 		while (*iter)
+-			handle_new_bss (self, *iter++);
++			bss_add_new (self, *iter++);
+ 		g_free (array);
+ 	}
+ 
+@@ -1517,7 +1577,7 @@ nm_supplicant_interface_init (NMSupplicantInterface * self)
+ 	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ 
+ 	priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT;
+-	priv->bss_proxies = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
++	priv->bss_proxies = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, bss_data_destroy);
+ }
+ 
+ NMSupplicantInterface *
diff --git a/debian/patches/supplicant-merge-NEW_BSS-signal-with-BSS_UPDATED.patch b/debian/patches/supplicant-merge-NEW_BSS-signal-with-BSS_UPDATED.patch
new file mode 100644
index 00000000..2f5e0fbc
--- /dev/null
+++ b/debian/patches/supplicant-merge-NEW_BSS-signal-with-BSS_UPDATED.patch
@@ -0,0 +1,274 @@
+From: Thomas Haller <thaller@redhat.com>
+Date: Wed, 15 Feb 2017 11:17:55 +0100
+Subject: supplicant: merge NEW_BSS signal with BSS_UPDATED
+
+Before, the NEW_BSS signal was not careful to emit the signal only when the BSS
+is seen for the first time. Consequently, supplicant_iface_new_bss_cb() checked
+whether it already knows about the new BSS.
+
+Merge NEW_BSS and BSS_UPDATED. Now we emit BSS_UPDATED when either the
+BSS is new or changed.
+
+Also, in supplicant_iface_new_bss_cb() (now supplicant_iface_bss_updated_cb())
+no longer constructs an @ap instance if we have a @found_ap.
+
+In some situations there can be a value of having a separate ADD signal.
+But only when there the consumers care, and if the consumers can trust that
+ADD is not just an UPDATE. The only consumer doesn't care and it not not be
+trusted, so merge the signals.
+
+(cherry picked from commit 29a53b1cd7fb48984221c71b38b049c3ced9b560)
+---
+ src/devices/wifi/nm-device-wifi.c        | 125 +++++++++++--------------------
+ src/supplicant/nm-supplicant-interface.c |  17 +----
+ src/supplicant/nm-supplicant-interface.h |   1 -
+ 3 files changed, 49 insertions(+), 94 deletions(-)
+
+diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
+index 33daa26..eaea9a2 100644
+--- a/src/devices/wifi/nm-device-wifi.c
++++ b/src/devices/wifi/nm-device-wifi.c
+@@ -157,11 +157,6 @@ static void supplicant_iface_state_cb (NMSupplicantInterface *iface,
+                                        int disconnect_reason,
+                                        gpointer user_data);
+ 
+-static void supplicant_iface_new_bss_cb (NMSupplicantInterface * iface,
+-                                         const char *object_path,
+-                                         GVariant *properties,
+-                                         NMDeviceWifi * self);
+-
+ static void supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
+                                              const char *object_path,
+                                              GVariant *properties,
+@@ -261,10 +256,6 @@ supplicant_interface_acquire (NMDeviceWifi *self)
+ 	                  G_CALLBACK (supplicant_iface_state_cb),
+ 	                  self);
+ 	g_signal_connect (priv->sup_iface,
+-	                  NM_SUPPLICANT_INTERFACE_NEW_BSS,
+-	                  G_CALLBACK (supplicant_iface_new_bss_cb),
+-	                  self);
+-	g_signal_connect (priv->sup_iface,
+ 	                  NM_SUPPLICANT_INTERFACE_BSS_UPDATED,
+ 	                  G_CALLBACK (supplicant_iface_bss_updated_cb),
+ 	                  self);
+@@ -1629,14 +1620,13 @@ try_fill_ssid_for_hidden_ap (NMDeviceWifi *self,
+ }
+ 
+ static void
+-supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
+-                             const char *object_path,
+-                             GVariant *properties,
+-                             NMDeviceWifi *self)
++supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
++                                 const char *object_path,
++                                 GVariant *properties,
++                                 NMDeviceWifi *self)
+ {
+ 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+ 	NMDeviceState state;
+-	NMWifiAP *ap;
+ 	NMWifiAP *found_ap = NULL;
+ 	const GByteArray *ssid;
+ 
+@@ -1651,41 +1641,41 @@ supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
+ 	if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP)
+ 		return;
+ 
+-	ap = nm_wifi_ap_new_from_properties (object_path, properties);
+-	if (!ap) {
+-		_LOGD (LOGD_WIFI, "invalid AP properties received for %s", object_path);
+-		return;
+-	}
++	found_ap = get_ap_by_supplicant_path (self, object_path);
++	if (found_ap) {
++		nm_wifi_ap_update_from_properties (found_ap, object_path, properties);
++		_ap_dump (self, found_ap, "updated", 0);
++	} else {
++		gs_unref_object NMWifiAP *ap = NULL;
+ 
+-	/* Let the manager try to fill in the SSID from seen-bssids lists */
+-	ssid = nm_wifi_ap_get_ssid (ap);
+-	if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) {
+-		/* Try to fill the SSID from the AP database */
+-		try_fill_ssid_for_hidden_ap (self, ap);
++		ap = nm_wifi_ap_new_from_properties (object_path, properties);
++		if (!ap) {
++			_LOGD (LOGD_WIFI, "invalid AP properties received for %s", object_path);
++			return;
++		}
+ 
++		/* Let the manager try to fill in the SSID from seen-bssids lists */
+ 		ssid = nm_wifi_ap_get_ssid (ap);
+-		if (ssid && (nm_utils_is_empty_ssid (ssid->data, ssid->len) == FALSE)) {
+-			/* Yay, matched it, no longer treat as hidden */
+-			_LOGD (LOGD_WIFI, "matched hidden AP %s => '%s'",
+-			       nm_wifi_ap_get_address (ap), nm_utils_escape_ssid (ssid->data, ssid->len));
+-		} else {
+-			/* Didn't have an entry for this AP in the database */
+-			_LOGD (LOGD_WIFI, "failed to match hidden AP %s",
+-			       nm_wifi_ap_get_address (ap));
++		if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) {
++			/* Try to fill the SSID from the AP database */
++			try_fill_ssid_for_hidden_ap (self, ap);
++
++			ssid = nm_wifi_ap_get_ssid (ap);
++			if (ssid && (nm_utils_is_empty_ssid (ssid->data, ssid->len) == FALSE)) {
++				/* Yay, matched it, no longer treat as hidden */
++				_LOGD (LOGD_WIFI, "matched hidden AP %s => '%s'",
++				       nm_wifi_ap_get_address (ap), nm_utils_escape_ssid (ssid->data, ssid->len));
++			} else {
++				/* Didn't have an entry for this AP in the database */
++				_LOGD (LOGD_WIFI, "failed to match hidden AP %s",
++				       nm_wifi_ap_get_address (ap));
++			}
+ 		}
+-	}
+ 
+-	found_ap = get_ap_by_supplicant_path (self, object_path);
+-	if (found_ap) {
+-		_ap_dump (self, ap, "updated", 0);
+-		nm_wifi_ap_update_from_properties (found_ap, object_path, properties);
+-	} else {
+-		_ap_dump (self, ap, "added", 0);
+ 		ap_add_remove (self, ACCESS_POINT_ADDED, ap, TRUE);
++		_ap_dump (self, ap, "added", 0);
+ 	}
+ 
+-	g_object_unref (ap);
+-
+ 	/* Update the current AP if the supplicant notified a current BSS change
+ 	 * before it sent the current BSS's scan result.
+ 	 */
+@@ -1696,32 +1686,6 @@ supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
+ }
+ 
+ static void
+-supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
+-                                 const char *object_path,
+-                                 GVariant *properties,
+-                                 NMDeviceWifi *self)
+-{
+-	NMDeviceState state;
+-	NMWifiAP *ap;
+-
+-	g_return_if_fail (self != NULL);
+-	g_return_if_fail (object_path != NULL);
+-	g_return_if_fail (properties != NULL);
+-
+-	/* Ignore new APs when unavailable or unmanaged */
+-	state = nm_device_get_state (NM_DEVICE (self));
+-	if (state <= NM_DEVICE_STATE_UNAVAILABLE)
+-		return;
+-
+-	ap = get_ap_by_supplicant_path (self, object_path);
+-	if (ap) {
+-		_ap_dump (self, ap, "updated", 0);
+-		nm_wifi_ap_update_from_properties (ap, object_path, properties);
+-		schedule_ap_list_dump (self);
+-	}
+-}
+-
+-static void
+ supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
+                                  const char *object_path,
+                                  NMDeviceWifi *self)
+@@ -1734,19 +1698,20 @@ supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
+ 
+ 	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+ 	ap = get_ap_by_supplicant_path (self, object_path);
+-	if (ap) {
+-		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.
+-			 */
+-			nm_wifi_ap_set_fake (ap, TRUE);
+-		} else {
+-			_ap_dump (self, ap, "removed", 0);
+-			ap_add_remove (self, ACCESS_POINT_REMOVED, ap, TRUE);
+-			schedule_ap_list_dump (self);
+-		}
++	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.
++		 */
++		nm_wifi_ap_set_fake (ap, TRUE);
++	} else {
++		_ap_dump (self, ap, "removed", 0);
++		ap_add_remove (self, ACCESS_POINT_REMOVED, ap, TRUE);
++		schedule_ap_list_dump (self);
+ 	}
+ }
+ 
+diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c
+index 44bd27a..7942bec 100644
+--- a/src/supplicant/nm-supplicant-interface.c
++++ b/src/supplicant/nm-supplicant-interface.c
+@@ -60,8 +60,7 @@ typedef struct _AddNetworkData {
+ enum {
+ 	STATE,               /* change in the interface's state */
+ 	REMOVED,             /* interface was removed by the supplicant */
+-	NEW_BSS,             /* interface saw a new access point from a scan */
+-	BSS_UPDATED,         /* a BSS property changed */
++	BSS_UPDATED,         /* a new BSS appeared or an existing had properties changed */
+ 	BSS_REMOVED,         /* supplicant removed BSS from its scan list */
+ 	SCAN_DONE,           /* wifi scan is complete */
+ 	CREDENTIALS_REQUEST, /* 802.1x identity or password requested */
+@@ -229,7 +228,7 @@ on_bss_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_da
+ 
+ 	g_object_set_data (G_OBJECT (proxy), BSS_PROXY_INITED, GUINT_TO_POINTER (TRUE));
+ 
+-	g_signal_emit (self, signals[NEW_BSS], 0,
++	g_signal_emit (self, signals[BSS_UPDATED], 0,
+ 	               g_dbus_proxy_get_object_path (proxy),
+ 	               g_variant_ref_sink (props));
+ }
+@@ -577,13 +576,13 @@ wpas_iface_scan_done (GDBusProxy *proxy,
+ 	/* Cache last scan completed time */
+ 	priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
+ 
+-	/* Emit NEW_BSS so that wifi device has the APs (in case it removed them) */
++	/* Emit BSS_UPDATED so that wifi device has the APs (in case it removed them) */
+ 	g_hash_table_iter_init (&iter, priv->bss_proxies);
+ 	while (g_hash_table_iter_next (&iter, (gpointer) &bss_path, (gpointer) &bss_proxy)) {
+ 		if (g_object_get_data (G_OBJECT (bss_proxy), BSS_PROXY_INITED)) {
+ 			props = _get_bss_proxy_properties (self, bss_proxy);
+ 			if (props) {
+-				g_signal_emit (self, signals[NEW_BSS], 0,
++				g_signal_emit (self, signals[BSS_UPDATED], 0,
+ 				               bss_path,
+ 				               g_variant_ref_sink (props));
+ 				g_variant_unref (props);
+@@ -1634,14 +1633,6 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
+ 	                  NULL, NULL, NULL,
+ 	                  G_TYPE_NONE, 0);
+ 
+-	signals[NEW_BSS] =
+-	    g_signal_new (NM_SUPPLICANT_INTERFACE_NEW_BSS,
+-	                  G_OBJECT_CLASS_TYPE (object_class),
+-	                  G_SIGNAL_RUN_LAST,
+-	                  0,
+-	                  NULL, NULL, NULL,
+-	                  G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_VARIANT);
+-
+ 	signals[BSS_UPDATED] =
+ 	    g_signal_new (NM_SUPPLICANT_INTERFACE_BSS_UPDATED,
+ 	                  G_OBJECT_CLASS_TYPE (object_class),
+diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h
+index 97ee38e..d60d4a5 100644
+--- a/src/supplicant/nm-supplicant-interface.h
++++ b/src/supplicant/nm-supplicant-interface.h
+@@ -64,7 +64,6 @@ typedef enum {
+ /* Signals */
+ #define NM_SUPPLICANT_INTERFACE_STATE            "state"
+ #define NM_SUPPLICANT_INTERFACE_REMOVED          "removed"
+-#define NM_SUPPLICANT_INTERFACE_NEW_BSS          "new-bss"
+ #define NM_SUPPLICANT_INTERFACE_BSS_UPDATED      "bss-updated"
+ #define NM_SUPPLICANT_INTERFACE_BSS_REMOVED      "bss-removed"
+ #define NM_SUPPLICANT_INTERFACE_SCAN_DONE        "scan-done"
diff --git a/debian/patches/supplicant-remove-unused-return-value-from-nm_supplicant_.patch b/debian/patches/supplicant-remove-unused-return-value-from-nm_supplicant_.patch
new file mode 100644
index 00000000..82e282e2
--- /dev/null
+++ b/debian/patches/supplicant-remove-unused-return-value-from-nm_supplicant_.patch
@@ -0,0 +1,107 @@
+From: Thomas Haller <thaller@redhat.com>
+Date: Wed, 15 Feb 2017 00:22:46 +0100
+Subject: supplicant: remove unused return value from
+ nm_supplicant_interface_request_scan()
+
+It cannot fail, remove code that anticipates a failure of request-scan.
+
+(cherry picked from commit dce13b6f11105422d54ee3aa3781ae77c875ae0f)
+---
+ src/devices/wifi/nm-device-wifi.c        | 22 +++++++---------------
+ src/supplicant/nm-supplicant-interface.c |  5 ++---
+ src/supplicant/nm-supplicant-interface.h |  2 +-
+ 3 files changed, 10 insertions(+), 19 deletions(-)
+
+diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
+index 9586817..33daa26 100644
+--- a/src/devices/wifi/nm-device-wifi.c
++++ b/src/devices/wifi/nm-device-wifi.c
+@@ -1422,9 +1422,7 @@ static void
+ request_wireless_scan (NMDeviceWifi *self, gboolean force_if_scanning, GVariant *scan_options)
+ {
+ 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+-	gboolean backoff = FALSE;
+-	GPtrArray *ssids = NULL;
+-	gboolean new_scan_requested = FALSE;
++	gboolean request_started = FALSE;
+ 
+ 	nm_clear_g_source (&priv->pending_scan_id);
+ 
+@@ -1434,6 +1432,8 @@ request_wireless_scan (NMDeviceWifi *self, gboolean force_if_scanning, GVariant
+ 	}
+ 
+ 	if (check_scanning_allowed (self)) {
++		gs_unref_ptrarray GPtrArray *ssids = NULL;
++
+ 		_LOGD (LOGD_WIFI, "wifi-scan: scanning requested");
+ 
+ 		if (scan_options) {
+@@ -1471,22 +1471,14 @@ request_wireless_scan (NMDeviceWifi *self, gboolean force_if_scanning, GVariant
+ 
+ 		_hw_addr_set_scanning (self, FALSE);
+ 
+-		if (nm_supplicant_interface_request_scan (priv->sup_iface, ssids)) {
+-			/* success */
+-			backoff = TRUE;
+-			_requested_scan_set (self, TRUE);
+-			new_scan_requested = TRUE;
+-		}
+-
+-		if (ssids)
+-			g_ptr_array_unref (ssids);
++		nm_supplicant_interface_request_scan (priv->sup_iface, ssids);
++		request_started = TRUE;
+ 	} else
+ 		_LOGD (LOGD_WIFI, "wifi-scan: scanning requested but not allowed at this time");
+ 
+-	if (!new_scan_requested)
+-		_requested_scan_set (self, FALSE);
++	_requested_scan_set (self, request_started);
+ 
+-	schedule_scan (self, backoff);
++	schedule_scan (self, request_started);
+ }
+ 
+ static gboolean
+diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c
+index 725f6d4..ebedad6 100644
+--- a/src/supplicant/nm-supplicant-interface.c
++++ b/src/supplicant/nm-supplicant-interface.c
+@@ -1340,14 +1340,14 @@ scan_request_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+ 	}
+ }
+ 
+-gboolean
++void
+ nm_supplicant_interface_request_scan (NMSupplicantInterface *self, const GPtrArray *ssids)
+ {
+ 	NMSupplicantInterfacePrivate *priv;
+ 	GVariantBuilder builder;
+ 	guint i;
+ 
+-	g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE);
++	g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self));
+ 
+ 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ 
+@@ -1375,7 +1375,6 @@ nm_supplicant_interface_request_scan (NMSupplicantInterface *self, const GPtrArr
+ 	                   priv->other_cancellable,
+ 	                   (GAsyncReadyCallback) scan_request_cb,
+ 	                   self);
+-	return TRUE;
+ }
+ 
+ /*****************************************************************************/
+diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h
+index 3498bb5..97ee38e 100644
+--- a/src/supplicant/nm-supplicant-interface.h
++++ b/src/supplicant/nm-supplicant-interface.h
+@@ -96,7 +96,7 @@ void nm_supplicant_interface_disconnect (NMSupplicantInterface * iface);
+ 
+ const char *nm_supplicant_interface_get_object_path (NMSupplicantInterface * iface);
+ 
+-gboolean nm_supplicant_interface_request_scan (NMSupplicantInterface * self, const GPtrArray *ssids);
++void nm_supplicant_interface_request_scan (NMSupplicantInterface * self, const GPtrArray *ssids);
+ 
+ NMSupplicantInterfaceState nm_supplicant_interface_get_state (NMSupplicantInterface * self);
+ 
diff --git a/debian/patches/supplicant-rework-nm_supplicant_interface_set_config-to-i.patch b/debian/patches/supplicant-rework-nm_supplicant_interface_set_config-to-i.patch
new file mode 100644
index 00000000..36b1af70
--- /dev/null
+++ b/debian/patches/supplicant-rework-nm_supplicant_interface_set_config-to-i.patch
@@ -0,0 +1,922 @@
+From: Thomas Haller <thaller@redhat.com>
+Date: Tue, 14 Feb 2017 19:30:21 +0100
+Subject: supplicant: rework nm_supplicant_interface_set_config() to invoke
+ result callback
+
+Instead of having a NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR signal to notify
+about failures during AddNetwork/SelectNetwork, accept a callback to report
+success/failure.
+
+Thereby, rename nm_supplicant_interface_set_config() to
+nm_supplicant_interface_assoc().
+
+The async callback is guaranteed to:
+
+ - be invoked exactly once, signalling success or failure
+ - always being invoked asyncronously.
+
+The pending request can be (synchronously) cancelled via
+nm_supplicant_interface_disconnect() or by disposing the
+interface instance. In those cases the callback will be invoked
+too, with error code cancelled/disposing.
+
+(cherry picked from commit 66c45d0fdcbd9e87b5164c5f77f914141cbb9419)
+---
+ src/devices/nm-device-ethernet.c         |  68 ++-----
+ src/devices/nm-device-macsec.c           |  56 ++----
+ src/devices/wifi/nm-device-wifi.c        |  63 ++-----
+ src/supplicant/nm-supplicant-interface.c | 312 ++++++++++++++++++-------------
+ src/supplicant/nm-supplicant-interface.h |  13 +-
+ 5 files changed, 236 insertions(+), 276 deletions(-)
+
+diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
+index 8dedd7e..1059c2b 100644
+--- a/src/devices/nm-device-ethernet.c
++++ b/src/devices/nm-device-ethernet.c
+@@ -71,7 +71,6 @@ typedef struct Supplicant {
+ 	NMSupplicantInterface *iface;
+ 
+ 	/* signal handler ids */
+-	gulong iface_error_id;
+ 	gulong iface_state_id;
+ 
+ 	/* Timeouts and idles */
+@@ -423,22 +422,12 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
+ /* 802.1X */
+ 
+ static void
+-supplicant_interface_clear_handlers (NMDeviceEthernet *self)
++supplicant_interface_release (NMDeviceEthernet *self)
+ {
+ 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+ 
+ 	nm_clear_g_source (&priv->supplicant_timeout_id);
+ 	nm_clear_g_source (&priv->supplicant.con_timeout_id);
+-	nm_clear_g_signal_handler (priv->supplicant.iface, &priv->supplicant.iface_error_id);
+-}
+-
+-static void
+-supplicant_interface_release (NMDeviceEthernet *self)
+-{
+-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+-
+-	supplicant_interface_clear_handlers (self);
+-
+ 	nm_clear_g_signal_handler (priv->supplicant.iface, &priv->supplicant.iface_state_id);
+ 
+ 	if (priv->supplicant.iface) {
+@@ -595,6 +584,21 @@ build_supplicant_config (NMDeviceEthernet *self,
+ }
+ 
+ static void
++supplicant_iface_assoc_cb (NMSupplicantInterface *iface,
++                           GError *error,
++                           gpointer user_data)
++{
++	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data);
++
++	if (error && !nm_utils_error_is_cancelled (error, TRUE)) {
++		supplicant_interface_release (self);
++		nm_device_queue_state (NM_DEVICE (self),
++		                       NM_DEVICE_STATE_FAILED,
++		                       NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
++	}
++}
++
++static void
+ supplicant_iface_state_cb (NMSupplicantInterface *iface,
+                            int new_state_i,
+                            int old_state_i,
+@@ -605,7 +609,6 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
+ 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+ 	NMDevice *device = NM_DEVICE (self);
+ 	NMSupplicantConfig *config;
+-	gboolean success = FALSE;
+ 	NMDeviceState devstate;
+ 	GError *error = NULL;
+ 	NMSupplicantInterfaceState new_state = new_state_i;
+@@ -624,30 +627,23 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
+ 	case NM_SUPPLICANT_INTERFACE_STATE_READY:
+ 		config = build_supplicant_config (self, &error);
+ 		if (config) {
+-			success = nm_supplicant_interface_set_config (priv->supplicant.iface, config, &error);
++			nm_supplicant_interface_assoc (priv->supplicant.iface, config,
++			                               supplicant_iface_assoc_cb, self);
+ 			g_object_unref (config);
+-
+-			if (!success) {
+-				_LOGE (LOGD_DEVICE | LOGD_ETHER,
+-				       "Activation: (ethernet) couldn't send security configuration to the supplicant: %s",
+-				       error->message);
+-				g_clear_error (&error);
+-			}
+ 		} else {
+ 			_LOGE (LOGD_DEVICE | LOGD_ETHER,
+ 			       "Activation: (ethernet) couldn't build security configuration: %s",
+ 			       error->message);
+ 			g_clear_error (&error);
+-		}
+ 
+-		if (!success) {
+ 			nm_device_state_changed (device,
+ 			                         NM_DEVICE_STATE_FAILED,
+ 			                         NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
+ 		}
+ 		break;
+ 	case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED:
+-		supplicant_interface_clear_handlers (self);
++		nm_clear_g_source (&priv->supplicant_timeout_id);
++		nm_clear_g_source (&priv->supplicant.con_timeout_id);
+ 
+ 		/* If this is the initial association during device activation,
+ 		 * schedule the next activation stage.
+@@ -679,24 +675,6 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
+ 	}
+ }
+ 
+-static void
+-supplicant_iface_connection_error_cb (NMSupplicantInterface *iface,
+-                                      const char *name,
+-                                      const char *message,
+-                                      gpointer user_data)
+-{
+-	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data);
+-
+-	_LOGW (LOGD_DEVICE | LOGD_ETHER,
+-	       "Activation: (ethernet) association request to the supplicant failed: %s - %s",
+-	       name, message);
+-
+-	supplicant_interface_release (self);
+-	nm_device_queue_state (NM_DEVICE (self),
+-	                       NM_DEVICE_STATE_FAILED,
+-	                       NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
+-}
+-
+ static NMActStageReturn
+ handle_auth_or_fail (NMDeviceEthernet *self,
+                      NMActRequest *req,
+@@ -792,12 +770,6 @@ supplicant_interface_init (NMDeviceEthernet *self)
+ 	                                                    G_CALLBACK (supplicant_iface_state_cb),
+ 	                                                    self);
+ 
+-	/* Hook up error signal handler to capture association errors */
+-	priv->supplicant.iface_error_id = g_signal_connect (priv->supplicant.iface,
+-	                                                    NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR,
+-	                                                    G_CALLBACK (supplicant_iface_connection_error_cb),
+-	                                                    self);
+-
+ 	/* Set up a timeout on the connection attempt to fail it after 25 seconds */
+ 	priv->supplicant.con_timeout_id = g_timeout_add_seconds (25, supplicant_connection_timeout_cb, self);
+ 
+diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c
+index 58ee4fe..e1a5a13 100644
+--- a/src/devices/nm-device-macsec.c
++++ b/src/devices/nm-device-macsec.c
+@@ -45,7 +45,6 @@ typedef struct Supplicant {
+ 	NMSupplicantInterface *iface;
+ 
+ 	/* signal handler ids */
+-	gulong iface_error_id;
+ 	gulong iface_state_id;
+ 
+ 	/* Timeouts and idles */
+@@ -248,22 +247,12 @@ build_supplicant_config (NMDeviceMacsec *self, GError **error)
+ }
+ 
+ static void
+-supplicant_interface_clear_handlers (NMDeviceMacsec *self)
++supplicant_interface_release (NMDeviceMacsec *self)
+ {
+ 	NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
+ 
+ 	nm_clear_g_source (&priv->supplicant_timeout_id);
+ 	nm_clear_g_source (&priv->supplicant.con_timeout_id);
+-	nm_clear_g_signal_handler (priv->supplicant.iface, &priv->supplicant.iface_error_id);
+-}
+-
+-static void
+-supplicant_interface_release (NMDeviceMacsec *self)
+-{
+-	NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
+-
+-	supplicant_interface_clear_handlers (self);
+-
+ 	nm_clear_g_signal_handler (priv->supplicant.iface, &priv->supplicant.iface_state_id);
+ 
+ 	if (priv->supplicant.iface) {
+@@ -273,21 +262,18 @@ supplicant_interface_release (NMDeviceMacsec *self)
+ }
+ 
+ static void
+-supplicant_iface_connection_error_cb (NMSupplicantInterface *iface,
+-                                      const char *name,
+-                                      const char *message,
+-                                      gpointer user_data)
++supplicant_iface_assoc_cb (NMSupplicantInterface *iface,
++                           GError *error,
++                           gpointer user_data)
+ {
+ 	NMDeviceMacsec *self = NM_DEVICE_MACSEC (user_data);
+ 
+-	_LOGW (LOGD_DEVICE,
+-	       "Activation: association request to the supplicant failed: %s - %s",
+-	       name, message);
+-
+-	supplicant_interface_release (self);
+-	nm_device_queue_state (NM_DEVICE (self),
+-	                       NM_DEVICE_STATE_FAILED,
+-	                       NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
++	if (error && !nm_utils_error_is_cancelled (error, TRUE)) {
++		supplicant_interface_release (self);
++		nm_device_queue_state (NM_DEVICE (self),
++		                       NM_DEVICE_STATE_FAILED,
++		                       NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
++	}
+ }
+ 
+ static void
+@@ -421,7 +407,6 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
+ 	NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
+ 	NMDevice *device = NM_DEVICE (self);
+ 	NMSupplicantConfig *config;
+-	gboolean success = FALSE;
+ 	NMDeviceState devstate;
+ 	GError *error = NULL;
+ 	NMSupplicantInterfaceState new_state = new_state_i;
+@@ -440,30 +425,23 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
+ 	case NM_SUPPLICANT_INTERFACE_STATE_READY:
+ 		config = build_supplicant_config (self, &error);
+ 		if (config) {
+-			success = nm_supplicant_interface_set_config (priv->supplicant.iface, config, &error);
++			nm_supplicant_interface_assoc (priv->supplicant.iface, config,
++			                               supplicant_iface_assoc_cb, self);
+ 			g_object_unref (config);
+-
+-			if (!success) {
+-				_LOGE (LOGD_DEVICE,
+-				       "Activation: couldn't send security configuration to the supplicant: %s",
+-				       error->message);
+-				g_clear_error (&error);
+-			}
+ 		} else {
+ 			_LOGE (LOGD_DEVICE,
+ 			       "Activation: couldn't build security configuration: %s",
+ 			       error->message);
+ 			g_clear_error (&error);
+-		}
+ 
+-		if (!success) {
+ 			nm_device_state_changed (device,
+ 			                         NM_DEVICE_STATE_FAILED,
+ 			                         NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
+ 		}
+ 		break;
+ 	case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED:
+-		supplicant_interface_clear_handlers (self);
++		nm_clear_g_source (&priv->supplicant_timeout_id);
++		nm_clear_g_source (&priv->supplicant.con_timeout_id);
+ 		nm_device_bring_up (device, TRUE, NULL);
+ 
+ 		/* If this is the initial association during device activation,
+@@ -595,12 +573,6 @@ supplicant_interface_init (NMDeviceMacsec *self)
+ 	                                                    G_CALLBACK (supplicant_iface_state_cb),
+ 	                                                    self);
+ 
+-	/* Hook up error signal handler to capture association errors */
+-	priv->supplicant.iface_error_id = g_signal_connect (priv->supplicant.iface,
+-	                                                    NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR,
+-	                                                    G_CALLBACK (supplicant_iface_connection_error_cb),
+-	                                                    self);
+-
+ 	/* Set up a timeout on the connection attempt to fail it after 25 seconds */
+ 	priv->supplicant.con_timeout_id = g_timeout_add_seconds (25, supplicant_connection_timeout_cb, self);
+ 
+diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
+index 07dc36d..9586817 100644
+--- a/src/devices/wifi/nm-device-wifi.c
++++ b/src/devices/wifi/nm-device-wifi.c
+@@ -190,8 +190,6 @@ static void ap_add_remove (NMDeviceWifi *self,
+                            NMWifiAP *ap,
+                            gboolean recheck_available_connections);
+ 
+-static void remove_supplicant_interface_error_handler (NMDeviceWifi *self);
+-
+ static void _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset);
+ 
+ /*****************************************************************************/
+@@ -1761,21 +1759,12 @@ supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
+ }
+ 
+ static void
+-remove_supplicant_timeouts (NMDeviceWifi *self)
++cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect)
+ {
+ 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+ 
+ 	nm_clear_g_source (&priv->sup_timeout_id);
+ 	nm_clear_g_source (&priv->link_timeout_id);
+-}
+-
+-static void
+-cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect)
+-{
+-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+-
+-	remove_supplicant_interface_error_handler (self);
+-	remove_supplicant_timeouts (self);
+ 	if (disconnect && priv->sup_iface)
+ 		nm_supplicant_interface_disconnect (priv->sup_iface);
+ }
+@@ -2082,8 +2071,8 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
+ 			nm_device_remove_pending_action (device, NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE);
+ 		break;
+ 	case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED:
+-		remove_supplicant_interface_error_handler (self);
+-		remove_supplicant_timeouts (self);
++		nm_clear_g_source (&priv->sup_timeout_id);
++		nm_clear_g_source (&priv->link_timeout_id);
+ 
+ 		/* If this is the initial association during device activation,
+ 		 * schedule the next activation stage.
+@@ -2173,36 +2162,21 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
+ }
+ 
+ static void
+-supplicant_iface_connection_error_cb (NMSupplicantInterface *iface,
+-                                      const char *name,
+-                                      const char *message,
+-                                      NMDeviceWifi *self)
++supplicant_iface_assoc_cb (NMSupplicantInterface *iface,
++                           GError *error,
++                           gpointer user_data)
+ {
++	NMDeviceWifi *self = NM_DEVICE_WIFI (user_data);
+ 	NMDevice *device = NM_DEVICE (self);
+ 
+-	if (nm_device_is_activating (device)) {
+-		_LOGW (LOGD_DEVICE | LOGD_WIFI,
+-		       "Activation: (wifi) supplicant association failed: %s - %s",
+-		       name, message);
+-
++	if (   error && !nm_utils_error_is_cancelled (error, TRUE)
++	    && 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);
+ 	}
+ }
+ 
+ static void
+-remove_supplicant_interface_error_handler (NMDeviceWifi *self)
+-{
+-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+-
+-	if (priv->sup_iface) {
+-		g_signal_handlers_disconnect_by_func (priv->sup_iface,
+-		                                      supplicant_iface_connection_error_cb,
+-		                                      self);
+-	}
+-}
+-
+-static void
+ supplicant_iface_notify_scanning_cb (NMSupplicantInterface *iface,
+                                      GParamSpec *pspec,
+                                      NMDeviceWifi *self)
+@@ -2620,7 +2594,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
+ 
+ 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+ 
+-	remove_supplicant_timeouts (self);
++	nm_clear_g_source (&priv->sup_timeout_id);
++	nm_clear_g_source (&priv->link_timeout_id);
+ 
+ 	req = nm_device_get_act_request (device);
+ 	g_assert (req);
+@@ -2684,20 +2659,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
+ 		goto out;
+ 	}
+ 
+-	/* Hook up error signal handler to capture association errors */
+-	g_signal_connect (priv->sup_iface,
+-	                  NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR,
+-	                  G_CALLBACK (supplicant_iface_connection_error_cb),
+-	                  self);
+-
+-	if (!nm_supplicant_interface_set_config (priv->sup_iface, config, &error)) {
+-		_LOGE (LOGD_DEVICE | LOGD_WIFI,
+-		       "Activation: (wifi) couldn't send wireless configuration to the supplicant: %s",
+-		       error->message);
+-		g_clear_error (&error);
+-		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
+-		goto out;
+-	}
++	nm_supplicant_interface_assoc (priv->sup_iface, config,
++	                               supplicant_iface_assoc_cb, self);
+ 
+ 	/* Set up a timeout on the association attempt to fail after 25 seconds */
+ 	priv->sup_timeout_id = g_timeout_add_seconds (25, supplicant_connection_timeout_cb, self);
+diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c
+index 334a8d2..acbc605 100644
+--- a/src/supplicant/nm-supplicant-interface.c
++++ b/src/supplicant/nm-supplicant-interface.c
+@@ -39,6 +39,15 @@
+ 
+ /*****************************************************************************/
+ 
++typedef struct {
++	NMSupplicantConfig *cfg;
++	GCancellable *cancellable;
++	NMSupplicantInterfaceAssocCb callback;
++	gpointer user_data;
++	guint fail_on_idle_id;
++	guint blobs_left;
++} AssocData;
++
+ enum {
+ 	STATE,               /* change in the interface's state */
+ 	REMOVED,             /* interface was removed by the supplicant */
+@@ -46,7 +55,6 @@ enum {
+ 	BSS_UPDATED,         /* a BSS property changed */
+ 	BSS_REMOVED,         /* supplicant removed BSS from its scan list */
+ 	SCAN_DONE,           /* wifi scan is complete */
+-	CONNECTION_ERROR,    /* an error occurred during a connection request */
+ 	CREDENTIALS_REQUEST, /* 802.1x identity or password requested */
+ 	LAST_SIGNAL
+ };
+@@ -80,15 +88,15 @@ typedef struct {
+ 	GCancellable * init_cancellable;
+ 	GDBusProxy *   iface_proxy;
+ 	GCancellable * other_cancellable;
+-	GCancellable * assoc_cancellable;
++
++	AssocData *    assoc_data;
++
+ 	char *         net_path;
+-	guint32        blobs_left;
+ 	GHashTable *   bss_proxies;
+ 	char *         current_bss;
+ 
+ 	gint32         last_scan; /* timestamp as returned by nm_utils_get_monotonic_timestamp_s() */
+ 
+-	NMSupplicantConfig *cfg;
+ } NMSupplicantInterfacePrivate;
+ 
+ struct _NMSupplicantInterface {
+@@ -127,18 +135,6 @@ G_DEFINE_TYPE (NMSupplicantInterface, nm_supplicant_interface, G_TYPE_OBJECT)
+ /*****************************************************************************/
+ 
+ static void
+-emit_error_helper (NMSupplicantInterface *self, GError *error)
+-{
+-	char *name = NULL;
+-
+-	if (g_dbus_error_is_remote_error (error))
+-		name = g_dbus_error_get_remote_error (error);
+-
+-	g_signal_emit (self, signals[CONNECTION_ERROR], 0, name, error->message);
+-	g_free (name);
+-}
+-
+-static void
+ bss_props_changed_cb (GDBusProxy *proxy,
+                       GVariant *changed_properties,
+                       char **invalidated_properties,
+@@ -1007,6 +1003,34 @@ log_result_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+ 	}
+ }
+ 
++/*****************************************************************************/
++
++static void
++assoc_return (NMSupplicantInterface *self, GError *error, const char *message)
++{
++	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
++	AssocData *assoc_data;
++
++	assoc_data = g_steal_pointer (&priv->assoc_data);
++	if (!assoc_data)
++		return;
++
++	if (error) {
++		g_dbus_error_strip_remote_error (error);
++		_LOGW ("assoc[%p]: %s: %s", assoc_data, message, error->message);
++	} else
++		_LOGD ("assoc[%p]: association request successful", assoc_data);
++
++	nm_clear_g_source (&assoc_data->fail_on_idle_id);
++	nm_clear_g_cancellable (&assoc_data->cancellable);
++
++	if (assoc_data->callback)
++		assoc_data->callback (self, error, assoc_data->user_data);
++
++	g_object_unref (assoc_data->cfg);
++	g_slice_free (AssocData, assoc_data);
++}
++
+ void
+ nm_supplicant_interface_disconnect (NMSupplicantInterface * self)
+ {
+@@ -1017,9 +1041,11 @@ nm_supplicant_interface_disconnect (NMSupplicantInterface * self)
+ 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ 
+ 	/* Cancel all pending calls related to a prior connection attempt */
+-	if (priv->assoc_cancellable) {
+-		g_cancellable_cancel (priv->assoc_cancellable);
+-		g_clear_object (&priv->assoc_cancellable);
++	if (priv->assoc_data) {
++		gs_free GError *error = NULL;
++
++		nm_utils_error_set_cancelled (&error, FALSE, "NMSupplicantInterface");
++		assoc_return (self, error, "abort due to disconnect");
+ 	}
+ 
+ 	/* Don't do anything if there is no connection to the supplicant yet. */
+@@ -1055,42 +1081,40 @@ nm_supplicant_interface_disconnect (NMSupplicantInterface * self)
+ }
+ 
+ static void
+-select_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
++assoc_select_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+ {
+ 	NMSupplicantInterface *self;
+ 	gs_unref_variant GVariant *reply = NULL;
+ 	gs_free_error GError *error = NULL;
+ 
+ 	reply = g_dbus_proxy_call_finish (proxy, result, &error);
+-	if (   !reply
+-	    && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+-		self = NM_SUPPLICANT_INTERFACE (user_data);
+-		g_dbus_error_strip_remote_error (error);
+-		_LOGW ("couldn't select network config: %s", error->message);
+-		emit_error_helper (self, error);
+-	}
++	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
++		return;
++
++	self = NM_SUPPLICANT_INTERFACE (user_data);
++	if (error)
++		assoc_return (self, error, "failure to select network config");
++	else
++		assoc_return (self, NULL, NULL);
+ }
+ 
+ static void
+-call_select_network (NMSupplicantInterface *self)
++assoc_call_select_network (NMSupplicantInterface *self)
+ {
+ 	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ 
+-	/* We only select the network after all blobs (if any) have been set */
+-	if (priv->blobs_left == 0) {
+-		g_dbus_proxy_call (priv->iface_proxy,
+-		                   "SelectNetwork",
+-		                   g_variant_new ("(o)", priv->net_path),
+-		                   G_DBUS_CALL_FLAGS_NONE,
+-		                   -1,
+-		                   priv->assoc_cancellable,
+-		                   (GAsyncReadyCallback) select_network_cb,
+-		                   self);
+-	}
++	g_dbus_proxy_call (priv->iface_proxy,
++	                   "SelectNetwork",
++	                   g_variant_new ("(o)", priv->net_path),
++	                   G_DBUS_CALL_FLAGS_NONE,
++	                   -1,
++	                   priv->assoc_data->cancellable,
++	                   (GAsyncReadyCallback) assoc_select_network_cb,
++	                   self);
+ }
+ 
+ static void
+-add_blob_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
++assoc_add_blob_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+ {
+ 	NMSupplicantInterface *self;
+ 	NMSupplicantInterfacePrivate *priv;
+@@ -1104,18 +1128,19 @@ add_blob_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+ 	self = NM_SUPPLICANT_INTERFACE (user_data);
+ 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ 
+-	priv->blobs_left--;
+-	if (reply)
+-		call_select_network (self);
+-	else {
+-		g_dbus_error_strip_remote_error (error);
+-		_LOGW ("couldn't set network certificates: %s", error->message);
+-		emit_error_helper (self, error);
++	if (error) {
++		assoc_return (self, error, "failure to set network certificates");
++		return;
+ 	}
++
++	priv->assoc_data->blobs_left--;
++	_LOGT ("assoc[%p]: blob added (%u left)", priv->assoc_data, priv->assoc_data->blobs_left);
++	if (priv->assoc_data->blobs_left == 0)
++		assoc_call_select_network (self);
+ }
+ 
+ static void
+-add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
++assoc_add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+ {
+ 	NMSupplicantInterface *self;
+ 	NMSupplicantInterfacePrivate *priv;
+@@ -1135,57 +1160,43 @@ add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+ 	self = NM_SUPPLICANT_INTERFACE (user_data);
+ 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ 
+-	g_free (priv->net_path);
+-	priv->net_path = NULL;
++	nm_clear_g_free (&priv->net_path);
+ 
+ 	if (error) {
+-		g_dbus_error_strip_remote_error (error);
+-		_LOGW ("adding network to supplicant failed: %s", error->message);
+-		emit_error_helper (self, error);
++		assoc_return (self, error, "failure to add network");
+ 		return;
+ 	}
+ 
+ 	g_variant_get (reply, "(o)", &priv->net_path);
+ 
+ 	/* Send blobs first; otherwise jump to selecting the network */
+-	blobs = nm_supplicant_config_get_blobs (priv->cfg);
+-	priv->blobs_left = g_hash_table_size (blobs);
++	blobs = nm_supplicant_config_get_blobs (priv->assoc_data->cfg);
++	priv->assoc_data->blobs_left = g_hash_table_size (blobs);
+ 
+-	g_hash_table_iter_init (&iter, blobs);
+-	while (g_hash_table_iter_next (&iter, (gpointer) &blob_name, (gpointer) &blob_data)) {
+-		g_dbus_proxy_call (priv->iface_proxy,
+-		                   "AddBlob",
+-		                   g_variant_new ("(s@ay)",
+-		                                  blob_name,
+-		                                  g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+-		                                                             blob_data->data, blob_data->len, 1)),
+-		                   G_DBUS_CALL_FLAGS_NONE,
+-		                   -1,
+-		                   priv->assoc_cancellable,
+-		                   (GAsyncReadyCallback) add_blob_cb,
+-		                   self);
+-	}
+-
+-	call_select_network (self);
+-}
+-
+-static void
+-add_network (NMSupplicantInterface *self)
+-{
+-	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
++	_LOGT ("assoc[%p]: network added (%s) (%u blobs left)", priv->assoc_data, priv->net_path, priv->assoc_data->blobs_left);
+ 
+-	g_dbus_proxy_call (priv->iface_proxy,
+-	                   "AddNetwork",
+-	                   g_variant_new ("(@a{sv})", nm_supplicant_config_to_variant (priv->cfg)),
+-	                   G_DBUS_CALL_FLAGS_NONE,
+-	                   -1,
+-	                   priv->assoc_cancellable,
+-	                   (GAsyncReadyCallback) add_network_cb,
+-	                   self);
++	if (priv->assoc_data->blobs_left == 0)
++		assoc_call_select_network (self);
++	else {
++		g_hash_table_iter_init (&iter, blobs);
++		while (g_hash_table_iter_next (&iter, (gpointer) &blob_name, (gpointer) &blob_data)) {
++			g_dbus_proxy_call (priv->iface_proxy,
++			                   "AddBlob",
++			                   g_variant_new ("(s@ay)",
++			                                  blob_name,
++			                                  g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
++			                                                             blob_data->data, blob_data->len, 1)),
++			                   G_DBUS_CALL_FLAGS_NONE,
++			                   -1,
++			                   priv->assoc_data->cancellable,
++			                   (GAsyncReadyCallback) assoc_add_blob_cb,
++			                   self);
++		}
++	}
+ }
+ 
+ static void
+-set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
++assoc_set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+ {
+ 	NMSupplicantInterface *self;
+ 	NMSupplicantInterfacePrivate *priv;
+@@ -1199,62 +1210,103 @@ set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+ 	self = NM_SUPPLICANT_INTERFACE (user_data);
+ 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ 
+-	if (!reply) {
+-		g_dbus_error_strip_remote_error (error);
+-		_LOGW ("couldn't send AP scan mode to the supplicant interface: %s",
+-		       error->message);
+-		emit_error_helper (self, error);
++	if (error) {
++		assoc_return (self, error, "failure to set AP scan mode");
+ 		return;
+ 	}
+ 
+-	_LOGI ("config: set interface ap_scan to %d",
+-	       nm_supplicant_config_get_ap_scan (priv->cfg));
++	_LOGT ("assoc[%p]: set interface ap_scan to %d",
++	       priv->assoc_data,
++	       nm_supplicant_config_get_ap_scan (priv->assoc_data->cfg));
++
++	g_dbus_proxy_call (priv->iface_proxy,
++	                   "AddNetwork",
++	                   g_variant_new ("(@a{sv})", nm_supplicant_config_to_variant (priv->assoc_data->cfg)),
++	                   G_DBUS_CALL_FLAGS_NONE,
++	                   -1,
++	                   priv->assoc_data->cancellable,
++	                   (GAsyncReadyCallback) assoc_add_network_cb,
++	                   self);
++}
++
++static gboolean
++assoc_fail_on_idle_cb (gpointer user_data)
++{
++	NMSupplicantInterface *self = user_data;
++	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
++	gs_free_error GError *error = NULL;
+ 
+-	add_network (self);
++	priv->assoc_data->fail_on_idle_id = 0;
++	g_set_error (&error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG,
++	             "EAP-FAST is not supported by the supplicant");
++	assoc_return (self, error, "failure due to missing supplicant support");
++	return G_SOURCE_REMOVE;
+ }
+ 
+-gboolean
+-nm_supplicant_interface_set_config (NMSupplicantInterface *self,
+-                                    NMSupplicantConfig *cfg,
+-                                    GError **error)
++/**
++ * nm_supplicant_interface_assoc:
++ * @self: the supplicant interface instance
++ * @cfg: the configuration with the data for the association
++ * @callback: callback invoked when the association completes or fails.
++ * @user_data: data for the callback.
++ *
++ * Calls AddNetwork and SelectNetwork to start associating according to @cfg.
++ *
++ * The callback is invoked exactly once (always) and always asynchronously.
++ * The pending association can be aborted via nm_supplicant_interface_disconnect()
++ * or by destroying @self. In that case, the @callback is invoked synchornously with
++ * an error reason indicating cancellation/disposing (see nm_utils_error_is_cancelled()).
++ */
++void
++nm_supplicant_interface_assoc (NMSupplicantInterface *self,
++                               NMSupplicantConfig *cfg,
++                               NMSupplicantInterfaceAssocCb callback,
++                               gpointer user_data)
+ {
+ 	NMSupplicantInterfacePrivate *priv;
++	AssocData *assoc_data;
+ 
+-	g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE);
++	g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self));
++	g_return_if_fail (NM_IS_SUPPLICANT_CONFIG (cfg));
+ 
+ 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ 
+ 	nm_supplicant_interface_disconnect (self);
+ 
++	assoc_data = g_slice_new0 (AssocData);
++	priv->assoc_data = assoc_data;
++
++	assoc_data->cfg = g_object_ref (cfg);
++	assoc_data->callback = callback;
++	assoc_data->user_data = user_data;
++
++	_LOGD ("assoc[%p]: starting association...", assoc_data);
++
+ 	/* Make sure the supplicant supports EAP-FAST before trying to send
+ 	 * it an EAP-FAST configuration.
+ 	 */
+ 	if (   priv->fast_support == NM_SUPPLICANT_FEATURE_NO
+ 	    && nm_supplicant_config_fast_required (cfg)) {
+-		g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG,
+-		             "EAP-FAST is not supported by the supplicant");
+-		return FALSE;
++		assoc_data->fail_on_idle_id = g_idle_add (assoc_fail_on_idle_cb, self);
++		return;
+ 	}
+ 
+-	g_clear_object (&priv->cfg);
+-	if (cfg) {
+-		priv->assoc_cancellable = g_cancellable_new ();
+-		priv->cfg = g_object_ref (cfg);
+-		g_dbus_proxy_call (priv->iface_proxy,
+-		                   DBUS_INTERFACE_PROPERTIES ".Set",
+-		                   g_variant_new ("(ssv)",
+-		                                  WPAS_DBUS_IFACE_INTERFACE,
+-		                                  "ApScan",
+-		                                  g_variant_new_uint32 (nm_supplicant_config_get_ap_scan (priv->cfg))),
+-		                   G_DBUS_CALL_FLAGS_NONE,
+-		                   -1,
+-		                   priv->assoc_cancellable,
+-		                   (GAsyncReadyCallback) set_ap_scan_cb,
+-		                   self);
+-	}
+-	return TRUE;
++	assoc_data->cancellable = g_cancellable_new();
++	g_dbus_proxy_call (priv->iface_proxy,
++	                   DBUS_INTERFACE_PROPERTIES ".Set",
++	                   g_variant_new ("(ssv)",
++	                                  WPAS_DBUS_IFACE_INTERFACE,
++	                                  "ApScan",
++	                                  g_variant_new_uint32 (nm_supplicant_config_get_ap_scan (priv->assoc_data->cfg))),
++	                   G_DBUS_CALL_FLAGS_NONE,
++	                   -1,
++	                   priv->assoc_data->cancellable,
++	                   (GAsyncReadyCallback) assoc_set_ap_scan_cb,
++	                   self);
+ }
+ 
++/*****************************************************************************/
++
+ static void
+ scan_request_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+ {
+@@ -1449,7 +1501,15 @@ get_property (GObject *object,
+ static void
+ dispose (GObject *object)
+ {
+-	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE ((NMSupplicantInterface *) object);
++	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (object);
++	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
++
++	if (priv->assoc_data) {
++		gs_free GError *error = NULL;
++
++		nm_utils_error_set_cancelled (&error, TRUE, "NMSupplicantInterface");
++		assoc_return (self, error, "cancelled due to dispose of supplicant interface");
++	}
+ 
+ 	if (priv->iface_proxy)
+ 		g_signal_handlers_disconnect_by_data (priv->iface_proxy, object);
+@@ -1457,7 +1517,6 @@ dispose (GObject *object)
+ 
+ 	nm_clear_g_cancellable (&priv->init_cancellable);
+ 	nm_clear_g_cancellable (&priv->other_cancellable);
+-	nm_clear_g_cancellable (&priv->assoc_cancellable);
+ 
+ 	g_clear_object (&priv->wpas_proxy);
+ 	g_clear_pointer (&priv->bss_proxies, (GDestroyNotify) g_hash_table_destroy);
+@@ -1467,9 +1526,6 @@ dispose (GObject *object)
+ 	g_clear_pointer (&priv->object_path, g_free);
+ 	g_clear_pointer (&priv->current_bss, g_free);
+ 
+-	g_clear_object (&priv->cfg);
+-
+-	/* Chain up to the parent class */
+ 	G_OBJECT_CLASS (nm_supplicant_interface_parent_class)->dispose (object);
+ }
+ 
+@@ -1571,14 +1627,6 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
+ 	                  NULL, NULL, NULL,
+ 	                  G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+ 
+-	signals[CONNECTION_ERROR] =
+-	    g_signal_new (NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR,
+-	                  G_OBJECT_CLASS_TYPE (object_class),
+-	                  G_SIGNAL_RUN_LAST,
+-	                  0,
+-	                  NULL, NULL, NULL,
+-	                  G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
+-
+ 	signals[CREDENTIALS_REQUEST] =
+ 	    g_signal_new (NM_SUPPLICANT_INTERFACE_CREDENTIALS_REQUEST,
+ 	                  G_OBJECT_CLASS_TYPE (object_class),
+diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h
+index d78cd28..3498bb5 100644
+--- a/src/supplicant/nm-supplicant-interface.h
++++ b/src/supplicant/nm-supplicant-interface.h
+@@ -68,7 +68,6 @@ typedef enum {
+ #define NM_SUPPLICANT_INTERFACE_BSS_UPDATED      "bss-updated"
+ #define NM_SUPPLICANT_INTERFACE_BSS_REMOVED      "bss-removed"
+ #define NM_SUPPLICANT_INTERFACE_SCAN_DONE        "scan-done"
+-#define NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR "connection-error"
+ #define NM_SUPPLICANT_INTERFACE_CREDENTIALS_REQUEST "credentials-request"
+ 
+ typedef struct _NMSupplicantInterfaceClass NMSupplicantInterfaceClass;
+@@ -83,9 +82,15 @@ NMSupplicantInterface * nm_supplicant_interface_new (const char *ifname,
+ void nm_supplicant_interface_set_supplicant_available (NMSupplicantInterface *self,
+                                                        gboolean available);
+ 
+-gboolean nm_supplicant_interface_set_config (NMSupplicantInterface * iface,
+-                                             NMSupplicantConfig * cfg,
+-                                             GError **error);
++typedef void (*NMSupplicantInterfaceAssocCb) (NMSupplicantInterface *iface,
++                                              GError *error,
++                                              gpointer user_data);
++
++void
++nm_supplicant_interface_assoc (NMSupplicantInterface *self,
++                               NMSupplicantConfig *cfg,
++                               NMSupplicantInterfaceAssocCb callback,
++                               gpointer user_data);
+ 
+ void nm_supplicant_interface_disconnect (NMSupplicantInterface * iface);
+ 
diff --git a/debian/patches/supplicant-trivial-move-code-around.patch b/debian/patches/supplicant-trivial-move-code-around.patch
new file mode 100644
index 00000000..568de27f
--- /dev/null
+++ b/debian/patches/supplicant-trivial-move-code-around.patch
@@ -0,0 +1,167 @@
+From: Thomas Haller <thaller@redhat.com>
+Date: Wed, 15 Feb 2017 00:11:16 +0100
+Subject: supplicant/trivial: move code around
+
+(cherry picked from commit e16bf4f3db3da36f8194195e766029d1b751b5ec)
+---
+ src/supplicant/nm-supplicant-interface.c | 114 ++++++++++++++++---------------
+ 1 file changed, 59 insertions(+), 55 deletions(-)
+
+diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c
+index acbc605..35fe7db 100644
+--- a/src/supplicant/nm-supplicant-interface.c
++++ b/src/supplicant/nm-supplicant-interface.c
+@@ -134,6 +134,27 @@ G_DEFINE_TYPE (NMSupplicantInterface, nm_supplicant_interface, G_TYPE_OBJECT)
+ 
+ /*****************************************************************************/
+ 
++NM_UTILS_LOOKUP_STR_DEFINE (nm_supplicant_interface_state_to_string, NMSupplicantInterfaceState,
++	NM_UTILS_LOOKUP_DEFAULT_WARN ("unknown"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INVALID,         "invalid"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INIT,            "init"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_STARTING,        "starting"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_READY,           "ready"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DISABLED,        "disabled"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED,    "disconnected"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INACTIVE,        "inactive"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_SCANNING,        "scanning"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING,  "authenticating"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING,     "associating"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED,      "associated"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE,  "4-way handshake"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE, "group handshake"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_COMPLETED,       "completed"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DOWN,            "down"),
++);
++
++/*****************************************************************************/
++
+ static void
+ bss_props_changed_cb (GDBusProxy *proxy,
+                       GVariant *changed_properties,
+@@ -1367,6 +1388,8 @@ nm_supplicant_interface_request_scan (NMSupplicantInterface *self, const GPtrArr
+ 	return TRUE;
+ }
+ 
++/*****************************************************************************/
++
+ NMSupplicantInterfaceState
+ nm_supplicant_interface_get_state (NMSupplicantInterface * self)
+ {
+@@ -1375,25 +1398,6 @@ nm_supplicant_interface_get_state (NMSupplicantInterface * self)
+ 	return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->state;
+ }
+ 
+-NM_UTILS_LOOKUP_STR_DEFINE (nm_supplicant_interface_state_to_string, NMSupplicantInterfaceState,
+-	NM_UTILS_LOOKUP_DEFAULT_WARN ("unknown"),
+-	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INVALID,         "invalid"),
+-	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INIT,            "init"),
+-	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_STARTING,        "starting"),
+-	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_READY,           "ready"),
+-	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DISABLED,        "disabled"),
+-	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED,    "disconnected"),
+-	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INACTIVE,        "inactive"),
+-	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_SCANNING,        "scanning"),
+-	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING,  "authenticating"),
+-	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING,     "associating"),
+-	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED,      "associated"),
+-	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE,  "4-way handshake"),
+-	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE, "group handshake"),
+-	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_COMPLETED,       "completed"),
+-	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DOWN,            "down"),
+-);
+-
+ const char *
+ nm_supplicant_interface_get_object_path (NMSupplicantInterface *self)
+ {
+@@ -1420,29 +1424,25 @@ nm_supplicant_interface_get_max_scan_ssids (NMSupplicantInterface *self)
+ 
+ /*****************************************************************************/
+ 
+-NMSupplicantInterface *
+-nm_supplicant_interface_new (const char *ifname,
+-                             NMSupplicantDriver driver,
+-                             NMSupplicantFeature fast_support,
+-                             NMSupplicantFeature ap_support)
+-{
+-	g_return_val_if_fail (ifname != NULL, NULL);
+-
+-	return g_object_new (NM_TYPE_SUPPLICANT_INTERFACE,
+-	                     NM_SUPPLICANT_INTERFACE_IFACE, ifname,
+-	                     NM_SUPPLICANT_INTERFACE_DRIVER, (guint) driver,
+-	                     NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, (int) fast_support,
+-	                     NM_SUPPLICANT_INTERFACE_AP_SUPPORT, (int) ap_support,
+-	                     NULL);
+-}
+-
+ static void
+-nm_supplicant_interface_init (NMSupplicantInterface * self)
++get_property (GObject *object,
++              guint prop_id,
++              GValue *value,
++              GParamSpec *pspec)
+ {
+-	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
++	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE ((NMSupplicantInterface *) object);
+ 
+-	priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT;
+-	priv->bss_proxies = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
++	switch (prop_id) {
++	case PROP_SCANNING:
++		g_value_set_boolean (value, priv->scanning);
++		break;
++	case PROP_CURRENT_BSS:
++		g_value_set_string (value, priv->current_bss);
++		break;
++	default:
++		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++		break;
++	}
+ }
+ 
+ static void
+@@ -1478,24 +1478,28 @@ set_property (GObject *object,
+ }
+ 
+ static void
+-get_property (GObject *object,
+-              guint prop_id,
+-              GValue *value,
+-              GParamSpec *pspec)
++nm_supplicant_interface_init (NMSupplicantInterface * self)
+ {
+-	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE ((NMSupplicantInterface *) object);
++	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ 
+-	switch (prop_id) {
+-	case PROP_SCANNING:
+-		g_value_set_boolean (value, priv->scanning);
+-		break;
+-	case PROP_CURRENT_BSS:
+-		g_value_set_string (value, priv->current_bss);
+-		break;
+-	default:
+-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+-		break;
+-	}
++	priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT;
++	priv->bss_proxies = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
++}
++
++NMSupplicantInterface *
++nm_supplicant_interface_new (const char *ifname,
++                             NMSupplicantDriver driver,
++                             NMSupplicantFeature fast_support,
++                             NMSupplicantFeature ap_support)
++{
++	g_return_val_if_fail (ifname != NULL, NULL);
++
++	return g_object_new (NM_TYPE_SUPPLICANT_INTERFACE,
++	                     NM_SUPPLICANT_INTERFACE_IFACE, ifname,
++	                     NM_SUPPLICANT_INTERFACE_DRIVER, (guint) driver,
++	                     NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, (int) fast_support,
++	                     NM_SUPPLICANT_INTERFACE_AP_SUPPORT, (int) ap_support,
++	                     NULL);
+ }
+ 
+ static void
diff --git a/debian/patches/supplicant-use-nm_clear_g_cancellable-helper.patch b/debian/patches/supplicant-use-nm_clear_g_cancellable-helper.patch
new file mode 100644
index 00000000..3db669e7
--- /dev/null
+++ b/debian/patches/supplicant-use-nm_clear_g_cancellable-helper.patch
@@ -0,0 +1,47 @@
+From: Thomas Haller <thaller@redhat.com>
+Date: Wed, 15 Feb 2017 00:16:38 +0100
+Subject: supplicant: use nm_clear_g_cancellable() helper
+
+(cherry picked from commit da34034b95651d8b68eaa19d7bf115ffc1cd1fb5)
+---
+ src/supplicant/nm-supplicant-interface.c | 18 ++++--------------
+ 1 file changed, 4 insertions(+), 14 deletions(-)
+
+diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c
+index 35fe7db..725f6d4 100644
+--- a/src/supplicant/nm-supplicant-interface.c
++++ b/src/supplicant/nm-supplicant-interface.c
+@@ -271,20 +271,11 @@ set_state (NMSupplicantInterface *self, NMSupplicantInterfaceState new_state)
+ 		g_return_if_fail (new_state > NM_SUPPLICANT_INTERFACE_STATE_READY);
+ 
+ 	if (new_state == NM_SUPPLICANT_INTERFACE_STATE_READY) {
+-		if (priv->other_cancellable) {
+-			g_warn_if_fail (priv->other_cancellable == NULL);
+-			g_cancellable_cancel (priv->other_cancellable);
+-			g_clear_object (&priv->other_cancellable);
+-		}
++		nm_clear_g_cancellable (&priv->other_cancellable);
+ 		priv->other_cancellable = g_cancellable_new ();
+ 	} else if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
+-		if (priv->init_cancellable)
+-			g_cancellable_cancel (priv->init_cancellable);
+-		g_clear_object (&priv->init_cancellable);
+-
+-		if (priv->other_cancellable)
+-			g_cancellable_cancel (priv->other_cancellable);
+-		g_clear_object (&priv->other_cancellable);
++		nm_clear_g_cancellable (&priv->init_cancellable);
++		nm_clear_g_cancellable (&priv->other_cancellable);
+ 
+ 		if (priv->iface_proxy)
+ 			g_signal_handlers_disconnect_by_data (priv->iface_proxy, self);
+@@ -970,8 +961,7 @@ interface_add (NMSupplicantInterface *self)
+ 	/* Move to starting to prevent double-calls of interface_add() */
+ 	set_state (self, NM_SUPPLICANT_INTERFACE_STATE_STARTING);
+ 
+-	g_warn_if_fail (priv->init_cancellable == NULL);
+-	g_clear_object (&priv->init_cancellable);
++	nm_clear_g_cancellable (&priv->init_cancellable);
+ 	priv->init_cancellable = g_cancellable_new ();
+ 
+ 	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
diff --git a/debian/patches/wifi-also-show-the-NM-D-Bus-path-for-the-Wi-Fi-AP-in-nm_w.patch b/debian/patches/wifi-also-show-the-NM-D-Bus-path-for-the-Wi-Fi-AP-in-nm_w.patch
new file mode 100644
index 00000000..c5df4f8c
--- /dev/null
+++ b/debian/patches/wifi-also-show-the-NM-D-Bus-path-for-the-Wi-Fi-AP-in-nm_w.patch
@@ -0,0 +1,51 @@
+From: Thomas Haller <thaller@redhat.com>
+Date: Wed, 15 Feb 2017 13:26:54 +0100
+Subject: wifi: also show the NM D-Bus path for the Wi-Fi AP in
+ nm_wifi_ap_to_string()
+
+(cherry picked from commit 257484e7bac2813d0cf1bc922ef57e6b781b1c96)
+---
+ src/devices/wifi/nm-wifi-ap.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
+index b60020a..7de0838 100644
+--- a/src/devices/wifi/nm-wifi-ap.c
++++ b/src/devices/wifi/nm-wifi-ap.c
+@@ -622,6 +622,7 @@ nm_wifi_ap_to_string (const NMWifiAP *self,
+ {
+ 	const NMWifiAPPrivate *priv;
+ 	const char *supplicant_id = "-";
++	const char *export_path;
+ 	guint32 chan;
+ 	char b1[200];
+ 
+@@ -630,10 +631,16 @@ nm_wifi_ap_to_string (const NMWifiAP *self,
+ 	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 = strrchr (priv->supplicant_path, '/') ?: supplicant_id;
++
++	export_path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (self));
++	if (export_path)
++		export_path = strrchr (export_path, '/') ?: export_path;
++	else
++		export_path = "/";
+ 
+ 	g_snprintf (str_buf, buf_len,
+-	            "%17s %-32s [ %c %3u %3u%% %c W:%04X R:%04X ] %3us %s",
++	            "%17s %-32s [ %c %3u %3u%% %c W:%04X R:%04X ] %3us sup:%s [nm:%s]",
+ 	            priv->address ?: "(none)",
+ 	            nm_sprintf_buf (b1, "%s%s%s",
+ 	                            NM_PRINT_FMT_QUOTED (priv->ssid, "\"", nm_utils_escape_ssid (priv->ssid->data, priv->ssid->len), "\"", "(none)")),
+@@ -650,7 +657,8 @@ nm_wifi_ap_to_string (const NMWifiAP *self,
+ 	            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,
+-	            supplicant_id);
++	            supplicant_id,
++	            export_path);
+ 	return str_buf;
+ }
+ 
diff --git a/debian/patches/wifi-check-for-invalid-BSSID-in-nm_wifi_ap_update_from_pr.patch b/debian/patches/wifi-check-for-invalid-BSSID-in-nm_wifi_ap_update_from_pr.patch
new file mode 100644
index 00000000..bddde3bf
--- /dev/null
+++ b/debian/patches/wifi-check-for-invalid-BSSID-in-nm_wifi_ap_update_from_pr.patch
@@ -0,0 +1,120 @@
+From: Thomas Haller <thaller@redhat.com>
+Date: Wed, 15 Feb 2017 11:50:59 +0100
+Subject: wifi: check for invalid BSSID in nm_wifi_ap_update_from_properties()
+
+In nm_wifi_ap_new_from_properties(), we checked that the BSSID is valid
+and bailed out otherwise. Since we call nm_wifi_ap_update_from_properties()
+on a created BSSID, we should ensure there too that an update does not cause
+the address to become invalid.
+
+In the unlikely case where an update would change a previously valid address
+to an invalid one, we would ignore the update.
+
+Thus, move the check for addresses inside nm_wifi_ap_update_from_properties().
+
+(cherry picked from commit e3a489180b83d55c796d2162eecae01b7351327a)
+---
+ src/devices/wifi/nm-wifi-ap.c | 44 +++++++++++++++++++++----------------------
+ 1 file changed, 22 insertions(+), 22 deletions(-)
+
+diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
+index 96e9d19..8c04137 100644
+--- a/src/devices/wifi/nm-wifi-ap.c
++++ b/src/devices/wifi/nm-wifi-ap.c
+@@ -29,6 +29,7 @@
+ #include "NetworkManagerUtils.h"
+ #include "nm-utils.h"
+ #include "nm-core-internal.h"
++#include "platform/nm-platform.h"
+ 
+ #include "nm-setting-wireless.h"
+ 
+@@ -202,10 +203,24 @@ nm_wifi_ap_get_address (const NMWifiAP *ap)
+ 	return NM_WIFI_AP_GET_PRIVATE (ap)->address;
+ }
+ 
++static void
++nm_wifi_ap_set_address_bin (NMWifiAP *ap, const guint8 *addr /* ETH_ALEN bytes */)
++{
++	NMWifiAPPrivate *priv;
++
++	priv = NM_WIFI_AP_GET_PRIVATE (ap);
++
++	if (   !priv->address
++	    || !nm_utils_hwaddr_matches (addr, ETH_ALEN, priv->address, -1)) {
++		g_free (priv->address);
++		priv->address = nm_utils_hwaddr_ntoa (addr, ETH_ALEN);
++		_notify (ap, PROP_HW_ADDRESS);
++	}
++}
++
+ void
+ nm_wifi_ap_set_address (NMWifiAP *ap, const char *addr)
+ {
+-	NMWifiAPPrivate *priv;
+ 	guint8 addr_buf[ETH_ALEN];
+ 
+ 	g_return_if_fail (NM_IS_WIFI_AP (ap));
+@@ -213,14 +228,7 @@ nm_wifi_ap_set_address (NMWifiAP *ap, const char *addr)
+ 	    || !nm_utils_hwaddr_aton (addr, addr_buf, sizeof (addr_buf)))
+ 		g_return_if_reached ();
+ 
+-	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+-
+-	if (   !priv->address
+-	    || !nm_utils_hwaddr_matches (addr_buf, sizeof (addr_buf), priv->address, -1)) {
+-		g_free (priv->address);
+-		priv->address = nm_utils_hwaddr_ntoa (addr_buf, sizeof (addr_buf));
+-		_notify (ap, PROP_HW_ADDRESS);
+-	}
++	nm_wifi_ap_set_address_bin (ap, addr_buf);
+ }
+ 
+ NM80211Mode
+@@ -406,7 +414,6 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap,
+                                    GVariant *properties)
+ {
+ 	NMWifiAPPrivate *priv;
+-	char *addr;
+ 	const guint8 *bytes;
+ 	GVariant *v;
+ 	gsize len;
+@@ -454,11 +461,10 @@ nm_wifi_ap_update_from_properties (NMWifiAP *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) {
+-			addr = nm_utils_hwaddr_ntoa (bytes, len);
+-			nm_wifi_ap_set_address (ap, addr);
+-			g_free (addr);
+-		}
++		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)
++			nm_wifi_ap_set_address_bin (ap, bytes);
+ 		g_variant_unref (v);
+ 	}
+ 
+@@ -793,10 +799,7 @@ nm_wifi_ap_init (NMWifiAP *ap)
+ NMWifiAP *
+ nm_wifi_ap_new_from_properties (const char *supplicant_path, GVariant *properties)
+ {
+-	const char bad_bssid1[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+-	const char bad_bssid2[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+ 	NMWifiAP *ap;
+-	const char *addr;
+ 
+ 	g_return_val_if_fail (supplicant_path != NULL, NULL);
+ 	g_return_val_if_fail (properties != NULL, NULL);
+@@ -805,10 +808,7 @@ nm_wifi_ap_new_from_properties (const char *supplicant_path, GVariant *propertie
+ 	nm_wifi_ap_update_from_properties (ap, supplicant_path, properties);
+ 
+ 	/* ignore APs with invalid or missing BSSIDs */
+-	addr = nm_wifi_ap_get_address (ap);
+-	if (   !addr
+-	    || nm_utils_hwaddr_matches (addr, -1, bad_bssid1, ETH_ALEN)
+-	    || nm_utils_hwaddr_matches (addr, -1, bad_bssid2, ETH_ALEN)) {
++	if (!nm_wifi_ap_get_address (ap)) {
+ 		g_object_unref (ap);
+ 		return NULL;
+ 	}
diff --git a/debian/patches/wifi-introduce-enum-type-NMSupplicantInterfaceState-inste.patch b/debian/patches/wifi-introduce-enum-type-NMSupplicantInterfaceState-inste.patch
new file mode 100644
index 00000000..a073c92a
--- /dev/null
+++ b/debian/patches/wifi-introduce-enum-type-NMSupplicantInterfaceState-inste.patch
@@ -0,0 +1,375 @@
+From: Thomas Haller <thaller@redhat.com>
+Date: Tue, 14 Feb 2017 01:30:25 +0100
+Subject: wifi: introduce enum type NMSupplicantInterfaceState instead of
+ plain int
+
+Also change the signature of the NM_SUPPLICANT_INTERFACE_STATE signal,
+to have three "int" type arguments. Thereby also fix the subscribers
+to this signal that wrongly had type guint32, instead of guint
+(which happens to be the same underlying type, so no real problem).
+
+https://mail.gnome.org/archives/networkmanager-list/2017-February/msg00021.html
+(cherry picked from commit 5a03de70518bd2f2ed3c6397d09fa9bbfac1608b)
+---
+ src/devices/nm-device-ethernet.c         |  6 ++-
+ src/devices/nm-device-macsec.c           |  6 ++-
+ src/devices/wifi/nm-device-wifi.c        | 40 +++++++--------
+ src/supplicant/nm-supplicant-interface.c | 83 ++++++++++++--------------------
+ src/supplicant/nm-supplicant-interface.h | 10 ++--
+ 5 files changed, 65 insertions(+), 80 deletions(-)
+
+diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
+index 5df16df..8dedd7e 100644
+--- a/src/devices/nm-device-ethernet.c
++++ b/src/devices/nm-device-ethernet.c
+@@ -596,8 +596,8 @@ build_supplicant_config (NMDeviceEthernet *self,
+ 
+ static void
+ supplicant_iface_state_cb (NMSupplicantInterface *iface,
+-                           guint32 new_state,
+-                           guint32 old_state,
++                           int new_state_i,
++                           int old_state_i,
+                            int disconnect_reason,
+                            gpointer user_data)
+ {
+@@ -608,6 +608,8 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
+ 	gboolean success = FALSE;
+ 	NMDeviceState devstate;
+ 	GError *error = NULL;
++	NMSupplicantInterfaceState new_state = new_state_i;
++	NMSupplicantInterfaceState old_state = old_state_i;
+ 
+ 	if (new_state == old_state)
+ 		return;
+diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c
+index c511a0e..58ee4fe 100644
+--- a/src/devices/nm-device-macsec.c
++++ b/src/devices/nm-device-macsec.c
+@@ -412,8 +412,8 @@ time_out:
+ 
+ static void
+ supplicant_iface_state_cb (NMSupplicantInterface *iface,
+-                           guint32 new_state,
+-                           guint32 old_state,
++                           int new_state_i,
++                           int old_state_i,
+                            int disconnect_reason,
+                            gpointer user_data)
+ {
+@@ -424,6 +424,8 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
+ 	gboolean success = FALSE;
+ 	NMDeviceState devstate;
+ 	GError *error = NULL;
++	NMSupplicantInterfaceState new_state = new_state_i;
++	NMSupplicantInterfaceState old_state = old_state_i;
+ 
+ 	if (new_state == old_state)
+ 		return;
+diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
+index 188ed54..07dc36d 100644
+--- a/src/devices/wifi/nm-device-wifi.c
++++ b/src/devices/wifi/nm-device-wifi.c
+@@ -152,8 +152,8 @@ static void cleanup_association_attempt (NMDeviceWifi * self,
+                                          gboolean disconnect);
+ 
+ static void supplicant_iface_state_cb (NMSupplicantInterface *iface,
+-                                       guint32 new_state,
+-                                       guint32 old_state,
++                                       int new_state_i,
++                                       int old_state_i,
+                                        int disconnect_reason,
+                                        gpointer user_data);
+ 
+@@ -428,7 +428,7 @@ periodic_update (NMDeviceWifi *self)
+ 	guint32 new_rate;
+ 	int percent;
+ 	NMDeviceState state;
+-	guint32 supplicant_state;
++	NMSupplicantInterfaceState supplicant_state;
+ 
+ 	/* BSSID and signal strength have meaningful values only if the device
+ 	 * is activated and not scanning.
+@@ -955,7 +955,7 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
+ {
+ 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+ 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+-	guint32 state;
++	NMSupplicantInterfaceState supplicant_state;
+ 
+ 	if (!priv->enabled)
+ 		return FALSE;
+@@ -963,9 +963,9 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
+ 	if (!priv->sup_iface)
+ 		return FALSE;
+ 
+-	state = nm_supplicant_interface_get_state (priv->sup_iface);
+-	if (   state < NM_SUPPLICANT_INTERFACE_STATE_READY
+-	    || state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED)
++	supplicant_state = nm_supplicant_interface_get_state (priv->sup_iface);
++	if (   supplicant_state < NM_SUPPLICANT_INTERFACE_STATE_READY
++	    || supplicant_state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED)
+ 		return FALSE;
+ 
+ 	return TRUE;
+@@ -1242,7 +1242,7 @@ static gboolean
+ scanning_allowed (NMDeviceWifi *self)
+ {
+ 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+-	guint32 sup_state;
++	NMSupplicantInterfaceState supplicant_state;
+ 	NMConnection *connection;
+ 
+ 	g_return_val_if_fail (priv->sup_iface != NULL, FALSE);
+@@ -1274,11 +1274,11 @@ scanning_allowed (NMDeviceWifi *self)
+ 	}
+ 
+ 	/* Don't scan if the supplicant is busy */
+-	sup_state = nm_supplicant_interface_get_state (priv->sup_iface);
+-	if (   sup_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING
+-	    || sup_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED
+-	    || sup_state == NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE
+-	    || sup_state == NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE
++	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
+ 	    || nm_supplicant_interface_get_scanning (priv->sup_iface))
+ 		return FALSE;
+ 
+@@ -1893,7 +1893,7 @@ link_timeout_cb (gpointer user_data)
+ 
+ static gboolean
+ need_new_8021x_secrets (NMDeviceWifi *self,
+-                        guint32 old_state,
++                        NMSupplicantInterfaceState old_state,
+                         const char **setting_name)
+ {
+ 	NMSetting8021x *s_8021x;
+@@ -1947,7 +1947,7 @@ need_new_8021x_secrets (NMDeviceWifi *self,
+ 
+ static gboolean
+ need_new_wpa_psk (NMDeviceWifi *self,
+-                  guint32 old_state,
++                  NMSupplicantInterfaceState old_state,
+                   gint disconnect_reason,
+                   const char **setting_name)
+ {
+@@ -1988,8 +1988,8 @@ need_new_wpa_psk (NMDeviceWifi *self,
+ 
+ static gboolean
+ handle_8021x_or_psk_auth_fail (NMDeviceWifi *self,
+-                               guint32 new_state,
+-                               guint32 old_state,
++                               NMSupplicantInterfaceState new_state,
++                               NMSupplicantInterfaceState old_state,
+                                int disconnect_reason)
+ {
+ 	NMDevice *device = NM_DEVICE (self);
+@@ -2042,8 +2042,8 @@ reacquire_interface_cb (gpointer user_data)
+ 
+ static void
+ supplicant_iface_state_cb (NMSupplicantInterface *iface,
+-                           guint32 new_state,
+-                           guint32 old_state,
++                           int new_state_i,
++                           int old_state_i,
+                            int disconnect_reason,
+                            gpointer user_data)
+ {
+@@ -2052,6 +2052,8 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
+ 	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;
+diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c
+index f932fe8..334a8d2 100644
+--- a/src/supplicant/nm-supplicant-interface.c
++++ b/src/supplicant/nm-supplicant-interface.c
+@@ -71,7 +71,7 @@ typedef struct {
+ 	guint32        ready_count;
+ 
+ 	char *         object_path;
+-	guint32        state;
++	NMSupplicantInterfaceState state;
+ 	int            disconnect_reason;
+ 
+ 	gboolean       scanning;
+@@ -238,12 +238,10 @@ handle_new_bss (NMSupplicantInterface *self, const char *object_path)
+ }
+ 
+ static void
+-set_state (NMSupplicantInterface *self, guint32 new_state)
++set_state (NMSupplicantInterface *self, NMSupplicantInterfaceState new_state)
+ {
+ 	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+-	guint32 old_state = priv->state;
+-
+-	g_return_if_fail (new_state < NM_SUPPLICANT_INTERFACE_STATE_LAST);
++	NMSupplicantInterfaceState old_state = priv->state;
+ 
+ 	if (new_state == priv->state)
+ 		return;
+@@ -286,12 +284,12 @@ set_state (NMSupplicantInterface *self, guint32 new_state)
+ 		priv->disconnect_reason = 0;
+ 
+ 	g_signal_emit (self, signals[STATE], 0,
+-	               priv->state,
+-	               old_state,
+-	               priv->disconnect_reason);
++	               (int) priv->state,
++	               (int) old_state,
++	               (int) priv->disconnect_reason);
+ }
+ 
+-static int
++static NMSupplicantInterfaceState
+ wpas_state_string_to_enum (const char *str_state)
+ {
+ 	if (!strcmp (str_state, "interface_disabled"))
+@@ -315,20 +313,20 @@ wpas_state_string_to_enum (const char *str_state)
+ 	else if (!strcmp (str_state, "completed"))
+ 		return NM_SUPPLICANT_INTERFACE_STATE_COMPLETED;
+ 
+-	return -1;
++	return NM_SUPPLICANT_INTERFACE_STATE_INVALID;
+ }
+ 
+ static void
+ set_state_from_string (NMSupplicantInterface *self, const char *new_state)
+ {
+-	int state;
++	NMSupplicantInterfaceState state;
+ 
+ 	state = wpas_state_string_to_enum (new_state);
+-	if (state == -1) {
++	if (state == NM_SUPPLICANT_INTERFACE_STATE_INVALID) {
+ 		_LOGW ("unknown supplicant state '%s'", new_state);
+ 		return;
+ 	}
+-	set_state (self, (guint32) state);
++	set_state (self, state);
+ }
+ 
+ static void
+@@ -1317,7 +1315,7 @@ nm_supplicant_interface_request_scan (NMSupplicantInterface *self, const GPtrArr
+ 	return TRUE;
+ }
+ 
+-guint32
++NMSupplicantInterfaceState
+ nm_supplicant_interface_get_state (NMSupplicantInterface * self)
+ {
+ 	g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NM_SUPPLICANT_INTERFACE_STATE_DOWN);
+@@ -1325,43 +1323,24 @@ nm_supplicant_interface_get_state (NMSupplicantInterface * self)
+ 	return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->state;
+ }
+ 
+-const char *
+-nm_supplicant_interface_state_to_string (guint32 state)
+-{
+-	switch (state) {
+-	case NM_SUPPLICANT_INTERFACE_STATE_INIT:
+-		return "init";
+-	case NM_SUPPLICANT_INTERFACE_STATE_STARTING:
+-		return "starting";
+-	case NM_SUPPLICANT_INTERFACE_STATE_READY:
+-		return "ready";
+-	case NM_SUPPLICANT_INTERFACE_STATE_DISABLED:
+-		return "disabled";
+-	case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED:
+-		return "disconnected";
+-	case NM_SUPPLICANT_INTERFACE_STATE_INACTIVE:
+-		return "inactive";
+-	case NM_SUPPLICANT_INTERFACE_STATE_SCANNING:
+-		return "scanning";
+-	case NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING:
+-		return "authenticating";
+-	case NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING:
+-		return "associating";
+-	case NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED:
+-		return "associated";
+-	case NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE:
+-		return "4-way handshake";
+-	case NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE:
+-		return "group handshake";
+-	case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED:
+-		return "completed";
+-	case NM_SUPPLICANT_INTERFACE_STATE_DOWN:
+-		return "down";
+-	default:
+-		break;
+-	}
+-	return "unknown";
+-}
++NM_UTILS_LOOKUP_STR_DEFINE (nm_supplicant_interface_state_to_string, NMSupplicantInterfaceState,
++	NM_UTILS_LOOKUP_DEFAULT_WARN ("unknown"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INVALID,         "invalid"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INIT,            "init"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_STARTING,        "starting"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_READY,           "ready"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DISABLED,        "disabled"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED,    "disconnected"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INACTIVE,        "inactive"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_SCANNING,        "scanning"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING,  "authenticating"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING,     "associating"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED,      "associated"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE,  "4-way handshake"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE, "group handshake"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_COMPLETED,       "completed"),
++	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DOWN,            "down"),
++);
+ 
+ const char *
+ nm_supplicant_interface_get_object_path (NMSupplicantInterface *self)
+@@ -1550,7 +1529,7 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
+ 	                  G_SIGNAL_RUN_LAST,
+ 	                  0,
+ 	                  NULL, NULL, NULL,
+-	                  G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INT);
++	                  G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
+ 
+ 	signals[REMOVED] =
+ 	    g_signal_new (NM_SUPPLICANT_INTERFACE_REMOVED,
+diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h
+index 2ef63d1..d78cd28 100644
+--- a/src/supplicant/nm-supplicant-interface.h
++++ b/src/supplicant/nm-supplicant-interface.h
+@@ -28,7 +28,8 @@
+  * Supplicant interface states
+  *   A mix of wpa_supplicant interface states and internal states.
+  */
+-enum {
++typedef enum {
++	NM_SUPPLICANT_INTERFACE_STATE_INVALID = -1,
+ 	NM_SUPPLICANT_INTERFACE_STATE_INIT = 0,
+ 	NM_SUPPLICANT_INTERFACE_STATE_STARTING,
+ 	NM_SUPPLICANT_INTERFACE_STATE_READY,
+@@ -43,8 +44,7 @@ enum {
+ 	NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE,
+ 	NM_SUPPLICANT_INTERFACE_STATE_COMPLETED,
+ 	NM_SUPPLICANT_INTERFACE_STATE_DOWN,
+-	NM_SUPPLICANT_INTERFACE_STATE_LAST
+-};
++} NMSupplicantInterfaceState;
+ 
+ #define NM_TYPE_SUPPLICANT_INTERFACE            (nm_supplicant_interface_get_type ())
+ #define NM_SUPPLICANT_INTERFACE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SUPPLICANT_INTERFACE, NMSupplicantInterface))
+@@ -93,9 +93,9 @@ const char *nm_supplicant_interface_get_object_path (NMSupplicantInterface * ifa
+ 
+ gboolean nm_supplicant_interface_request_scan (NMSupplicantInterface * self, const GPtrArray *ssids);
+ 
+-guint32 nm_supplicant_interface_get_state (NMSupplicantInterface * self);
++NMSupplicantInterfaceState nm_supplicant_interface_get_state (NMSupplicantInterface * self);
+ 
+-const char *nm_supplicant_interface_state_to_string (guint32 state);
++const char *nm_supplicant_interface_state_to_string (NMSupplicantInterfaceState state);
+ 
+ gboolean nm_supplicant_interface_get_scanning (NMSupplicantInterface *self);
+ 
diff --git a/debian/patches/wifi-only-react-on-AP-update-signal-when-there-are-actual.patch b/debian/patches/wifi-only-react-on-AP-update-signal-when-there-are-actual.patch
new file mode 100644
index 00000000..edb6e141
--- /dev/null
+++ b/debian/patches/wifi-only-react-on-AP-update-signal-when-there-are-actual.patch
@@ -0,0 +1,552 @@
+From: Thomas Haller <thaller@redhat.com>
+Date: Wed, 15 Feb 2017 13:20:06 +0100
+Subject: wifi: only react on AP update signal when there are actual changes
+
+Since we emit BSS_UPDATED signal before SCAN_DONE, it is very likely
+that nothing actually changed. This clutters the logs with update
+messages.
+
+Also move the added/removed logging messages inside ap_add_remove().
+We would call ap_add_remove() at several places without logging the
+change.
+
+(cherry picked from commit c9dc0eba65565d361e32200894386da5e5c2d001)
+---
+ src/devices/wifi/nm-device-wifi.c |  29 ++++++---
+ src/devices/wifi/nm-wifi-ap.c     | 133 ++++++++++++++++++++++++--------------
+ src/devices/wifi/nm-wifi-ap.h     |  14 ++--
+ 3 files changed, 109 insertions(+), 67 deletions(-)
+
+diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
+index eaea9a2..6085b37 100644
+--- a/src/devices/wifi/nm-device-wifi.c
++++ b/src/devices/wifi/nm-device-wifi.c
+@@ -444,7 +444,8 @@ periodic_update (NMDeviceWifi *self)
+ 		/* Smooth out the strength to work around crappy drivers */
+ 		percent = nm_platform_wifi_get_quality (NM_PLATFORM_GET, ifindex);
+ 		if (percent >= 0 || ++priv->invalid_strength_counter > 3) {
+-			nm_wifi_ap_set_strength (priv->current_ap, (gint8) percent);
++			if (nm_wifi_ap_set_strength (priv->current_ap, (gint8) percent))
++				_ap_dump (self, priv->current_ap, "updated", 0);
+ 			priv->invalid_strength_counter = 0;
+ 		}
+ 	}
+@@ -477,7 +478,9 @@ ap_add_remove (NMDeviceWifi *self,
+ 		g_hash_table_insert (priv->aps,
+ 		                     (gpointer) nm_exported_object_export ((NMExportedObject *) ap),
+ 		                     g_object_ref (ap));
+-	}
++		_ap_dump (self, ap, "added", 0);
++	} else
++		_ap_dump (self, ap, "removed", 0);
+ 
+ 	g_signal_emit (self, signals[signum], 0, ap);
+ 
+@@ -1643,7 +1646,8 @@ supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
+ 
+ 	found_ap = get_ap_by_supplicant_path (self, object_path);
+ 	if (found_ap) {
+-		nm_wifi_ap_update_from_properties (found_ap, object_path, properties);
++		if (!nm_wifi_ap_update_from_properties (found_ap, object_path, properties))
++			return;
+ 		_ap_dump (self, found_ap, "updated", 0);
+ 	} else {
+ 		gs_unref_object NMWifiAP *ap = NULL;
+@@ -1673,7 +1677,6 @@ supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
+ 		}
+ 
+ 		ap_add_remove (self, ACCESS_POINT_ADDED, ap, TRUE);
+-		_ap_dump (self, ap, "added", 0);
+ 	}
+ 
+ 	/* Update the current AP if the supplicant notified a current BSS change
+@@ -1707,9 +1710,9 @@ supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
+ 		 * when the current AP is changed or cleared.  Set 'fake' to
+ 		 * indicate that this AP is now unknown to the supplicant.
+ 		 */
+-		nm_wifi_ap_set_fake (ap, TRUE);
++		if (nm_wifi_ap_set_fake (ap, TRUE))
++			_ap_dump (self, ap, "updated", 0);
+ 	} else {
+-		_ap_dump (self, ap, "removed", 0);
+ 		ap_add_remove (self, ACCESS_POINT_REMOVED, ap, TRUE);
+ 		schedule_ap_list_dump (self);
+ 	}
+@@ -2500,7 +2503,8 @@ ensure_hotspot_frequency (NMDeviceWifi *self,
+ 	if (!freq)
+ 		freq = (g_strcmp0 (band, "a") == 0) ? 5180 : 2462;
+ 
+-	nm_wifi_ap_set_freq (ap, freq);
++	if (nm_wifi_ap_set_freq (ap, freq))
++		_ap_dump (self, ap, "updated", 0);
+ }
+ 
+ static void
+@@ -2849,6 +2853,8 @@ activation_success_handler (NMDevice *device)
+ 	g_warn_if_fail (priv->current_ap);
+ 	if (priv->current_ap) {
+ 		if (nm_wifi_ap_get_fake (priv->current_ap)) {
++			gboolean ap_changed = FALSE;
++
+ 			/* If the activation AP hasn't been seen by the supplicant in a scan
+ 			 * yet, it will be "fake".  This usually happens for Ad-Hoc and
+ 			 * AP-mode connections.  Fill in the details from the device itself
+@@ -2861,13 +2867,16 @@ activation_success_handler (NMDevice *device)
+ 				if (   nm_platform_wifi_get_bssid (NM_PLATFORM_GET, ifindex, bssid)
+ 				    && nm_ethernet_address_is_valid (bssid, ETH_ALEN)) {
+ 					bssid_str = nm_utils_hwaddr_ntoa (bssid, ETH_ALEN);
+-					nm_wifi_ap_set_address (priv->current_ap, bssid_str);
++					ap_changed |= nm_wifi_ap_set_address (priv->current_ap, bssid_str);
+ 				}
+ 			}
+ 			if (!nm_wifi_ap_get_freq (priv->current_ap))
+-				nm_wifi_ap_set_freq (priv->current_ap, nm_platform_wifi_get_frequency (NM_PLATFORM_GET, ifindex));
++				ap_changed |= nm_wifi_ap_set_freq (priv->current_ap, nm_platform_wifi_get_frequency (NM_PLATFORM_GET, ifindex));
+ 			if (!nm_wifi_ap_get_max_bitrate (priv->current_ap))
+-				nm_wifi_ap_set_max_bitrate (priv->current_ap, nm_platform_wifi_get_rate (NM_PLATFORM_GET, ifindex));
++				ap_changed |= nm_wifi_ap_set_max_bitrate (priv->current_ap, nm_platform_wifi_get_rate (NM_PLATFORM_GET, ifindex));
++
++			if (ap_changed)
++				_ap_dump (self, priv->current_ap, "updated", 0);
+ 		}
+ 
+ 		nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
+diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
+index 8c04137..b60020a 100644
+--- a/src/devices/wifi/nm-wifi-ap.c
++++ b/src/devices/wifi/nm-wifi-ap.c
+@@ -69,8 +69,8 @@ typedef struct {
+ 	NM80211ApSecurityFlags rsn_flags;  /* RSN (WPA2) -related flags */
+ 
+ 	/* Non-scanned attributes */
+-	bool                fake;       /* Whether or not the AP is from a scan */
+-	bool                hotspot;    /* Whether the AP is a local device's hotspot network */
++	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()) */
+ } NMWifiAPPrivate;
+ 
+@@ -123,20 +123,20 @@ const GByteArray * nm_wifi_ap_get_ssid (const NMWifiAP *ap)
+ 	return NM_WIFI_AP_GET_PRIVATE (ap)->ssid;
+ }
+ 
+-void
++gboolean
+ nm_wifi_ap_set_ssid (NMWifiAP *ap, const guint8 *ssid, gsize len)
+ {
+ 	NMWifiAPPrivate *priv;
+ 
+-	g_return_if_fail (NM_IS_WIFI_AP (ap));
+-	g_return_if_fail (ssid == NULL || len > 0);
++	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
++	g_return_val_if_fail (ssid == NULL || len > 0, FALSE);
+ 
+ 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+ 
+ 	/* same SSID */
+ 	if ((ssid && priv->ssid) && (len == priv->ssid->len)) {
+ 		if (!memcmp (ssid, priv->ssid->data, len))
+-			return;
++			return FALSE;
+ 	}
+ 
+ 	if (priv->ssid) {
+@@ -150,49 +150,56 @@ nm_wifi_ap_set_ssid (NMWifiAP *ap, const guint8 *ssid, gsize len)
+ 	}
+ 
+ 	_notify (ap, PROP_SSID);
++	return TRUE;
+ }
+ 
+-static void
++static gboolean
+ nm_wifi_ap_set_flags (NMWifiAP *ap, NM80211ApFlags flags)
+ {
+ 	NMWifiAPPrivate *priv;
+ 
+-	g_return_if_fail (NM_IS_WIFI_AP (ap));
++	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+ 
+ 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+ 
+ 	if (priv->flags != flags) {
+ 		priv->flags = flags;
+ 		_notify (ap, PROP_FLAGS);
++		return TRUE;
+ 	}
++	return FALSE;
+ }
+ 
+-static void
++static gboolean
+ nm_wifi_ap_set_wpa_flags (NMWifiAP *ap, NM80211ApSecurityFlags flags)
+ {
+ 	NMWifiAPPrivate *priv;
+ 
+-	g_return_if_fail (NM_IS_WIFI_AP (ap));
++	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+ 
+ 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+ 	if (priv->wpa_flags != flags) {
+ 		priv->wpa_flags = flags;
+ 		_notify (ap, PROP_WPA_FLAGS);
++		return TRUE;
+ 	}
++	return FALSE;
+ }
+ 
+-static void
++static gboolean
+ nm_wifi_ap_set_rsn_flags (NMWifiAP *ap, NM80211ApSecurityFlags flags)
+ {
+ 	NMWifiAPPrivate *priv;
+ 
+-	g_return_if_fail (NM_IS_WIFI_AP (ap));
++	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+ 
+ 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+ 	if (priv->rsn_flags != flags) {
+ 		priv->rsn_flags = flags;
+ 		_notify (ap, PROP_RSN_FLAGS);
++		return TRUE;
+ 	}
++	return FALSE;
+ }
+ 
+ const char *
+@@ -203,7 +210,7 @@ nm_wifi_ap_get_address (const NMWifiAP *ap)
+ 	return NM_WIFI_AP_GET_PRIVATE (ap)->address;
+ }
+ 
+-static void
++static gboolean
+ nm_wifi_ap_set_address_bin (NMWifiAP *ap, const guint8 *addr /* ETH_ALEN bytes */)
+ {
+ 	NMWifiAPPrivate *priv;
+@@ -215,20 +222,22 @@ nm_wifi_ap_set_address_bin (NMWifiAP *ap, const guint8 *addr /* ETH_ALEN bytes *
+ 		g_free (priv->address);
+ 		priv->address = nm_utils_hwaddr_ntoa (addr, ETH_ALEN);
+ 		_notify (ap, PROP_HW_ADDRESS);
++		return TRUE;
+ 	}
++	return FALSE;
+ }
+ 
+-void
++gboolean
+ nm_wifi_ap_set_address (NMWifiAP *ap, const char *addr)
+ {
+ 	guint8 addr_buf[ETH_ALEN];
+ 
+-	g_return_if_fail (NM_IS_WIFI_AP (ap));
++	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+ 	if (   !addr
+ 	    || !nm_utils_hwaddr_aton (addr, addr_buf, sizeof (addr_buf)))
+-		g_return_if_reached ();
++		g_return_val_if_reached (FALSE);
+ 
+-	nm_wifi_ap_set_address_bin (ap, addr_buf);
++	return nm_wifi_ap_set_address_bin (ap, addr_buf);
+ }
+ 
+ NM80211Mode
+@@ -239,21 +248,23 @@ nm_wifi_ap_get_mode (NMWifiAP *ap)
+ 	return NM_WIFI_AP_GET_PRIVATE (ap)->mode;
+ }
+ 
+-static void
++static gboolean
+ nm_wifi_ap_set_mode (NMWifiAP *ap, const NM80211Mode mode)
+ {
+ 	NMWifiAPPrivate *priv;
+ 
+-	g_return_if_fail (NM_IS_WIFI_AP (ap));
+-	g_return_if_fail (   mode == NM_802_11_MODE_ADHOC
+-	                  || mode == NM_802_11_MODE_INFRA);
++	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, FALSE);
+ 
+ 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+ 
+ 	if (priv->mode != mode) {
+ 		priv->mode = mode;
+ 		_notify (ap, PROP_MODE);
++		return TRUE;
+ 	}
++	return FALSE;
+ }
+ 
+ gboolean
+@@ -272,19 +283,21 @@ nm_wifi_ap_get_strength (NMWifiAP *ap)
+ 	return NM_WIFI_AP_GET_PRIVATE (ap)->strength;
+ }
+ 
+-void
++gboolean
+ nm_wifi_ap_set_strength (NMWifiAP *ap, const gint8 strength)
+ {
+ 	NMWifiAPPrivate *priv;
+ 
+-	g_return_if_fail (NM_IS_WIFI_AP (ap));
++	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+ 
+ 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+ 
+ 	if (priv->strength != strength) {
+ 		priv->strength = strength;
+ 		_notify (ap, PROP_STRENGTH);
++		return TRUE;
+ 	}
++	return FALSE;
+ }
+ 
+ guint32
+@@ -295,20 +308,22 @@ nm_wifi_ap_get_freq (NMWifiAP *ap)
+ 	return NM_WIFI_AP_GET_PRIVATE (ap)->freq;
+ }
+ 
+-void
++gboolean
+ nm_wifi_ap_set_freq (NMWifiAP *ap,
+                      const guint32 freq)
+ {
+ 	NMWifiAPPrivate *priv;
+ 
+-	g_return_if_fail (NM_IS_WIFI_AP (ap));
++	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+ 
+ 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+ 
+ 	if (priv->freq != freq) {
+ 		priv->freq = freq;
+ 		_notify (ap, PROP_FREQUENCY);
++		return TRUE;
+ 	}
++	return FALSE;
+ }
+ 
+ guint32
+@@ -320,19 +335,21 @@ nm_wifi_ap_get_max_bitrate (NMWifiAP *ap)
+ 	return NM_WIFI_AP_GET_PRIVATE (ap)->max_bitrate;
+ }
+ 
+-void
++gboolean
+ nm_wifi_ap_set_max_bitrate (NMWifiAP *ap, guint32 bitrate)
+ {
+ 	NMWifiAPPrivate *priv;
+ 
+-	g_return_if_fail (NM_IS_WIFI_AP (ap));
++	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+ 
+ 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+ 
+ 	if (priv->max_bitrate != bitrate) {
+ 		priv->max_bitrate = bitrate;
+ 		_notify (ap, PROP_MAX_BITRATE);
++		return TRUE;
+ 	}
++	return FALSE;
+ }
+ 
+ gboolean
+@@ -343,27 +360,37 @@ nm_wifi_ap_get_fake (const NMWifiAP *ap)
+ 	return NM_WIFI_AP_GET_PRIVATE (ap)->fake;
+ }
+ 
+-void
++gboolean
+ nm_wifi_ap_set_fake (NMWifiAP *ap, gboolean fake)
+ {
+-	g_return_if_fail (NM_IS_WIFI_AP (ap));
++	NMWifiAPPrivate *priv;
++
++	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+ 
+-	NM_WIFI_AP_GET_PRIVATE (ap)->fake = fake;
++	priv = NM_WIFI_AP_GET_PRIVATE (ap);
++
++	if (priv->fake != !!fake) {
++		priv->fake = fake;
++		return TRUE;
++	}
++	return FALSE;
+ }
+ 
+-static void
++static gboolean
+ nm_wifi_ap_set_last_seen (NMWifiAP *ap, gint32 last_seen)
+ {
+ 	NMWifiAPPrivate *priv;
+ 
+-	g_return_if_fail (NM_IS_WIFI_AP (ap));
++	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+ 
+ 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+ 
+ 	if (priv->last_seen != last_seen) {
+ 		priv->last_seen = last_seen;
+ 		_notify (ap, PROP_LAST_SEEN);
++		return TRUE;
+ 	}
++	return FALSE;
+ }
+ 
+ /*****************************************************************************/
+@@ -408,7 +435,7 @@ security_from_vardict (GVariant *security)
+ 	return flags;
+ }
+ 
+-void
++gboolean
+ nm_wifi_ap_update_from_properties (NMWifiAP *ap,
+                                    const char *supplicant_path,
+                                    GVariant *properties)
+@@ -421,28 +448,30 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap,
+ 	const char *s;
+ 	gint16 i16;
+ 	guint16 u16;
++	gboolean changed = FALSE;
++
++	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
++	g_return_val_if_fail (properties, FALSE);
+ 
+-	g_return_if_fail (ap != NULL);
+-	g_return_if_fail (properties != NULL);
+ 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+ 
+ 	g_object_freeze_notify (G_OBJECT (ap));
+ 
+ 	if (g_variant_lookup (properties, "Privacy", "b", &b) && b)
+-		nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_PRIVACY);
++		changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_PRIVACY);
+ 
+ 	if (g_variant_lookup (properties, "Mode", "&s", &s)) {
+ 		if (!g_strcmp0 (s, "infrastructure"))
+-			nm_wifi_ap_set_mode (ap, NM_802_11_MODE_INFRA);
++			changed |= nm_wifi_ap_set_mode (ap, NM_802_11_MODE_INFRA);
+ 		else if (!g_strcmp0 (s, "ad-hoc"))
+-			nm_wifi_ap_set_mode (ap, NM_802_11_MODE_ADHOC);
++			changed |= nm_wifi_ap_set_mode (ap, NM_802_11_MODE_ADHOC);
+ 	}
+ 
+ 	if (g_variant_lookup (properties, "Signal", "n", &i16))
+-		nm_wifi_ap_set_strength (ap, nm_wifi_utils_level_to_quality (i16));
++		changed |= nm_wifi_ap_set_strength (ap, nm_wifi_utils_level_to_quality (i16));
+ 
+ 	if (g_variant_lookup (properties, "Frequency", "q", &u16))
+-		nm_wifi_ap_set_freq (ap, u16);
++		changed |= nm_wifi_ap_set_freq (ap, u16);
+ 
+ 	v = g_variant_lookup_value (properties, "SSID", G_VARIANT_TYPE_BYTESTRING);
+ 	if (v) {
+@@ -453,7 +482,7 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap,
+ 		if (   bytes && len
+ 		    && !(((len == 8) || (len == 9)) && !memcmp (bytes, "<hidden>", 8))
+ 		    && !nm_utils_is_empty_ssid (bytes, len))
+-			nm_wifi_ap_set_ssid (ap, bytes, len);
++			changed |= nm_wifi_ap_set_ssid (ap, bytes, len);
+ 
+ 		g_variant_unref (v);
+ 	}
+@@ -464,7 +493,7 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap,
+ 		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)
+-			nm_wifi_ap_set_address_bin (ap, bytes);
++			changed |= nm_wifi_ap_set_address_bin (ap, bytes);
+ 		g_variant_unref (v);
+ 	}
+ 
+@@ -476,33 +505,37 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap,
+ 
+ 		/* Find the max AP rate */
+ 		for (i = 0; i < len; i++) {
+-			if (rates[i] > maxrate) {
++			if (rates[i] > maxrate)
+ 				maxrate = rates[i];
+-				nm_wifi_ap_set_max_bitrate (ap, rates[i] / 1000);
+-			}
+ 		}
++		if (maxrate)
++			changed |= nm_wifi_ap_set_max_bitrate (ap, maxrate / 1000);
+ 		g_variant_unref (v);
+ 	}
+ 
+ 	v = g_variant_lookup_value (properties, "WPA", G_VARIANT_TYPE_VARDICT);
+ 	if (v) {
+-		nm_wifi_ap_set_wpa_flags (ap, priv->wpa_flags | security_from_vardict (v));
++		changed |= nm_wifi_ap_set_wpa_flags (ap, priv->wpa_flags | security_from_vardict (v));
+ 		g_variant_unref (v);
+ 	}
+ 
+ 	v = g_variant_lookup_value (properties, "RSN", G_VARIANT_TYPE_VARDICT);
+ 	if (v) {
+-		nm_wifi_ap_set_rsn_flags (ap, priv->rsn_flags | security_from_vardict (v));
++		changed |= nm_wifi_ap_set_rsn_flags (ap, priv->rsn_flags | security_from_vardict (v));
+ 		g_variant_unref (v);
+ 	}
+ 
+-	if (!priv->supplicant_path)
++	if (!priv->supplicant_path) {
+ 		priv->supplicant_path = g_strdup (supplicant_path);
++		changed = TRUE;
++	}
+ 
+-	nm_wifi_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_s ());
+-	priv->fake = FALSE;
++	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));
++
++	return changed;
+ }
+ 
+ static gboolean
+diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h
+index a68aece..5e64087 100644
+--- a/src/devices/wifi/nm-wifi-ap.h
++++ b/src/devices/wifi/nm-wifi-ap.h
+@@ -53,7 +53,7 @@ NMWifiAP *   nm_wifi_ap_new_from_properties      (const char *supplicant_path,
+                                                   GVariant *properties);
+ NMWifiAP *   nm_wifi_ap_new_fake_from_connection (NMConnection *connection);
+ 
+-void              nm_wifi_ap_update_from_properties   (NMWifiAP *ap,
++gboolean          nm_wifi_ap_update_from_properties   (NMWifiAP *ap,
+                                                        const char *supplicant_path,
+                                                        GVariant *properties);
+ 
+@@ -68,25 +68,25 @@ gboolean          nm_wifi_ap_complete_connection      (NMWifiAP *self,
+ const char *      nm_wifi_ap_get_supplicant_path      (NMWifiAP *ap);
+ guint64           nm_wifi_ap_get_id                   (NMWifiAP *ap);
+ const GByteArray *nm_wifi_ap_get_ssid                 (const NMWifiAP *ap);
+-void              nm_wifi_ap_set_ssid                 (NMWifiAP *ap,
++gboolean          nm_wifi_ap_set_ssid                 (NMWifiAP *ap,
+                                                        const guint8 *ssid,
+                                                        gsize len);
+ const char *      nm_wifi_ap_get_address              (const NMWifiAP *ap);
+-void              nm_wifi_ap_set_address              (NMWifiAP *ap,
++gboolean          nm_wifi_ap_set_address              (NMWifiAP *ap,
+                                                        const char *addr);
+ NM80211Mode       nm_wifi_ap_get_mode                 (NMWifiAP *ap);
+ gboolean          nm_wifi_ap_is_hotspot               (NMWifiAP *ap);
+ gint8             nm_wifi_ap_get_strength             (NMWifiAP *ap);
+-void              nm_wifi_ap_set_strength             (NMWifiAP *ap,
++gboolean          nm_wifi_ap_set_strength             (NMWifiAP *ap,
+                                                        gint8 strength);
+ guint32           nm_wifi_ap_get_freq                 (NMWifiAP *ap);
+-void              nm_wifi_ap_set_freq                 (NMWifiAP *ap,
++gboolean          nm_wifi_ap_set_freq                 (NMWifiAP *ap,
+                                                        guint32 freq);
+ guint32           nm_wifi_ap_get_max_bitrate          (NMWifiAP *ap);
+-void              nm_wifi_ap_set_max_bitrate          (NMWifiAP *ap,
++gboolean          nm_wifi_ap_set_max_bitrate          (NMWifiAP *ap,
+                                                        guint32 bitrate);
+ gboolean          nm_wifi_ap_get_fake                 (const NMWifiAP *ap);
+-void              nm_wifi_ap_set_fake                 (NMWifiAP *ap,
++gboolean          nm_wifi_ap_set_fake                 (NMWifiAP *ap,
+                                                        gboolean fake);
+ 
+ const char       *nm_wifi_ap_to_string                (const NMWifiAP *self,