summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorintrigeri <intrigeri@boum.org>2017-04-16 17:04:24 +0000
committerintrigeri <intrigeri@boum.org>2017-04-16 17:04:24 +0000
commitc24108ed8867d450058024b30cd32587d4280693 (patch)
tree67f7850418d9c688832893e81e4ab237ac6af92d
parent9b560dcb094d4e00f370caf2298b827723557501 (diff)
parent8fb79ad7892267b481a5a1ad892d19ae5abbcea5 (diff)
Merge tag 'debian/1.6.2-3'
network-manager Debian release 1.6.2-3
-rw-r--r--debian/changelog20
-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/dhcp-dhclient-parse-interface-statements.patch205
-rw-r--r--debian/patches/libnm-disconnect-signal-from-D-Bus-proxies-on-dispose.patch80
-rw-r--r--debian/patches/series16
-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
18 files changed, 3790 insertions, 0 deletions
diff --git a/debian/changelog b/debian/changelog
index 0981cec..523f634 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,23 @@
+network-manager (1.6.2-3) unstable; urgency=medium
+
+ * 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. (Closes: #854078)
+
+ -- Michael Biebl <biebl@debian.org> Sat, 18 Mar 2017 12:59:56 +0100
+
+network-manager (1.6.2-2) unstable; urgency=medium
+
+ * libnm: disconnect signal from D-Bus proxies on dispose (Closes: #854810)
+ * dhcp/dhclient: parse "interface" statements.
+ Until now any "interface" statement was ignored and any enclosed
+ statement for which we have a special handling was considered, even if
+ belonging to a different interface. This can cause wrong options to be
+ set in the generated dhclient configuration. (Closes: #855910)
+
+ -- Michael Biebl <biebl@debian.org> Tue, 14 Mar 2017 22:12:18 +0100
+
network-manager (1.6.2-1+0.tails1) feature-stretch; urgency=medium
* Non-maintainer upload.
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 0000000..edeb7b0
--- /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 0000000..f2b52eb
--- /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 0000000..3f5a1cc
--- /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/dhcp-dhclient-parse-interface-statements.patch b/debian/patches/dhcp-dhclient-parse-interface-statements.patch
new file mode 100644
index 0000000..c266e89
--- /dev/null
+++ b/debian/patches/dhcp-dhclient-parse-interface-statements.patch
@@ -0,0 +1,205 @@
+From: Beniamino Galvani <bgalvani@redhat.com>
+Date: Mon, 27 Feb 2017 11:11:51 +0100
+Subject: dhcp/dhclient: parse "interface" statements
+
+Until now any "interface" statement was ignored and any enclosed
+statement for which we have a special handling was considered, even if
+belonging to a different interface. This can cause wrong options to be
+set in the generated dhclient configuration.
+
+Change the code to parse "interface" statements and skip the content
+if the interface doesn't match.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=778430
+(cherry picked from commit d405cfd9089f9552969e6a3e1a1c4550fc3c1695)
+(cherry picked from commit 7a05d2a228cd6f65e28ff71c9052e1ca55682ce0)
+---
+ src/dhcp/nm-dhcp-dhclient-utils.c | 49 ++++++++++++++++++++
+ src/dhcp/tests/test-dhcp-dhclient.c | 91 +++++++++++++++++++++++++++++++++++++
+ 2 files changed, 140 insertions(+)
+
+diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c
+index f36451b..d63aba4 100644
+--- a/src/dhcp/nm-dhcp-dhclient-utils.c
++++ b/src/dhcp/nm-dhcp-dhclient-utils.c
+@@ -232,6 +232,39 @@ nm_dhcp_dhclient_get_client_id_from_config_file (const char *path)
+ return NULL;
+ }
+
++static gboolean
++read_interface (const char *line, char *interface, guint size)
++{
++ gs_free char *dup = g_strdup (line + NM_STRLEN ("interface"));
++ char *ptr = dup, *end;
++
++ while (g_ascii_isspace (*ptr))
++ ptr++;
++
++ if (*ptr == '"') {
++ ptr++;
++ end = strchr (ptr, '"');
++ if (!end)
++ return FALSE;
++ *end = '\0';
++ } else {
++ end = strchr (ptr, ' ');
++ if (!end)
++ end = strchr (ptr, '{');
++ if (!end)
++ return FALSE;
++ *end = '\0';
++ }
++
++ if ( ptr[0] == '\0'
++ || strlen (ptr) + 1 > size)
++ return FALSE;
++
++ snprintf (interface, size, "%s", ptr);
++
++ return TRUE;
++}
++
+ char *
+ nm_dhcp_dhclient_create_config (const char *interface,
+ gboolean is_ip6,
+@@ -258,8 +291,10 @@ nm_dhcp_dhclient_create_config (const char *interface,
+ char **lines, **line;
+ gboolean in_alsoreq = FALSE;
+ gboolean in_req = FALSE;
++ char intf[IFNAMSIZ];
+
+ g_string_append_printf (new_contents, _("# Merged from %s\n\n"), orig_path);
++ intf[0] = '\0';
+
+ lines = g_strsplit_set (orig_contents, "\n\r", 0);
+ for (line = lines; lines && *line; line++) {
+@@ -268,6 +303,20 @@ nm_dhcp_dhclient_create_config (const char *interface,
+ if (!strlen (g_strstrip (p)))
+ continue;
+
++ if ( !intf[0]
++ && g_str_has_prefix (p, "interface")) {
++ if (read_interface (p, intf, sizeof (intf)))
++ continue;
++ }
++
++ if (intf[0] && strchr (p, '}')) {
++ intf[0] = '\0';
++ continue;
++ }
++
++ if (intf[0] && !nm_streq (intf, interface))
++ continue;
++
+ if (!strncmp (p, CLIENTID_TAG, strlen (CLIENTID_TAG))) {
+ /* Override config file "dhcp-client-id" and use one from the connection */
+ if (client_id)
+diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c
+index f4cf9c9..361c733 100644
+--- a/src/dhcp/tests/test-dhcp-dhclient.c
++++ b/src/dhcp/tests/test-dhcp-dhclient.c
+@@ -744,6 +744,95 @@ test_write_existing_commented_duid (void)
+
+ /*****************************************************************************/
+
++static const char *interface1_orig = \
++ "interface \"eth0\" {\n"
++ " also request my-option;\n"
++ " initial-delay 5;\n"
++ "}\n"
++ "interface \"eth1\" {\n"
++ " also request another-option;\n"
++ " initial-delay 0;\n"
++ "}\n"
++ "\n"
++ "also request yet-another-option;\n";
++
++static const char *interface1_expected = \
++ "# Created by NetworkManager\n"
++ "# Merged from /path/to/dhclient.conf\n"
++ "\n"
++ "initial-delay 5;\n"
++ "\n"
++ "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
++ "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
++ "option wpad code 252 = string;\n"
++ "\n"
++ "also request my-option;\n"
++ "also request yet-another-option;\n"
++ "also request rfc3442-classless-static-routes;\n"
++ "also request ms-classless-static-routes;\n"
++ "also request static-routes;\n"
++ "also request wpad;\n"
++ "also request ntp-servers;\n"
++ "\n";
++
++static void
++test_interface1 (void)
++{
++ test_config (interface1_orig, interface1_expected,
++ FALSE, NULL, NULL,
++ NULL,
++ NULL,
++ "eth0",
++ NULL);
++}
++
++/*****************************************************************************/
++
++static const char *interface2_orig = \
++ "interface eth0 {\n"
++ " also request my-option;\n"
++ " initial-delay 5;\n"
++ " }\n"
++ "interface eth1 {\n"
++ " initial-delay 0;\n"
++ " request another-option;\n"
++ " } \n"
++ "\n"
++ "also request yet-another-option;\n";
++
++static const char *interface2_expected = \
++ "# Created by NetworkManager\n"
++ "# Merged from /path/to/dhclient.conf\n"
++ "\n"
++ "initial-delay 0;\n"
++ "\n"
++ "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
++ "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
++ "option wpad code 252 = string;\n"
++ "\n"
++ "request; # override dhclient defaults\n"
++ "also request another-option;\n"
++ "also request yet-another-option;\n"
++ "also request rfc3442-classless-static-routes;\n"
++ "also request ms-classless-static-routes;\n"
++ "also request static-routes;\n"
++ "also request wpad;\n"
++ "also request ntp-servers;\n"
++ "\n";
++
++static void
++test_interface2 (void)
++{
++ test_config (interface2_orig, interface2_expected,
++ FALSE, NULL, NULL,
++ NULL,
++ NULL,
++ "eth1",
++ NULL);
++}
++
++/*****************************************************************************/
++
+ static void
+ test_read_lease_ip4_config_basic (void)
+ {
+@@ -891,6 +980,8 @@ main (int argc, char **argv)
+ g_test_add_func ("/dhcp/dhclient/existing_alsoreq", test_existing_alsoreq);
+ g_test_add_func ("/dhcp/dhclient/existing_multiline_alsoreq", test_existing_multiline_alsoreq);
+ g_test_add_func ("/dhcp/dhclient/duids", test_duids);
++ g_test_add_func ("/dhcp/dhclient/interface/1", test_interface1);
++ g_test_add_func ("/dhcp/dhclient/interface/2", test_interface2);
+
+ g_test_add_func ("/dhcp/dhclient/read_duid_from_leasefile", test_read_duid_from_leasefile);
+ g_test_add_func ("/dhcp/dhclient/read_commented_duid_from_leasefile", test_read_commented_duid_from_leasefile);
diff --git a/debian/patches/libnm-disconnect-signal-from-D-Bus-proxies-on-dispose.patch b/debian/patches/libnm-disconnect-signal-from-D-Bus-proxies-on-dispose.patch
new file mode 100644
index 0000000..a7f9d28
--- /dev/null
+++ b/debian/patches/libnm-disconnect-signal-from-D-Bus-proxies-on-dispose.patch
@@ -0,0 +1,80 @@
+From: Beniamino Galvani <bgalvani@redhat.com>
+Date: Thu, 16 Feb 2017 18:48:38 +0100
+Subject: libnm: disconnect signal from D-Bus proxies on dispose
+
+https://bugzilla.gnome.org/show_bug.cgi?id=778615
+(cherry picked from commit 5ae3db75158b2a5d71ea7027fe12638a3d243a4e)
+(cherry picked from commit 0429753dab39c245f280c47ee269d88cf92d7061)
+---
+ libnm/nm-device.c | 2 ++
+ libnm/nm-object.c | 16 +++++++++++++++-
+ 2 files changed, 17 insertions(+), 1 deletion(-)
+
+diff --git a/libnm/nm-device.c b/libnm/nm-device.c
+index 02b5cad..85cd060 100644
+--- a/libnm/nm-device.c
++++ b/libnm/nm-device.c
+@@ -297,6 +297,8 @@ dispose (GObject *object)
+ g_clear_pointer (&priv->available_connections, g_ptr_array_unref);
+ g_clear_pointer (&priv->lldp_neighbors, g_ptr_array_unref);
+
++ if (priv->proxy)
++ g_signal_handlers_disconnect_by_func (priv->proxy, device_state_reason_changed, object);
+ g_clear_object (&priv->proxy);
+
+ G_OBJECT_CLASS (nm_device_parent_class)->dispose (object);
+diff --git a/libnm/nm-object.c b/libnm/nm-object.c
+index 6a47c65..91ced0e 100644
+--- a/libnm/nm-object.c
++++ b/libnm/nm-object.c
+@@ -85,6 +85,7 @@ typedef struct {
+ GError *reload_error;
+
+ GSList *pending; /* ordered list of pending property updates. */
++ GPtrArray *proxies;
+ } NMObjectPrivate;
+
+ enum {
+@@ -939,7 +940,7 @@ _nm_object_register_properties (NMObject *object,
+ proxy = _nm_object_get_proxy (object, interface);
+ g_signal_connect (proxy, "g-properties-changed",
+ G_CALLBACK (properties_changed), object);
+- g_object_unref (proxy);
++ g_ptr_array_add (priv->proxies, proxy);
+
+ instance = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->property_tables = g_slist_prepend (priv->property_tables, instance);
+@@ -1188,6 +1189,7 @@ nm_object_async_initable_iface_init (GAsyncInitableIface *iface)
+ static void
+ nm_object_init (NMObject *object)
+ {
++ NM_OBJECT_GET_PRIVATE (object)->proxies = g_ptr_array_new ();
+ }
+
+ static void
+@@ -1240,6 +1242,7 @@ static void
+ dispose (GObject *object)
+ {
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
++ guint i;
+
+ nm_clear_g_source (&priv->notify_id);
+
+@@ -1251,6 +1254,17 @@ dispose (GObject *object)
+ g_clear_object (&priv->object);
+ g_clear_object (&priv->object_manager);
+
++ if (priv->proxies) {
++ for (i = 0; i < priv->proxies->len; i++) {
++ g_signal_handlers_disconnect_by_func (priv->proxies->pdata[i],
++ properties_changed,
++ object);
++ g_object_unref (priv->proxies->pdata[i]);
++ }
++ g_ptr_array_free (priv->proxies, TRUE);
++ priv->proxies = NULL;
++ }
++
+ G_OBJECT_CLASS (nm_object_parent_class)->dispose (object);
+ }
+
diff --git a/debian/patches/series b/debian/patches/series
index 7aba806..acef697 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -4,4 +4,20 @@ Fix-arping-path.patch
Don-t-make-NetworkManager-D-Bus-activatable.patch
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
dhclient-never-send-the-hostname-in-DHCP-requests.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 0000000..50ca92d
--- /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 0000000..8a83aad
--- /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 0000000..2f5e0fb
--- /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 0000000..82e282e
--- /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 0000000..36b1af7
--- /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 0000000..568de27
--- /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 0000000..3db669e
--- /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 0000000..c5df4f8
--- /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 0000000..bddde3b
--- /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 0000000..a073c92
--- /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 0000000..edb6e14
--- /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,