summary refs log tree commit diff
path: root/src/devices
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2015-05-05 17:48:57 +0200
committerMichael Biebl <biebl@debian.org>2015-05-05 17:48:57 +0200
commitf408e27bccfacf347605a8d98649975a68f38a17 (patch)
tree654fd6695c31511baf919b1c0870d119a352ed75 /src/devices
parent2c032d8f1c6292c1338a615e6ec40252889ba85c (diff)
Imported Upstream version 1.0.2 upstream/1.0.2
Diffstat (limited to 'src/devices')
-rw-r--r--src/devices/adsl/Makefile.in30
-rw-r--r--src/devices/bluetooth/Makefile.in30
-rw-r--r--src/devices/bluetooth/nm-device-bt.c7
-rw-r--r--src/devices/nm-device-bond.c3
-rw-r--r--src/devices/nm-device-bridge.c3
-rw-r--r--src/devices/nm-device-private.h3
-rw-r--r--src/devices/nm-device.c429
-rw-r--r--src/devices/nm-device.h67
-rw-r--r--src/devices/team/Makefile.in30
-rw-r--r--src/devices/team/nm-device-team.c333
-rw-r--r--src/devices/wifi/Makefile.in30
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.c2
-rw-r--r--src/devices/wifi/nm-device-wifi.c82
-rw-r--r--src/devices/wifi/nm-wifi-ap.c2
-rw-r--r--src/devices/wifi/tests/Makefile.am1
-rw-r--r--src/devices/wifi/tests/Makefile.in32
-rw-r--r--src/devices/wifi/tests/test-wifi-ap-utils.c3
-rw-r--r--src/devices/wimax/Makefile.in30
-rw-r--r--src/devices/wimax/nm-device-wimax.c31
-rw-r--r--src/devices/wwan/Makefile.in30
-rw-r--r--src/devices/wwan/nm-device-modem.c71
-rw-r--r--src/devices/wwan/nm-modem-broadband.c385
-rw-r--r--src/devices/wwan/nm-modem.c310
-rw-r--r--src/devices/wwan/nm-modem.h24
-rw-r--r--src/devices/wwan/wwan-exports.ver2
25 files changed, 1376 insertions, 594 deletions
diff --git a/src/devices/adsl/Makefile.in b/src/devices/adsl/Makefile.in
index 7942c559..02da85e7 100644
--- a/src/devices/adsl/Makefile.in
+++ b/src/devices/adsl/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.15 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -15,7 +15,17 @@
 @SET_MAKE@
 
 VPATH = @srcdir@
-am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -79,8 +89,6 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 subdir = src/devices/adsl
-DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
-	$(top_srcdir)/build-aux/depcomp
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/compiler_warnings.m4 \
@@ -97,6 +105,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
@@ -207,6 +216,8 @@ am__define_uniq_tagged_files = \
   done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+	$(top_srcdir)/build-aux/depcomp
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
@@ -233,7 +244,6 @@ CXXCPP = @CXXCPP@
 CXXDEPMODE = @CXXDEPMODE@
 CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
-DATADIRNAME = @DATADIRNAME@
 DBUS_CFLAGS = @DBUS_CFLAGS@
 DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
 DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
@@ -268,6 +278,7 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
 GNUTLS_LIBS = @GNUTLS_LIBS@
 GREP = @GREP@
 GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
 GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
 GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
 GTKDOC_MKPDF = @GTKDOC_MKPDF@
@@ -524,7 +535,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/devices/adsl/Makefile'; \
 	$(am__cd) $(top_srcdir) && \
 	  $(AUTOMAKE) --gnu src/devices/adsl/Makefile
-.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 	@case '$?' in \
 	  *config.status*) \
@@ -596,14 +606,14 @@ distclean-compile:
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
 
 .c.obj:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
 
 .c.lo:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -831,6 +841,8 @@ uninstall-am: uninstall-pkglibLTLIBRARIES
 	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
 	uninstall-am uninstall-pkglibLTLIBRARIES
 
+.PRECIOUS: Makefile
+
 include $(GLIB_MAKEFILE)
 
 @GNOME_CODE_COVERAGE_RULES@
diff --git a/src/devices/bluetooth/Makefile.in b/src/devices/bluetooth/Makefile.in
index 0a285fda..b8e6dcda 100644
--- a/src/devices/bluetooth/Makefile.in
+++ b/src/devices/bluetooth/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.15 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -15,7 +15,17 @@
 @SET_MAKE@
 
 VPATH = @srcdir@
-am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -85,8 +95,6 @@ host_triplet = @host@
 
 @WITH_BLUEZ5_DUN_TRUE@am__append_3 = $(BLUEZ5_LIBS)
 subdir = src/devices/bluetooth
-DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
-	$(top_srcdir)/build-aux/depcomp
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/compiler_warnings.m4 \
@@ -103,6 +111,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
@@ -227,6 +236,8 @@ am__define_uniq_tagged_files = \
   done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+	$(top_srcdir)/build-aux/depcomp
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
@@ -253,7 +264,6 @@ CXXCPP = @CXXCPP@
 CXXDEPMODE = @CXXDEPMODE@
 CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
-DATADIRNAME = @DATADIRNAME@
 DBUS_CFLAGS = @DBUS_CFLAGS@
 DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
 DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
@@ -288,6 +298,7 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
 GNUTLS_LIBS = @GNUTLS_LIBS@
 GREP = @GREP@
 GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
 GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
 GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
 GTKDOC_MKPDF = @GTKDOC_MKPDF@
@@ -536,7 +547,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/devices/bluetooth/Makefile'; \
 	$(am__cd) $(top_srcdir) && \
 	  $(AUTOMAKE) --gnu src/devices/bluetooth/Makefile
-.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 	@case '$?' in \
 	  *config.status*) \
@@ -614,14 +624,14 @@ distclean-compile:
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
 
 .c.obj:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
 
 .c.lo:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -849,6 +859,8 @@ uninstall-am: uninstall-pkglibLTLIBRARIES
 	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
 	uninstall-am uninstall-pkglibLTLIBRARIES
 
+.PRECIOUS: Makefile
+
 include $(GLIB_MAKEFILE)
 
 @GNOME_CODE_COVERAGE_RULES@
diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c
index 689f37c9..61371ddd 100644
--- a/src/devices/bluetooth/nm-device-bt.c
+++ b/src/devices/bluetooth/nm-device-bt.c
@@ -187,6 +187,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 static gboolean
 check_connection_available (NMDevice *device,
                             NMConnection *connection,
+                            NMDeviceCheckConAvailableFlags flags,
                             const char *specific_object)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
@@ -929,7 +930,7 @@ bluez_device_removed (NMBluezDevice *bdev, gpointer user_data)
 /*****************************************************************************/
 
 static gboolean
-is_available (NMDevice *dev)
+is_available (NMDevice *dev, NMDeviceCheckDevAvailableFlags flags)
 {
 	NMDeviceBt *self = NM_DEVICE_BT (dev);
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
@@ -957,7 +958,7 @@ handle_availability_change (NMDeviceBt *self,
 		return;
 	}
 
-	available = nm_device_is_available (device);
+	available = nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE);
 	if (available == old_available)
 		return;
 
@@ -987,7 +988,7 @@ set_mm_running (NMDeviceBt *self, gboolean running)
 	_LOGD (LOGD_BT, "ModemManager now %s",
 	       running ? "available" : "unavailable");
 
-	old_available = nm_device_is_available (NM_DEVICE (self));
+	old_available = nm_device_is_available (NM_DEVICE (self), NM_DEVICE_CHECK_DEV_AVAILABLE_NONE);
 	priv->mm_running = running;
 	handle_availability_change (self, old_available, NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE);
 
diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c
index 9d9fe426..0b2c97d1 100644
--- a/src/devices/nm-device-bond.c
+++ b/src/devices/nm-device-bond.c
@@ -67,7 +67,7 @@ get_generic_capabilities (NMDevice *dev)
 }
 
 static gboolean
-is_available (NMDevice *dev)
+is_available (NMDevice *dev, NMDeviceCheckDevAvailableFlags flags)
 {
 	if (NM_DEVICE_GET_CLASS (dev)->is_up)
 		return NM_DEVICE_GET_CLASS (dev)->is_up (dev);
@@ -77,6 +77,7 @@ is_available (NMDevice *dev)
 static gboolean
 check_connection_available (NMDevice *device,
                             NMConnection *connection,
+                            NMDeviceCheckConAvailableFlags flags,
                             const char *specific_object)
 {
 	/* Connections are always available because the carrier state is determined
diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c
index a70cfd95..a2ed4b41 100644
--- a/src/devices/nm-device-bridge.c
+++ b/src/devices/nm-device-bridge.c
@@ -66,7 +66,7 @@ get_generic_capabilities (NMDevice *dev)
 }
 
 static gboolean
-is_available (NMDevice *dev)
+is_available (NMDevice *dev, NMDeviceCheckDevAvailableFlags flags)
 {
 	if (NM_DEVICE_GET_CLASS (dev)->is_up)
 		return NM_DEVICE_GET_CLASS (dev)->is_up (dev);
@@ -76,6 +76,7 @@ is_available (NMDevice *dev)
 static gboolean
 check_connection_available (NMDevice *device,
                             NMConnection *connection,
+                            NMDeviceCheckConAvailableFlags flags,
                             const char *specific_object)
 {
 	/* Connections are always available because the carrier state is determined
diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h
index 65a9a71a..1f781bbc 100644
--- a/src/devices/nm-device-private.h
+++ b/src/devices/nm-device-private.h
@@ -33,7 +33,8 @@ enum NMActStageReturn {
 	NM_ACT_STAGE_RETURN_SUCCESS,     /* Activation stage done */
 	NM_ACT_STAGE_RETURN_POSTPONE,    /* Long-running operation in progress */
 	NM_ACT_STAGE_RETURN_WAIT,        /* Not ready to start stage; wait */
-	NM_ACT_STAGE_RETURN_STOP         /* Activation stage done; nothing to do */
+	NM_ACT_STAGE_RETURN_STOP,        /* Activation not wanted */
+	NM_ACT_STAGE_RETURN_FINISH       /* Activation stage done; nothing to do */
 };
 
 #define NM_DEVICE_CAP_NONSTANDARD_CARRIER 0x80000000
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index a9899535..47526281 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -201,14 +201,18 @@ typedef struct {
 	char *        hw_addr;
 	guint         hw_addr_len;
 	char *        physical_port_id;
+	guint         dev_id;
 
 	NMUnmanagedFlags        unmanaged_flags;
 	gboolean                is_nm_owned; /* whether the device is a device owned and created by NM */
 	DeleteOnDeactivateData *delete_on_deactivate_data; /* data for scheduled cleanup when deleting link (g_idle_add) */
 
+	GCancellable *deactivating_cancellable;
+
 	guint32         ip4_address;
 
 	NMActRequest *  queued_act_request;
+	gboolean        queued_act_request_is_waiting_for_carrier;
 	NMActRequest *  act_request;
 	guint           act_source_id;
 	gpointer        act_source_func;
@@ -337,6 +341,8 @@ static gboolean addrconf6_start_with_link_ready (NMDevice *self);
 static gboolean dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection);
 static NMActStageReturn linklocal6_start (NMDevice *self);
 
+static void _carrier_wait_check_queued_act_request (NMDevice *self);
+
 static gboolean nm_device_get_default_unmanaged (NMDevice *self);
 
 static void _set_state_full (NMDevice *self,
@@ -381,6 +387,7 @@ state_to_string (NMDeviceState state)
 }
 
 static const char *reason_table[] = {
+	[NM_DEVICE_STATE_REASON_UNKNOWN]                  = "unknown",
 	[NM_DEVICE_STATE_REASON_NONE]                     = "none",
 	[NM_DEVICE_STATE_REASON_NOW_MANAGED]              = "managed",
 	[NM_DEVICE_STATE_REASON_NOW_UNMANAGED]            = "unmanaged",
@@ -440,6 +447,7 @@ static const char *reason_table[] = {
 	[NM_DEVICE_STATE_REASON_MODEM_FAILED]             = "modem-failed",
 	[NM_DEVICE_STATE_REASON_MODEM_AVAILABLE]          = "modem-available",
 	[NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT]        = "sim-pin-incorrect",
+	[NM_DEVICE_STATE_REASON_NEW_ACTIVATION]           = "new-activation",
 };
 
 static const char *
@@ -588,6 +596,7 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface)
 static gboolean
 get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid)
 {
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMLinkType link_type;
 	const guint8 *hwaddr = NULL;
 	size_t hwaddr_len = 0;
@@ -608,6 +617,7 @@ get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid)
 	success = nm_utils_get_ipv6_interface_identifier (link_type,
 	                                                  hwaddr,
 	                                                  hwaddr_len,
+	                                                  priv->dev_id,
 	                                                  out_iid);
 	if (!success) {
 		_LOGW (LOGD_HW, "failed to generate interface identifier "
@@ -692,6 +702,8 @@ nm_device_get_priority (NMDevice *self)
 		return 350;
 	case NM_DEVICE_TYPE_VLAN:
 		return 400;
+	case NM_DEVICE_TYPE_BRIDGE:
+		return 425;
 	case NM_DEVICE_TYPE_MODEM:
 		return 450;
 	case NM_DEVICE_TYPE_BT:
@@ -700,23 +712,37 @@ nm_device_get_priority (NMDevice *self)
 		return 600;
 	case NM_DEVICE_TYPE_OLPC_MESH:
 		return 650;
-	default:
+	case NM_DEVICE_TYPE_GENERIC:
 		return 950;
+	case NM_DEVICE_TYPE_UNKNOWN:
+		return 10000;
+	case NM_DEVICE_TYPE_UNUSED1:
+	case NM_DEVICE_TYPE_UNUSED2:
+		/* omit default: to get compiler warning about missing switch cases */
+		break;
 	}
+	return 11000;
 }
 
 guint32
 nm_device_get_ip4_route_metric (NMDevice *self)
 {
 	NMConnection *connection;
+	NMSettingIPConfig *s_ip = NULL;
 	gint64 route_metric = -1;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32);
 
 	connection = nm_device_get_connection (self);
-
 	if (connection)
-		route_metric = nm_setting_ip_config_get_route_metric (nm_connection_get_setting_ip4_config (connection));
+		s_ip = nm_connection_get_setting_ip4_config (connection);
+
+	/* Slave interfaces don't have IP settings, but we may get here when
+	 * external changes are made or when noticing IP changes when starting
+	 * the slave connection.
+	 */
+	if (s_ip)
+		route_metric = nm_setting_ip_config_get_route_metric (s_ip);
 
 	return route_metric >= 0 ? route_metric : nm_device_get_priority (self);
 }
@@ -725,14 +751,21 @@ guint32
 nm_device_get_ip6_route_metric (NMDevice *self)
 {
 	NMConnection *connection;
+	NMSettingIPConfig *s_ip = NULL;
 	gint64 route_metric = -1;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32);
 
 	connection = nm_device_get_connection (self);
-
 	if (connection)
-		route_metric = nm_setting_ip_config_get_route_metric (nm_connection_get_setting_ip6_config (connection));
+		s_ip = nm_connection_get_setting_ip6_config (connection);
+
+	/* Slave interfaces don't have IP settings, but we may get here when
+	 * external changes are made or when noticing IP changes when starting
+	 * the slave connection.
+	 */
+	if (s_ip)
+		route_metric = nm_setting_ip_config_get_route_metric (s_ip);
 
 	return route_metric >= 0 ? route_metric : nm_device_get_priority (self);
 }
@@ -1128,6 +1161,7 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier)
 			g_source_remove (priv->carrier_wait_id);
 			priv->carrier_wait_id = 0;
 			nm_device_remove_pending_action (self, "carrier wait", TRUE);
+			_carrier_wait_check_queued_act_request (self);
 		}
 	} else if (state <= NM_DEVICE_STATE_DISCONNECTED) {
 		_LOGI (LOGD_DEVICE, "link disconnected");
@@ -1717,16 +1751,24 @@ nm_device_removed (NMDevice *self)
 
 
 static gboolean
-is_available (NMDevice *self)
+is_available (NMDevice *self, NMDeviceCheckDevAvailableFlags flags)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	return priv->carrier || priv->ignore_carrier;
+	if (priv->carrier || priv->ignore_carrier)
+		return TRUE;
+
+	if (NM_FLAGS_HAS (flags, NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER))
+		return TRUE;
+
+	return FALSE;
 }
 
 /**
  * nm_device_is_available:
  * @self: the #NMDevice
+ * @flags: additional flags to influence the check. Flags have the
+ *   meaning to increase the availability of a device.
  *
  * Checks if @self would currently be capable of activating a
  * connection. In particular, it checks that the device is ready (eg,
@@ -1742,14 +1784,14 @@ is_available (NMDevice *self)
  * Returns: %TRUE or %FALSE
  */
 gboolean
-nm_device_is_available (NMDevice *self)
+nm_device_is_available (NMDevice *self, NMDeviceCheckDevAvailableFlags flags)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	if (priv->firmware_missing)
 		return FALSE;
 
-	return NM_DEVICE_GET_CLASS (self)->is_available (self);
+	return NM_DEVICE_GET_CLASS (self)->is_available (self, flags);
 }
 
 gboolean
@@ -1873,7 +1915,7 @@ can_auto_connect (NMDevice *self,
 	if (!nm_setting_connection_get_autoconnect (s_con))
 		return FALSE;
 
-	return nm_device_connection_is_available (self, connection, FALSE);
+	return nm_device_check_connection_available (self, connection, NM_DEVICE_CHECK_CON_AVAILABLE_NONE, NULL);
 }
 
 /**
@@ -3760,7 +3802,7 @@ dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason)
 		}
 
 		/* success; already have the LL address; kick off DHCP */
-		g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
+		g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS || ret == NM_ACT_STAGE_RETURN_FINISH);
 	}
 
 	if (!dhcp6_start_with_link_ready (self, connection)) {
@@ -3925,7 +3967,7 @@ linklocal6_start (NMDevice *self)
 	linklocal6_cleanup (self);
 
 	if (have_ip6_address (priv->ip6_config, TRUE))
-		return NM_ACT_STAGE_RETURN_SUCCESS;
+		return NM_ACT_STAGE_RETURN_FINISH;
 
 	connection = nm_device_get_connection (self);
 	g_assert (connection);
@@ -4126,14 +4168,8 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 		}
 	}
 
-	/* hop_limit == 0 is a special value "unspecified", so do not touch
-	 * in this case */
-	if (changed & NM_RDISC_CONFIG_HOP_LIMIT && rdisc->hop_limit > 0) {
-		char val[16];
-
-		g_snprintf (val, sizeof (val), "%d", rdisc->hop_limit);
-		nm_device_ipv6_sysctl_set (self, "hop_limit", val);
-	}
+	if (changed & NM_RDISC_CONFIG_HOP_LIMIT)
+		nm_platform_sysctl_set_ip6_hop_limit_safe (nm_device_get_ip_iface (self), rdisc->hop_limit);
 
 	if (changed & NM_RDISC_CONFIG_MTU) {
 		char val[16];
@@ -4241,7 +4277,7 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 	}
 
 	/* success; already have the LL address; kick off router discovery */
-	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
+	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS || ret == NM_ACT_STAGE_RETURN_FINISH);
 	return addrconf6_start_with_link_ready (self);
 }
 
@@ -4345,11 +4381,12 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable)
 		if (enable) {
 			/* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */
 			value = nm_platform_sysctl_get (nm_utils_ip6_property_path (iface, "disable_ipv6"));
-			if (g_strcmp0 (value, "0") == 0) {
+			if (g_strcmp0 (value, "0") == 0)
 				nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1");
-				nm_device_ipv6_sysctl_set (self, "disable_ipv6", "0");
-			}
 			g_free (value);
+
+			/* Ensure IPv6 is enabled */
+			nm_device_ipv6_sysctl_set (self, "disable_ipv6", "0");
 		}
 
 	}
@@ -4445,7 +4482,7 @@ act_stage3_ip6_config_start (NMDevice *self,
 	connection = nm_device_get_connection (self);
 	g_assert (connection);
 
-	if (   connection_ip4_method_requires_carrier (connection, NULL)
+	if (   connection_ip6_method_requires_carrier (connection, NULL)
 	    && priv->is_master
 	    && !priv->carrier) {
 		_LOGI (LOGD_IP6 | LOGD_DEVICE,
@@ -4519,7 +4556,7 @@ act_stage3_ip6_config_start (NMDevice *self,
 			ret = NM_ACT_STAGE_RETURN_POSTPONE;
 	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) {
 		ret = linklocal6_start (self);
-		if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
+		if (ret == NM_ACT_STAGE_RETURN_FINISH) {
 			/* New blank config; LL address is already in priv->ext_ip6_config */
 			*out_config = nm_ip6_config_new ();
 			g_assert (*out_config);
@@ -4626,8 +4663,13 @@ nm_device_activate_stage3_ip6_start (NMDevice *self)
 		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
 		return FALSE;
 	} else if (ret == NM_ACT_STAGE_RETURN_STOP) {
-		/* Early finish */
+		/* Activation not wanted */
 		priv->ip6_state = IP_FAIL;
+	} else if (ret == NM_ACT_STAGE_RETURN_FINISH) {
+		/* Early finish, nothing more to do */
+		priv->ip6_state = IP_DONE;
+		if (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG)
+			nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE);
 	} else if (ret == NM_ACT_STAGE_RETURN_WAIT) {
 		/* Wait for something to try IP config again */
 		priv->ip6_state = IP_WAIT;
@@ -5569,6 +5611,15 @@ disconnect_cb (NMDevice *self,
 }
 
 static void
+_clear_queued_act_request (NMDevicePrivate *priv)
+{
+	if (priv->queued_act_request) {
+		nm_active_connection_set_state ((NMActiveConnection *) priv->queued_act_request, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED);
+		g_clear_object (&priv->queued_act_request);
+	}
+}
+
+static void
 impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context)
 {
 	NMConnection *connection;
@@ -5670,26 +5721,97 @@ _device_activate (NMDevice *self, NMActRequest *req)
 	nm_device_activate_schedule_stage1_device_prepare (self);
 }
 
+static void
+_carrier_wait_check_queued_act_request (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMActRequest *queued_req;
+
+	if (   !priv->queued_act_request
+	    || !priv->queued_act_request_is_waiting_for_carrier)
+		return;
+
+	priv->queued_act_request_is_waiting_for_carrier = FALSE;
+	if (!priv->carrier) {
+		_LOGD (LOGD_DEVICE, "Cancel queued activation request as we have no carrier after timeout");
+		g_clear_object (&priv->queued_act_request);
+	} else {
+		_LOGD (LOGD_DEVICE, "Activate queued activation request as we now have carrier");
+		queued_req = priv->queued_act_request;
+		priv->queued_act_request = NULL;
+		_device_activate (self, queued_req);
+		g_object_unref (queued_req);
+	}
+}
+
+static gboolean
+_carrier_wait_check_act_request_must_queue (NMDevice *self, NMActRequest *req)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection;
+
+	/* If we have carrier or if we are not waiting for it, the activation
+	 * request is not blocked waiting for carrier. */
+	if (priv->carrier)
+		return FALSE;
+	if (priv->carrier_wait_id == 0)
+		return FALSE;
+
+	connection = nm_act_request_get_connection (req);
+
+	if (!nm_device_check_connection_available (self, connection, NM_DEVICE_CHECK_CON_AVAILABLE_ALL, NULL)) {
+		/* We passed all @flags we have, and no @specific_object.
+		 * This equals maximal availability, if a connection is not available
+		 * in this case, it is not waiting for carrier.
+		 *
+		 * Actually, why are we even trying to activate it? Strange, but whatever
+		 * the reason, don't wait for carrier.
+		 */
+		return FALSE;
+	}
+
+	if (nm_device_check_connection_available (self, connection, NM_DEVICE_CHECK_CON_AVAILABLE_ALL & ~_NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER, NULL)) {
+		/* The connection was available with flags ALL, and it is still available
+		 * if we pretend not to wait for carrier. That means that the
+		 * connection is available now, and does not wait for carrier.
+		 *
+		 * Since the flags increase the availability of a connection, when checking
+		 * ALL&~WAITING_CARRIER, it means that we certainly would wait for carrier. */
+		return FALSE;
+	}
+
+	/* The activation request must wait for carrier. */
+	return TRUE;
+}
+
 void
 nm_device_queue_activation (NMDevice *self, NMActRequest *req)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	gboolean must_queue;
+
+	must_queue = _carrier_wait_check_act_request_must_queue (self, req);
 
-	if (!priv->act_request) {
+	if (!priv->act_request && !must_queue) {
 		/* Just activate immediately */
 		_device_activate (self, req);
 		return;
 	}
 
 	/* supercede any already-queued request */
-	g_clear_object (&priv->queued_act_request);
+	_clear_queued_act_request (priv);
 	priv->queued_act_request = g_object_ref (req);
+	priv->queued_act_request_is_waiting_for_carrier = must_queue;
 
-	/* Deactivate existing activation request first */
-	_LOGI (LOGD_DEVICE, "disconnecting for new activation request.");
-	nm_device_state_changed (self,
-	                         NM_DEVICE_STATE_DEACTIVATING,
-	                         NM_DEVICE_STATE_REASON_NONE);
+	_LOGD (LOGD_DEVICE, "queue activation request waiting for %s", must_queue ? "carrier" : "currently active connection to disconnect");
+
+	if (priv->act_request) {
+		/* Deactivate existing activation request first */
+		_LOGI (LOGD_DEVICE, "disconnecting for new activation request.");
+		nm_device_state_changed (self,
+		                         NM_DEVICE_STATE_DEACTIVATING,
+		                         NM_DEVICE_STATE_REASON_NEW_ACTIVATION);
+	}
 }
 
 /*
@@ -5915,7 +6037,7 @@ nm_device_set_ip6_config (NMDevice *self,
 				nm_ip6_config_export (new_config);
 			}
 
-			_LOGD (LOGD_IP4, "set IP6Config instance (%s)",
+			_LOGD (LOGD_IP6, "set IP6Config instance (%s)",
 			       nm_ip6_config_get_dbus_path (new_config));
 		}
 	} else if (old_config) {
@@ -6231,6 +6353,9 @@ carrier_wait_timeout (gpointer user_data)
 
 	NM_DEVICE_GET_PRIVATE (self)->carrier_wait_id = 0;
 	nm_device_remove_pending_action (self, "carrier wait", TRUE);
+
+	_carrier_wait_check_queued_act_request (self);
+
 	return G_SOURCE_REMOVE;
 }
 
@@ -6295,12 +6420,11 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 	 * a timeout is reached.
 	 */
 	if (device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) {
-		if (priv->carrier_wait_id) {
+		if (priv->carrier_wait_id)
 			g_source_remove (priv->carrier_wait_id);
-			nm_device_remove_pending_action (self, "carrier wait", TRUE);
-		}
+		else
+			nm_device_add_pending_action (self, "carrier wait", TRUE);
 		priv->carrier_wait_id = g_timeout_add_seconds (5, carrier_wait_timeout, self);
-		nm_device_add_pending_action (self, "carrier wait", TRUE);
 	}
 
 	/* Can only get HW address of some devices when they are up */
@@ -6728,12 +6852,12 @@ nm_device_set_unmanaged_quitting (NMDevice *self)
 
 	/* It's OK to block here because we're quitting */
 	if (nm_device_is_activating (self) || priv->state == NM_DEVICE_STATE_ACTIVATED)
-		_set_state_full (self, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_REMOVED, TRUE);
+		_set_state_full (self, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_NOW_UNMANAGED, TRUE);
 
 	nm_device_set_unmanaged (self,
 	                         NM_UNMANAGED_INTERNAL,
 	                         TRUE,
-	                         NM_DEVICE_STATE_REASON_REMOVED);
+	                         NM_DEVICE_STATE_REASON_NOW_UNMANAGED);
 }
 
 /**
@@ -6788,48 +6912,45 @@ nm_device_set_dhcp_anycast_address (NMDevice *self, const char *addr)
 }
 
 /**
- * nm_device_connection_is_available():
+ * nm_device_check_connection_available():
  * @self: the #NMDevice
  * @connection: the #NMConnection to check for availability
- * @allow_device_override: set to %TRUE to let the device do specific checks
+ * @flags: flags to affect the decision making of whether a connection
+ *   is available. Adding a flag can only make a connection more available,
+ *   not less.
+ * @specific_object: a device type dependent argument to further
+ *   filter the result. Passing a non %NULL specific object can only reduce
+ *   the availability of a connection.
  *
- * Check if @connection is available to be activated on @self.  Normally this
- * only checks if the connection is in @self's AvailableConnections property.
- * If @allow_device_override is %TRUE then the device is asked to do specific
- * checks that may bypass the AvailableConnections property.
+ * Check if @connection is available to be activated on @self.
  *
  * Returns: %TRUE if @connection can be activated on @self
  */
 gboolean
-nm_device_connection_is_available (NMDevice *self,
-                                   NMConnection *connection,
-                                   gboolean allow_device_override)
+nm_device_check_connection_available (NMDevice *self,
+                                      NMConnection *connection,
+                                      NMDeviceCheckConAvailableFlags flags,
+                                      const char *specific_object)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	gboolean available = FALSE;
+	NMDeviceState state;
 
-	if (nm_device_get_default_unmanaged (self) && (priv->state == NM_DEVICE_STATE_UNMANAGED)) {
-		/* default-unmanaged  devices in UNMANAGED state have no available connections
-		 * so we must manually check whether the connection is available here.
-		 */
-		if (   nm_device_check_connection_compatible (self, connection)
-		    && NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, NULL))
-			return TRUE;
-	}
+	state = nm_device_get_state (self);
+	if (state < NM_DEVICE_STATE_UNMANAGED)
+		return FALSE;
+	if (   state < NM_DEVICE_STATE_UNAVAILABLE
+	    && nm_device_get_unmanaged_flag (self, NM_UNMANAGED_ALL & ~NM_UNMANAGED_DEFAULT))
+		return FALSE;
+	if (   state < NM_DEVICE_STATE_DISCONNECTED
+	    && (   (   !NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER)
+	            && !nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE))
+	        || (    NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER)
+	            && !nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER))))
+		return FALSE;
 
-	available = !!g_hash_table_lookup (priv->available_connections, connection);
-	if (!available && allow_device_override) {
-		/* FIXME: hack for hidden WiFi becuase clients didn't consistently
-		 * set the 'hidden' property to indicate hidden SSID networks.  If
-		 * activating but the network isn't available let the device recheck
-		 * availability.
-		 */
-		if (   nm_device_check_connection_compatible (self, connection)
-		    && NM_DEVICE_GET_CLASS (self)->check_connection_available_wifi_hidden)
-			available = NM_DEVICE_GET_CLASS (self)->check_connection_available_wifi_hidden (self, connection);
-	}
+	if (!nm_device_check_connection_compatible (self, connection))
+		return FALSE;
 
-	return available;
+	return NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, flags, specific_object);
 }
 
 static void
@@ -6849,17 +6970,10 @@ _clear_available_connections (NMDevice *self, gboolean do_signal)
 static gboolean
 _try_add_available_connection (NMDevice *self, NMConnection *connection)
 {
-	if (   nm_device_get_state (self) < NM_DEVICE_STATE_DISCONNECTED
-	    && !nm_device_get_default_unmanaged (self))
-		return FALSE;
-
-	if (nm_device_check_connection_compatible (self, connection)) {
-		if (NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, NULL)) {
-			g_hash_table_insert (NM_DEVICE_GET_PRIVATE (self)->available_connections,
-			                     g_object_ref (connection),
-			                     GUINT_TO_POINTER (1));
-			return TRUE;
-		}
+	if (nm_device_check_connection_available (self, connection, NM_DEVICE_CHECK_CON_AVAILABLE_NONE, NULL)) {
+		g_hash_table_add (NM_DEVICE_GET_PRIVATE (self)->available_connections,
+		                  g_object_ref (connection));
+		return TRUE;
 	}
 	return FALSE;
 }
@@ -6873,15 +6987,28 @@ _del_available_connection (NMDevice *self, NMConnection *connection)
 static gboolean
 check_connection_available (NMDevice *self,
                             NMConnection *connection,
+                            NMDeviceCheckConAvailableFlags flags,
                             const char *specific_object)
 {
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
+
 	/* Connections which require a network connection are not available when
 	 * the device has no carrier, even with ignore-carrer=TRUE.
 	 */
-	if (NM_DEVICE_GET_PRIVATE (self)->carrier == FALSE)
-		return connection_requires_carrier (connection) ? FALSE : TRUE;
+	if (   priv->carrier
+	    || !connection_requires_carrier (connection))
+		return TRUE;
 
-	return TRUE;
+	if (   NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER)
+	    && priv->carrier_wait_id != 0) {
+		/* The device has no carrier though the connection requires it.
+		 *
+		 * If we are still waiting for carrier, the connection is available
+		 * for an explicit user-request. */
+		return TRUE;
+	}
+
+	return FALSE;
 }
 
 void
@@ -6933,8 +7060,8 @@ nm_device_get_available_connections (NMDevice *self, const char *specific_object
 			/* If a specific object is given, only include connections that are
 			 * compatible with it.
 			 */
-			if (   !specific_object
-			    || NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, specific_object))
+			if (   !specific_object /* << Optimization: we know that the connection is available without @specific_object.  */
+			    || nm_device_check_connection_available (self, connection, NM_DEVICE_CHECK_CON_AVAILABLE_NONE, specific_object))
 				g_ptr_array_add (array, connection);
 		}
 	}
@@ -7190,7 +7317,7 @@ _cleanup_generic_post (NMDevice *self, gboolean deconfigure)
  *
  */
 static void
-nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason)
+nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, gboolean deconfigure)
 {
 	NMDevicePrivate *priv;
 	int ifindex;
@@ -7205,12 +7332,14 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason)
 	/* Save whether or not we tried IPv6 for later */
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	_cleanup_generic_pre (self, TRUE);
+	_cleanup_generic_pre (self, deconfigure);
 
 	/* Turn off kernel IPv6 */
-	set_disable_ipv6 (self, "1");
-	nm_device_ipv6_sysctl_set (self, "accept_ra", "0");
-	nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0");
+	if (deconfigure) {
+		set_disable_ipv6 (self, "1");
+		nm_device_ipv6_sysctl_set (self, "accept_ra", "0");
+		nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0");
+	}
 
 	/* Call device type-specific deactivation */
 	if (NM_DEVICE_GET_CLASS (self)->deactivate)
@@ -7231,7 +7360,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason)
 		nm_platform_address_flush (ifindex);
 	}
 
-	_cleanup_generic_post (self, TRUE);
+	_cleanup_generic_post (self, deconfigure);
 }
 
 static char *
@@ -7332,7 +7461,7 @@ nm_device_spawn_iface_helper (NMDevice *self)
 		}
 
 		hostname = nm_dhcp_client_get_hostname (priv->dhcp4_client);
-		if (client_id) {
+		if (hostname) {
 			g_ptr_array_add (argv, g_strdup ("--dhcp4-hostname"));
 			g_ptr_array_add (argv, g_strdup (hostname));
 		}
@@ -7430,6 +7559,69 @@ ip6_managed_setup (NMDevice *self)
 }
 
 static void
+deactivate_async_ready (NMDevice *self,
+                        GAsyncResult *res,
+                        gpointer user_data)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDeviceStateReason reason = GPOINTER_TO_UINT (user_data);
+	GError *error = NULL;
+
+	NM_DEVICE_GET_CLASS (self)->deactivate_async_finish (self, res, &error);
+
+	/* If operation cancelled, just return */
+	if (   g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)
+	    || (priv->deactivating_cancellable && g_cancellable_is_cancelled (priv->deactivating_cancellable))) {
+		nm_log_warn (LOGD_DEVICE, "Deactivation (%s) cancelled",
+		             nm_device_get_iface (self));
+	}
+	/* In every other case, transition to the DISCONNECTED state */
+	else {
+		if (error)
+			nm_log_warn (LOGD_DEVICE, "Deactivation (%s) failed: %s",
+			             nm_device_get_iface (self),
+			             error->message);
+		nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, reason);
+	}
+
+	g_clear_object (&priv->deactivating_cancellable);
+	g_clear_error (&error);
+}
+
+static void
+deactivate_dispatcher_complete (guint call_id, gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDeviceStateReason reason;
+
+	g_return_if_fail (call_id == priv->dispatcher.call_id);
+	g_return_if_fail (priv->dispatcher.post_state == NM_DEVICE_STATE_DISCONNECTED);
+
+	reason = priv->dispatcher.post_state_reason;
+
+	priv->dispatcher.call_id = 0;
+	priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN;
+	priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE;
+
+	if (priv->deactivating_cancellable) {
+		g_warn_if_reached ();
+		g_cancellable_cancel (priv->deactivating_cancellable);
+		g_clear_object (&priv->deactivating_cancellable);
+	}
+
+	if (   NM_DEVICE_GET_CLASS (self)->deactivate_async
+	    && NM_DEVICE_GET_CLASS (self)->deactivate_async_finish) {
+		priv->deactivating_cancellable = g_cancellable_new ();
+		NM_DEVICE_GET_CLASS (self)->deactivate_async (self,
+		                                              priv->deactivating_cancellable,
+		                                              (GAsyncReadyCallback) deactivate_async_ready,
+		                                              GUINT_TO_POINTER (reason));
+	} else
+		nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, reason);
+}
+
+static void
 _set_state_full (NMDevice *self,
                  NMDeviceState state,
                  NMDeviceStateReason reason,
@@ -7473,13 +7665,15 @@ _set_state_full (NMDevice *self,
 	nm_device_queued_state_clear (self);
 
 	dispatcher_cleanup (self);
+	if (priv->deactivating_cancellable)
+		g_cancellable_cancel (priv->deactivating_cancellable);
 
 	/* Cache the activation request for the dispatcher */
 	req = priv->act_request ? g_object_ref (priv->act_request) : NULL;
 
 	if (state <= NM_DEVICE_STATE_UNAVAILABLE) {
 		_clear_available_connections (self, TRUE);
-		g_clear_object (&priv->queued_act_request);
+		_clear_queued_act_request (priv);
 	}
 
 	/* Update the available connections list when a device first becomes available */
@@ -7494,12 +7688,16 @@ _set_state_full (NMDevice *self,
 	case NM_DEVICE_STATE_UNMANAGED:
 		nm_device_set_firmware_missing (self, FALSE);
 		if (old_state > NM_DEVICE_STATE_UNMANAGED) {
-			/* Clean up if the device is now unmanaged but was activated */
-			if (nm_device_get_act_request (self))
-				nm_device_cleanup (self, reason);
-			nm_device_take_down (self, TRUE);
-			set_nm_ipv6ll (self, FALSE);
-			restore_ip6_properties (self);
+			if (reason == NM_DEVICE_STATE_REASON_REMOVED) {
+				nm_device_cleanup (self, reason, FALSE);
+			} else {
+				/* Clean up if the device is now unmanaged but was activated */
+				if (nm_device_get_act_request (self))
+					nm_device_cleanup (self, reason, TRUE);
+				nm_device_take_down (self, TRUE);
+				set_nm_ipv6ll (self, FALSE);
+				restore_ip6_properties (self);
+			}
 		}
 		break;
 	case NM_DEVICE_STATE_UNAVAILABLE:
@@ -7524,7 +7722,7 @@ _set_state_full (NMDevice *self,
 			 * Note that we "deactivate" the device even when coming from
 			 * UNMANAGED, to ensure that it's in a clean state.
 			 */
-			nm_device_cleanup (self, reason);
+			nm_device_cleanup (self, reason, TRUE);
 		}
 		break;
 	case NM_DEVICE_STATE_DISCONNECTED:
@@ -7534,7 +7732,7 @@ _set_state_full (NMDevice *self,
 			 */
 			set_nm_ipv6ll (self, TRUE);
 
-			nm_device_cleanup (self, reason);
+			nm_device_cleanup (self, reason, TRUE);
 		} else if (old_state < NM_DEVICE_STATE_DISCONNECTED) {
 			if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) {
 				/* Ensure IPv6 is set up as it may not have been done when
@@ -7575,7 +7773,7 @@ _set_state_full (NMDevice *self,
 		 * we can't change states again from the state handler for a variety of
 		 * reasons.
 		 */
-		if (nm_device_is_available (self)) {
+		if (nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) {
 			_LOGD (LOGD_DEVICE, "device is available, will transition to DISCONNECTED");
 			nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
 		} else {
@@ -7597,16 +7795,17 @@ _set_state_full (NMDevice *self,
 			if (!nm_dispatcher_call (DISPATCHER_ACTION_PRE_DOWN,
 			                         nm_act_request_get_connection (req),
 			                         self,
-			                         dispatcher_complete_proceed_state,
+			                         deactivate_dispatcher_complete,
 			                         self,
 			                         &priv->dispatcher.call_id)) {
 				/* Just proceed on errors */
-				dispatcher_complete_proceed_state (0, self);
+				deactivate_dispatcher_complete (0, self);
 			}
 		}
 		break;
 	case NM_DEVICE_STATE_DISCONNECTED:
-		if (priv->queued_act_request) {
+		if (   priv->queued_act_request
+		    && !priv->queued_act_request_is_waiting_for_carrier) {
 			NMActRequest *queued_req;
 
 			queued_req = priv->queued_act_request;
@@ -8057,6 +8256,9 @@ constructor (GType type,
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), self);
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), self);
 
+	/* trigger initial ip config change to initialize ip-config */
+	priv->queued_ip_config_id = g_idle_add (queued_ip_config_change, self);
+
 	if (nm_platform_check_support_user_ipv6ll ()) {
 		int ip_ifindex = nm_device_get_ip_ifindex (self);
 
@@ -8103,6 +8305,7 @@ constructed (GObject *object)
 	if (priv->ifindex > 0) {
 		priv->is_software = nm_platform_link_is_software (priv->ifindex);
 		priv->physical_port_id = nm_platform_link_get_physical_port_id (priv->ifindex);
+		priv->dev_id = nm_platform_link_get_dev_id (priv->ifindex);
 		priv->mtu = nm_platform_link_get_mtu (priv->ifindex);
 	}
 	/* Indicate software device in capabilities. */
@@ -8164,7 +8367,7 @@ dispose (GObject *object)
 
 	_cleanup_generic_post (self, FALSE);
 
-	g_clear_pointer (&priv->ip6_saved_properties, g_hash_table_unref);
+	g_hash_table_remove_all (priv->ip6_saved_properties);
 
 	if (priv->recheck_assume_id) {
 		g_source_remove (priv->recheck_assume_id);
@@ -8180,15 +8383,14 @@ dispose (GObject *object)
 		priv->con_provider = NULL;
 	}
 
-	g_hash_table_unref (priv->available_connections);
-	priv->available_connections = NULL;
+	g_hash_table_remove_all (priv->available_connections);
 
 	if (priv->carrier_wait_id) {
 		g_source_remove (priv->carrier_wait_id);
 		priv->carrier_wait_id = 0;
 	}
 
-	g_clear_object (&priv->queued_act_request);
+	_clear_queued_act_request (priv);
 
 	platform = nm_platform_get ();
 	g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ip_changed), self);
@@ -8218,6 +8420,9 @@ finalize (GObject *object)
 	g_free (priv->type_desc);
 	g_free (priv->dhcp_anycast_address);
 
+	g_hash_table_unref (priv->ip6_saved_properties);
+	g_hash_table_unref (priv->available_connections);
+
 	G_OBJECT_CLASS (nm_device_parent_class)->finalize (object);
 }
 
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index b3855c74..e204af84 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -23,6 +23,7 @@
 #define __NETWORKMANAGER_DEVICE_H__
 
 #include <glib-object.h>
+#include <gio/gio.h>
 #include <dbus/dbus-glib.h>
 #include <netinet/in.h>
 
@@ -85,10 +86,37 @@ G_BEGIN_DECLS
 
 typedef enum NMActStageReturn NMActStageReturn;
 
+/* These flags affect whether a connection is considered available on a device
+ * (check_connection_available()). The flags should have the meaning of relaxing
+ * a condition, so that adding a flag might make a connection available that would
+ * not be available otherwise. Adding a flag should never make a connection
+ * not available if it would be available otherwise. */
+typedef enum { /*< skip >*/
+	NM_DEVICE_CHECK_CON_AVAILABLE_NONE                                  = 0,
+
+	_NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER     = (1L << 0),
+	_NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP           = (1L << 1),
+	NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST                      = _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER
+	                                                                    | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP,
+
+	__NM_DEVICE_CHECK_CON_AVAILABLE_ALL,
+	NM_DEVICE_CHECK_CON_AVAILABLE_ALL                                   = (((__NM_DEVICE_CHECK_CON_AVAILABLE_ALL - 1) << 1) - 1),
+} NMDeviceCheckConAvailableFlags;
+
 struct _NMDevice {
 	GObject parent;
 };
 
+/* The flags have an relaxing meaning, that means, specifying more flags, can make
+ * a device appear more available. It can never make a device less available. */
+typedef enum { /*< skip >*/
+	NM_DEVICE_CHECK_DEV_AVAILABLE_NONE                                  = 0,
+	NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER                        = (1L << 0),
+
+	__NM_DEVICE_CHECK_DEV_AVAILABLE_ALL,
+	NM_DEVICE_CHECK_DEV_AVAILABLE_ALL                                   = (((__NM_DEVICE_CHECK_DEV_AVAILABLE_ALL - 1) << 1) - 1),
+} NMDeviceCheckDevAvailableFlags;
+
 typedef struct {
 	GObjectClass parent;
 
@@ -116,7 +144,7 @@ typedef struct {
 
 	guint32		(* get_generic_capabilities)	(NMDevice *self);
 
-	gboolean	(* is_available) (NMDevice *self);
+	gboolean	(* is_available) (NMDevice *self, NMDeviceCheckDevAvailableFlags flags);
 
 	gboolean    (* get_enabled) (NMDevice *self);
 
@@ -136,19 +164,18 @@ typedef struct {
 	 * including any live network information like scan lists.  The connection
 	 * is checked against the object defined by @specific_object, if given.
 	 * Returns TRUE if the connection is available; FALSE if not.
+	 *
+	 * The passed @flags affect whether a connection is considered
+	 * available or not. Adding more flags, means the connection is
+	 * *more* available.
+	 *
+	 * Specifying @specific_object can only reduce the availability of a connection.
 	 */
 	gboolean    (* check_connection_available) (NMDevice *self,
 	                                            NMConnection *connection,
+	                                            NMDeviceCheckConAvailableFlags flags,
 	                                            const char *specific_object);
 
-	/* Same as check_connection_available() but called if the connection
-	 * is not present in the activating-connections array during activation,
-	 * to give the device a chance to allow/deny the activation.  This is a
-	 * hack only meant for hidden WiFi networks.
-	 */
-	gboolean    (* check_connection_available_wifi_hidden) (NMDevice *self,
-	                                                        NMConnection *connection);
-
 	gboolean    (* complete_connection)         (NMDevice *self,
 	                                             NMConnection *connection,
 	                                             const char *specific_object,
@@ -174,6 +201,16 @@ typedef struct {
 	void                (* ip4_config_pre_commit) (NMDevice *self, NMIP4Config *config);
 	void                (* ip6_config_pre_commit) (NMDevice *self, NMIP6Config *config);
 
+	/* Async deactivating (in the DEACTIVATING phase) */
+	void            (* deactivate_async)        (NMDevice *self,
+	                                             GCancellable *cancellable,
+	                                             GAsyncReadyCallback callback,
+	                                             gpointer user_data);
+	gboolean        (* deactivate_async_finish) (NMDevice *self,
+	                                             GAsyncResult *res,
+	                                             GError **error);
+
+	/* Sync deactivating (in the DISCONNECTED phase) */
 	void			(* deactivate)			(NMDevice *self);
 
 	gboolean        (* spec_match_list)     (NMDevice *self, const GSList *specs);
@@ -257,7 +294,7 @@ NMConnection *  nm_device_get_connection	(NMDevice *dev);
 
 void            nm_device_removed        (NMDevice *dev);
 
-gboolean        nm_device_is_available   (NMDevice *dev);
+gboolean        nm_device_is_available   (NMDevice *dev, NMDeviceCheckDevAvailableFlags flags);
 gboolean        nm_device_has_carrier    (NMDevice *dev);
 
 NMConnection * nm_device_generate_connection (NMDevice *self, NMDevice *master);
@@ -316,7 +353,8 @@ typedef enum {
 
 	/* Boundary value */
 	__NM_UNMANAGED_LAST,
-	NM_UNMANAGED_LAST     = __NM_UNMANAGED_LAST - 1,
+	NM_UNMANAGED_LAST          = __NM_UNMANAGED_LAST - 1,
+	NM_UNMANAGED_ALL           = ((NM_UNMANAGED_LAST << 1) - 1),
 } NMUnmanagedFlags;
 
 gboolean nm_device_get_managed (NMDevice *device);
@@ -360,9 +398,10 @@ gboolean nm_device_has_pending_action    (NMDevice *device);
 GPtrArray *nm_device_get_available_connections (NMDevice *device,
                                                 const char *specific_object);
 
-gboolean   nm_device_connection_is_available (NMDevice *device,
-                                              NMConnection *connection,
-                                              gboolean allow_device_override);
+gboolean   nm_device_check_connection_available (NMDevice *device,
+                                                 NMConnection *connection,
+                                                 NMDeviceCheckConAvailableFlags flags,
+                                                 const char *specific_object);
 
 gboolean nm_device_notify_component_added (NMDevice *device, GObject *component);
 
diff --git a/src/devices/team/Makefile.in b/src/devices/team/Makefile.in
index 4ee30e66..29ad0598 100644
--- a/src/devices/team/Makefile.in
+++ b/src/devices/team/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.15 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -15,7 +15,17 @@
 @SET_MAKE@
 
 VPATH = @srcdir@
-am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -81,8 +91,6 @@ host_triplet = @host@
 @WITH_TEAMDCTL_TRUE@am__append_1 = ${LIBTEAMDCTL_CFLAGS}
 @WITH_TEAMDCTL_TRUE@am__append_2 = $(LIBTEAMDCTL_LIBS)
 subdir = src/devices/team
-DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
-	$(top_srcdir)/build-aux/depcomp
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/compiler_warnings.m4 \
@@ -99,6 +107,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
@@ -211,6 +220,8 @@ am__define_uniq_tagged_files = \
   done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+	$(top_srcdir)/build-aux/depcomp
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
@@ -237,7 +248,6 @@ CXXCPP = @CXXCPP@
 CXXDEPMODE = @CXXDEPMODE@
 CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
-DATADIRNAME = @DATADIRNAME@
 DBUS_CFLAGS = @DBUS_CFLAGS@
 DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
 DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
@@ -272,6 +282,7 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
 GNUTLS_LIBS = @GNUTLS_LIBS@
 GREP = @GREP@
 GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
 GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
 GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
 GTKDOC_MKPDF = @GTKDOC_MKPDF@
@@ -523,7 +534,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/devices/team/Makefile'; \
 	$(am__cd) $(top_srcdir) && \
 	  $(AUTOMAKE) --gnu src/devices/team/Makefile
-.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 	@case '$?' in \
 	  *config.status*) \
@@ -595,14 +605,14 @@ distclean-compile:
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
 
 .c.obj:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
 
 .c.lo:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -830,6 +840,8 @@ uninstall-am: uninstall-pkglibLTLIBRARIES
 	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
 	uninstall-am uninstall-pkglibLTLIBRARIES
 
+.PRECIOUS: Makefile
+
 include $(GLIB_MAKEFILE)
 
 @GNOME_CODE_COVERAGE_RULES@
diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c
index 16f1fcc2..49202c36 100644
--- a/src/devices/team/nm-device-team.c
+++ b/src/devices/team/nm-device-team.c
@@ -41,6 +41,7 @@
 #include "nm-team-enum-types.h"
 #include "nm-posix-signals.h"
 #include "nm-core-internal.h"
+#include "gsystem-local-alloc.h"
 
 #include "nm-device-team-glue.h"
 
@@ -51,8 +52,6 @@ G_DEFINE_TYPE (NMDeviceTeam, nm_device_team, NM_TYPE_DEVICE)
 
 #define NM_DEVICE_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_TEAM, NMDeviceTeamPrivate))
 
-static gboolean teamd_start (NMDevice *device, NMSettingTeam *s_team);
-
 typedef struct {
 	struct teamdctl *tdc;
 	GPid teamd_pid;
@@ -68,6 +67,8 @@ enum {
 	LAST_PROP
 };
 
+static gboolean teamd_start (NMDevice *device, NMSettingTeam *s_team);
+
 /******************************************************************/
 
 static guint32
@@ -77,7 +78,7 @@ get_generic_capabilities (NMDevice *device)
 }
 
 static gboolean
-is_available (NMDevice *device)
+is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 {
 	if (NM_DEVICE_GET_CLASS (device)->is_up)
 		return NM_DEVICE_GET_CLASS (device)->is_up (device);
@@ -87,6 +88,7 @@ is_available (NMDevice *device)
 static gboolean
 check_connection_available (NMDevice *device,
                             NMConnection *connection,
+                            NMDeviceCheckConAvailableFlags flags,
                             const char *specific_object)
 {
 	/* Connections are always available because the carrier state is determined
@@ -171,6 +173,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMSettingTeam *s_team = nm_connection_get_setting_team (connection);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 
 	if (!s_team) {
 		s_team = (NMSettingTeam *) nm_setting_team_new ();
@@ -178,8 +181,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 	}
 	g_object_set (G_OBJECT (s_team), NM_SETTING_TEAM_CONFIG, NULL, NULL);
 
-	teamd_start (device, s_team);
-	if (NM_DEVICE_TEAM_GET_PRIVATE (device)->teamd_pid > 0 && ensure_teamd_connection (device)) {
+	if (priv->tdc) {
 		const char *config = NULL;
 		int err;
 
@@ -261,49 +263,30 @@ master_update_slave_connection (NMDevice *self,
 /******************************************************************/
 
 static void
-teamd_timeout_remove (NMDevice *device)
+teamd_cleanup (NMDevice *device, gboolean free_tdc)
 {
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device);
 
-	if (priv->teamd_timeout) {
-		g_source_remove (priv->teamd_timeout);
-		priv->teamd_timeout = 0;
-	}
-}
-
-static void
-teamd_cleanup (NMDevice *device, gboolean device_state_failed)
-{
-	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device);
-
-	if (priv->teamd_dbus_watch) {
-		g_bus_unwatch_name (priv->teamd_dbus_watch);
-		priv->teamd_dbus_watch = 0;
-	}
-
 	if (priv->teamd_process_watch) {
 		g_source_remove (priv->teamd_process_watch);
 		priv->teamd_process_watch = 0;
 	}
 
+	if (priv->teamd_timeout) {
+		g_source_remove (priv->teamd_timeout);
+		priv->teamd_timeout = 0;
+	}
+
 	if (priv->teamd_pid > 0) {
 		nm_utils_kill_child_async (priv->teamd_pid, SIGTERM, LOGD_TEAM, "teamd", 2000, NULL, NULL);
 		priv->teamd_pid = 0;
 	}
 
-	if (priv->tdc) {
+	if (priv->tdc && free_tdc) {
 		teamdctl_disconnect (priv->tdc);
 		teamdctl_free (priv->tdc);
 		priv->tdc = NULL;
 	}
-
-	teamd_timeout_remove (device);
-
-	if (device_state_failed) {
-		if (nm_device_is_activating (device) ||
-		    (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED))
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
-	}
 }
 
 static gboolean
@@ -314,11 +297,18 @@ teamd_timeout_cb (gpointer user_data)
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device);
 
 	g_return_val_if_fail (priv->teamd_timeout, FALSE);
+	priv->teamd_timeout = 0;
 
-	_LOGI (LOGD_TEAM, "teamd timed out.");
-	teamd_cleanup (device, TRUE);
+	if (priv->teamd_pid && !priv->tdc) {
+		/* Timed out launching our own teamd process */
+		_LOGW (LOGD_TEAM, "teamd timed out.");
+		teamd_cleanup (device, TRUE);
 
-	return FALSE;
+		g_warn_if_fail (nm_device_is_activating (device));
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
+	}
+
+	return G_SOURCE_REMOVE;
 }
 
 static void
@@ -335,43 +325,77 @@ teamd_dbus_appeared (GDBusConnection *connection,
 	g_return_if_fail (priv->teamd_dbus_watch);
 
 	_LOGI (LOGD_TEAM, "teamd appeared on D-Bus");
-	teamd_timeout_remove (device);
+	nm_device_queue_recheck_assume (device);
+
+	/* If another teamd grabbed the bus name while our teamd was starting,
+	 * just ignore the death of our teamd and run with the existing one.
+	 */
+	if (priv->teamd_process_watch) {
+		gs_unref_variant GVariant *ret = NULL;
+		guint32 pid;
+
+		ret = g_dbus_connection_call_sync (connection,
+		                                   "org.freedesktop.DBus",
+		                                   "/org/freedesktop/DBus",
+		                                   "org.freedesktop.DBus",
+		                                   "GetConnectionUnixProcessID",
+		                                   g_variant_new ("(s)", name_owner),
+		                                   NULL,
+		                                   G_DBUS_CALL_FLAGS_NO_AUTO_START,
+		                                   2000,
+		                                   NULL,
+		                                   NULL);
+		g_variant_get (ret, "(u)", &pid);
+
+		if (pid != priv->teamd_pid)
+			teamd_cleanup (device, FALSE);
+	}
 
+	/* Grab a teamd control handle even if we aren't going to use it
+	 * immediately.  But if we are, and grabbing it failed, fail the
+	 * device activation.
+	 */
 	success = ensure_teamd_connection (device);
 	if (nm_device_get_state (device) == NM_DEVICE_STATE_PREPARE) {
 		if (success)
 			nm_device_activate_schedule_stage2_device_config (device);
 		else if (!nm_device_uses_assumed_connection (device))
 			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
-		return;
 	}
 }
 
 static void
-teamd_dbus_vanished (GDBusConnection *connection,
+teamd_dbus_vanished (GDBusConnection *dbus_connection,
                      const gchar *name,
                      gpointer user_data)
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (user_data);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (self);
+	NMDeviceState state = nm_device_get_state (device);
 
 	g_return_if_fail (priv->teamd_dbus_watch);
 
-	if (priv->teamd_timeout) {
+	if (!priv->tdc) {
 		/* g_bus_watch_name will always raise an initial signal, to indicate whether the
-		 * name exists/not exists initially. Do not take this as a failure, until the
-		 * startup timeout is over.
-		 *
-		 * Note that g_bus_watch_name is guaranteed to alternate vanished/appeared signals,
-		 * so we won't hit this condition again (because the next signal is either 'appeared'
-		 * or 'timeout'). */
-		_LOGD (LOGD_TEAM, "teamd vanished from D-Bus (ignored)");
+		 * name exists/not exists initially. Do not take this as a failure if it hadn't
+		 * previously appeared.
+		 */
+		_LOGD (LOGD_TEAM, "teamd not on D-Bus (ignored)");
 		return;
 	}
 
 	_LOGI (LOGD_TEAM, "teamd vanished from D-Bus");
 	teamd_cleanup (device, TRUE);
+
+	/* Attempt to respawn teamd */
+	if (state >= NM_DEVICE_STATE_PREPARE && state <= NM_DEVICE_STATE_ACTIVATED) {
+		NMConnection *connection = nm_device_get_connection (device);
+
+		g_assert (connection);
+		if (!teamd_start (device, nm_connection_get_setting_team (connection)))
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
+	}
 }
 
 static void
@@ -380,13 +404,24 @@ teamd_process_watch_cb (GPid pid, gint status, gpointer user_data)
 	NMDeviceTeam *self = NM_DEVICE_TEAM (user_data);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (self);
+	NMDeviceState state = nm_device_get_state (device);
 
 	g_return_if_fail (priv->teamd_process_watch);
 
-	_LOGI (LOGD_TEAM, "teamd died with status %d", status);
-	priv->teamd_process_watch = 0;
+	_LOGD (LOGD_TEAM, "teamd died with status %d", status);
 	priv->teamd_pid = 0;
-	teamd_cleanup (device, TRUE);
+	priv->teamd_process_watch = 0;
+
+	/* If teamd quit within 5 seconds of starting, it's probably hosed
+	 * and will just die again, so fail the activation.
+	 */
+	if (priv->teamd_timeout &&
+	    (state >= NM_DEVICE_STATE_PREPARE) &&
+	    (state <= NM_DEVICE_STATE_ACTIVATED)) {
+		_LOGW (LOGD_TEAM, "teamd process quit unexpectedly; failing activation");
+		teamd_cleanup (device, TRUE);
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
+	}
 }
 
 static void
@@ -407,28 +442,41 @@ teamd_child_setup (gpointer user_data G_GNUC_UNUSED)
 }
 
 static gboolean
+teamd_kill (NMDeviceTeam *self, const char *teamd_binary, GError **error)
+{
+	gs_unref_ptrarray GPtrArray *argv = NULL;
+	gs_free char *tmp_str = NULL;
+
+	if (!teamd_binary) {
+		teamd_binary = nm_utils_find_helper ("teamd", NULL, NULL);
+		if (!teamd_binary) {
+			_LOGW (LOGD_TEAM, "Activation: (team) failed to start teamd: teamd binary not found");
+			return FALSE;
+		}
+	}
+
+	argv = g_ptr_array_new ();
+	g_ptr_array_add (argv, (gpointer) teamd_binary);
+	g_ptr_array_add (argv, (gpointer) "-k");
+	g_ptr_array_add (argv, (gpointer) "-t");
+	g_ptr_array_add (argv, (gpointer) nm_device_get_iface (NM_DEVICE (self)));
+	g_ptr_array_add (argv, NULL);
+
+	_LOGD (LOGD_TEAM, "running: %s", (tmp_str = g_strjoinv (" ", (gchar **) argv->pdata)));
+	return g_spawn_sync ("/", (char **) argv->pdata, NULL, 0, nm_unblock_posix_signals, NULL, NULL, NULL, NULL, error);
+}
+
+static gboolean
 teamd_start (NMDevice *device, NMSettingTeam *s_team)
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 	const char *iface = nm_device_get_ip_iface (device);
-	char *tmp_str = NULL;
-	const char *config;
+	gs_unref_ptrarray GPtrArray *argv = NULL;
+	gs_free_error GError *error = NULL;
+	gs_free char *tmp_str = NULL;
 	const char *teamd_binary;
-	GPtrArray *argv;
-	GError *error = NULL;
-	gboolean ret;
-	int status;
-
-	if (priv->teamd_dbus_watch ||
-	    priv->teamd_process_watch ||
-	    priv->teamd_pid > 0 ||
-	    priv->tdc ||
-	    priv->teamd_timeout)
-	{
-		/* Just return if teamd_start() was already called */
-		return TRUE;
-	}
+	const char *config;
 
 	teamd_binary = nm_utils_find_helper ("teamd", NULL, NULL);
 	if (!teamd_binary) {
@@ -436,20 +484,12 @@ teamd_start (NMDevice *device, NMSettingTeam *s_team)
 		return FALSE;
 	}
 
-	/* Kill teamd for same named device first if it is there */
-	argv = g_ptr_array_new ();
-	g_ptr_array_add (argv, (gpointer) teamd_binary);
-	g_ptr_array_add (argv, (gpointer) "-k");
-	g_ptr_array_add (argv, (gpointer) "-t");
-	g_ptr_array_add (argv, (gpointer) iface);
-	g_ptr_array_add (argv, NULL);
-
-	_LOGD (LOGD_TEAM, "running: %s",
-	       (tmp_str = g_strjoinv (" ", (gchar **) argv->pdata)));
-	g_clear_pointer (&tmp_str, g_free);
-
-	ret = g_spawn_sync ("/", (char **) argv->pdata, NULL, 0, nm_unblock_posix_signals, NULL, NULL, NULL, &status, &error);
-	g_ptr_array_free (argv, TRUE);
+	if (priv->teamd_process_watch || priv->teamd_pid > 0 || priv->tdc) {
+		g_warn_if_reached ();
+		if (!priv->teamd_pid)
+			teamd_kill (self, teamd_binary, NULL);
+		teamd_cleanup (device, TRUE);
+	}
 
 	/* Start teamd now */
 	argv = g_ptr_array_new ();
@@ -472,83 +512,90 @@ teamd_start (NMDevice *device, NMSettingTeam *s_team)
 		g_ptr_array_add (argv, (gpointer) "-gg");
 	g_ptr_array_add (argv, NULL);
 
-	_LOGD (LOGD_TEAM, "running: %s",
-	       (tmp_str = g_strjoinv (" ", (gchar **) argv->pdata)));
-	g_clear_pointer (&tmp_str, g_free);
-
-	/* Start a timeout for teamd to appear at D-Bus */
-	priv->teamd_timeout = g_timeout_add_seconds (5, teamd_timeout_cb, device);
-
-	/* Register D-Bus name watcher */
-	tmp_str = g_strdup_printf ("org.libteam.teamd.%s", iface);
-	priv->teamd_dbus_watch = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
-	                                           tmp_str,
-	                                           G_BUS_NAME_WATCHER_FLAGS_NONE,
-	                                           teamd_dbus_appeared,
-	                                           teamd_dbus_vanished,
-	                                           device,
-	                                           NULL);
-	g_free (tmp_str);
-
-	ret = g_spawn_async ("/", (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
-	                    &teamd_child_setup, NULL, &priv->teamd_pid, &error);
-	g_ptr_array_free (argv, TRUE);
-	if (!ret) {
+	_LOGD (LOGD_TEAM, "running: %s", (tmp_str = g_strjoinv (" ", (gchar **) argv->pdata)));
+	if (!g_spawn_async ("/", (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+	                    teamd_child_setup, NULL, &priv->teamd_pid, &error)) {
 		_LOGW (LOGD_TEAM, "Activation: (team) failed to start teamd: %s", error->message);
-		g_clear_error (&error);
-		teamd_cleanup (device, FALSE);
+		teamd_cleanup (device, TRUE);
 		return FALSE;
 	}
 
+	/* Start a timeout for teamd to appear at D-Bus */
+	if (!priv->teamd_timeout)
+		priv->teamd_timeout = g_timeout_add_seconds (5, teamd_timeout_cb, device);
+
 	/* Monitor the child process so we know when it dies */
 	priv->teamd_process_watch = g_child_watch_add (priv->teamd_pid,
 	                                               teamd_process_watch_cb,
 	                                               device);
 
-	_LOGI (LOGD_TEAM, "Activation: (team) started teamd...");
+	_LOGI (LOGD_TEAM, "Activation: (team) started teamd [pid %u]...", (guint) priv->teamd_pid);
 	return TRUE;
 }
 
-static void
-teamd_stop (NMDevice *device)
-{
-	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
-	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
-
-	if (priv->teamd_pid > 0)
-		_LOGI (LOGD_TEAM, "Deactivation: stopping teamd...");
-	else
-		_LOGD (LOGD_TEAM, "Deactivation: stopping teamd (not started)...");
-	teamd_cleanup (device, FALSE);
-}
-
 static NMActStageReturn
 act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 {
+	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
+	gs_free_error GError *error = NULL;
 	NMConnection *connection;
 	NMSettingTeam *s_team;
+	const char *cfg;
 
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
 	ret = NM_DEVICE_CLASS (nm_device_team_parent_class)->act_stage1_prepare (device, reason);
-	if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
-		connection = nm_device_get_connection (device);
-		g_assert (connection);
-		s_team = nm_connection_get_setting_team (connection);
-		g_assert (s_team);
-		if (teamd_start (device, s_team))
-			ret = NM_ACT_STAGE_RETURN_POSTPONE;
-		else
-			ret = NM_ACT_STAGE_RETURN_FAILURE;
+	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
+		return ret;
+
+	connection = nm_device_get_connection (device);
+	g_assert (connection);
+	s_team = nm_connection_get_setting_team (connection);
+	g_assert (s_team);
+
+	if (priv->tdc) {
+		/* If the existing teamd config is the same as we're about to use,
+		 * then we can proceed.  If it's not the same, and we have a PID,
+		 * kill it so we can respawn it with the right config.  If we don't
+		 * have a PID, then we must fail.
+		 */
+		cfg = teamdctl_config_get_raw (priv->tdc);
+		if (cfg && strcmp (cfg,  nm_setting_team_get_config (s_team)) == 0) {
+			_LOGD (LOGD_TEAM, "using existing matching teamd config");
+			return NM_ACT_STAGE_RETURN_SUCCESS;
+		}
+
+		if (!priv->teamd_pid) {
+			_LOGD (LOGD_TEAM, "existing teamd config mismatch; killing existing via teamdctl");
+			if (!teamd_kill (self, NULL, &error)) {
+				_LOGW (LOGD_TEAM, "existing teamd config mismatch; failed to kill existing teamd: %s", error->message);
+				*reason = NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED;
+				return NM_ACT_STAGE_RETURN_FAILURE;
+			}
+		}
+
+		_LOGD (LOGD_TEAM, "existing teamd config mismatch; respawning...");
+		teamd_cleanup (device, TRUE);
 	}
-	return ret;
+
+	return teamd_start (device, s_team) ?
+		NM_ACT_STAGE_RETURN_POSTPONE : NM_ACT_STAGE_RETURN_FAILURE;
 }
 
 static void
 deactivate (NMDevice *device)
 {
-	teamd_stop (device);
+	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
+
+	if (priv->teamd_pid || priv->tdc)
+		_LOGI (LOGD_TEAM, "deactivation: stopping teamd...");
+
+	if (!priv->teamd_pid)
+		teamd_kill (self, NULL, NULL);
+	teamd_cleanup (device, TRUE);
 }
 
 static gboolean
@@ -694,6 +741,27 @@ nm_device_team_init (NMDeviceTeam * self)
 }
 
 static void
+constructed (GObject *object)
+{
+	NMDevice *device = NM_DEVICE (object);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (object);
+	char *tmp_str = NULL;
+
+	G_OBJECT_CLASS (nm_device_team_parent_class)->constructed (object);
+
+	/* Register D-Bus name watcher */
+	tmp_str = g_strdup_printf ("org.libteam.teamd.%s", nm_device_get_ip_iface (device));
+	priv->teamd_dbus_watch = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
+	                                           tmp_str,
+	                                           G_BUS_NAME_WATCHER_FLAGS_NONE,
+	                                           teamd_dbus_appeared,
+	                                           teamd_dbus_vanished,
+	                                           NM_DEVICE (device),
+	                                           NULL);
+	g_free (tmp_str);
+}
+
+static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
@@ -730,7 +798,15 @@ set_property (GObject *object, guint prop_id,
 static void
 dispose (GObject *object)
 {
-	teamd_cleanup (NM_DEVICE (object), FALSE);
+	NMDevice *device = NM_DEVICE (object);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (object);
+
+	if (priv->teamd_dbus_watch) {
+		g_bus_unwatch_name (priv->teamd_dbus_watch);
+		priv->teamd_dbus_watch = 0;
+	}
+
+	teamd_cleanup (device, TRUE);
 
 	G_OBJECT_CLASS (nm_device_team_parent_class)->dispose (object);
 }
@@ -746,6 +822,7 @@ nm_device_team_class_init (NMDeviceTeamClass *klass)
 	parent_class->connection_type = NM_SETTING_TEAM_SETTING_NAME;
 
 	/* virtual methods */
+	object_class->constructed = constructed;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 	object_class->dispose = dispose;
diff --git a/src/devices/wifi/Makefile.in b/src/devices/wifi/Makefile.in
index 175dd900..f5e88007 100644
--- a/src/devices/wifi/Makefile.in
+++ b/src/devices/wifi/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.15 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -15,7 +15,17 @@
 @SET_MAKE@
 
 VPATH = @srcdir@
-am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -79,8 +89,6 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 subdir = src/devices/wifi
-DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
-	$(top_srcdir)/build-aux/depcomp
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/compiler_warnings.m4 \
@@ -97,6 +105,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
@@ -226,6 +235,8 @@ am__define_uniq_tagged_files = \
 ETAGS = etags
 CTAGS = ctags
 DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+	$(top_srcdir)/build-aux/depcomp
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 am__relativize = \
   dir0=`pwd`; \
@@ -277,7 +288,6 @@ CXXCPP = @CXXCPP@
 CXXDEPMODE = @CXXDEPMODE@
 CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
-DATADIRNAME = @DATADIRNAME@
 DBUS_CFLAGS = @DBUS_CFLAGS@
 DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
 DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
@@ -312,6 +322,7 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
 GNUTLS_LIBS = @GNUTLS_LIBS@
 GREP = @GREP@
 GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
 GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
 GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
 GTKDOC_MKPDF = @GTKDOC_MKPDF@
@@ -583,7 +594,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/devices/wifi/Makefile'; \
 	$(am__cd) $(top_srcdir) && \
 	  $(AUTOMAKE) --gnu src/devices/wifi/Makefile
-.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 	@case '$?' in \
 	  *config.status*) \
@@ -658,14 +668,14 @@ distclean-compile:
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
 
 .c.obj:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
 
 .c.lo:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -968,6 +978,8 @@ uninstall-am: uninstall-pkglibLTLIBRARIES
 	tags tags-am uninstall uninstall-am \
 	uninstall-pkglibLTLIBRARIES
 
+.PRECIOUS: Makefile
+
 include $(GLIB_MAKEFILE)
 
 @GNOME_CODE_COVERAGE_RULES@
diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c
index 0f5aa164..dc3dfbc6 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.c
+++ b/src/devices/wifi/nm-device-olpc-mesh.c
@@ -235,7 +235,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 }
 
 static gboolean
-is_available (NMDevice *device)
+is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 {
 	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device);
 
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index dd7754b3..ce9a3f75 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -863,10 +863,10 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 
 
 static gboolean
-_internal_check_connection_available (NMDevice *device,
-                                      NMConnection *connection,
-                                      const char *specific_object,
-                                      gboolean ignore_ap_list)
+check_connection_available (NMDevice *device,
+                            NMConnection *connection,
+                            NMDeviceCheckConAvailableFlags flags,
+                            const char *specific_object)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device);
 	NMSettingWireless *s_wifi;
@@ -876,6 +876,9 @@ _internal_check_connection_available (NMDevice *device,
 	s_wifi = nm_connection_get_setting_wireless (connection);
 	g_return_val_if_fail (s_wifi, FALSE);
 
+	/* a connection that is available for a certain @specific_object, MUST
+	 * also be available in general (without @specific_object). */
+
 	if (specific_object) {
 		NMAccessPoint *ap;
 
@@ -891,8 +894,15 @@ _internal_check_connection_available (NMDevice *device,
 	    || g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_AP) == 0)
 		return TRUE;
 
-	/* Hidden SSIDs obviously don't always appear in the scan list either */
-	if (nm_setting_wireless_get_hidden (s_wifi) || ignore_ap_list)
+	/* Hidden SSIDs obviously don't always appear in the scan list either.
+	 *
+	 * For an explict user-activation-request, a connection is considered
+	 * available because for hidden Wi-Fi, clients didn't consistently
+	 * set the 'hidden' property to indicate hidden SSID networks.  If
+	 * activating but the network isn't available let the device recheck
+	 * availability.
+	 */
+	if (nm_setting_wireless_get_hidden (s_wifi) || NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP))
 		return TRUE;
 
 	/* check if its visible */
@@ -904,24 +914,6 @@ _internal_check_connection_available (NMDevice *device,
 	return FALSE;
 }
 
-static gboolean
-check_connection_available (NMDevice *device,
-                            NMConnection *connection,
-                            const char *specific_object)
-{
-	return _internal_check_connection_available (device, connection, specific_object, FALSE);
-}
-
-/* FIXME: remove this function when we require the 'hidden' property to be
- * set before a hidden connection can be activated.
- */
-static gboolean
-check_connection_available_wifi_hidden (NMDevice *device,
-                                        NMConnection *connection)
-{
-	return _internal_check_connection_available (device, connection, NULL, TRUE);
-}
-
 /*
  * List of manufacturer default SSIDs that are often unchanged by users.
  *
@@ -1143,28 +1135,22 @@ complete_connection (NMDevice *device,
 }
 
 static gboolean
-is_available (NMDevice *device)
+is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	guint32 state;
 
-	if (!priv->enabled) {
-		_LOGD (LOGD_WIFI, "not available because not enabled");
+	if (!priv->enabled)
 		return FALSE;
-	}
 
-	if (!priv->sup_iface) {
-		_LOGD (LOGD_WIFI, "not available because supplicant not running");
+	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) {
-		_LOGD (LOGD_WIFI, "not available because supplicant interface not ready");
+	    || state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED)
 		return FALSE;
-	}
 
 	return TRUE;
 }
@@ -1522,7 +1508,9 @@ request_wireless_scan (gpointer user_data)
 
 				for (i = 0; i < ssids->len; i++) {
 					ssid = g_ptr_array_index (ssids, i);
-					foo = nm_utils_ssid_to_utf8 (ssid->data, ssid->len);
+					foo = ssid->len > 0
+					      ? nm_utils_ssid_to_utf8 (ssid->data, ssid->len)
+					      : NULL;
 					_LOGD (LOGD_WIFI_SCAN, "(%d) probe scanning SSID '%s'",
 					            i, foo ? foo : "<hidden>");
 					g_free (foo);
@@ -1636,6 +1624,8 @@ supplicant_iface_scan_done_cb (NMSupplicantInterface *iface,
  *
  */
 
+#define WPAS_REMOVED_TAG "supplicant-removed"
+
 static void
 try_fill_ssid_for_hidden_ap (NMAccessPoint *ap)
 {
@@ -1743,6 +1733,7 @@ merge_scanned_ap (NMDeviceWifi *self,
 		 * fake, since it clearly exists somewhere.
 		 */
 		nm_ap_set_fake (found_ap, FALSE);
+		g_object_set_data (G_OBJECT (found_ap), WPAS_REMOVED_TAG, NULL);
 	} else {
 		/* New entry in the list */
 		_LOGD (LOGD_WIFI_SCAN, "adding new AP '%s' %s (%p)",
@@ -1756,8 +1747,6 @@ merge_scanned_ap (NMDeviceWifi *self,
 	}
 }
 
-#define WPAS_REMOVED_TAG "supplicant-removed"
-
 static gboolean
 cull_scan_list (NMDeviceWifi *self)
 {
@@ -1912,9 +1901,23 @@ supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
 	g_return_if_fail (object_path != NULL);
 
 	ap = get_ap_by_supplicant_path (self, object_path);
-	if (ap)
+	if (ap) {
+		gint32 now = nm_utils_get_monotonic_timestamp_s ();
+		gint32 last_seen = nm_ap_get_last_seen (ap);
+
+		/* We don't know when the supplicant last saw the AP's beacons,
+		 * it could be two minutes or it could be 2 seconds.  Because the
+		 * supplicant doesn't send property change notifications if the
+		 * AP's other properties don't change, our last-seen time may be
+		 * much older the supplicant's, and the AP would be immediately
+		 * removed from the list on the next cleanup.  So update the
+		 * last-seen time to ensure the AP sticks around for at least
+		 * one more periodic scan.
+		 */
+		nm_ap_set_last_seen (ap, MAX (last_seen, now - SCAN_INTERVAL_MAX));
 		g_object_set_data (G_OBJECT (ap), WPAS_REMOVED_TAG, GUINT_TO_POINTER (TRUE));
 }
+}
 
 static void
 remove_supplicant_timeouts (NMDeviceWifi *self)
@@ -2169,7 +2172,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 		/* If the interface can now be activated because the supplicant is now
 		 * available, transition to DISCONNECTED.
 		 */
-		if ((devstate == NM_DEVICE_STATE_UNAVAILABLE) && nm_device_is_available (device)) {
+		if ((devstate == NM_DEVICE_STATE_UNAVAILABLE) && nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) {
 			nm_device_state_changed (device,
 			                         NM_DEVICE_STATE_DISCONNECTED,
 			                         NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE);
@@ -3335,7 +3338,6 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 	parent_class->is_available = is_available;
 	parent_class->check_connection_compatible = check_connection_compatible;
 	parent_class->check_connection_available = check_connection_available;
-	parent_class->check_connection_available_wifi_hidden = check_connection_available_wifi_hidden;
 	parent_class->complete_connection = complete_connection;
 	parent_class->set_enabled = set_enabled;
 
diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
index ee07cf40..66d73563 100644
--- a/src/devices/wifi/nm-wifi-ap.c
+++ b/src/devices/wifi/nm-wifi-ap.c
@@ -808,7 +808,7 @@ nm_ap_set_ssid (NMAccessPoint *ap, const guint8 *ssid, gsize len)
 NM80211ApFlags
 nm_ap_get_flags (NMAccessPoint *ap)
 {
-	g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_SEC_NONE);
+	g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_FLAGS_NONE);
 
 	return NM_AP_GET_PRIVATE (ap)->flags;
 }
diff --git a/src/devices/wifi/tests/Makefile.am b/src/devices/wifi/tests/Makefile.am
index 09c6401f..2a7d7849 100644
--- a/src/devices/wifi/tests/Makefile.am
+++ b/src/devices/wifi/tests/Makefile.am
@@ -23,5 +23,6 @@ test_wifi_ap_utils_SOURCES = \
 
 test_wifi_ap_utils_LDADD = $(top_builddir)/src/libNetworkManager.la
 
+@VALGRIND_RULES@
 TESTS = test-wifi-ap-utils
 
diff --git a/src/devices/wifi/tests/Makefile.in b/src/devices/wifi/tests/Makefile.in
index a8ae2e9d..e3b7c767 100644
--- a/src/devices/wifi/tests/Makefile.in
+++ b/src/devices/wifi/tests/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.15 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -15,7 +15,17 @@
 @SET_MAKE@
 
 VPATH = @srcdir@
-am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -81,8 +91,6 @@ host_triplet = @host@
 noinst_PROGRAMS = test-wifi-ap-utils$(EXEEXT)
 TESTS = test-wifi-ap-utils$(EXEEXT)
 subdir = src/devices/wifi/tests
-DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
-	$(top_srcdir)/build-aux/depcomp
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/compiler_warnings.m4 \
@@ -99,6 +107,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
@@ -195,6 +204,8 @@ am__tty_colors = { \
     std=''; \
   fi; \
 }
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+	$(top_srcdir)/build-aux/depcomp
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
@@ -221,7 +232,6 @@ CXXCPP = @CXXCPP@
 CXXDEPMODE = @CXXDEPMODE@
 CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
-DATADIRNAME = @DATADIRNAME@
 DBUS_CFLAGS = @DBUS_CFLAGS@
 DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
 DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
@@ -256,6 +266,7 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
 GNUTLS_LIBS = @GNUTLS_LIBS@
 GREP = @GREP@
 GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
 GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
 GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
 GTKDOC_MKPDF = @GTKDOC_MKPDF@
@@ -494,7 +505,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/devices/wifi/tests/Makefile'; \
 	$(am__cd) $(top_srcdir) && \
 	  $(AUTOMAKE) --gnu src/devices/wifi/tests/Makefile
-.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 	@case '$?' in \
 	  *config.status*) \
@@ -541,14 +551,14 @@ distclean-compile:
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
 
 .c.obj:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
 
 .c.lo:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -888,6 +898,10 @@ uninstall-am:
 	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
 	tags tags-am uninstall uninstall-am
 
+.PRECIOUS: Makefile
+
+
+@VALGRIND_RULES@
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/src/devices/wifi/tests/test-wifi-ap-utils.c b/src/devices/wifi/tests/test-wifi-ap-utils.c
index fb335b89..3c405e26 100644
--- a/src/devices/wifi/tests/test-wifi-ap-utils.c
+++ b/src/devices/wifi/tests/test-wifi-ap-utils.c
@@ -785,6 +785,7 @@ test_priv_ap_leap_connection_1 (gconstpointer add_wifi)
 	COMPARE (src, expected, success, error, 0, 0);
 
 	g_object_unref (src);
+	g_object_unref (expected);
 }
 
 /*******************************************/
@@ -866,6 +867,7 @@ test_priv_ap_dynamic_wep_1 (void)
 	COMPARE (src, expected, success, error, 0, 0);
 
 	g_object_unref (src);
+	g_object_unref (expected);
 }
 
 /*******************************************/
@@ -913,6 +915,7 @@ test_priv_ap_dynamic_wep_2 (void)
 	COMPARE (src, expected, success, error, 0, 0);
 
 	g_object_unref (src);
+	g_object_unref (expected);
 }
 
 /*******************************************/
diff --git a/src/devices/wimax/Makefile.in b/src/devices/wimax/Makefile.in
index f1ffb09c..32800fb7 100644
--- a/src/devices/wimax/Makefile.in
+++ b/src/devices/wimax/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.15 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -15,7 +15,17 @@
 @SET_MAKE@
 
 VPATH = @srcdir@
-am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -79,8 +89,6 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 subdir = src/devices/wimax
-DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
-	$(top_srcdir)/build-aux/depcomp
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/compiler_warnings.m4 \
@@ -97,6 +105,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
@@ -205,6 +214,8 @@ am__define_uniq_tagged_files = \
   done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+	$(top_srcdir)/build-aux/depcomp
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
@@ -231,7 +242,6 @@ CXXCPP = @CXXCPP@
 CXXDEPMODE = @CXXDEPMODE@
 CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
-DATADIRNAME = @DATADIRNAME@
 DBUS_CFLAGS = @DBUS_CFLAGS@
 DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
 DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
@@ -266,6 +276,7 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
 GNUTLS_LIBS = @GNUTLS_LIBS@
 GREP = @GREP@
 GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
 GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
 GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
 GTKDOC_MKPDF = @GTKDOC_MKPDF@
@@ -526,7 +537,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/devices/wimax/Makefile'; \
 	$(am__cd) $(top_srcdir) && \
 	  $(AUTOMAKE) --gnu src/devices/wimax/Makefile
-.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 	@case '$?' in \
 	  *config.status*) \
@@ -600,14 +610,14 @@ distclean-compile:
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
 
 .c.obj:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
 
 .c.lo:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -835,6 +845,8 @@ uninstall-am: uninstall-pkglibLTLIBRARIES
 	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
 	uninstall-am uninstall-pkglibLTLIBRARIES
 
+.PRECIOUS: Makefile
+
 
 nm-wimax-nsp-glue.h: $(top_srcdir)/introspection/nm-wimax-nsp.xml
 	dbus-binding-tool --prefix=nm_wimax_nsp --mode=glib-server --output=$@ $<
diff --git a/src/devices/wimax/nm-device-wimax.c b/src/devices/wimax/nm-device-wimax.c
index 466cd979..0c8f1cbb 100644
--- a/src/devices/wimax/nm-device-wimax.c
+++ b/src/devices/wimax/nm-device-wimax.c
@@ -239,7 +239,7 @@ update_availability (NMDeviceWimax *self, gboolean old_available)
 	NMDeviceState state;
 	gboolean new_available, changed = FALSE;
 
-	new_available = nm_device_is_available (device);
+	new_available = nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE);
 	if (new_available == old_available)
 		return FALSE;
 
@@ -281,7 +281,7 @@ set_enabled (NMDevice *device, gboolean enabled)
 	if (priv->enabled == enabled)
 		return;
 
-	old_available = nm_device_is_available (NM_DEVICE (device));
+	old_available = nm_device_is_available (NM_DEVICE (device), NM_DEVICE_CHECK_DEV_AVAILABLE_NONE);
 	priv->enabled = enabled;
 
 	nm_log_dbg (LOGD_WIMAX, "(%s): radio now %s",
@@ -334,12 +334,16 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 static gboolean
 check_connection_available (NMDevice *device,
                             NMConnection *connection,
+                            NMDeviceCheckConAvailableFlags flags,
                             const char *specific_object)
 {
 	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (device);
 	const GSList *ns_iter = NULL;
 	NMWimaxNsp *nsp;
 
+	/* a connection that is available for a certain @specific_object, MUST
+	 * also be available in general (without @specific_object). */
+
 	if (specific_object) {
 		nsp = get_nsp_by_path (NM_DEVICE_WIMAX (device), specific_object);
 		return nsp ? nm_wimax_nsp_check_compatible (nsp, connection) : FALSE;
@@ -484,30 +488,21 @@ can_auto_connect (NMDevice *device,
 }
 
 static gboolean
-is_available (NMDevice *device)
+is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 {
 	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (device);
-	const char *iface = nm_device_get_iface (device);
 
-	if (!priv->enabled) {
-		nm_log_dbg (LOGD_WIMAX, "(%s): not available because not enabled", iface);
+	if (!priv->enabled)
 		return FALSE;
-	}
 
-	if (!priv->wimaxd_enabled) {
-		nm_log_dbg (LOGD_WIMAX, "(%s): not available because not enabled in wimaxd", iface);
+	if (!priv->wimaxd_enabled)
 		return FALSE;
-	}
 
-	if (!nm_wimax_util_sdk_is_initialized ()) {
-		nm_log_dbg (LOGD_WIMAX, "(%s): not available because WiMAX SDK not initialized", iface);
+	if (!nm_wimax_util_sdk_is_initialized ())
 		return FALSE;
-	}
 
-	if (!priv->sdk) {
-		nm_log_dbg (LOGD_WIMAX, "(%s): not available because not known to WiMAX SDK", iface);
+	if (!priv->sdk)
 		return FALSE;
-	}
 
 	return iwmxsdk_status_get (priv->sdk) >= WIMAX_API_DEVICE_STATUS_Ready;
 }
@@ -712,7 +707,7 @@ wmx_state_change_cb (struct wmxsdk *wmxsdk,
 		return;
 
 	state = nm_device_get_state (NM_DEVICE (self));
-	old_available = nm_device_is_available (NM_DEVICE (self));
+	old_available = nm_device_is_available (NM_DEVICE (self), NM_DEVICE_CHECK_DEV_AVAILABLE_NONE);
 
 	priv->status = new_status;
 	if (priv->current_nsp)
@@ -1153,7 +1148,7 @@ static gboolean
 sdk_action_defer_cb (gpointer user_data)
 {
 	NMDeviceWimax *self = NM_DEVICE_WIMAX (user_data);
-	gboolean old_available = nm_device_is_available (NM_DEVICE (self));
+	gboolean old_available = nm_device_is_available (NM_DEVICE (self), NM_DEVICE_CHECK_DEV_AVAILABLE_NONE);
 
 	NM_DEVICE_WIMAX_GET_PRIVATE (self)->sdk_action_defer_id = 0;
 	update_availability (self, old_available);
diff --git a/src/devices/wwan/Makefile.in b/src/devices/wwan/Makefile.in
index 18cbfdd3..1a8aef3b 100644
--- a/src/devices/wwan/Makefile.in
+++ b/src/devices/wwan/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.15 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -15,7 +15,17 @@
 @SET_MAKE@
 
 VPATH = @srcdir@
-am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -79,8 +89,6 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 subdir = src/devices/wwan
-DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
-	$(top_srcdir)/build-aux/depcomp
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/compiler_warnings.m4 \
@@ -97,6 +105,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
@@ -216,6 +225,8 @@ am__define_uniq_tagged_files = \
   done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+	$(top_srcdir)/build-aux/depcomp
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
@@ -242,7 +253,6 @@ CXXCPP = @CXXCPP@
 CXXDEPMODE = @CXXDEPMODE@
 CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
-DATADIRNAME = @DATADIRNAME@
 DBUS_CFLAGS = @DBUS_CFLAGS@
 DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
 DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
@@ -277,6 +287,7 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
 GNUTLS_LIBS = @GNUTLS_LIBS@
 GREP = @GREP@
 GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
 GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
 GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
 GTKDOC_MKPDF = @GTKDOC_MKPDF@
@@ -553,7 +564,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/devices/wwan/Makefile'; \
 	$(am__cd) $(top_srcdir) && \
 	  $(AUTOMAKE) --gnu src/devices/wwan/Makefile
-.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 	@case '$?' in \
 	  *config.status*) \
@@ -631,14 +641,14 @@ distclean-compile:
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
 
 .c.obj:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
 
 .c.lo:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -866,6 +876,8 @@ uninstall-am: uninstall-pkglibLTLIBRARIES
 	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
 	uninstall-am uninstall-pkglibLTLIBRARIES
 
+.PRECIOUS: Makefile
+
 include $(GLIB_MAKEFILE)
 
 @GNOME_CODE_COVERAGE_RULES@
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index f681e59c..f819d1d7 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -295,16 +295,19 @@ modem_state_cb (NMModem *modem,
 		 * device's enabled/disabled state.
 		 */
 		nm_modem_set_mm_enabled (priv->modem, priv->rf_enabled);
+
+		/* Now allow connections without a PIN to be available */
+		nm_device_recheck_available_connections (device);
 	}
 
-	if ((dev_state >= NM_DEVICE_STATE_DISCONNECTED) && !nm_device_is_available (device)) {
+	if ((dev_state >= NM_DEVICE_STATE_DISCONNECTED) && !nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) {
 		nm_device_state_changed (device,
 		                         NM_DEVICE_STATE_UNAVAILABLE,
 		                         NM_DEVICE_STATE_REASON_MODEM_FAILED);
 		return;
 	}
 
-	if ((dev_state == NM_DEVICE_STATE_UNAVAILABLE) && nm_device_is_available (device)) {
+	if ((dev_state == NM_DEVICE_STATE_UNAVAILABLE) && nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) {
 		nm_device_state_changed (device,
 		                         NM_DEVICE_STATE_DISCONNECTED,
 		                         NM_DEVICE_STATE_REASON_MODEM_AVAILABLE);
@@ -391,6 +394,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 static gboolean
 check_connection_available (NMDevice *device,
                             NMConnection *connection,
+                            NMDeviceCheckConAvailableFlags flags,
                             const char *specific_object)
 {
 	NMDeviceModem *self = NM_DEVICE_MODEM (device);
@@ -433,6 +437,50 @@ deactivate (NMDevice *device)
 	nm_modem_deactivate (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, device);
 }
 
+/***********************************************************/
+
+static gboolean
+deactivate_async_finish (NMDevice *self,
+                         GAsyncResult *res,
+                         GError **error)
+{
+	return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+modem_deactivate_async_ready (NMModem *modem,
+                              GAsyncResult *res,
+                              GSimpleAsyncResult *simple)
+{
+	GError *error = NULL;
+
+	if (!nm_modem_deactivate_async_finish (modem, res, &error))
+		g_simple_async_result_take_error (simple, error);
+	g_simple_async_result_complete (simple);
+	g_object_unref (simple);
+}
+
+static void
+deactivate_async (NMDevice *self,
+                  GCancellable *cancellable,
+                  GAsyncReadyCallback callback,
+                  gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+
+	simple = g_simple_async_result_new (G_OBJECT (self),
+	                                    callback,
+	                                    user_data,
+	                                    deactivate_async);
+	nm_modem_deactivate_async (NM_DEVICE_MODEM_GET_PRIVATE (self)->modem,
+	                           self,
+	                           cancellable,
+	                           (GAsyncReadyCallback) modem_deactivate_async_ready,
+	                           simple);
+}
+
+/***********************************************************/
+
 static NMActStageReturn
 act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 {
@@ -536,24 +584,19 @@ set_enabled (NMDevice *device, gboolean enabled)
 }
 
 static gboolean
-is_available (NMDevice *device)
+is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 {
 	NMDeviceModem *self = NM_DEVICE_MODEM (device);
-	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
 	NMModemState modem_state;
 
-	if (!priv->rf_enabled) {
-		_LOGD (LOGD_MB, "not available because WWAN airplane mode is on");
+	if (!priv->rf_enabled)
 		return FALSE;
-	}
 
 	g_assert (priv->modem);
 	modem_state = nm_modem_get_state (priv->modem);
-	if (modem_state <= NM_MODEM_STATE_INITIALIZING) {
-		_LOGD (LOGD_MB, "not available because modem is not ready (%s)",
-		       nm_modem_state_to_string (modem_state));
+	if (modem_state <= NM_MODEM_STATE_INITIALIZING)
 		return FALSE;
-	}
 
 	return TRUE;
 }
@@ -638,7 +681,7 @@ set_modem (NMDeviceModem *self, NMModem *modem)
 
 static void
 set_property (GObject *object, guint prop_id,
-			  const GValue *value, GParamSpec *pspec)
+              const GValue *value, GParamSpec *pspec)
 {
 	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object);
 
@@ -661,7 +704,7 @@ set_property (GObject *object, guint prop_id,
 
 static void
 get_property (GObject *object, guint prop_id,
-			  GValue *value, GParamSpec *pspec)
+              GValue *value, GParamSpec *pspec)
 {
 	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object);
 
@@ -711,6 +754,8 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass)
 	device_class->check_connection_compatible = check_connection_compatible;
 	device_class->check_connection_available = check_connection_available;
 	device_class->complete_connection = complete_connection;
+	device_class->deactivate_async = deactivate_async;
+	device_class->deactivate_async_finish = deactivate_async_finish;
 	device_class->deactivate = deactivate;
 	device_class->act_stage1_prepare = act_stage1_prepare;
 	device_class->act_stage2_config = act_stage2_config;
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index f8239b79..4bb72fd1 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -42,7 +42,7 @@ struct _NMModemBroadbandPrivate {
 	MMModemSimple *simple_iface;
 
 	/* Connection setup */
-	MMSimpleConnectProperties *connect_properties;
+
 	MMBearer *bearer;
 	MMBearerIpConfig *ipv4_config;
 	MMBearerIpConfig *ipv6_config;
@@ -176,78 +176,6 @@ get_bearer_ip_method (MMBearerIpConfig *config)
 	return NM_MODEM_IP_METHOD_UNKNOWN;
 }
 
-static void
-connect_ready (MMModemSimple *simple_iface,
-               GAsyncResult *res,
-               NMModemBroadband *self)
-{
-	GError *error = NULL;
-	NMModemIPMethod ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
-	NMModemIPMethod ip6_method = NM_MODEM_IP_METHOD_UNKNOWN;
-
-	g_clear_object (&self->priv->connect_properties);
-
-	self->priv->bearer = mm_modem_simple_connect_finish (simple_iface, res, &error);
-	if (!self->priv->bearer) {
-		if (g_error_matches (error,
-		                     MM_MOBILE_EQUIPMENT_ERROR,
-		                     MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) ||
-			(g_error_matches (error,
-		                          MM_CORE_ERROR,
-		                          MM_CORE_ERROR_UNAUTHORIZED) &&
-			 mm_modem_get_unlock_required (self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN)) {
-			/* Request PIN */
-			ask_for_pin (self);
-		} else {
-			/* Strip remote error info before logging it */
-			if (g_dbus_error_is_remote_error (error))
-				g_dbus_error_strip_remote_error (error);
-
-			nm_log_warn (LOGD_MB, "(%s) failed to connect modem: %s",
-			             nm_modem_get_uid (NM_MODEM (self)),
-			             error && error->message ? error->message : "(unknown)");
-			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error));
-		}
-
-		g_clear_error (&error);
-		g_object_unref (self);
-		return;
-	}
-
-	/* Grab IP configurations */
-	self->priv->ipv4_config = mm_bearer_get_ipv4_config (self->priv->bearer);
-	if (self->priv->ipv4_config)
-		ip4_method = get_bearer_ip_method (self->priv->ipv4_config);
-
-	self->priv->ipv6_config = mm_bearer_get_ipv6_config (self->priv->bearer);
-	if (self->priv->ipv6_config)
-		ip6_method = get_bearer_ip_method (self->priv->ipv6_config);
-
-	if (ip4_method == NM_MODEM_IP_METHOD_UNKNOWN &&
-	    ip6_method == NM_MODEM_IP_METHOD_UNKNOWN) {
-		nm_log_warn (LOGD_MB, "(%s) failed to connect modem: invalid bearer IP configuration",
-		             nm_modem_get_uid (NM_MODEM (self)));
-
-		error = g_error_new_literal (NM_DEVICE_ERROR,
-		                             NM_DEVICE_ERROR_INVALID_CONNECTION,
-		                             "invalid bearer IP configuration");
-		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, error);
-		g_error_free (error);
-		g_object_unref (self);
-		return;
-	}
-
-	g_object_set (self,
-	              NM_MODEM_DATA_PORT,  mm_bearer_get_interface (self->priv->bearer),
-	              NM_MODEM_IP4_METHOD, ip4_method,
-	              NM_MODEM_IP6_METHOD, ip6_method,
-	              NM_MODEM_IP_TIMEOUT, mm_bearer_get_ip_timeout (self->priv->bearer),
-	              NULL);
-
-	g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, TRUE, NM_DEVICE_STATE_REASON_NONE);
-	g_object_unref (self);
-}
-
 static MMSimpleConnectProperties *
 create_cdma_connect_properties (NMConnection *connection)
 {
@@ -266,15 +194,12 @@ create_cdma_connect_properties (NMConnection *connection)
 }
 
 static MMSimpleConnectProperties *
-create_gsm_connect_properties (NMModem *modem,
-                               NMConnection *connection,
-                               GError **error)
+create_gsm_connect_properties (NMConnection *connection)
 {
 	NMSettingGsm *setting;
 	NMSettingPpp *s_ppp;
 	MMSimpleConnectProperties *properties;
 	const gchar *str;
-	NMModemIPType ip_type;
 
 	setting = nm_connection_get_setting_gsm (connection);
 	properties = mm_simple_connect_properties_new ();
@@ -329,23 +254,137 @@ create_gsm_connect_properties (NMModem *modem,
 		mm_simple_connect_properties_set_allowed_auth (properties, allowed_auth);
 	}
 
-	/* Determine IP types to use when connecting */
-	ip_type = nm_modem_get_connection_ip_type (modem, connection, error);
-	if (ip_type == NM_MODEM_IP_TYPE_UNKNOWN) {
-		g_object_unref (properties);
-		return NULL;
+	return properties;
+}
+
+typedef struct {
+	NMModemBroadband *self;
+	MMModemCapability caps;
+	MMSimpleConnectProperties *connect_properties;
+	GArray *ip_types;
+	guint ip_types_i;
+	GError *first_error;
+} ActStageContext;
+
+static void
+act_stage_context_free (ActStageContext *ctx)
+{
+	g_clear_error (&ctx->first_error);
+	g_clear_pointer (&ctx->ip_types, (GDestroyNotify) g_array_unref);
+	g_clear_object (&ctx->connect_properties);
+	g_object_unref (ctx->self);
+	g_slice_free (ActStageContext, ctx);
+}
+
+static void act_stage_context_step (ActStageContext *ctx);
+
+static void
+connect_ready (MMModemSimple *simple_iface,
+               GAsyncResult *res,
+               ActStageContext *ctx)
+{
+	GError *error = NULL;
+	NMModemIPMethod ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
+	NMModemIPMethod ip6_method = NM_MODEM_IP_METHOD_UNKNOWN;
+
+	ctx->self->priv->bearer = mm_modem_simple_connect_finish (simple_iface, res, &error);
+	if (!ctx->self->priv->bearer) {
+		if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) ||
+		    (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) &&
+		     mm_modem_get_unlock_required (ctx->self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN)) {
+			/* Request PIN */
+			ask_for_pin (ctx->self);
+			g_error_free (error);
+			act_stage_context_free (ctx);
+			return;
+		}
+
+		/* Save the error, if it's the first one */
+		if (!ctx->first_error) {
+			/* Strip remote error info before saving it */
+			if (g_dbus_error_is_remote_error (error))
+				g_dbus_error_strip_remote_error (error);
+			ctx->first_error = error;
+		} else
+			g_error_free (error);
+
+		/* If the modem/provider lies and the IP type we tried isn't supported,
+		 * retry with the next one, if any.
+		 */
+		ctx->ip_types_i++;
+		act_stage_context_step (ctx);
+		return;
 	}
 
-	if (ip_type == NM_MODEM_IP_TYPE_IPV4)
-		mm_simple_connect_properties_set_ip_type (properties, MM_BEARER_IP_FAMILY_IPV4);
-	else if (ip_type == NM_MODEM_IP_TYPE_IPV6)
-		mm_simple_connect_properties_set_ip_type (properties, MM_BEARER_IP_FAMILY_IPV6);
-	else if (ip_type == NM_MODEM_IP_TYPE_IPV4V6)
-		mm_simple_connect_properties_set_ip_type (properties, MM_BEARER_IP_FAMILY_IPV4V6);
-	else
-		g_assert_not_reached ();
+	/* Grab IP configurations */
+	ctx->self->priv->ipv4_config = mm_bearer_get_ipv4_config (ctx->self->priv->bearer);
+	if (ctx->self->priv->ipv4_config)
+		ip4_method = get_bearer_ip_method (ctx->self->priv->ipv4_config);
 
-	return properties;
+	ctx->self->priv->ipv6_config = mm_bearer_get_ipv6_config (ctx->self->priv->bearer);
+	if (ctx->self->priv->ipv6_config)
+		ip6_method = get_bearer_ip_method (ctx->self->priv->ipv6_config);
+
+	if (ip4_method == NM_MODEM_IP_METHOD_UNKNOWN &&
+	    ip6_method == NM_MODEM_IP_METHOD_UNKNOWN) {
+		nm_log_warn (LOGD_MB, "(%s): failed to connect modem: invalid bearer IP configuration",
+		             nm_modem_get_uid (NM_MODEM (ctx->self)));
+		g_signal_emit_by_name (ctx->self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+		act_stage_context_free (ctx);
+		return;
+	}
+
+	g_object_set (ctx->self,
+	              NM_MODEM_DATA_PORT,  mm_bearer_get_interface (ctx->self->priv->bearer),
+	              NM_MODEM_IP4_METHOD, ip4_method,
+	              NM_MODEM_IP6_METHOD, ip6_method,
+	              NM_MODEM_IP_TIMEOUT, mm_bearer_get_ip_timeout (ctx->self->priv->bearer),
+	              NULL);
+
+	g_signal_emit_by_name (ctx->self, NM_MODEM_PREPARE_RESULT, TRUE, NM_DEVICE_STATE_REASON_NONE);
+	act_stage_context_free (ctx);
+}
+
+static void
+act_stage_context_step (ActStageContext *ctx)
+{
+	if (ctx->ip_types_i < ctx->ip_types->len) {
+		NMModemIPType current;
+
+		current = g_array_index (ctx->ip_types, NMModemIPType, ctx->ip_types_i);
+
+		if (current == NM_MODEM_IP_TYPE_IPV4)
+			mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4);
+		else if (current == NM_MODEM_IP_TYPE_IPV6)
+			mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV6);
+		else if (current == NM_MODEM_IP_TYPE_IPV4V6)
+			mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4V6);
+		else
+			g_assert_not_reached ();
+
+		nm_log_dbg (LOGD_MB, "(%s): launching connection with ip type '%s'",
+		            nm_modem_get_uid (NM_MODEM (ctx->self)),
+		            nm_modem_ip_type_to_string (current));
+
+		mm_modem_simple_connect (ctx->self->priv->simple_iface,
+		                         ctx->connect_properties,
+		                         NULL,
+		                         (GAsyncReadyCallback)connect_ready,
+		                         ctx);
+		return;
+	}
+
+	/* If we have a saved error from a previous attempt, use it */
+	if (!ctx->first_error)
+		ctx->first_error = g_error_new_literal (NM_DEVICE_ERROR,
+		                                        NM_DEVICE_ERROR_INVALID_CONNECTION,
+		                                        "invalid bearer IP configuration");
+
+	nm_log_warn (LOGD_MB, "(%s): failed to connect modem: %s",
+	             nm_modem_get_uid (NM_MODEM (ctx->self)),
+	             ctx->first_error->message);
+	g_signal_emit_by_name (ctx->self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (ctx->first_error));
+	act_stage_context_free (ctx);
 }
 
 static NMActStageReturn
@@ -354,42 +393,55 @@ act_stage1_prepare (NMModem *_self,
                     NMDeviceStateReason *reason)
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
-	MMModemCapability caps;
+	ActStageContext *ctx;
 	GError *error = NULL;
 
-	g_clear_object (&self->priv->connect_properties);
+	/* Make sure we can get the Simple interface from the modem */
+	if (!self->priv->simple_iface) {
+		self->priv->simple_iface = mm_object_get_modem_simple (self->priv->modem_object);
+		if (!self->priv->simple_iface) {
+			nm_log_warn (LOGD_MB, "(%s) cannot access the Simple mobile broadband modem interface",
+			             nm_modem_get_uid (NM_MODEM (self)));
+			*reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED;
+			return NM_ACT_STAGE_RETURN_FAILURE;
+		}
+	}
+
+	/* Allocate new context for this activation stage attempt */
+	ctx = g_slice_new0 (ActStageContext);
+	ctx->self = NM_MODEM_BROADBAND (g_object_ref (self));
+	ctx->caps = mm_modem_get_current_capabilities (self->priv->modem_iface);
 
-	caps = mm_modem_get_current_capabilities (self->priv->modem_iface);
-	if (MODEM_CAPS_3GPP (caps))
-		self->priv->connect_properties = create_gsm_connect_properties (_self, connection, &error);
-	else if (MODEM_CAPS_3GPP2 (caps))
-		self->priv->connect_properties = create_cdma_connect_properties (connection);
+	/* Create core connect properties based on the modem capabilities */
+	if (MODEM_CAPS_3GPP (ctx->caps))
+		ctx->connect_properties = create_gsm_connect_properties (connection);
+	else if (MODEM_CAPS_3GPP2 (ctx->caps))
+		ctx->connect_properties = create_cdma_connect_properties (connection);
 	else {
-		nm_log_warn (LOGD_MB, "(%s) not a mobile broadband modem",
-					 nm_modem_get_uid (NM_MODEM (self)));
+		nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': not a mobile broadband modem",
+		             nm_modem_get_uid (NM_MODEM (self)),
+		             nm_connection_get_id (connection));
+		act_stage_context_free (ctx);
 		*reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED;
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
+	g_assert (ctx->connect_properties);
 
-	if (error) {
+	/* Checkout list of IP types that we need to use in the retries */
+	ctx->ip_types = nm_modem_get_connection_ip_type (NM_MODEM (self), connection, &error);
+	if (!ctx->ip_types) {
 		nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': %s",
 		             nm_modem_get_uid (NM_MODEM (self)),
 		             nm_connection_get_id (connection),
-		             error->message);
+		             error ? error->message : "unknown error");
 		g_clear_error (&error);
+		act_stage_context_free (ctx);
 		*reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED;
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	if (!self->priv->simple_iface)
-		self->priv->simple_iface = mm_object_get_modem_simple (self->priv->modem_object);
-
 	g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (self->priv->simple_iface), MODEM_CONNECT_TIMEOUT_SECS * 1000);
-	mm_modem_simple_connect (self->priv->simple_iface,
-	                         self->priv->connect_properties,
-	                         NULL,
-	                         (GAsyncReadyCallback)connect_ready,
-	                         g_object_ref (self));
+	act_stage_context_step (ctx);
 
 	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
@@ -566,9 +618,9 @@ set_power_state_low_ready (MMModem *modem,
 
 	if (!mm_modem_set_power_state_finish (modem, result, &error)) {
 		/* Log but ignore errors; not all modems support low power state */
-		nm_log_dbg (LOGD_MB, "(%s) failed to set modem low power state: %s",
-		             nm_modem_get_uid (NM_MODEM (self)),
-		             error && error->message ? error->message : "(unknown)");
+		nm_log_dbg (LOGD_MB, "(%s): failed to set modem low power state: %s",
+		            nm_modem_get_uid (NM_MODEM (self)),
+		            error && error->message ? error->message : "(unknown)");
 		g_clear_error (&error);
 	}
 
@@ -591,7 +643,7 @@ modem_disable_ready (MMModem *modem_iface,
 		                          (GAsyncReadyCallback) set_power_state_low_ready,
 		                          g_object_ref (self));
 	} else {
-		nm_log_warn (LOGD_MB, "(%s) failed to disable modem: %s",
+		nm_log_warn (LOGD_MB, "(%s): failed to disable modem: %s",
 		             nm_modem_get_uid (NM_MODEM (self)),
 		             error && error->message ? error->message : "(unknown)");
 		nm_modem_set_prev_state (NM_MODEM (self), "disable failed");
@@ -708,7 +760,7 @@ static_stage3_ip4_done (NMModemBroadband *self)
 
 	/* DNS servers */
 	dns = mm_bearer_ip_config_get_dns (self->priv->ipv4_config);
-	for (i = 0; dns[i]; i++) {
+	for (i = 0; dns && dns[i]; i++) {
 		if (   ip4_string_to_num (dns[i], &address_network)
 		    && address_network > 0) {
 			nm_ip4_config_add_nameserver (config, address_network);
@@ -761,9 +813,9 @@ stage3_ip6_done (NMModemBroadband *self)
 		/* DHCP/SLAAC is allowed to skip addresses; other methods require it */
 		if (ip_method != NM_MODEM_IP_METHOD_AUTO) {
 			error = g_error_new (NM_DEVICE_ERROR,
-				                 NM_DEVICE_ERROR_INVALID_CONNECTION,
-				                 "(%s) retrieving IPv6 configuration failed: no address given",
-				                 nm_modem_get_uid (NM_MODEM (self)));
+			                     NM_DEVICE_ERROR_INVALID_CONNECTION,
+			                     "(%s) retrieving IPv6 configuration failed: no address given",
+			                     nm_modem_get_uid (NM_MODEM (self)));
 		}
 		goto out;
 	}
@@ -792,10 +844,10 @@ stage3_ip6_done (NMModemBroadband *self)
 	if (address_string) {
 		if (!inet_pton (AF_INET6, address_string, (void *) &(address.address))) {
 			error = g_error_new (NM_DEVICE_ERROR,
-				                 NM_DEVICE_ERROR_INVALID_CONNECTION,
-				                 "(%s) retrieving IPv6 configuration failed: invalid gateway given '%s'",
-				                 nm_modem_get_uid (NM_MODEM (self)),
-				                 address_string);
+			                     NM_DEVICE_ERROR_INVALID_CONNECTION,
+			                     "(%s) retrieving IPv6 configuration failed: invalid gateway given '%s'",
+			                     nm_modem_get_uid (NM_MODEM (self)),
+			                     address_string);
 			goto out;
 		}
 		nm_log_info (LOGD_MB, "  gateway %s", address_string);
@@ -844,20 +896,47 @@ stage3_ip6_config_request (NMModem *_self, NMDeviceStateReason *reason)
 
 typedef struct {
 	NMModemBroadband *self;
+	GSimpleAsyncResult *result;
+	GCancellable *cancellable;
 	gboolean warn;
-} SimpleDisconnectContext;
+} DisconnectContext;
 
 static void
-simple_disconnect_context_free (SimpleDisconnectContext *ctx)
+disconnect_context_complete (DisconnectContext *ctx)
 {
+	g_simple_async_result_complete_in_idle (ctx->result);
+	if (ctx->cancellable)
+		g_object_unref (ctx->cancellable);
+	g_object_unref (ctx->result);
 	g_object_unref (ctx->self);
-	g_slice_free (SimpleDisconnectContext, ctx);
+	g_slice_free (DisconnectContext, ctx);
+}
+
+static gboolean
+disconnect_context_complete_if_cancelled (DisconnectContext *ctx)
+{
+	GError *error = NULL;
+
+	if (g_cancellable_set_error_if_cancelled (ctx->cancellable, &error)) {
+		g_simple_async_result_take_error (ctx->result, error);
+		disconnect_context_complete (ctx);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static gboolean
+disconnect_finish (NMModem *self,
+                   GAsyncResult *res,
+                   GError **error)
+{
+	return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
 }
 
 static void
 simple_disconnect_ready (MMModemSimple *modem_iface,
                          GAsyncResult *res,
-                         SimpleDisconnectContext *ctx)
+                         DisconnectContext *ctx)
 {
 	GError *error = NULL;
 
@@ -865,33 +944,48 @@ simple_disconnect_ready (MMModemSimple *modem_iface,
 		if (ctx->warn)
 			nm_log_warn (LOGD_MB, "(%s) failed to disconnect modem: %s",
 			             nm_modem_get_uid (NM_MODEM (ctx->self)),
-			             error && error->message ? error->message : "(unknown)");
-		g_clear_error (&error);
+			             error->message);
+		g_simple_async_result_take_error (ctx->result, error);
 	}
 
-	simple_disconnect_context_free (ctx);
+	disconnect_context_complete (ctx);
 }
 
 static void
-disconnect (NMModem *modem,
-            gboolean warn)
+disconnect (NMModem *self,
+            gboolean warn,
+            GCancellable *cancellable,
+            GAsyncReadyCallback callback,
+            gpointer user_data)
 {
-	NMModemBroadband *self = NM_MODEM_BROADBAND (modem);
-	SimpleDisconnectContext *ctx;
-
-	if (!self->priv->simple_iface)
-		return;
+	DisconnectContext *ctx;
 
-	ctx = g_slice_new (SimpleDisconnectContext);
+	ctx = g_slice_new (DisconnectContext);
 	ctx->self = g_object_ref (self);
-
+	ctx->result = g_simple_async_result_new (G_OBJECT (self),
+	                                         callback,
+	                                         user_data,
+	                                         disconnect);
 	/* Don't bother warning on FAILED since the modem is already gone */
 	ctx->warn = warn;
 
+	/* Setup cancellable */
+	ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+	if (disconnect_context_complete_if_cancelled (ctx))
+		return;
+
+	/* If no simple iface, we're done */
+	if (!ctx->self->priv->simple_iface) {
+		disconnect_context_complete (ctx);
+		return;
+	}
+
+	nm_log_dbg (LOGD_MB, "(%s): notifying ModemManager about the modem disconnection",
+	            nm_modem_get_uid (NM_MODEM (ctx->self)));
 	mm_modem_simple_disconnect (
 		ctx->self->priv->simple_iface,
 		NULL, /* bearer path; if NULL given ALL get disconnected */
-		NULL, /* cancellable */
+		cancellable,
 		(GAsyncReadyCallback)simple_disconnect_ready,
 		ctx);
 }
@@ -899,7 +993,7 @@ disconnect (NMModem *modem,
 /*****************************************************************************/
 
 static void
-deactivate (NMModem *_self, NMDevice *device)
+deactivate_cleanup (NMModem *_self, NMDevice *device)
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
 
@@ -913,7 +1007,7 @@ deactivate (NMModem *_self, NMDevice *device)
 	self->priv->pin_tries = 0;
 
 	/* Chain up parent's */
-	NM_MODEM_CLASS (nm_modem_broadband_parent_class)->deactivate (_self, device);
+	NM_MODEM_CLASS (nm_modem_broadband_parent_class)->deactivate_cleanup (_self, device);
 }
 
 /*****************************************************************************/
@@ -1027,7 +1121,7 @@ get_sim_ready (MMModem *modem,
 		              NULL);
 		g_object_unref (new_sim);
 	} else {
-		nm_log_warn (LOGD_MB, "(%s) failed to retrieve SIM object: %s",
+		nm_log_warn (LOGD_MB, "(%s): failed to retrieve SIM object: %s",
 		             nm_modem_get_uid (NM_MODEM (self)),
 		             error && error->message ? error->message : "(unknown)");
 	}
@@ -1062,7 +1156,7 @@ nm_modem_broadband_init (NMModemBroadband *self)
 static void
 set_property (GObject *object,
               guint prop_id,
-			  const GValue *value,
+              const GValue *value,
               GParamSpec *pspec)
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (object);
@@ -1095,7 +1189,7 @@ set_property (GObject *object,
 static void
 get_property (GObject *object,
               guint prop_id,
-			  GValue *value,
+              GValue *value,
               GParamSpec *pspec)
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (object);
@@ -1142,7 +1236,8 @@ nm_modem_broadband_class_init (NMModemBroadbandClass *klass)
 	modem_class->static_stage3_ip4_config_start = static_stage3_ip4_config_start;
 	modem_class->stage3_ip6_config_request = stage3_ip6_config_request;
 	modem_class->disconnect = disconnect;
-	modem_class->deactivate = deactivate;
+	modem_class->disconnect_finish = disconnect_finish;
+	modem_class->deactivate_cleanup = deactivate_cleanup;
 	modem_class->set_mm_enabled = set_mm_enabled;
 	modem_class->get_user_pass = get_user_pass;
 	modem_class->check_connection_compatible = check_connection_compatible;
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index ba1db8a4..69eaddf3 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -175,23 +175,23 @@ nm_modem_set_mm_enabled (NMModem *self,
 	NMModemState prev_state = priv->state;
 
 	if (enabled && priv->state >= NM_MODEM_STATE_ENABLING) {
-		nm_log_dbg (LOGD_MB, "(%s) cannot enable modem: already enabled",
+		nm_log_dbg (LOGD_MB, "(%s): cannot enable modem: already enabled",
 		            nm_modem_get_uid (self));
 		return;
 	}
 	if (!enabled && priv->state <= NM_MODEM_STATE_DISABLING) {
-		nm_log_dbg (LOGD_MB, "(%s) cannot disable modem: already disabled",
+		nm_log_dbg (LOGD_MB, "(%s): cannot disable modem: already disabled",
 		            nm_modem_get_uid (self));
 		return;
 	}
 
 	if (priv->state <= NM_MODEM_STATE_INITIALIZING) {
-		nm_log_dbg (LOGD_MB, "(%s) cannot enable/disable modem: initializing or failed",
+		nm_log_dbg (LOGD_MB, "(%s): cannot enable/disable modem: initializing or failed",
 		            nm_modem_get_uid (self));
 		return;
 	} else if (priv->state == NM_MODEM_STATE_LOCKED) {
 		/* Don't try to enable if the modem is locked since that will fail */
-		nm_log_warn (LOGD_MB, "(%s) cannot enable/disable modem: locked",
+		nm_log_warn (LOGD_MB, "(%s): cannot enable/disable modem: locked",
 		             nm_modem_get_uid (self));
 
 		/* Try to unlock the modem if it's being enabled */
@@ -221,17 +221,39 @@ nm_modem_get_supported_ip_types (NMModem *self)
 	return NM_MODEM_GET_PRIVATE (self)->ip_types;
 }
 
+const gchar *
+nm_modem_ip_type_to_string (NMModemIPType ip_type)
+{
+	switch (ip_type) {
+	case NM_MODEM_IP_TYPE_IPV4:
+		return "ipv4";
+	case NM_MODEM_IP_TYPE_IPV6:
+		return "ipv6";
+	case NM_MODEM_IP_TYPE_IPV4V6:
+		return "ipv4v6";
+	default:
+		g_return_val_if_reached ("unknown");
+	}
+}
+
+static GArray *
+build_single_ip_type_array (NMModemIPType type)
+{
+	return g_array_append_val (g_array_sized_new (FALSE, FALSE, sizeof (NMModemIPType), 1), type);
+}
+
 /**
  * nm_modem_get_connection_ip_type:
  * @self: the #NMModem
  * @connection: the #NMConnection to determine IP type to use
  *
- * Given a modem and a connection, determine which NMModemIpType to use
+ * Given a modem and a connection, determine which #NMModemIPTypes to use
  * when connecting.
  *
- * Returns: a single %NMModemIpType value
+ * Returns: an array of #NMModemIpType values, in the order in which they
+ * should be tried.
  */
-NMModemIPType
+GArray *
 nm_modem_get_connection_ip_type (NMModem *self,
                                  NMConnection *connection,
                                  GError **error)
@@ -265,9 +287,9 @@ nm_modem_get_connection_ip_type (NMModem *self,
 			                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
 			                     "Connection requested IPv4 but IPv4 is "
 			                     "unsuported by the modem.");
-			return NM_MODEM_IP_TYPE_UNKNOWN;
+			return NULL;
 		}
-		return NM_MODEM_IP_TYPE_IPV4;
+		return build_single_ip_type_array (NM_MODEM_IP_TYPE_IPV4);
 	}
 
 	if (ip6 && !ip4) {
@@ -277,38 +299,54 @@ nm_modem_get_connection_ip_type (NMModem *self,
 			                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
 			                     "Connection requested IPv6 but IPv6 is "
 			                     "unsuported by the modem.");
-			return NM_MODEM_IP_TYPE_UNKNOWN;
+			return NULL;
 		}
-		return NM_MODEM_IP_TYPE_IPV6;
+		return build_single_ip_type_array (NM_MODEM_IP_TYPE_IPV6);
 	}
 
 	if (ip4 && ip6) {
-		/* Modem supports dual-stack */
-		if (priv->ip_types & NM_MODEM_IP_TYPE_IPV4V6)
-			return NM_MODEM_IP_TYPE_IPV4V6;
+		NMModemIPType type;
+		GArray *out;
 
-		/* Both IPv4 and IPv6 requested, but modem doesn't support dual-stack;
-		 * if one method is marked "may-fail" then use the other.
-		 */
-		if (ip6_may_fail)
-			return NM_MODEM_IP_TYPE_IPV4;
-		else if (ip4_may_fail)
-			return NM_MODEM_IP_TYPE_IPV6;
+		out = g_array_sized_new (FALSE, FALSE, sizeof (NMModemIPType), 3);
 
+		/* Modem supports dual-stack? */
+		if (priv->ip_types & NM_MODEM_IP_TYPE_IPV4V6) {
+			type = NM_MODEM_IP_TYPE_IPV4V6;
+			g_array_append_val (out, type);
+		}
+
+		/* If IPv6 may-fail=false, we should NOT try IPv4 as fallback */
+		if ((priv->ip_types & NM_MODEM_IP_TYPE_IPV4) && ip6_may_fail) {
+			type = NM_MODEM_IP_TYPE_IPV4;
+			g_array_append_val (out, type);
+		}
+
+		/* If IPv4 may-fail=false, we should NOT try IPv6 as fallback */
+		if ((priv->ip_types & NM_MODEM_IP_TYPE_IPV6) && ip4_may_fail) {
+			type = NM_MODEM_IP_TYPE_IPV6;
+			g_array_append_val (out, type);
+		}
+
+		if (out->len > 0)
+			return out;
+
+		/* Error... */
+		g_array_unref (out);
 		g_set_error_literal (error,
 		                     NM_DEVICE_ERROR,
 		                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
 		                     "Connection requested both IPv4 and IPv6 "
 		                     "but dual-stack addressing is unsupported "
 		                     "by the modem.");
-		return NM_MODEM_IP_TYPE_UNKNOWN;
+		return NULL;
 	}
 
 	g_set_error_literal (error,
 	                     NM_DEVICE_ERROR,
 	                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
 	                     "Connection specified no IP configuration!");
-	return NM_MODEM_IP_TYPE_UNKNOWN;
+	return NULL;
 }
 
 /*****************************************************************************/
@@ -343,9 +381,9 @@ set_data_port (NMModem *self, const char *new_data_port)
 
 static void
 ppp_ip4_config (NMPPPManager *ppp_manager,
-				const char *iface,
-				NMIP4Config *config,
-				gpointer user_data)
+                const char *iface,
+                NMIP4Config *config,
+                gpointer user_data)
 {
 	NMModem *self = NM_MODEM (user_data);
 	guint32 i, num;
@@ -416,9 +454,9 @@ ppp_ip6_config (NMPPPManager *ppp_manager,
 
 static void
 ppp_stats (NMPPPManager *ppp_manager,
-		   guint32 in_bytes,
-		   guint32 out_bytes,
-		   gpointer user_data)
+           guint32 in_bytes,
+           guint32 out_bytes,
+           gpointer user_data)
 {
 	NMModem *self = NM_MODEM (user_data);
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
@@ -464,7 +502,8 @@ ppp_stage3_ip_config_start (NMModem *self,
 	/* Check if ModemManager requested a specific IP timeout to be used. If 0 reported,
 	 * use the default one (30s) */
 	if (priv->mm_ip_timeout > 0) {
-		nm_log_info (LOGD_PPP, "using modem-specified IP timeout: %u seconds",
+		nm_log_info (LOGD_PPP, "(%s): using modem-specified IP timeout: %u seconds",
+		             nm_modem_get_uid (self),
 		             priv->mm_ip_timeout);
 		ip_timeout = priv->mm_ip_timeout;
 	}
@@ -486,7 +525,8 @@ ppp_stage3_ip_config_start (NMModem *self,
 
 		ret = NM_ACT_STAGE_RETURN_POSTPONE;
 	} else {
-		nm_log_err (LOGD_PPP, "error starting PPP: (%d) %s",
+		nm_log_err (LOGD_PPP, "(%s): error starting PPP: (%d) %s",
+		            nm_modem_get_uid (self),
 		            error ? error->code : -1,
 		            error && error->message ? error->message : "(unknown)");
 		g_error_free (error);
@@ -705,7 +745,7 @@ modem_secrets_cb (NMActRequest *req,
 	priv->secrets_id = 0;
 
 	if (error)
-		nm_log_warn (LOGD_MB, "%s", error->message);
+		nm_log_warn (LOGD_MB, "(%s): %s", nm_modem_get_uid (self), error->message);
 
 	g_signal_emit (self, signals[AUTH_RESULT], 0, error);
 }
@@ -839,13 +879,12 @@ nm_modem_complete_connection (NMModem *self,
 /*****************************************************************************/
 
 static void
-deactivate (NMModem *self, NMDevice *device)
+deactivate_cleanup (NMModem *self, NMDevice *device)
 {
 	NMModemPrivate *priv;
 	int ifindex;
 
 	g_return_if_fail (NM_IS_MODEM (self));
-	g_return_if_fail (NM_IS_DEVICE (device));
 
 	priv = NM_MODEM_GET_PRIVATE (self);
 
@@ -864,15 +903,19 @@ deactivate (NMModem *self, NMDevice *device)
 		priv->ppp_manager = NULL;
 	}
 
-	if (priv->ip4_method == NM_MODEM_IP_METHOD_STATIC ||
-	    priv->ip4_method == NM_MODEM_IP_METHOD_AUTO ||
-	    priv->ip6_method == NM_MODEM_IP_METHOD_STATIC ||
-	    priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) {
-		ifindex = nm_device_get_ip_ifindex (device);
-		if (ifindex > 0) {
-			nm_platform_route_flush (ifindex);
-			nm_platform_address_flush (ifindex);
-			nm_platform_link_set_down (ifindex);
+	if (device) {
+		g_return_if_fail (NM_IS_DEVICE (device));
+
+		if (priv->ip4_method == NM_MODEM_IP_METHOD_STATIC ||
+		    priv->ip4_method == NM_MODEM_IP_METHOD_AUTO ||
+		    priv->ip6_method == NM_MODEM_IP_METHOD_STATIC ||
+		    priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) {
+			ifindex = nm_device_get_ip_ifindex (device);
+			if (ifindex > 0) {
+				nm_platform_route_flush (ifindex);
+				nm_platform_address_flush (ifindex);
+				nm_platform_link_set_down (ifindex);
+			}
 		}
 	}
 	priv->ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
@@ -884,10 +927,176 @@ deactivate (NMModem *self, NMDevice *device)
 
 /*****************************************************************************/
 
+typedef enum {
+	DEACTIVATE_CONTEXT_STEP_FIRST,
+	DEACTIVATE_CONTEXT_STEP_CLEANUP,
+	DEACTIVATE_CONTEXT_STEP_PPP_MANAGER_STOP,
+	DEACTIVATE_CONTEXT_STEP_MM_DISCONNECT,
+	DEACTIVATE_CONTEXT_STEP_LAST
+} DeactivateContextStep;
+
+typedef struct {
+	NMModem *self;
+	NMDevice *device;
+	GCancellable *cancellable;
+	GSimpleAsyncResult *result;
+	DeactivateContextStep step;
+	NMPPPManager *ppp_manager;
+} DeactivateContext;
+
+static void
+deactivate_context_complete (DeactivateContext *ctx)
+{
+	if (ctx->ppp_manager)
+		g_object_unref (ctx->ppp_manager);
+	if (ctx->cancellable)
+		g_object_unref (ctx->cancellable);
+	g_simple_async_result_complete_in_idle (ctx->result);
+	g_object_unref (ctx->result);
+	g_object_unref (ctx->device);
+	g_object_unref (ctx->self);
+	g_slice_free (DeactivateContext, ctx);
+}
+
+gboolean
+nm_modem_deactivate_async_finish (NMModem *self,
+                                  GAsyncResult *res,
+                                  GError **error)
+{
+	return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void deactivate_step (DeactivateContext *ctx);
+
+static void
+disconnect_ready (NMModem *self,
+                  GAsyncResult *res,
+                  DeactivateContext *ctx)
+{
+	GError *error = NULL;
+
+	if (!NM_MODEM_GET_CLASS (self)->disconnect_finish (self, res, &error)) {
+		g_simple_async_result_take_error (ctx->result, error);
+		deactivate_context_complete (ctx);
+		return;
+	}
+
+	/* Go on */
+	ctx->step++;
+	deactivate_step (ctx);
+}
+
+static void
+ppp_manager_stop_ready (NMPPPManager *ppp_manager,
+                        GAsyncResult *res,
+                        DeactivateContext *ctx)
+{
+	GError *error = NULL;
+
+	if (!nm_ppp_manager_stop_finish (ppp_manager, res, &error)) {
+		nm_log_warn (LOGD_MB, "(%s): cannot stop PPP manager: %s",
+		             nm_modem_get_uid (ctx->self),
+		             error->message);
+		g_simple_async_result_take_error (ctx->result, error);
+		deactivate_context_complete (ctx);
+		return;
+	}
+
+	/* Go on */
+	ctx->step++;
+	deactivate_step (ctx);
+}
+
+static void
+deactivate_step (DeactivateContext *ctx)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (ctx->self);
+	GError *error = NULL;
+
+	/* Check cancellable in each step */
+	if (g_cancellable_set_error_if_cancelled (ctx->cancellable, &error)) {
+		g_simple_async_result_take_error (ctx->result, error);
+		deactivate_context_complete (ctx);
+		return;
+	}
+
+	switch (ctx->step) {
+	case DEACTIVATE_CONTEXT_STEP_FIRST:
+		ctx->step++;
+		/* Fall down */
+
+	case DEACTIVATE_CONTEXT_STEP_CLEANUP:
+		/* Make sure we keep a ref to the PPP manager if there is one */
+		if (priv->ppp_manager)
+			ctx->ppp_manager = g_object_ref (priv->ppp_manager);
+		/* Run cleanup */
+		NM_MODEM_GET_CLASS (ctx->self)->deactivate_cleanup (ctx->self, ctx->device);
+		ctx->step++;
+		/* Fall down */
+
+	case DEACTIVATE_CONTEXT_STEP_PPP_MANAGER_STOP:
+		/* If we have a PPP manager, stop it */
+		if (ctx->ppp_manager) {
+			nm_ppp_manager_stop (ctx->ppp_manager,
+			                     ctx->cancellable,
+			                     (GAsyncReadyCallback) ppp_manager_stop_ready,
+			                     ctx);
+			return;
+		}
+		ctx->step++;
+		/* Fall down */
+
+	case DEACTIVATE_CONTEXT_STEP_MM_DISCONNECT:
+		/* Disconnect asynchronously */
+		NM_MODEM_GET_CLASS (ctx->self)->disconnect (ctx->self,
+		                                            FALSE,
+		                                            ctx->cancellable,
+		                                            (GAsyncReadyCallback) disconnect_ready,
+		                                            ctx);
+		return;
+
+	case DEACTIVATE_CONTEXT_STEP_LAST:
+		nm_log_dbg (LOGD_MB, "(%s): modem deactivation finished",
+		            nm_modem_get_uid (ctx->self));
+		deactivate_context_complete (ctx);
+		return;
+	}
+
+	g_assert_not_reached ();
+}
+
+void
+nm_modem_deactivate_async (NMModem *self,
+                           NMDevice *device,
+                           GCancellable *cancellable,
+                           GAsyncReadyCallback callback,
+                           gpointer user_data)
+{
+	DeactivateContext *ctx;
+
+	ctx = g_slice_new0 (DeactivateContext);
+	ctx->self = g_object_ref (self);
+	ctx->device = g_object_ref (device);
+	ctx->result = g_simple_async_result_new (G_OBJECT (self),
+	                                         callback,
+	                                         user_data,
+	                                         nm_modem_deactivate_async);
+	ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+
+	/* Start */
+	ctx->step = DEACTIVATE_CONTEXT_STEP_FIRST;
+	deactivate_step (ctx);
+}
+
+/*****************************************************************************/
+
 void
 nm_modem_deactivate (NMModem *self, NMDevice *device)
 {
-	NM_MODEM_GET_CLASS (self)->deactivate (self, device);
+	/* First cleanup */
+	NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, device);
+	/* Then disconnect without waiting */
+	NM_MODEM_GET_CLASS (self)->disconnect (self, FALSE, NULL, NULL, NULL);
 }
 
 /*****************************************************************************/
@@ -912,7 +1121,6 @@ nm_modem_device_state_changed (NMModem *self,
 	switch (new_state) {
 	case NM_DEVICE_STATE_UNMANAGED:
 	case NM_DEVICE_STATE_UNAVAILABLE:
-	case NM_DEVICE_STATE_DISCONNECTED:
 	case NM_DEVICE_STATE_FAILED:
 		if (priv->act_request) {
 			cancel_get_secrets (self);
@@ -924,7 +1132,9 @@ nm_modem_device_state_changed (NMModem *self,
 			/* Don't bother warning on FAILED since the modem is already gone */
 			if (new_state == NM_DEVICE_STATE_FAILED)
 				warn = FALSE;
-			NM_MODEM_GET_CLASS (self)->disconnect (self, warn);
+			/* First cleanup */
+			NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, NULL);
+			NM_MODEM_GET_CLASS (self)->disconnect (self, warn, NULL, NULL, NULL);
 		}
 		break;
 	default:
@@ -1030,8 +1240,8 @@ nm_modem_init (NMModem *self)
 
 static GObject*
 constructor (GType type,
-			 guint n_construct_params,
-			 GObjectConstructParam *construct_params)
+             guint n_construct_params,
+             GObjectConstructParam *construct_params)
 {
 	GObject *object;
 	NMModemPrivate *priv;
@@ -1056,7 +1266,7 @@ constructor (GType type,
 
 	return object;
 
- err:
+err:
 	g_object_unref (object);
 	return NULL;
 }
@@ -1209,7 +1419,7 @@ nm_modem_class_init (NMModemClass *klass)
 
 	klass->act_stage1_prepare = act_stage1_prepare;
 	klass->stage3_ip6_config_request = stage3_ip6_config_request;
-	klass->deactivate = deactivate;
+	klass->deactivate_cleanup = deactivate_cleanup;
 
 	/* Properties */
 
diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h
index 2a4d9173..2cace890 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -143,9 +143,16 @@ typedef struct {
 
 	void (*set_mm_enabled)                     (NMModem *self, gboolean enabled);
 
-	void (*disconnect)                         (NMModem *self, gboolean warn);
+	void     (*disconnect)                     (NMModem *self,
+	                                            gboolean warn,
+	                                            GCancellable *cancellable,
+	                                            GAsyncReadyCallback callback,
+	                                            gpointer user_data);
+	gboolean (*disconnect_finish)              (NMModem *self,
+	                                            GAsyncResult *res,
+	                                            GError **error);
 
-	void (*deactivate)                         (NMModem *self, NMDevice *device);
+	void     (*deactivate_cleanup)             (NMModem *self, NMDevice *device);
 
 	gboolean (*owns_port)                      (NMModem *self, const char *iface);
 
@@ -218,6 +225,15 @@ gboolean nm_modem_get_secrets (NMModem *modem,
 
 void nm_modem_deactivate (NMModem *modem, NMDevice *device);
 
+void     nm_modem_deactivate_async        (NMModem *self,
+                                           NMDevice *device,
+                                           GCancellable *cancellable,
+                                           GAsyncReadyCallback callback,
+                                           gpointer user_data);
+gboolean nm_modem_deactivate_async_finish (NMModem *self,
+                                           GAsyncResult *res,
+                                           GError **error);
+
 void nm_modem_device_state_changed (NMModem *modem,
                                     NMDeviceState new_state,
                                     NMDeviceState old_state,
@@ -237,7 +253,7 @@ NMModemIPType nm_modem_get_supported_ip_types (NMModem *self);
 /* For the modem-manager only */
 void          nm_modem_emit_removed (NMModem *self);
 
-NMModemIPType nm_modem_get_connection_ip_type (NMModem *self,
+GArray       *nm_modem_get_connection_ip_type (NMModem *self,
                                                NMConnection *connection,
                                                GError **error);
 
@@ -246,6 +262,8 @@ void nm_modem_emit_ip6_config_result (NMModem *self,
                                       NMIP6Config *config,
                                       GError *error);
 
+const gchar *nm_modem_ip_type_to_string (NMModemIPType ip_type);
+
 G_END_DECLS
 
 #endif /* __NETWORKMANAGER_MODEM_H__ */
diff --git a/src/devices/wwan/wwan-exports.ver b/src/devices/wwan/wwan-exports.ver
index c23ab24b..23412de6 100644
--- a/src/devices/wwan/wwan-exports.ver
+++ b/src/devices/wwan/wwan-exports.ver
@@ -5,6 +5,8 @@ global:
 	nm_modem_check_connection_compatible;
 	nm_modem_complete_connection;
 	nm_modem_deactivate;
+	nm_modem_deactivate_async;
+	nm_modem_deactivate_async_finish;
 	nm_modem_device_state_changed;
 	nm_modem_get_capabilities;
 	nm_modem_get_control_port;