summary refs log tree commit diff
path: root/src/devices/bluetooth
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2014-07-06 02:16:10 +0200
committerMichael Biebl <biebl@debian.org>2014-07-06 02:16:10 +0200
commit33491bc4279481db8ae47213e34a6d695a0e8830 (patch)
tree097d2b0fdff3fae6885381ae5e57a182cd8cbbba /src/devices/bluetooth
parent59c3714a494c3b3765657c0551ad82842d98a7d2 (diff)
Imported Upstream version 0.9.10.0 upstream/0.9.10.0
Diffstat (limited to 'src/devices/bluetooth')
-rw-r--r--src/devices/bluetooth/Makefile.am74
-rw-r--r--src/devices/bluetooth/Makefile.in864
-rw-r--r--src/devices/bluetooth/exports.ver7
-rw-r--r--src/devices/bluetooth/nm-bluez-common.h45
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c1212
-rw-r--r--src/devices/bluetooth/nm-bluez-device.h98
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.c428
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.h44
-rw-r--r--src/devices/bluetooth/nm-bluez4-adapter.c413
-rw-r--r--src/devices/bluetooth/nm-bluez4-adapter.h69
-rw-r--r--src/devices/bluetooth/nm-bluez4-manager.c361
-rw-r--r--src/devices/bluetooth/nm-bluez4-manager.h62
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.c421
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.h62
-rw-r--r--src/devices/bluetooth/nm-bt-enum-types.c32
-rw-r--r--src/devices/bluetooth/nm-bt-enum-types.h19
-rw-r--r--src/devices/bluetooth/nm-device-bt-glue.h73
-rw-r--r--src/devices/bluetooth/nm-device-bt.c1278
-rw-r--r--src/devices/bluetooth/nm-device-bt.h74
19 files changed, 5636 insertions, 0 deletions
diff --git a/src/devices/bluetooth/Makefile.am b/src/devices/bluetooth/Makefile.am
new file mode 100644
index 00000000..639a1ad7
--- /dev/null
+++ b/src/devices/bluetooth/Makefile.am
@@ -0,0 +1,74 @@
+include $(GLIB_MAKEFILE)
+
+@GNOME_CODE_COVERAGE_RULES@
+
+AM_CPPFLAGS = \
+	-I${top_srcdir}/src \
+	-I${top_builddir}/src \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/src/devices \
+	-I${top_srcdir}/src/settings \
+	-I${top_srcdir}/src/platform \
+	-I${top_srcdir}/src/devices/wwan \
+	-I${top_builddir}/include \
+	-I${top_srcdir}/include \
+	-I${top_builddir}/libnm-util \
+	-I${top_srcdir}/libnm-util \
+	-DG_LOG_DOMAIN=\""NetworkManager-bluetooth"\" \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
+	$(DBUS_CFLAGS) \
+	$(POLKIT_CFLAGS) \
+	$(LIBNL_CFLAGS) \
+	$(GUDEV_CFLAGS)
+
+GLIB_GENERATED = nm-bt-enum-types.h nm-bt-enum-types.c
+GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
+GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
+nm_bt_enum_types_sources = $(srcdir)/nm-device-bt.h
+
+nm-device-bt-glue.h: $(top_srcdir)/introspection/nm-device-bt.xml
+	dbus-binding-tool --prefix=nm_device_bt --mode=glib-server --output=$@ $<
+
+BUILT_SOURCES = $(GLIB_GENERATED) nm-device-bt-glue.h
+
+pkglib_LTLIBRARIES = libnm-device-plugin-bluetooth.la
+
+SYMBOL_VIS_FILE=$(srcdir)/exports.ver
+
+libnm_device_plugin_bluetooth_la_SOURCES = \
+	nm-bluez-manager.c \
+	nm-bluez-manager.h \
+	nm-bluez-common.h \
+	nm-bluez-device.c \
+	nm-bluez-device.h \
+	nm-bluez4-adapter.c \
+	nm-bluez4-adapter.h \
+	nm-bluez4-manager.c \
+	nm-bluez4-manager.h \
+	nm-bluez5-manager.c \
+	nm-bluez5-manager.h \
+	\
+	nm-device-bt.c \
+	nm-device-bt.h \
+	\
+	$(BUILT_SOURCES)
+
+libnm_device_plugin_bluetooth_la_LDFLAGS = \
+	-module -avoid-version \
+	-Wl,--version-script=$(SYMBOL_VIS_FILE)
+
+libnm_device_plugin_bluetooth_la_LIBADD = \
+	$(top_builddir)/src/devices/wwan/libnm-wwan.la \
+	$(DBUS_LIBS) \
+	$(GUDEV_LIBS)
+
+CLEANFILES = $(BUILT_SOURCES)
+EXTRA_DIST = $(SYMBOL_VIS_FILE)
+
+if ENABLE_TESTS
+
+check-local:
+	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-bluetooth.so $(SYMBOL_VIS_FILE)
+
+endif
+
diff --git a/src/devices/bluetooth/Makefile.in b/src/devices/bluetooth/Makefile.in
new file mode 100644
index 00000000..3145c52d
--- /dev/null
+++ b/src/devices/bluetooth/Makefile.in
@@ -0,0 +1,864 @@
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+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 \
+	$(top_srcdir)/m4/gettext.m4 \
+	$(top_srcdir)/m4/gnome-code-coverage.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
+	$(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \
+	$(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+	$(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(pkglibdir)"
+LTLIBRARIES = $(pkglib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libnm_device_plugin_bluetooth_la_DEPENDENCIES =  \
+	$(top_builddir)/src/devices/wwan/libnm-wwan.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am__objects_1 = nm-bt-enum-types.lo
+am__objects_2 = $(am__objects_1)
+am_libnm_device_plugin_bluetooth_la_OBJECTS = nm-bluez-manager.lo \
+	nm-bluez-device.lo nm-bluez4-adapter.lo nm-bluez4-manager.lo \
+	nm-bluez5-manager.lo nm-device-bt.lo $(am__objects_2)
+libnm_device_plugin_bluetooth_la_OBJECTS =  \
+	$(am_libnm_device_plugin_bluetooth_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libnm_device_plugin_bluetooth_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(AM_CFLAGS) $(CFLAGS) \
+	$(libnm_device_plugin_bluetooth_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(libnm_device_plugin_bluetooth_la_SOURCES)
+DIST_SOURCES = $(libnm_device_plugin_bluetooth_la_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CKDB_PATH = @CKDB_PATH@
+CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+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@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
+DLLTOOL = @DLLTOOL@
+DNSMASQ_PATH = @DNSMASQ_PATH@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MAKEFILE = @GLIB_MAKEFILE@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@
+INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@
+INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@
+INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@
+INTROSPECTION_LIBS = @INTROSPECTION_LIBS@
+INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
+INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
+INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
+IPTABLES_PATH = @IPTABLES_PATH@
+IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@
+IWMX_SDK_LIBS = @IWMX_SDK_LIBS@
+KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBM = @LIBM@
+LIBNDP_CFLAGS = @LIBNDP_CFLAGS@
+LIBNDP_LIBS = @LIBNDP_LIBS@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
+LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
+LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MM_GLIB_CFLAGS = @MM_GLIB_CFLAGS@
+MM_GLIB_LIBS = @MM_GLIB_LIBS@
+MOC = @MOC@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NEWT_CFLAGS = @NEWT_CFLAGS@
+NEWT_LIBS = @NEWT_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
+NM_MICRO_VERSION = @NM_MICRO_VERSION@
+NM_MINOR_VERSION = @NM_MINOR_VERSION@
+NM_MODIFY_SYSTEM_POLICY = @NM_MODIFY_SYSTEM_POLICY@
+NM_VERSION = @NM_VERSION@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PPPD_PATH = @PPPD_PATH@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+PPPOE_PATH = @PPPOE_PATH@
+QT_CFLAGS = @QT_CFLAGS@
+QT_LIBS = @QT_LIBS@
+RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
+SED = @SED@
+SELINUX_CFLAGS = @SELINUX_CFLAGS@
+SELINUX_LIBS = @SELINUX_LIBS@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@
+SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@
+SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@
+SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
+SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
+SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VALGRIND_RULES = @VALGRIND_RULES@
+VAPIGEN = @VAPIGEN@
+VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@
+VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+nmbinary = @nmbinary@
+nmconfdir = @nmconfdir@
+nmdatadir = @nmdatadir@
+nmrundir = @nmrundir@
+nmstatedir = @nmstatedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+systemdsystemunitdir = @systemdsystemunitdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+with_dhclient = @with_dhclient@
+with_dhcpcd = @with_dhcpcd@
+with_netconfig = @with_netconfig@
+with_resolvconf = @with_resolvconf@
+with_valgrind = @with_valgrind@
+AM_CPPFLAGS = \
+	-I${top_srcdir}/src \
+	-I${top_builddir}/src \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/src/devices \
+	-I${top_srcdir}/src/settings \
+	-I${top_srcdir}/src/platform \
+	-I${top_srcdir}/src/devices/wwan \
+	-I${top_builddir}/include \
+	-I${top_srcdir}/include \
+	-I${top_builddir}/libnm-util \
+	-I${top_srcdir}/libnm-util \
+	-DG_LOG_DOMAIN=\""NetworkManager-bluetooth"\" \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
+	$(DBUS_CFLAGS) \
+	$(POLKIT_CFLAGS) \
+	$(LIBNL_CFLAGS) \
+	$(GUDEV_CFLAGS)
+
+GLIB_GENERATED = nm-bt-enum-types.h nm-bt-enum-types.c
+GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
+GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
+nm_bt_enum_types_sources = $(srcdir)/nm-device-bt.h
+BUILT_SOURCES = $(GLIB_GENERATED) nm-device-bt-glue.h
+pkglib_LTLIBRARIES = libnm-device-plugin-bluetooth.la
+SYMBOL_VIS_FILE = $(srcdir)/exports.ver
+libnm_device_plugin_bluetooth_la_SOURCES = \
+	nm-bluez-manager.c \
+	nm-bluez-manager.h \
+	nm-bluez-common.h \
+	nm-bluez-device.c \
+	nm-bluez-device.h \
+	nm-bluez4-adapter.c \
+	nm-bluez4-adapter.h \
+	nm-bluez4-manager.c \
+	nm-bluez4-manager.h \
+	nm-bluez5-manager.c \
+	nm-bluez5-manager.h \
+	\
+	nm-device-bt.c \
+	nm-device-bt.h \
+	\
+	$(BUILT_SOURCES)
+
+libnm_device_plugin_bluetooth_la_LDFLAGS = \
+	-module -avoid-version \
+	-Wl,--version-script=$(SYMBOL_VIS_FILE)
+
+libnm_device_plugin_bluetooth_la_LIBADD = \
+	$(top_builddir)/src/devices/wwan/libnm-wwan.la \
+	$(DBUS_LIBS) \
+	$(GUDEV_LIBS)
+
+CLEANFILES = $(BUILT_SOURCES)
+EXTRA_DIST = $(SYMBOL_VIS_FILE)
+all: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	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*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	@list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \
+	}
+
+uninstall-pkglibLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \
+	done
+
+clean-pkglibLTLIBRARIES:
+	-test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES)
+	@list='$(pkglib_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
+libnm-device-plugin-bluetooth.la: $(libnm_device_plugin_bluetooth_la_OBJECTS) $(libnm_device_plugin_bluetooth_la_DEPENDENCIES) $(EXTRA_libnm_device_plugin_bluetooth_la_DEPENDENCIES) 
+	$(AM_V_CCLD)$(libnm_device_plugin_bluetooth_la_LINK) -rpath $(pkglibdir) $(libnm_device_plugin_bluetooth_la_OBJECTS) $(libnm_device_plugin_bluetooth_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-bluez-device.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-bluez-manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-bluez4-adapter.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-bluez4-manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-bluez5-manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-bt-enum-types.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-device-bt.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@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 $<
+
+.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) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+@ENABLE_TESTS_FALSE@check-local:
+check-am: all-am
+	$(MAKE) $(AM_MAKEFLAGS) check-local
+check: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+	for dir in "$(DESTDIR)$(pkglibdir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+	-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-pkglibLTLIBRARIES \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-pkglibLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-pkglibLTLIBRARIES
+
+.MAKE: all check check-am install install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am check-local clean \
+	clean-generic clean-libtool clean-pkglibLTLIBRARIES \
+	cscopelist-am ctags ctags-am distclean distclean-compile \
+	distclean-generic distclean-libtool distclean-tags distdir dvi \
+	dvi-am html html-am info info-am install install-am \
+	install-data install-data-am install-dvi install-dvi-am \
+	install-exec install-exec-am install-html install-html-am \
+	install-info install-info-am install-man install-pdf \
+	install-pdf-am install-pkglibLTLIBRARIES install-ps \
+	install-ps-am install-strip installcheck installcheck-am \
+	installdirs maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+	uninstall-am uninstall-pkglibLTLIBRARIES
+
+include $(GLIB_MAKEFILE)
+
+@GNOME_CODE_COVERAGE_RULES@
+
+nm-device-bt-glue.h: $(top_srcdir)/introspection/nm-device-bt.xml
+	dbus-binding-tool --prefix=nm_device_bt --mode=glib-server --output=$@ $<
+
+@ENABLE_TESTS_TRUE@check-local:
+@ENABLE_TESTS_TRUE@	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-bluetooth.so $(SYMBOL_VIS_FILE)
+
+# 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.
+.NOEXPORT:
diff --git a/src/devices/bluetooth/exports.ver b/src/devices/bluetooth/exports.ver
new file mode 100644
index 00000000..d2c45124
--- /dev/null
+++ b/src/devices/bluetooth/exports.ver
@@ -0,0 +1,7 @@
+{
+global:
+	nm_device_factory_create;
+	nm_device_factory_get_device_type;
+local:
+	*;
+};
diff --git a/src/devices/bluetooth/nm-bluez-common.h b/src/devices/bluetooth/nm-bluez-common.h
new file mode 100644
index 00000000..f80cfc2e
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez-common.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#ifndef NM_BLUEZ_COMMON_H
+#define NM_BLUEZ_COMMON_H
+
+#include <config.h>
+
+#define BLUETOOTH_CONNECT_DUN "dun"
+#define BLUETOOTH_CONNECT_NAP "nap"
+
+#define BLUEZ_SERVICE           "org.bluez"
+
+#define BLUEZ_MANAGER_PATH      "/"
+#define OBJECT_MANAGER_INTERFACE "org.freedesktop.DBus.ObjectManager"
+
+#define BLUEZ5_ADAPTER_INTERFACE "org.bluez.Adapter1"
+#define BLUEZ5_DEVICE_INTERFACE  "org.bluez.Device1"
+#define BLUEZ5_NETWORK_INTERFACE "org.bluez.Network1"
+
+#define BLUEZ4_MANAGER_INTERFACE "org.bluez.Manager"
+#define BLUEZ4_ADAPTER_INTERFACE "org.bluez.Adapter"
+#define BLUEZ4_DEVICE_INTERFACE  "org.bluez.Device"
+#define BLUEZ4_SERIAL_INTERFACE  "org.bluez.Serial"
+#define BLUEZ4_NETWORK_INTERFACE "org.bluez.Network"
+
+#endif  /* NM_BLUEZ_COMMON_H */
+
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
new file mode 100644
index 00000000..4c448a37
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez-device.c
@@ -0,0 +1,1212 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2009 - 2012 Red Hat, Inc.
+ * Copyright (C) 2013 Intel Corporation.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <string.h>
+#include <net/ethernet.h>
+#include <netinet/ether.h>
+
+#include "NetworkManager.h"
+#include "nm-setting-bluetooth.h"
+
+#include "nm-bluez-common.h"
+#include "nm-bluez-device.h"
+#include "nm-logging.h"
+#include "nm-utils.h"
+#include "nm-settings-connection.h"
+
+
+G_DEFINE_TYPE (NMBluezDevice, nm_bluez_device, G_TYPE_OBJECT)
+
+#define NM_BLUEZ_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_BLUEZ_DEVICE, NMBluezDevicePrivate))
+
+typedef struct {
+	char *path;
+	GDBusConnection *dbus_connection;
+
+	GDBusProxy *proxy;
+
+	GDBusProxy *adapter5;
+	gboolean adapter_powered;
+
+	int bluez_version;
+
+	gboolean initialized;
+	gboolean usable;
+	NMBluetoothCapabilities connection_bt_type;
+
+	char *address;
+	guint8 bin_address[ETH_ALEN];
+	char *name;
+	guint32 capabilities;
+	gboolean connected;
+
+	char *bt_iface;
+
+	NMConnectionProvider *provider;
+	GSList *connections;
+
+	NMConnection *pan_connection;
+	NMConnection *pan_connection_original;
+	gboolean pan_connection_no_autocreate;
+} NMBluezDevicePrivate;
+
+
+enum {
+	PROP_0,
+	PROP_PATH,
+	PROP_ADDRESS,
+	PROP_NAME,
+	PROP_CAPABILITIES,
+	PROP_USABLE,
+	PROP_CONNECTED,
+
+	LAST_PROP
+};
+
+/* Signals */
+enum {
+	INITIALIZED,
+	REMOVED,
+	LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+static void cp_connection_added (NMConnectionProvider *provider,
+                                 NMConnection *connection, NMBluezDevice *self);
+static gboolean connection_compatible (NMBluezDevice *self, NMConnection *connection);
+
+
+#define VARIANT_IS_OF_TYPE_BOOLEAN(v)      ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_BOOLEAN) ))
+#define VARIANT_IS_OF_TYPE_STRING(v)       ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_STRING) ))
+#define VARIANT_IS_OF_TYPE_OBJECT_PATH(v)  ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_OBJECT_PATH) ))
+#define VARIANT_IS_OF_TYPE_STRING_ARRAY(v) ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_STRING_ARRAY) ))
+
+/***********************************************************/
+
+const char *
+nm_bluez_device_get_path (NMBluezDevice *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
+
+	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->path;
+}
+
+const char *
+nm_bluez_device_get_address (NMBluezDevice *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
+
+	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->address;
+}
+
+gboolean
+nm_bluez_device_get_initialized (NMBluezDevice *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
+
+	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->initialized;
+}
+
+gboolean
+nm_bluez_device_get_usable (NMBluezDevice *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
+
+	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->usable;
+}
+
+const char *
+nm_bluez_device_get_name (NMBluezDevice *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
+
+	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->name;
+}
+
+guint32
+nm_bluez_device_get_capabilities (NMBluezDevice *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), 0);
+
+	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->capabilities;
+}
+
+gboolean
+nm_bluez_device_get_connected (NMBluezDevice *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
+
+	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->connected;
+}
+
+static void
+pan_connection_check_create (NMBluezDevice *self)
+{
+	NMConnection *connection;
+	NMConnection *added;
+	NMSetting *setting;
+	char *uuid, *id;
+	GByteArray *bdaddr_array;
+	GError *error = NULL;
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+
+	g_return_if_fail (priv->capabilities & NM_BT_CAPABILITY_NAP);
+	g_return_if_fail (priv->connections == NULL);
+	g_return_if_fail (priv->name);
+
+	if (priv->pan_connection || priv->pan_connection_no_autocreate) {
+		/* already have a connection or we don't want to create one, nothing to do. */
+		return;
+	}
+
+	/* Only try once to create a connection. If it does not succeed, we do not try again. Also,
+	 * if the connection gets deleted later, do not create another one for this device. */
+	priv->pan_connection_no_autocreate = TRUE;
+
+	/* create a new connection */
+
+	connection = nm_connection_new ();
+
+	/* Setting: Connection */
+	uuid = nm_utils_uuid_generate ();
+	id = g_strdup_printf (_("%s Network"), priv->name);
+	setting = nm_setting_connection_new ();
+	g_object_set (setting,
+	              NM_SETTING_CONNECTION_ID, id,
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME,
+	              NULL);
+	nm_connection_add_setting (connection, setting);
+
+	/* Setting: Bluetooth */
+	bdaddr_array = g_byte_array_sized_new (sizeof (priv->bin_address));
+	g_byte_array_append (bdaddr_array, priv->bin_address, sizeof (priv->bin_address));
+	setting = nm_setting_bluetooth_new ();
+	g_object_set (G_OBJECT (setting),
+	              NM_SETTING_BLUETOOTH_BDADDR, bdaddr_array,
+	              NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU,
+	              NULL);
+	nm_connection_add_setting (connection, setting);
+	g_byte_array_free (bdaddr_array, TRUE);
+
+	/* Setting: IPv4 */
+	setting = nm_setting_ip4_config_new ();
+	g_object_set (G_OBJECT (setting),
+	              NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+	              NM_SETTING_IP4_CONFIG_MAY_FAIL, FALSE,
+	              NULL);
+	nm_connection_add_setting (connection, setting);
+
+	/* Setting: IPv6 */
+	setting = nm_setting_ip6_config_new ();
+	g_object_set (G_OBJECT (setting),
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
+	nm_connection_add_setting (connection, setting);
+
+	/* Adding a new connection raises a signal which eventually calls check_emit_usable (again)
+	 * which then already finds the suitable connection in priv->connections. This is confusing,
+	 * so block the signal. check_emit_usable will succeed after this function call returns. */
+	g_signal_handlers_block_by_func (priv->provider, cp_connection_added, self);
+	added = nm_connection_provider_add_connection (priv->provider, connection, FALSE, &error);
+	g_signal_handlers_unblock_by_func (priv->provider, cp_connection_added, self);
+
+	if (added) {
+		g_assert (!g_slist_find (priv->connections, added));
+		g_assert (connection_compatible (self, added));
+		g_assert (nm_connection_compare (added, connection, NM_SETTING_COMPARE_FLAG_EXACT));
+
+		priv->connections = g_slist_prepend (priv->connections, g_object_ref (added));
+		priv->pan_connection = added;
+		priv->pan_connection_original = connection;
+		nm_log_dbg (LOGD_BT, "bluez[%s] added new Bluetooth connection for NAP device: '%s' (%s)", priv->path, id, uuid);
+	} else {
+		nm_log_warn (LOGD_BT, "bluez[%s] couldn't add new Bluetooth connection for NAP device: '%s' (%s): %d / %s",
+		             priv->path, id, uuid, error ? error->code : -1,
+		             (error && error->message) ? error->message : "(unknown)");
+		g_clear_error (&error);
+
+		g_object_unref (connection);
+	}
+
+	g_free (id);
+	g_free (uuid);
+}
+
+static void
+check_emit_usable (NMBluezDevice *self)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	gboolean new_usable;
+
+	/* only expect the supported capabilities set. */
+	g_assert (priv->bluez_version != 4 || ((priv->capabilities & ~(NM_BT_CAPABILITY_NAP | NM_BT_CAPABILITY_DUN)) == NM_BT_CAPABILITY_NONE ));
+	g_assert (priv->bluez_version != 5 || ((priv->capabilities & ~(NM_BT_CAPABILITY_NAP                       )) == NM_BT_CAPABILITY_NONE ));
+
+	new_usable = (priv->initialized && priv->capabilities && priv->name &&
+	              ((priv->bluez_version == 4) ||
+	               (priv->bluez_version == 5 && priv->adapter5 && priv->adapter_powered) ) &&
+	              priv->dbus_connection && priv->address);
+
+	if (!new_usable)
+		goto END;
+
+	if (priv->connections)
+		goto END;
+
+	if (!(priv->capabilities & NM_BT_CAPABILITY_NAP)) {
+		/* non NAP devices are only usable, if they already have a connection. */
+		new_usable = FALSE;
+		goto END;
+	}
+
+	pan_connection_check_create (self);
+	new_usable = !!priv->pan_connection;
+
+END:
+	if (new_usable != priv->usable) {
+		priv->usable = new_usable;
+		g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_USABLE);
+	}
+}
+
+/********************************************************************/
+
+static gboolean
+connection_compatible (NMBluezDevice *self, NMConnection *connection)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	NMSettingBluetooth *s_bt;
+	const char *bt_type;
+	const GByteArray *bdaddr;
+
+	if (!nm_connection_is_type (connection, NM_SETTING_BLUETOOTH_SETTING_NAME))
+		return FALSE;
+
+	s_bt = nm_connection_get_setting_bluetooth (connection);
+	if (!s_bt)
+		return FALSE;
+
+	if (!priv->address) {
+		/* unless address is set, bin_address is not initialized. */
+		return FALSE;
+	}
+	bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
+	if (!bdaddr || bdaddr->len != ETH_ALEN)
+		return FALSE;
+	if (memcmp (bdaddr->data, priv->bin_address, ETH_ALEN) != 0)
+		return FALSE;
+
+	bt_type = nm_setting_bluetooth_get_connection_type (s_bt);
+	if (   g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)
+	    && !(priv->capabilities & NM_BT_CAPABILITY_DUN))
+		return FALSE;
+
+	if (   g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)
+	    && !(priv->capabilities & NM_BT_CAPABILITY_NAP))
+		return FALSE;
+
+	return TRUE;
+}
+
+static void
+_internal_add_connection (NMBluezDevice *self, NMConnection *connection)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+
+	if (!g_slist_find (priv->connections, connection)) {
+		priv->connections = g_slist_prepend (priv->connections, g_object_ref (connection));
+		check_emit_usable (self);
+	}
+}
+
+static void
+cp_connection_added (NMConnectionProvider *provider,
+                     NMConnection *connection,
+                     NMBluezDevice *self)
+{
+	if (connection_compatible (self, connection))
+		_internal_add_connection (self, connection);
+}
+
+static void
+cp_connection_removed (NMConnectionProvider *provider,
+                       NMConnection *connection,
+                       NMBluezDevice *self)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+
+	if (g_slist_find (priv->connections, connection)) {
+		priv->connections = g_slist_remove (priv->connections, connection);
+		if (priv->pan_connection == connection) {
+			priv->pan_connection = NULL;
+			g_clear_object (&priv->pan_connection_original);
+		}
+		g_object_unref (connection);
+		check_emit_usable (self);
+	}
+}
+
+static void
+cp_connection_updated (NMConnectionProvider *provider,
+                       NMConnection *connection,
+                       NMBluezDevice *self)
+{
+	if (connection_compatible (self, connection))
+		_internal_add_connection (self, connection);
+	else
+		cp_connection_removed (provider, connection, self);
+}
+
+static void
+load_connections (NMBluezDevice *self)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	const GSList *connections, *iter;
+
+	connections = nm_connection_provider_get_connections (priv->provider);
+	for (iter = connections; iter; iter = g_slist_next (iter))
+		cp_connection_added (priv->provider, NM_CONNECTION (iter->data), self);
+}
+
+/***********************************************************/
+
+static void
+bluez_disconnect_cb (GDBusConnection *dbus_connection,
+                     GAsyncResult *res,
+                     gpointer user_data)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (user_data);
+	GError *error = NULL;
+	GVariant *variant;
+
+	variant = g_dbus_connection_call_finish (dbus_connection, res, &error);
+	if (!variant) {
+		if (!strstr (error->message, "org.bluez.Error.NotConnected"))
+			nm_log_warn (LOGD_BT, "bluez[%s]: failed to disconnect: %s", priv->path, error->message);
+		g_error_free (error);
+	} else
+		g_variant_unref (variant);
+
+	g_object_unref (NM_BLUEZ_DEVICE (user_data));
+}
+
+void
+nm_bluez_device_disconnect (NMBluezDevice *self)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	GVariant *args = NULL;
+	const char *dbus_iface;
+
+	g_return_if_fail (priv->dbus_connection);
+
+	if (priv->bluez_version == 5) {
+		g_return_if_fail (priv->connection_bt_type == NM_BT_CAPABILITY_NAP);
+		dbus_iface = BLUEZ5_NETWORK_INTERFACE;
+	} else if (priv->bluez_version == 4 && priv->connection_bt_type == NM_BT_CAPABILITY_DUN) {
+		/* Can't pass a NULL interface name through dbus to bluez, so just
+		 * ignore the disconnect if the interface isn't known.
+		 */
+		if (!priv->bt_iface)
+			return;
+
+		args = g_variant_new ("(s)", priv->bt_iface),
+		dbus_iface = BLUEZ4_SERIAL_INTERFACE;
+	} else {
+		g_return_if_fail (priv->bluez_version == 4 && priv->connection_bt_type == NM_BT_CAPABILITY_NAP);
+		dbus_iface = BLUEZ4_NETWORK_INTERFACE;
+	}
+
+	g_dbus_connection_call (priv->dbus_connection,
+	                        BLUEZ_SERVICE,
+	                        priv->path,
+	                        dbus_iface,
+	                        "Disconnect",
+	                        args ? args : g_variant_new ("()"),
+	                        NULL,
+	                        G_DBUS_CALL_FLAGS_NONE,
+	                        10000,
+	                        NULL,
+	                        (GAsyncReadyCallback) bluez_disconnect_cb,
+	                        g_object_ref (self));
+
+	priv->connection_bt_type = NM_BT_CAPABILITY_NONE;
+}
+
+static void
+bluez_connect_cb (GDBusConnection *dbus_connection,
+                  GAsyncResult *res,
+                  gpointer user_data)
+{
+	GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
+	GObject *result_object = g_async_result_get_source_object (G_ASYNC_RESULT (result));
+	NMBluezDevice *self = NM_BLUEZ_DEVICE (result_object);
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	GError *error = NULL;
+	char *device;
+	GVariant *variant;
+
+	variant = g_dbus_connection_call_finish (dbus_connection, res, &error);
+
+	if (!variant) {
+		g_simple_async_result_take_error (result, error);
+	} else {
+		g_variant_get (variant, "(s)", &device);
+
+		g_simple_async_result_set_op_res_gpointer (result,
+		                                           g_strdup (device),
+		                                           g_free);
+		priv->bt_iface = device;
+		g_variant_unref (variant);
+	}
+
+	g_simple_async_result_complete (result);
+	g_object_unref (result);
+	g_object_unref (result_object);
+}
+
+void
+nm_bluez_device_connect_async (NMBluezDevice *self,
+                               NMBluetoothCapabilities connection_bt_type,
+                               GAsyncReadyCallback callback,
+                               gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	const char *dbus_iface;
+	const char *connect_type = BLUETOOTH_CONNECT_NAP;
+
+	g_return_if_fail (priv->capabilities & connection_bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP));
+
+	if (priv->bluez_version == 5) {
+		g_return_if_fail (connection_bt_type == NM_BT_CAPABILITY_NAP);
+		dbus_iface = BLUEZ5_NETWORK_INTERFACE;
+	} else if (priv->bluez_version == 4 && connection_bt_type == NM_BT_CAPABILITY_DUN) {
+		dbus_iface = BLUEZ4_SERIAL_INTERFACE;
+		connect_type = BLUETOOTH_CONNECT_DUN;
+	} else {
+		g_return_if_fail (priv->bluez_version == 4 && connection_bt_type == NM_BT_CAPABILITY_NAP);
+		dbus_iface = BLUEZ4_NETWORK_INTERFACE;
+	}
+
+	simple = g_simple_async_result_new (G_OBJECT (self),
+	                                    callback,
+	                                    user_data,
+	                                    nm_bluez_device_connect_async);
+
+	g_dbus_connection_call (priv->dbus_connection,
+	                        BLUEZ_SERVICE,
+	                        priv->path,
+	                        dbus_iface,
+	                        "Connect",
+	                        g_variant_new ("(s)", connect_type),
+	                        NULL,
+	                        G_DBUS_CALL_FLAGS_NONE,
+	                        20000,
+	                        NULL,
+	                        (GAsyncReadyCallback) bluez_connect_cb,
+	                        simple);
+
+	priv->connection_bt_type = connection_bt_type;
+}
+
+const char *
+nm_bluez_device_connect_finish (NMBluezDevice *self,
+                                GAsyncResult *result,
+                                GError **error)
+{
+	GSimpleAsyncResult *simple;
+	const char *device;
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result,
+	                                                      G_OBJECT (self),
+	                                                      nm_bluez_device_connect_async),
+	                      NULL);
+
+	simple = (GSimpleAsyncResult *) result;
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		return NULL;
+
+	device = (const char *) g_simple_async_result_get_op_res_gpointer (simple);
+	return device;
+}
+
+/***********************************************************/
+
+static guint32
+convert_uuids_to_capabilities (const char **strings, int bluez_version)
+{
+	const char **iter;
+	guint32 capabilities = 0;
+
+	for (iter = strings; iter && *iter; iter++) {
+		char **parts;
+
+		parts = g_strsplit (*iter, "-", -1);
+		if (parts && parts[0]) {
+			switch (g_ascii_strtoull (parts[0], NULL, 16)) {
+			case 0x1103:
+				if (bluez_version == 4)
+					capabilities |= NM_BT_CAPABILITY_DUN;
+				break;
+			case 0x1116:
+				capabilities |= NM_BT_CAPABILITY_NAP;
+				break;
+			default:
+				break;
+			}
+		}
+		g_strfreev (parts);
+	}
+
+	return capabilities;
+}
+
+static void
+_set_property_capabilities (NMBluezDevice *self, const char **uuids)
+{
+	guint32 uint_val;
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+
+	uint_val = convert_uuids_to_capabilities (uuids, priv->bluez_version);
+	if (priv->capabilities != uint_val) {
+		if (priv->capabilities) {
+			/* changing (relevant) capabilities is not supported and ignored -- except setting initially */
+			nm_log_warn (LOGD_BT, "bluez[%s] ignore change of capabilities for Bluetooth device from %u to %u",
+			             priv->path, priv->capabilities, uint_val);
+			return;
+		}
+		nm_log_dbg (LOGD_BT, "bluez[%s] set capabilities for Bluetooth device: %s%s%s", priv->path,
+		            uint_val & NM_BT_CAPABILITY_NAP ? "NAP" : "",
+		            ((uint_val & NM_BT_CAPABILITY_DUN) && (uint_val &NM_BT_CAPABILITY_NAP)) ? " | " : "",
+		            uint_val & NM_BT_CAPABILITY_DUN ? "DUN" : "");
+		priv->capabilities = uint_val;
+		g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_CAPABILITIES);
+	}
+}
+
+/**
+ * priv->address can only be set one to a certain (non NULL) value. Every later attempt
+ * to reset it to another value will be ignored and a warning will be logged.
+ *
+ * When setting the address for the first time, we also set bin_address.
+ **/
+static void
+_set_property_address (NMBluezDevice *self, const char *addr)
+{
+	struct ether_addr *tmp;
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+
+	if (g_strcmp0 (priv->address, addr) == 0)
+		return;
+
+	if (!addr) {
+		nm_log_warn (LOGD_BT, "bluez[%s] cannot reset address from '%s' to NULL", priv->path, priv->address);
+		return;
+	}
+
+	if (priv->address != NULL) {
+		nm_log_warn (LOGD_BT, "bluez[%s] cannot reset address from '%s' to '%s'", priv->path, priv->address, addr);
+		return;
+	}
+
+	tmp = ether_aton (addr);
+	if (!tmp) {
+		if (priv->address)
+			nm_log_warn (LOGD_BT, "bluez[%s] cannot reset address from '%s' to '%s' (invalid value)", priv->path, priv->address, addr);
+		else
+			nm_log_warn (LOGD_BT, "bluez[%s] cannot reset address from NULL to '%s' (invalid value)", priv->path, addr);
+		return;
+	}
+	memcpy (priv->bin_address, tmp->ether_addr_octet, ETH_ALEN);
+	priv->address = g_strdup (addr);
+	g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_ADDRESS);
+}
+
+static void
+_take_variant_property_address (NMBluezDevice *self, GVariant *v)
+{
+	_set_property_address (self, VARIANT_IS_OF_TYPE_STRING (v) ? g_variant_get_string (v, NULL) : NULL);
+	if (v)
+		g_variant_unref (v);
+}
+
+static void
+_take_variant_property_name (NMBluezDevice *self, GVariant *v)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	const char *str;
+
+	if (VARIANT_IS_OF_TYPE_STRING (v)) {
+		str = g_variant_get_string (v, NULL);
+		if (g_strcmp0 (priv->name, str)) {
+			g_free (priv->name);
+			priv->name = g_strdup (str);
+			g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_NAME);
+		}
+	}
+	if (v)
+		g_variant_unref (v);
+}
+
+static void
+_take_variant_property_uuids (NMBluezDevice *self, GVariant *v)
+{
+	if (VARIANT_IS_OF_TYPE_STRING_ARRAY (v)) {
+		const char **uuids = g_variant_get_strv (v, NULL);
+
+		_set_property_capabilities (self, uuids);
+		g_free (uuids);
+	}
+	if (v)
+		g_variant_unref (v);
+}
+
+static void
+_take_variant_property_connected (NMBluezDevice *self, GVariant *v)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+
+	if (VARIANT_IS_OF_TYPE_BOOLEAN (v)) {
+		gboolean connected = g_variant_get_boolean (v);
+
+		if (priv->connected != connected) {
+			priv->connected = connected;
+			g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_CONNECTED);
+		}
+	}
+	if (v)
+		g_variant_unref (v);
+}
+
+
+static void
+adapter5_on_properties_changed (GDBusProxy *proxy,
+                                GVariant *changed_properties,
+                                GStrv invalidated_properties,
+                                gpointer user_data)
+{
+	NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	GVariantIter i;
+	const char *property;
+	GVariant *v;
+
+	g_variant_iter_init (&i, changed_properties);
+	while (g_variant_iter_next (&i, "{&sv}", &property, &v)) {
+		if (!strcmp (property, "Powered") && VARIANT_IS_OF_TYPE_BOOLEAN (v)) {
+			gboolean powered = g_variant_get_boolean (v);
+			if (priv->adapter_powered != powered)
+				priv->adapter_powered = powered;
+		}
+		g_variant_unref (v);
+	}
+
+	check_emit_usable (self);
+}
+
+static void
+adapter5_on_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	GError *error;
+	GVariant *v;
+
+	priv->adapter5 = g_dbus_proxy_new_for_bus_finish (res, &error);
+	if (!priv->adapter5) {
+		nm_log_warn (LOGD_BT, "bluez[%s] failed to acquire adapter proxy: %s.", priv->path, error->message);
+		g_clear_error (&error);
+		g_signal_emit (self, signals[INITIALIZED], 0, FALSE);
+	} else {
+		g_signal_connect (priv->adapter5, "g-properties-changed",
+		                  G_CALLBACK (adapter5_on_properties_changed), self);
+
+		/* Check adapter's powered state */
+		v = g_dbus_proxy_get_cached_property (priv->adapter5, "Powered");
+		priv->adapter_powered = VARIANT_IS_OF_TYPE_BOOLEAN (v) ? g_variant_get_boolean (v) : FALSE;
+		if (v)
+			g_variant_unref (v);
+
+		priv->initialized = TRUE;
+		g_signal_emit (self, signals[INITIALIZED], 0, TRUE);
+
+		check_emit_usable (self);
+	}
+
+	g_object_unref (self);
+}
+
+static void
+_take_one_variant_property (NMBluezDevice *self, const char *property, GVariant *v)
+{
+	if (v) {
+		if (!g_strcmp0 (property, "Address"))
+			_take_variant_property_address (self, v);
+		else if (!g_strcmp0 (property, "Connected"))
+			_take_variant_property_connected (self, v);
+		else if (!g_strcmp0 (property, "Name"))
+			_take_variant_property_name (self, v);
+		else if (!g_strcmp0 (property, "UUIDs"))
+			_take_variant_property_uuids (self, v);
+		else
+			g_variant_unref (v);
+	}
+}
+
+static void
+_set_properties (NMBluezDevice *self, GVariant *properties)
+{
+	GVariantIter i;
+	const char *property;
+	GVariant *v;
+
+	g_object_freeze_notify (G_OBJECT (self));
+	g_variant_iter_init (&i, properties);
+	while (g_variant_iter_next (&i, "{&sv}", &property, &v))
+		_take_one_variant_property (self, property, v);
+	g_object_thaw_notify (G_OBJECT (self));
+}
+
+static void
+properties_changed (GDBusProxy *proxy,
+                    GVariant *changed_properties,
+                    GStrv invalidated_properties,
+                    gpointer user_data)
+{
+	NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
+
+	_set_properties (self, changed_properties);
+	check_emit_usable (self);
+}
+
+static void
+bluez4_property_changed (GDBusProxy *proxy,
+                         const char *sender,
+                         const char *signal_name,
+                         GVariant   *parameters,
+                         gpointer user_data)
+{
+	NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
+
+	if (g_strcmp0 (signal_name, "PropertyChanged") == 0) {
+		const char *property = NULL;
+		GVariant *v = NULL;
+
+		g_variant_get (parameters, "(&sv)", &property, &v);
+		_take_one_variant_property (self, property, v);
+		check_emit_usable (self);
+	}
+}
+
+static void
+get_properties_cb_4 (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+	NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	GError *err = NULL;
+	GVariant *v_properties, *v_dict;
+	GVariantType *v_type;
+
+	v_properties = g_dbus_proxy_call_finish (priv->proxy, res, &err);
+	if (!v_properties) {
+		nm_log_warn (LOGD_BT, "bluez[%s] error getting device properties: %s",
+		             priv->path, err && err->message ? err->message : "(unknown)");
+		g_error_free (err);
+		g_signal_emit (self, signals[INITIALIZED], 0, FALSE);
+		goto END;
+	}
+
+	v_type = g_variant_type_new ("(a{sv})");
+	if (g_variant_is_of_type (v_properties, v_type)) {
+		v_dict = g_variant_get_child_value (v_properties, 0);
+		_set_properties (self, v_dict);
+		g_variant_unref (v_dict);
+	} else {
+		nm_log_warn (LOGD_BT, "bluez[%s] GetProperties returns unexpected result of type %s", priv->path, g_variant_get_type_string (v_properties));
+	}
+	g_variant_type_free (v_type);
+
+	g_variant_unref (v_properties);
+
+	/* Check if any connections match this device */
+	load_connections (self);
+
+	priv->initialized = TRUE;
+	g_signal_emit (self, signals[INITIALIZED], 0, TRUE);
+
+
+	check_emit_usable (self);
+
+END:
+	g_object_unref (self);
+}
+
+static void
+query_properties (NMBluezDevice *self)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	GVariant *v;
+
+	switch (priv->bluez_version) {
+	case 4:
+		g_dbus_proxy_call (priv->proxy, "GetProperties", NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, 3000,
+		                   NULL, get_properties_cb_4, g_object_ref (self));
+		break;
+	case 5:
+		g_object_freeze_notify (G_OBJECT (self));
+		_take_variant_property_address   (self, g_dbus_proxy_get_cached_property (priv->proxy, "Address"));
+		_take_variant_property_connected (self, g_dbus_proxy_get_cached_property (priv->proxy, "Connected"));
+		_take_variant_property_name      (self, g_dbus_proxy_get_cached_property (priv->proxy, "Name"));
+		_take_variant_property_uuids     (self, g_dbus_proxy_get_cached_property (priv->proxy, "UUIDs"));
+		g_object_thaw_notify (G_OBJECT (self));
+
+		v = g_dbus_proxy_get_cached_property (priv->proxy, "Adapter");
+		if (VARIANT_IS_OF_TYPE_OBJECT_PATH (v)) {
+			g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+			                          G_DBUS_PROXY_FLAGS_NONE,
+			                          NULL,
+			                          BLUEZ_SERVICE,
+			                          g_variant_get_string (v, NULL),
+			                          BLUEZ5_ADAPTER_INTERFACE,
+			                          NULL,
+			                          (GAsyncReadyCallback) adapter5_on_acquired,
+			                          g_object_ref (self));
+			g_variant_unref (v);
+		} else {
+			/* If the Adapter property is unset at this point, we won't try to acquire the adapter later on
+			 * and the device stays unusable. This should not happen, but if it does, log a debug message. */
+			nm_log_dbg (LOGD_BT, "bluez[%s] device has no adapter property and cannot be used.", priv->path);
+		}
+
+		/* Check if any connections match this device */
+		load_connections (self);
+
+		break;
+	}
+}
+
+static void
+on_proxy_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	GError *error = NULL;
+
+	priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+
+	if (!priv->proxy) {
+		nm_log_warn (LOGD_BT, "bluez[%s] failed to acquire device proxy: %s.", priv->path, error->message);
+		g_clear_error (&error);
+		g_signal_emit (self, signals[INITIALIZED], 0, FALSE);
+	} else {
+		g_signal_connect (priv->proxy, "g-properties-changed",
+		                  G_CALLBACK (properties_changed), self);
+		if (priv->bluez_version == 4) {
+			/* Watch for custom Bluez4 PropertyChanged signals */
+			g_signal_connect (priv->proxy, "g-signal",
+			                  G_CALLBACK (bluez4_property_changed), self);
+		}
+
+		query_properties (self);
+	}
+	g_object_unref (self);
+}
+
+static void
+on_bus_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	GError *error = NULL;
+
+	priv->dbus_connection = g_bus_get_finish (res, &error);
+
+	if (!priv->dbus_connection) {
+		nm_log_warn (LOGD_BT, "bluez[%s] failed to acquire bus connection: %s.", priv->path, error->message);
+		g_clear_error (&error);
+		g_signal_emit (self, signals[INITIALIZED], 0, FALSE);
+	} else
+		check_emit_usable (self);
+
+	g_object_unref (self);
+}
+
+/********************************************************************/
+
+NMBluezDevice *
+nm_bluez_device_new (const char *path, NMConnectionProvider *provider, int bluez_version)
+{
+	NMBluezDevice *self;
+	NMBluezDevicePrivate *priv;
+	const char *interface_name = NULL;
+
+	g_return_val_if_fail (path != NULL, NULL);
+	g_return_val_if_fail (provider != NULL, NULL);
+	g_return_val_if_fail (bluez_version == 4 || bluez_version == 5, NULL);
+
+	self = (NMBluezDevice *) g_object_new (NM_TYPE_BLUEZ_DEVICE,
+	                                       NM_BLUEZ_DEVICE_PATH, path,
+	                                       NULL);
+	if (!self)
+		return NULL;
+
+	nm_log_dbg (LOGD_BT, "bluez[%s] create NMBluezDevice", path);
+
+	priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+
+	priv->bluez_version = bluez_version;
+
+	priv->provider = provider;
+
+	g_signal_connect (priv->provider,
+	                  NM_CP_SIGNAL_CONNECTION_ADDED,
+	                  G_CALLBACK (cp_connection_added),
+	                  self);
+
+	g_signal_connect (priv->provider,
+	                  NM_CP_SIGNAL_CONNECTION_REMOVED,
+	                  G_CALLBACK (cp_connection_removed),
+	                  self);
+
+	g_signal_connect (priv->provider,
+	                  NM_CP_SIGNAL_CONNECTION_UPDATED,
+	                  G_CALLBACK (cp_connection_updated),
+	                  self);
+
+	g_bus_get (G_BUS_TYPE_SYSTEM,
+	           NULL,
+	           (GAsyncReadyCallback) on_bus_acquired,
+	           g_object_ref (self));
+
+	switch (priv->bluez_version) {
+	case 4:
+		interface_name = BLUEZ4_DEVICE_INTERFACE;
+		break;
+	case 5:
+		interface_name = BLUEZ5_DEVICE_INTERFACE;
+		break;
+	}
+
+	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+	                          G_DBUS_PROXY_FLAGS_NONE,
+	                          NULL,
+	                          BLUEZ_SERVICE,
+	                          priv->path,
+	                          interface_name,
+	                          NULL,
+	                          (GAsyncReadyCallback) on_proxy_acquired,
+	                          g_object_ref (self));
+	return self;
+}
+
+static void
+nm_bluez_device_init (NMBluezDevice *self)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+	NMBluezDevice *self = NM_BLUEZ_DEVICE (object);
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	NMConnection *to_delete = NULL;
+
+	if (priv->pan_connection) {
+		/* Check whether we want to remove the created connection. If so, we take a reference
+		 * and delete it at the end of dispose(). */
+		if (   nm_settings_connection_get_unsaved (NM_SETTINGS_CONNECTION (priv->pan_connection))
+		    && nm_connection_compare (priv->pan_connection, priv->pan_connection_original, NM_SETTING_COMPARE_FLAG_EXACT))
+			to_delete = g_object_ref (priv->pan_connection);
+
+		priv->pan_connection = NULL;
+		g_clear_object (&priv->pan_connection_original);
+	}
+
+	g_signal_handlers_disconnect_by_func (priv->provider, cp_connection_added, self);
+	g_signal_handlers_disconnect_by_func (priv->provider, cp_connection_removed, self);
+	g_signal_handlers_disconnect_by_func (priv->provider, cp_connection_updated, self);
+
+	g_slist_free_full (priv->connections, g_object_unref);
+	priv->connections = NULL;
+
+	g_clear_object (&priv->adapter5);
+	g_clear_object (&priv->dbus_connection);
+
+	G_OBJECT_CLASS (nm_bluez_device_parent_class)->dispose (object);
+
+	if (to_delete) {
+		nm_log_dbg (LOGD_BT, "bluez[%s] removing Bluetooth connection for NAP device: '%s' (%s)", priv->path,
+		            nm_connection_get_id (to_delete), nm_connection_get_uuid (to_delete));
+		nm_settings_connection_delete (NM_SETTINGS_CONNECTION (to_delete), NULL, NULL);
+		g_object_unref (to_delete);
+	}
+}
+
+static void
+finalize (GObject *object)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (object);
+
+	nm_log_dbg (LOGD_BT, "bluez[%s]: finalize NMBluezDevice", priv->path);
+
+	g_free (priv->path);
+	g_free (priv->address);
+	g_free (priv->name);
+	g_free (priv->bt_iface);
+
+	if (priv->proxy)
+		g_signal_handlers_disconnect_by_data (priv->proxy, object);
+	g_clear_object (&priv->proxy);
+
+	G_OBJECT_CLASS (nm_bluez_device_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_PATH:
+		g_value_set_string (value, priv->path);
+		break;
+	case PROP_ADDRESS:
+		g_value_set_string (value, priv->address);
+		break;
+	case PROP_NAME:
+		g_value_set_string (value, priv->name);
+		break;
+	case PROP_CAPABILITIES:
+		g_value_set_uint (value, priv->capabilities);
+		break;
+	case PROP_USABLE:
+		g_value_set_boolean (value, priv->usable);
+		break;
+	case PROP_CONNECTED:
+		g_value_set_boolean (value, priv->connected);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_PATH:
+		/* construct only */
+		priv->path = g_value_dup_string (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_bluez_device_class_init (NMBluezDeviceClass *config_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
+
+	g_type_class_add_private (config_class, sizeof (NMBluezDevicePrivate));
+
+	/* virtual methods */
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+	object_class->dispose = dispose;
+	object_class->finalize = finalize;
+
+	/* Properties */
+	g_object_class_install_property
+		(object_class, PROP_PATH,
+		 g_param_spec_string (NM_BLUEZ_DEVICE_PATH,
+		                      "DBus Path",
+		                      "DBus Path",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_ADDRESS,
+		 g_param_spec_string (NM_BLUEZ_DEVICE_ADDRESS,
+		                      "Address",
+		                      "Address",
+		                      NULL,
+		                      G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_NAME,
+		 g_param_spec_string (NM_BLUEZ_DEVICE_NAME,
+		                      "Name",
+		                      "Name",
+		                      NULL,
+		                      G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_CAPABILITIES,
+		 g_param_spec_uint (NM_BLUEZ_DEVICE_CAPABILITIES,
+		                      "Capabilities",
+		                      "Capabilities",
+		                      0, G_MAXUINT, 0,
+		                      G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_USABLE,
+		 g_param_spec_boolean (NM_BLUEZ_DEVICE_USABLE,
+		                       "Usable",
+		                       "Usable",
+		                       FALSE,
+		                       G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_CONNECTED,
+		 g_param_spec_boolean (NM_BLUEZ_DEVICE_CONNECTED,
+		                       "Connected",
+		                       "Connected",
+		                       FALSE,
+		                       G_PARAM_READABLE));
+
+	/* Signals */
+	signals[INITIALIZED] = g_signal_new ("initialized",
+	                                     G_OBJECT_CLASS_TYPE (object_class),
+	                                     G_SIGNAL_RUN_LAST,
+	                                     G_STRUCT_OFFSET (NMBluezDeviceClass, initialized),
+	                                     NULL, NULL, NULL,
+	                                     G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+	signals[REMOVED] =     g_signal_new (NM_BLUEZ_DEVICE_REMOVED,
+	                                     G_OBJECT_CLASS_TYPE (object_class),
+	                                     G_SIGNAL_RUN_LAST,
+	                                     G_STRUCT_OFFSET (NMBluezDeviceClass, removed),
+	                                     NULL, NULL, NULL,
+	                                     G_TYPE_NONE, 0);
+}
+
diff --git a/src/devices/bluetooth/nm-bluez-device.h b/src/devices/bluetooth/nm-bluez-device.h
new file mode 100644
index 00000000..0bf7d898
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez-device.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2009 - 2014 Red Hat, Inc.
+ */
+
+#ifndef NM_BLUEZ_DEVICE_H
+#define NM_BLUEZ_DEVICE_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <config.h>
+#include "nm-connection.h"
+#include "nm-connection-provider.h"
+
+#define NM_TYPE_BLUEZ_DEVICE            (nm_bluez_device_get_type ())
+#define NM_BLUEZ_DEVICE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ_DEVICE, NMBluezDevice))
+#define NM_BLUEZ_DEVICE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ_DEVICE, NMBluezDeviceClass))
+#define NM_IS_BLUEZ_DEVICE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ_DEVICE))
+#define NM_IS_BLUEZ_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ_DEVICE))
+#define NM_BLUEZ_DEVICE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ_DEVICE, NMBluezDeviceClass))
+
+/* Properties */
+#define NM_BLUEZ_DEVICE_PATH         "path"
+#define NM_BLUEZ_DEVICE_ADDRESS      "address"
+#define NM_BLUEZ_DEVICE_NAME         "name"
+#define NM_BLUEZ_DEVICE_CAPABILITIES "capabilities"
+#define NM_BLUEZ_DEVICE_USABLE       "usable"
+#define NM_BLUEZ_DEVICE_CONNECTED    "connected"
+
+/* Signals */
+#define NM_BLUEZ_DEVICE_REMOVED      "removed"
+
+typedef struct {
+	GObject parent;
+} NMBluezDevice;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* virtual functions */
+	void (*initialized) (NMBluezDevice *self, gboolean success);
+
+	void (*removed)     (NMBluezDevice *self);
+} NMBluezDeviceClass;
+
+GType nm_bluez_device_get_type (void);
+
+NMBluezDevice *nm_bluez_device_new (const char *path, NMConnectionProvider *provider, int bluez_version);
+
+const char *nm_bluez_device_get_path (NMBluezDevice *self);
+
+gboolean nm_bluez_device_get_initialized (NMBluezDevice *self);
+
+gboolean nm_bluez_device_get_usable (NMBluezDevice *self);
+
+const char *nm_bluez_device_get_address (NMBluezDevice *self);
+
+const char *nm_bluez_device_get_name (NMBluezDevice *self);
+
+guint32 nm_bluez_device_get_class (NMBluezDevice *self);
+
+guint32 nm_bluez_device_get_capabilities (NMBluezDevice *self);
+
+gboolean nm_bluez_device_get_connected (NMBluezDevice *self);
+
+void
+nm_bluez_device_connect_async (NMBluezDevice *self,
+                               NMBluetoothCapabilities connection_bt_type,
+                               GAsyncReadyCallback callback,
+                               gpointer user_data);
+
+const char *
+nm_bluez_device_connect_finish (NMBluezDevice *self,
+                                GAsyncResult *result,
+                                GError **error);
+
+void
+nm_bluez_device_disconnect (NMBluezDevice *self);
+
+#endif /* NM_BLUEZ_DEVICE_H */
+
diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c
new file mode 100644
index 00000000..04ffb0a4
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez-manager.c
@@ -0,0 +1,428 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 - 2014 Red Hat, Inc.
+ */
+
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <gmodule.h>
+#include <gio/gio.h>
+
+#include "nm-logging.h"
+#include "nm-bluez-manager.h"
+#include "nm-device-factory.h"
+#include "nm-bluez4-manager.h"
+#include "nm-bluez5-manager.h"
+#include "nm-bluez-device.h"
+#include "nm-bluez-common.h"
+#include "nm-connection-provider.h"
+#include "nm-device-bt.h"
+
+#include "nm-dbus-manager.h"
+
+typedef struct {
+	int bluez_version;
+
+	NMConnectionProvider *provider;
+	NMBluez4Manager *manager4;
+	NMBluez5Manager *manager5;
+
+	guint watch_name_id;
+
+	GDBusProxy *introspect_proxy;
+	GCancellable *async_cancellable;
+} NMBluezManagerPrivate;
+
+#define NM_BLUEZ_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerPrivate))
+
+static GType nm_bluez_manager_get_type (void);
+
+static void device_factory_interface_init (NMDeviceFactory *factory_iface);
+
+G_DEFINE_TYPE_EXTENDED (NMBluezManager, nm_bluez_manager, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_FACTORY, device_factory_interface_init))
+
+static void check_bluez_and_try_setup (NMBluezManager *self);
+
+/**************************************************************************/
+
+#define PLUGIN_TYPE NM_DEVICE_TYPE_BT
+
+G_MODULE_EXPORT NMDeviceFactory *
+nm_device_factory_create (GError **error)
+{
+	return (NMDeviceFactory *) g_object_new (NM_TYPE_BLUEZ_MANAGER, NULL);
+}
+
+G_MODULE_EXPORT NMDeviceType
+nm_device_factory_get_device_type (void)
+{
+	return PLUGIN_TYPE;
+}
+
+/************************************************************************/
+
+struct AsyncData {
+	NMBluezManager *self;
+	GCancellable *async_cancellable;
+};
+
+static struct AsyncData *
+async_data_pack (NMBluezManager *self)
+{
+	struct AsyncData *data = g_new (struct AsyncData, 1);
+
+	data->self = self;
+	data->async_cancellable = g_object_ref (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->async_cancellable);
+	return data;
+}
+
+static NMBluezManager *
+async_data_unpack (struct AsyncData *async_data)
+{
+	NMBluezManager *self = g_cancellable_is_cancelled (async_data->async_cancellable)
+	                       ? NULL : async_data->self;
+
+	g_object_unref (async_data->async_cancellable);
+	g_free (async_data);
+	return self;
+}
+
+
+/**
+ * Cancel any current attempt to detect the version and cleanup
+ * the related fields.
+ **/
+static void
+cleanup_checking (NMBluezManager *self, gboolean do_unwatch_name)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	if (priv->async_cancellable) {
+		g_cancellable_cancel (priv->async_cancellable);
+		g_clear_object (&priv->async_cancellable);
+	}
+
+	g_clear_object (&priv->introspect_proxy);
+
+	if (do_unwatch_name && priv->watch_name_id) {
+		g_bus_unwatch_name (priv->watch_name_id);
+		priv->watch_name_id = 0;
+	}
+}
+
+
+static void
+manager_bdaddr_added_cb (NMBluez4Manager *bluez_mgr,
+                         NMBluezDevice *bt_device,
+                         const char *bdaddr,
+                         const char *name,
+                         const char *object_path,
+                         guint32 capabilities,
+                         gpointer user_data)
+{
+	NMBluezManager *self = NM_BLUEZ_MANAGER (user_data);
+	NMDevice *device;
+	gboolean has_dun = (capabilities & NM_BT_CAPABILITY_DUN);
+	gboolean has_nap = (capabilities & NM_BT_CAPABILITY_NAP);
+
+	g_return_if_fail (bdaddr != NULL);
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (object_path != NULL);
+	g_return_if_fail (capabilities != NM_BT_CAPABILITY_NONE);
+	g_return_if_fail (NM_IS_BLUEZ_DEVICE (bt_device));
+
+	device = nm_device_bt_new (bt_device, object_path, bdaddr, name, capabilities);
+	if (!device)
+		return;
+
+	nm_log_info (LOGD_BT, "BT device %s (%s) added (%s%s%s)",
+	             name,
+	             bdaddr,
+	             has_dun ? "DUN" : "",
+	             has_dun && has_nap ? " " : "",
+	             has_nap ? "NAP" : "");
+	g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device);
+	g_object_unref (device);
+}
+
+static void
+setup_version_number (NMBluezManager *self, int bluez_version)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	g_return_if_fail (!priv->bluez_version);
+
+	nm_log_info (LOGD_BT, "use BlueZ version %d", bluez_version);
+
+	priv->bluez_version = bluez_version;
+
+	/* Just detected the version. Cleanup the ongoing checking/detection. */
+	cleanup_checking (self, TRUE);
+}
+
+static void
+setup_bluez4 (NMBluezManager *self)
+{
+	NMBluez4Manager *manager;
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	g_return_if_fail (!priv->manager4 && !priv->manager5 && !priv->bluez_version);
+
+	setup_version_number (self, 4);
+	priv->manager4 = manager = nm_bluez4_manager_new (priv->provider);
+
+	g_signal_connect (manager,
+	                  NM_BLUEZ_MANAGER_BDADDR_ADDED,
+	                  G_CALLBACK (manager_bdaddr_added_cb),
+	                  self);
+
+	nm_bluez4_manager_query_devices (manager);
+}
+
+static void
+setup_bluez5 (NMBluezManager *self)
+{
+	NMBluez5Manager *manager;
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	g_return_if_fail (!priv->manager4 && !priv->manager5 && !priv->bluez_version);
+
+	setup_version_number (self, 5);
+	priv->manager5 = manager = nm_bluez5_manager_new (priv->provider);
+
+	g_signal_connect (manager,
+	                  NM_BLUEZ_MANAGER_BDADDR_ADDED,
+	                  G_CALLBACK (manager_bdaddr_added_cb),
+	                  self);
+
+	nm_bluez5_manager_query_devices (manager);
+}
+
+
+static void
+watch_name_on_appeared (GDBusConnection *connection,
+                        const gchar *name,
+                        const gchar *name_owner,
+                        gpointer user_data)
+{
+	check_bluez_and_try_setup (NM_BLUEZ_MANAGER (user_data));
+}
+
+
+static void
+check_bluez_and_try_setup_final_step (NMBluezManager *self, int bluez_version, const char *reason)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	g_return_if_fail (!priv->bluez_version);
+
+	switch (bluez_version) {
+	case 4:
+		setup_bluez4 (self);
+		break;
+	case 5:
+		setup_bluez5 (self);
+		break;
+	default:
+		nm_log_dbg (LOGD_BT, "detecting BlueZ version failed: %s", reason);
+
+		/* cancel current attempts to detect the version. */
+		cleanup_checking (self, FALSE);
+		if (!priv->watch_name_id) {
+			priv->watch_name_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
+			                                        BLUEZ_SERVICE,
+			                                        G_BUS_NAME_WATCHER_FLAGS_NONE,
+			                                        watch_name_on_appeared,
+			                                        NULL,
+			                                        self,
+			                                        NULL);
+		}
+		break;
+	}
+}
+
+static void
+check_bluez_and_try_setup_do_introspect (GObject *source_object,
+                                         GAsyncResult *res,
+                                         gpointer user_data)
+{
+	NMBluezManager *self = async_data_unpack (user_data);
+	NMBluezManagerPrivate *priv;
+	GError *error = NULL;
+	GVariant *result;
+	const char *xml_data;
+	int bluez_version = 0;
+	const char *reason = NULL;
+
+	if (!self)
+		return;
+
+	priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	g_return_if_fail (priv->introspect_proxy);
+	g_return_if_fail (!g_cancellable_is_cancelled (priv->async_cancellable));
+	g_return_if_fail (!priv->bluez_version);
+
+	g_clear_object (&priv->async_cancellable);
+
+	result = g_dbus_proxy_call_finish (priv->introspect_proxy, res, &error);
+
+	if (!result) {
+		char *reason2 = g_strdup_printf ("introspect failed with %s", error->message);
+		check_bluez_and_try_setup_final_step (self, 0, reason2);
+		g_error_free (error);
+		g_free (reason2);
+		return;
+	}
+
+	g_variant_get (result, "(&s)", &xml_data);
+
+	/* might not be the best approach to detect the version, but it's good enough in practice. */
+	if (strstr (xml_data, "org.freedesktop.DBus.ObjectManager"))
+		bluez_version = 5;
+	else if (strstr (xml_data, BLUEZ4_MANAGER_INTERFACE))
+		bluez_version = 4;
+	else
+		reason = "unexpected introspect result";
+
+	g_variant_unref (result);
+
+	check_bluez_and_try_setup_final_step (self, bluez_version, reason);
+}
+
+static void
+check_bluez_and_try_setup_on_new_proxy (GObject *source_object,
+                                        GAsyncResult *res,
+                                        gpointer user_data)
+{
+	NMBluezManager *self = async_data_unpack (user_data);
+	NMBluezManagerPrivate *priv;
+	GError *error = NULL;
+
+	if (!self)
+		return;
+
+	priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	g_return_if_fail (!priv->introspect_proxy);
+	g_return_if_fail (!g_cancellable_is_cancelled (priv->async_cancellable));
+	g_return_if_fail (!priv->bluez_version);
+
+	priv->introspect_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+
+	if (!priv->introspect_proxy) {
+		char *reason = g_strdup_printf ("bluez error creating dbus proxy: %s", error->message);
+		check_bluez_and_try_setup_final_step (self, 0, reason);
+		g_error_free (error);
+		g_free (reason);
+		return;
+	}
+
+	g_dbus_proxy_call (priv->introspect_proxy,
+	                   "Introspect",
+	                   NULL,
+	                   G_DBUS_CALL_FLAGS_NO_AUTO_START,
+	                   3000,
+	                   priv->async_cancellable,
+	                   check_bluez_and_try_setup_do_introspect,
+	                   async_data_pack (self));
+}
+
+static void
+check_bluez_and_try_setup (NMBluezManager *self)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	g_return_if_fail (!priv->bluez_version);
+
+	/* there should be no ongoing detection. Anyway, cleanup_checking. */
+	cleanup_checking (self, FALSE);
+
+	priv->async_cancellable = g_cancellable_new ();
+
+	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+	                          G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+	                          NULL,
+	                          BLUEZ_SERVICE,
+	                          "/",
+	                          DBUS_INTERFACE_INTROSPECTABLE,
+	                          priv->async_cancellable,
+	                          check_bluez_and_try_setup_on_new_proxy,
+	                          async_data_pack (self));
+}
+
+/*********************************************************************/
+
+static void
+dispose (GObject *object)
+{
+	NMBluezManager *self = NM_BLUEZ_MANAGER (object);
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	if (priv->manager4) {
+		g_signal_handlers_disconnect_by_func (priv->manager4, manager_bdaddr_added_cb, self);
+		g_clear_object (&priv->manager4);
+	}
+	if (priv->manager5) {
+		g_signal_handlers_disconnect_by_func (priv->manager5, manager_bdaddr_added_cb, self);
+		g_clear_object (&priv->manager5);
+	}
+
+	cleanup_checking (self, TRUE);
+
+	priv->bluez_version = 0;
+}
+
+static void
+constructed (GObject *object)
+{
+	NMBluezManager *self = NM_BLUEZ_MANAGER (object);
+
+	G_OBJECT_CLASS (nm_bluez_manager_parent_class)->constructed (object);
+
+	check_bluez_and_try_setup (self);
+}
+
+static void
+nm_bluez_manager_init (NMBluezManager *self)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	priv->provider = nm_connection_provider_get ();
+	g_assert (priv->provider);
+}
+
+static void
+device_factory_interface_init (NMDeviceFactory *factory_iface)
+{
+}
+
+static void
+nm_bluez_manager_class_init (NMBluezManagerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMBluezManagerPrivate));
+
+	/* virtual methods */
+	object_class->dispose = dispose;
+	object_class->constructed = constructed;
+}
+
diff --git a/src/devices/bluetooth/nm-bluez-manager.h b/src/devices/bluetooth/nm-bluez-manager.h
new file mode 100644
index 00000000..68d6dbe5
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez-manager.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2007 - 2008 Novell, Inc.
+ * Copyright (C) 2007 - 2014 Red Hat, Inc.
+ */
+
+#ifndef NM_BLUEZ_MANAGER_H
+#define NM_BLUEZ_MANAGER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_BLUEZ_MANAGER (nm_bluez_manager_get_type ())
+#define NM_BLUEZ_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ_MANAGER, NMBluezManager))
+
+#define NM_BLUEZ_MANAGER_BDADDR_ADDED "bdaddr-added"
+
+typedef struct {
+	GObject parent;
+} NMBluezManager;
+
+typedef struct {
+	GObjectClass parent;
+} NMBluezManagerClass;
+
+#endif /* NM_BLUEZ_MANAGER_H */
+
diff --git a/src/devices/bluetooth/nm-bluez4-adapter.c b/src/devices/bluetooth/nm-bluez4-adapter.c
new file mode 100644
index 00000000..ad1786f0
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez4-adapter.c
@@ -0,0 +1,413 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2009 - 2012 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "NetworkManager.h"
+#include "nm-dbus-manager.h"
+#include "nm-bluez4-adapter.h"
+#include "nm-bluez-device.h"
+#include "nm-bluez-common.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-logging.h"
+
+
+G_DEFINE_TYPE (NMBluez4Adapter, nm_bluez4_adapter, G_TYPE_OBJECT)
+
+#define NM_BLUEZ4_ADAPTER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_BLUEZ4_ADAPTER, NMBluez4AdapterPrivate))
+
+typedef struct {
+	char *path;
+	DBusGProxy *proxy;
+	gboolean initialized;
+
+	char *address;
+	GHashTable *devices;
+
+	/* Cached for devices */
+	NMConnectionProvider *provider;
+} NMBluez4AdapterPrivate;
+
+
+enum {
+	PROP_0,
+	PROP_PATH,
+	PROP_ADDRESS,
+
+	LAST_PROP
+};
+
+/* Signals */
+enum {
+	INITIALIZED,
+	DEVICE_ADDED,
+	DEVICE_REMOVED,
+	LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void device_do_remove (NMBluez4Adapter *self, NMBluezDevice *device);
+
+const char *
+nm_bluez4_adapter_get_path (NMBluez4Adapter *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ4_ADAPTER (self), NULL);
+
+	return NM_BLUEZ4_ADAPTER_GET_PRIVATE (self)->path;
+}
+
+const char *
+nm_bluez4_adapter_get_address (NMBluez4Adapter *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ4_ADAPTER (self), NULL);
+
+	return NM_BLUEZ4_ADAPTER_GET_PRIVATE (self)->address;
+}
+
+gboolean
+nm_bluez4_adapter_get_initialized (NMBluez4Adapter *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ4_ADAPTER (self), FALSE);
+
+	return NM_BLUEZ4_ADAPTER_GET_PRIVATE (self)->initialized;
+}
+
+GSList *
+nm_bluez4_adapter_get_devices (NMBluez4Adapter *self)
+{
+	GSList *devices = NULL;
+	GHashTableIter iter;
+	NMBluezDevice *device;
+
+	g_hash_table_iter_init (&iter, NM_BLUEZ4_ADAPTER_GET_PRIVATE (self)->devices);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) {
+		if (nm_bluez_device_get_usable (device))
+			devices = g_slist_append (devices, device);
+	}
+	return devices;
+}
+
+static void
+emit_device_removed (NMBluez4Adapter *self, NMBluezDevice *device)
+{
+	nm_log_dbg (LOGD_BT, "(%s): bluez device now unusable",
+	            nm_bluez_device_get_path (device));
+	g_signal_emit (self, signals[DEVICE_REMOVED], 0, device);
+}
+
+static void
+device_usable (NMBluezDevice *device, GParamSpec *pspec, gpointer user_data)
+{
+	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data);
+
+	if (nm_bluez_device_get_usable (device)) {
+		nm_log_dbg (LOGD_BT, "(%s): bluez device now usable (device address is %s)",
+		            nm_bluez_device_get_path (device),
+		            nm_bluez_device_get_address (device));
+		g_signal_emit (self, signals[DEVICE_ADDED], 0, device);
+	} else
+		emit_device_removed (self, device);
+}
+
+static void
+device_initialized (NMBluezDevice *device, gboolean success, gpointer user_data)
+{
+	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data);
+
+	nm_log_dbg (LOGD_BT, "(%s): bluez device %s",
+	            nm_bluez_device_get_path (device),
+	            success ? "initialized" : "failed to initialize");
+	if (!success)
+		device_do_remove (self, device);
+}
+
+static void
+device_do_remove (NMBluez4Adapter *self, NMBluezDevice *device)
+{
+	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
+
+	if (g_hash_table_remove (priv->devices, nm_bluez_device_get_path (device))) {
+		g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_initialized), self);
+		g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_usable), self);
+
+		if (nm_bluez_device_get_usable (device))
+			emit_device_removed (self, device);
+
+		g_object_unref (device);
+	}
+}
+
+static void
+device_created (DBusGProxy *proxy, const char *path, gpointer user_data)
+{
+	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data);
+	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
+	NMBluezDevice *device;
+
+	device = nm_bluez_device_new (path, priv->provider, 4);
+	g_signal_connect (device, "initialized", G_CALLBACK (device_initialized), self);
+	g_signal_connect (device, "notify::usable", G_CALLBACK (device_usable), self);
+	g_hash_table_insert (priv->devices, (gpointer) nm_bluez_device_get_path (device), device);
+
+	nm_log_dbg (LOGD_BT, "(%s): new bluez device found", path);
+}
+
+static void
+device_removed (DBusGProxy *proxy, const char *path, gpointer user_data)
+{
+	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data);
+	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
+	NMBluezDevice *device;
+
+	nm_log_dbg (LOGD_BT, "(%s): bluez device removed", path);
+
+	device = g_hash_table_lookup (priv->devices, path);
+	if (device)
+		device_do_remove (self, device);
+}
+
+
+static void
+get_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data);
+	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
+	GHashTable *properties = NULL;
+	GError *err = NULL;
+	GValue *value;
+	GPtrArray *devices;
+	int i;
+
+	if (!dbus_g_proxy_end_call (proxy, call, &err,
+	                            DBUS_TYPE_G_MAP_OF_VARIANT, &properties,
+	                            G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_BT, "bluez error getting adapter properties: %s",
+		             err && err->message ? err->message : "(unknown)");
+		g_error_free (err);
+		goto done;
+	}
+
+	value = g_hash_table_lookup (properties, "Address");
+	priv->address = value ? g_value_dup_string (value) : NULL;
+
+	value = g_hash_table_lookup (properties, "Devices");
+	devices = value ? g_value_get_boxed (value) : NULL;
+
+	for (i = 0; devices && i < devices->len; i++)
+ 		device_created (priv->proxy, g_ptr_array_index (devices, i), self);
+
+	g_hash_table_unref (properties);
+
+	priv->initialized = TRUE;
+
+done:
+	g_signal_emit (self, signals[INITIALIZED], 0, priv->initialized);
+}
+
+static void
+query_properties (NMBluez4Adapter *self)
+{
+	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
+	DBusGProxyCall *call;
+
+	call = dbus_g_proxy_begin_call (priv->proxy, "GetProperties",
+	                                get_properties_cb,
+	                                self,
+	                                NULL, G_TYPE_INVALID);
+	if (!call) {
+		nm_log_warn (LOGD_BT, "failed to request Bluetooth adapter properties for %s.",
+		             priv->path);
+	}
+}
+
+/***********************************************************/
+
+NMBluez4Adapter *
+nm_bluez4_adapter_new (const char *path, NMConnectionProvider *provider)
+{
+	NMBluez4Adapter *self;
+	NMBluez4AdapterPrivate *priv;
+	DBusGConnection *connection;
+
+	self = (NMBluez4Adapter *) g_object_new (NM_TYPE_BLUEZ4_ADAPTER,
+	                                         NM_BLUEZ4_ADAPTER_PATH, path,
+	                                         NULL);
+	if (!self)
+		return NULL;
+
+	priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
+
+	priv->provider = provider;
+
+	connection = nm_dbus_manager_get_connection (nm_dbus_manager_get ());
+
+	priv->proxy = dbus_g_proxy_new_for_name (connection,
+	                                         BLUEZ_SERVICE,
+	                                         priv->path,
+	                                         BLUEZ4_ADAPTER_INTERFACE);
+
+	dbus_g_proxy_add_signal (priv->proxy, "DeviceCreated",
+	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->proxy, "DeviceCreated",
+	                             G_CALLBACK (device_created), self, NULL);
+
+	dbus_g_proxy_add_signal (priv->proxy, "DeviceRemoved",
+	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->proxy, "DeviceRemoved",
+	                             G_CALLBACK (device_removed), self, NULL);
+
+	query_properties (self);
+	return self;
+}
+
+static void
+nm_bluez4_adapter_init (NMBluez4Adapter *self)
+{
+	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
+
+	priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal,
+	                                       NULL, NULL);
+}
+
+static gboolean
+_find_all (gpointer key, gpointer value, gpointer user_data)
+{
+	return TRUE;
+}
+
+static void
+dispose (GObject *object)
+{
+	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (object);
+	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
+	NMBluezDevice *device;
+
+	while ((device = g_hash_table_find (priv->devices, _find_all, NULL)))
+		device_do_remove (self, device);
+
+	G_OBJECT_CLASS (nm_bluez4_adapter_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (object);
+
+	g_hash_table_destroy (priv->devices);
+	g_free (priv->address);
+	g_free (priv->path);
+	g_object_unref (priv->proxy);
+
+	G_OBJECT_CLASS (nm_bluez4_adapter_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_PATH:
+		g_value_set_string (value, priv->path);
+		break;
+	case PROP_ADDRESS:
+		g_value_set_string (value, priv->address);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_PATH:
+		/* construct only */
+		priv->path = g_value_dup_string (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_bluez4_adapter_class_init (NMBluez4AdapterClass *config_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
+
+	g_type_class_add_private (config_class, sizeof (NMBluez4AdapterPrivate));
+
+	/* virtual methods */
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+	object_class->dispose = dispose;
+	object_class->finalize = finalize;
+
+	/* Properties */
+	g_object_class_install_property
+		(object_class, PROP_PATH,
+		 g_param_spec_string (NM_BLUEZ4_ADAPTER_PATH,
+		                      "DBus Path",
+		                      "DBus Path",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_ADDRESS,
+		 g_param_spec_string (NM_BLUEZ4_ADAPTER_ADDRESS,
+		                      "Address",
+		                      "Address",
+		                      NULL,
+		                      G_PARAM_READABLE));
+
+	/* Signals */
+	signals[INITIALIZED] = g_signal_new ("initialized",
+	                                     G_OBJECT_CLASS_TYPE (object_class),
+	                                     G_SIGNAL_RUN_LAST,
+	                                     G_STRUCT_OFFSET (NMBluez4AdapterClass, initialized),
+	                                     NULL, NULL,
+	                                     g_cclosure_marshal_VOID__BOOLEAN,
+	                                     G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+	signals[DEVICE_ADDED] = g_signal_new ("device-added",
+	                                      G_OBJECT_CLASS_TYPE (object_class),
+	                                      G_SIGNAL_RUN_LAST,
+	                                      G_STRUCT_OFFSET (NMBluez4AdapterClass, device_added),
+	                                      NULL, NULL,
+	                                      g_cclosure_marshal_VOID__OBJECT,
+	                                      G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
+	signals[DEVICE_REMOVED] = g_signal_new ("device-removed",
+	                                        G_OBJECT_CLASS_TYPE (object_class),
+	                                        G_SIGNAL_RUN_LAST,
+	                                        G_STRUCT_OFFSET (NMBluez4AdapterClass, device_removed),
+	                                        NULL, NULL,
+	                                        g_cclosure_marshal_VOID__OBJECT,
+	                                        G_TYPE_NONE, 1, G_TYPE_OBJECT);
+}
+
diff --git a/src/devices/bluetooth/nm-bluez4-adapter.h b/src/devices/bluetooth/nm-bluez4-adapter.h
new file mode 100644
index 00000000..454ca557
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez4-adapter.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2009 - 2012 Red Hat, Inc.
+ */
+
+#ifndef NM_BLUEZ4_ADAPTER_H
+#define NM_BLUEZ4_ADAPTER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "nm-bluez-device.h"
+#include "nm-connection-provider.h"
+
+#define NM_TYPE_BLUEZ4_ADAPTER            (nm_bluez4_adapter_get_type ())
+#define NM_BLUEZ4_ADAPTER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ4_ADAPTER, NMBluez4Adapter))
+#define NM_BLUEZ4_ADAPTER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ4_ADAPTER, NMBluez4AdapterClass))
+#define NM_IS_BLUEZ4_ADAPTER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ4_ADAPTER))
+#define NM_IS_BLUEZ4_ADAPTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ4_ADAPTER))
+#define NM_BLUEZ4_ADAPTER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ4_ADAPTER, NMBluez4AdapterClass))
+
+#define NM_BLUEZ4_ADAPTER_PATH    "path"
+#define NM_BLUEZ4_ADAPTER_ADDRESS "address"
+
+typedef struct {
+	GObject parent;
+} NMBluez4Adapter;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* virtual functions */
+	void (*initialized)    (NMBluez4Adapter *self, gboolean success);
+
+	void (*device_added)   (NMBluez4Adapter *self, NMBluezDevice *device);
+
+	void (*device_removed) (NMBluez4Adapter *self, NMBluezDevice *device);
+} NMBluez4AdapterClass;
+
+GType nm_bluez4_adapter_get_type (void);
+
+NMBluez4Adapter *nm_bluez4_adapter_new (const char *path,
+                                        NMConnectionProvider *provider);
+
+const char *nm_bluez4_adapter_get_path (NMBluez4Adapter *self);
+
+const char *nm_bluez4_adapter_get_address (NMBluez4Adapter *self);
+
+gboolean nm_bluez4_adapter_get_initialized (NMBluez4Adapter *self);
+
+GSList *nm_bluez4_adapter_get_devices (NMBluez4Adapter *self);
+
+#endif /* NM_BLUEZ4_ADAPTER_H */
+
diff --git a/src/devices/bluetooth/nm-bluez4-manager.c b/src/devices/bluetooth/nm-bluez4-manager.c
new file mode 100644
index 00000000..2660cbd9
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez4-manager.c
@@ -0,0 +1,361 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2007 - 2008 Novell, Inc.
+ * Copyright (C) 2007 - 2013 Red Hat, Inc.
+ */
+
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <dbus/dbus-glib.h>
+
+#include "nm-logging.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-bluez-manager.h"
+#include "nm-bluez4-manager.h"
+#include "nm-bluez4-adapter.h"
+#include "nm-dbus-manager.h"
+#include "nm-bluez-common.h"
+
+
+typedef struct {
+	NMDBusManager *dbus_mgr;
+	gulong name_owner_changed_id;
+
+	NMConnectionProvider *provider;
+
+	DBusGProxy *proxy;
+
+	NMBluez4Adapter *adapter;
+} NMBluez4ManagerPrivate;
+
+#define NM_BLUEZ4_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_BLUEZ4_MANAGER, NMBluez4ManagerPrivate))
+
+G_DEFINE_TYPE (NMBluez4Manager, nm_bluez4_manager, G_TYPE_OBJECT)
+
+enum {
+	BDADDR_ADDED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+
+emit_bdaddr_added (NMBluez4Manager *self, NMBluezDevice *device)
+{
+	g_signal_emit (self, signals[BDADDR_ADDED], 0,
+	               device,
+	               nm_bluez_device_get_address (device),
+	               nm_bluez_device_get_name (device),
+	               nm_bluez_device_get_path (device),
+	               nm_bluez_device_get_capabilities (device));
+}
+
+void
+nm_bluez4_manager_query_devices (NMBluez4Manager *self)
+{
+	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
+	GSList *devices, *iter;
+
+	if (!priv->adapter)
+		return;
+
+	devices = nm_bluez4_adapter_get_devices (priv->adapter);
+	for (iter = devices; iter; iter = g_slist_next (iter))
+		emit_bdaddr_added (self, NM_BLUEZ_DEVICE (iter->data));
+	g_slist_free (devices);
+}
+
+static void
+device_added (NMBluez4Adapter *adapter, NMBluezDevice *device, gpointer user_data)
+{
+	emit_bdaddr_added (NM_BLUEZ4_MANAGER (user_data), device);
+}
+
+static void
+device_removed (NMBluez4Adapter *adapter, NMBluezDevice *device, gpointer user_data)
+{
+	/* Re-emit the signal on the device for now; flatten this later */
+	g_signal_emit_by_name (device, NM_BLUEZ_DEVICE_REMOVED);
+}
+
+static void
+adapter_initialized (NMBluez4Adapter *adapter, gboolean success, gpointer user_data)
+{
+	NMBluez4Manager *self = NM_BLUEZ4_MANAGER (user_data);
+	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
+
+	if (success) {
+		GSList *devices, *iter;
+
+		devices = nm_bluez4_adapter_get_devices (adapter);
+		for (iter = devices; iter; iter = g_slist_next (iter))
+			emit_bdaddr_added (self, NM_BLUEZ_DEVICE (iter->data));
+		g_slist_free (devices);
+
+		g_signal_connect (adapter, "device-added", G_CALLBACK (device_added), self);
+		g_signal_connect (adapter, "device-removed", G_CALLBACK (device_removed), self);
+	} else {
+		g_object_unref (priv->adapter);
+		priv->adapter = NULL;
+	}
+}
+
+static void
+adapter_removed (DBusGProxy *proxy, const char *path, NMBluez4Manager *self)
+{
+	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
+
+	if (priv->adapter && !strcmp (path, nm_bluez4_adapter_get_path (priv->adapter))) {
+		if (nm_bluez4_adapter_get_initialized (priv->adapter)) {
+			GSList *devices, *iter;
+
+			devices = nm_bluez4_adapter_get_devices (priv->adapter);
+			for (iter = devices; iter; iter = g_slist_next (iter))
+				g_signal_emit_by_name (NM_BLUEZ_DEVICE (iter->data), NM_BLUEZ_DEVICE_REMOVED);
+			g_slist_free (devices);
+		}
+
+		g_object_unref (priv->adapter);
+		priv->adapter = NULL;
+	}
+}
+
+static void
+default_adapter_changed (DBusGProxy *proxy, const char *path, NMBluez4Manager *self)
+{
+	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
+	const char *cur_path = NULL;
+
+	if (priv->adapter)
+		cur_path = nm_bluez4_adapter_get_path (priv->adapter);
+
+	if (cur_path) {
+		if (!path || strcmp (path, cur_path)) {
+			/* Default adapter changed */
+			adapter_removed (priv->proxy, cur_path, self);
+		} else {
+			/* This adapter is already the default */
+			return;
+		}
+	}
+
+	/* Add the new default adapter */
+	if (path) {
+		priv->adapter = nm_bluez4_adapter_new (path, priv->provider);
+		g_signal_connect (priv->adapter, "initialized", G_CALLBACK (adapter_initialized), self);
+	}
+}
+
+static void
+default_adapter_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+	NMBluez4Manager *self = NM_BLUEZ4_MANAGER (user_data);
+	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
+	const char *default_adapter = NULL;
+	GError *err = NULL;
+
+	if (!dbus_g_proxy_end_call (proxy, call, &err,
+	                            DBUS_TYPE_G_OBJECT_PATH, &default_adapter,
+	                            G_TYPE_INVALID)) {
+		/* Ignore "No such adapter" errors; just means bluetooth isn't active */
+		if (   !dbus_g_error_has_name (err, "org.bluez.Error.NoSuchAdapter")
+		    && !dbus_g_error_has_name (err, "org.freedesktop.systemd1.LoadFailed")
+		    && !g_error_matches (err, DBUS_GERROR, DBUS_GERROR_SERVICE_UNKNOWN)) {
+			nm_log_warn (LOGD_BT, "bluez error getting default adapter: %s",
+			             err && err->message ? err->message : "(unknown)");
+		}
+		g_error_free (err);
+		return;
+	}
+
+	default_adapter_changed (priv->proxy, default_adapter, self);
+}
+
+static void
+query_default_adapter (NMBluez4Manager *self)
+{
+	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
+	DBusGProxyCall *call;
+
+	call = dbus_g_proxy_begin_call (priv->proxy, "DefaultAdapter",
+	                                default_adapter_cb,
+	                                self,
+	                                NULL, G_TYPE_INVALID);
+	if (!call)
+		nm_log_warn (LOGD_BT, "failed to request default Bluetooth adapter.");
+}
+
+static void
+bluez_connect (NMBluez4Manager *self)
+{
+	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
+	DBusGConnection *connection;
+
+	g_return_if_fail (priv->proxy == NULL);
+
+	connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
+	if (!connection)
+		return;
+
+	priv->proxy = dbus_g_proxy_new_for_name (connection,
+	                                         BLUEZ_SERVICE,
+	                                         BLUEZ_MANAGER_PATH,
+	                                         BLUEZ4_MANAGER_INTERFACE);
+
+	dbus_g_proxy_add_signal (priv->proxy, "AdapterRemoved",
+	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->proxy, "AdapterRemoved",
+	                             G_CALLBACK (adapter_removed), self, NULL);
+
+	dbus_g_proxy_add_signal (priv->proxy, "DefaultAdapterChanged",
+	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->proxy, "DefaultAdapterChanged",
+	                             G_CALLBACK (default_adapter_changed), self, NULL);
+
+	query_default_adapter (self);
+}
+
+static void
+name_owner_changed_cb (NMDBusManager *dbus_mgr,
+                       const char *name,
+                       const char *old_owner,
+                       const char *new_owner,
+                       gpointer user_data)
+{
+	NMBluez4Manager *self = NM_BLUEZ4_MANAGER (user_data);
+	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
+	gboolean old_owner_good = (old_owner && strlen (old_owner));
+	gboolean new_owner_good = (new_owner && strlen (new_owner));
+
+	/* Can't handle the signal if its not from the Bluez */
+	if (strcmp (BLUEZ_SERVICE, name))
+		return;
+
+	if (!old_owner_good && new_owner_good)
+		query_default_adapter (self);
+	else if (old_owner_good && !new_owner_good) {
+		/* Throwing away the adapter removes all devices too */
+		if (priv->adapter) {
+			g_object_unref (priv->adapter);
+			priv->adapter = NULL;
+		}
+	}
+}
+
+static void
+bluez_cleanup (NMBluez4Manager *self, gboolean do_signal)
+{
+	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
+
+	if (priv->proxy) {
+		g_object_unref (priv->proxy);
+		priv->proxy = NULL;
+	}
+
+	if (priv->adapter) {
+		g_object_unref (priv->adapter);
+		priv->adapter = NULL;
+	}
+}
+
+static void
+dbus_connection_changed_cb (NMDBusManager *dbus_mgr,
+                            DBusGConnection *connection,
+                            gpointer user_data)
+{
+	NMBluez4Manager *self = NM_BLUEZ4_MANAGER (user_data);
+
+	if (!connection)
+		bluez_cleanup (self, TRUE);
+	else
+		bluez_connect (self);
+}
+
+/****************************************************************/
+
+NMBluez4Manager *
+nm_bluez4_manager_new (NMConnectionProvider *provider)
+{
+	NMBluez4Manager *instance;
+
+	instance = g_object_new (NM_TYPE_BLUEZ4_MANAGER, NULL);
+	NM_BLUEZ4_MANAGER_GET_PRIVATE (instance)->provider = provider;
+	return instance;
+}
+
+static void
+nm_bluez4_manager_init (NMBluez4Manager *self)
+{
+	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
+
+	priv->dbus_mgr = nm_dbus_manager_get ();
+	g_assert (priv->dbus_mgr);
+
+	g_signal_connect (priv->dbus_mgr,
+	                  NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
+	                  G_CALLBACK (name_owner_changed_cb),
+	                  self);
+
+	g_signal_connect (priv->dbus_mgr,
+	                  NM_DBUS_MANAGER_DBUS_CONNECTION_CHANGED,
+	                  G_CALLBACK (dbus_connection_changed_cb),
+	                  self);
+
+	bluez_connect (self);
+}
+
+static void
+dispose (GObject *object)
+{
+	NMBluez4Manager *self = NM_BLUEZ4_MANAGER (object);
+	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
+
+	bluez_cleanup (self, FALSE);
+
+	if (priv->dbus_mgr) {
+		g_signal_handlers_disconnect_by_func (priv->dbus_mgr, name_owner_changed_cb, self);
+		g_signal_handlers_disconnect_by_func (priv->dbus_mgr, dbus_connection_changed_cb, self);
+		priv->dbus_mgr = NULL;
+	}
+
+	G_OBJECT_CLASS (nm_bluez4_manager_parent_class)->dispose (object);
+}
+
+static void
+nm_bluez4_manager_class_init (NMBluez4ManagerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMBluez4ManagerPrivate));
+
+	/* virtual methods */
+	object_class->dispose = dispose;
+
+	/* Signals */
+	signals[BDADDR_ADDED] =
+		g_signal_new (NM_BLUEZ_MANAGER_BDADDR_ADDED,
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMBluez4ManagerClass, bdaddr_added),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 5, G_TYPE_OBJECT, G_TYPE_STRING,
+		              G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
+}
+
diff --git a/src/devices/bluetooth/nm-bluez4-manager.h b/src/devices/bluetooth/nm-bluez4-manager.h
new file mode 100644
index 00000000..19b1c65a
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez4-manager.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2007 - 2008 Novell, Inc.
+ * Copyright (C) 2007 - 2013 Red Hat, Inc.
+ */
+
+#ifndef NM_BLUEZ4_MANAGER_H
+#define NM_BLUEZ4_MANAGER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include  <config.h>
+#include  "nm-connection-provider.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_BLUEZ4_MANAGER            (nm_bluez4_manager_get_type ())
+#define NM_BLUEZ4_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ4_MANAGER, NMBluez4Manager))
+#define NM_BLUEZ4_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ4_MANAGER, NMBluez4ManagerClass))
+#define NM_IS_BLUEZ4_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ4_MANAGER))
+#define NM_IS_BLUEZ4_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ4_MANAGER))
+#define NM_BLUEZ4_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ4_MANAGER, NMBluez4ManagerClass))
+
+typedef struct {
+	GObject parent;
+} NMBluez4Manager;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+	void (*bdaddr_added) (NMBluez4Manager *manager,
+	                      const char *bdaddr,
+	                      const char *name,
+	                      const char *object_path,
+	                      guint uuids);
+} NMBluez4ManagerClass;
+
+GType nm_bluez4_manager_get_type (void);
+
+NMBluez4Manager *nm_bluez4_manager_new (NMConnectionProvider *provider);
+
+void nm_bluez4_manager_query_devices (NMBluez4Manager *manager);
+
+#endif /* NM_BLUEZ4_MANAGER_H */
+
diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c
new file mode 100644
index 00000000..63006b3a
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez5-manager.c
@@ -0,0 +1,421 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2007 - 2008 Novell, Inc.
+ * Copyright (C) 2007 - 2013 Red Hat, Inc.
+ * Copyright (C) 2013 Intel Corporation.
+ */
+
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <gio/gio.h>
+
+#include "nm-logging.h"
+#include "nm-bluez-manager.h"
+#include "nm-bluez5-manager.h"
+#include "nm-bluez-device.h"
+#include "nm-bluez-common.h"
+
+#include "nm-dbus-manager.h"
+
+typedef struct {
+	NMDBusManager *dbus_mgr;
+	gulong name_owner_changed_id;
+
+	NMConnectionProvider *provider;
+
+	GDBusProxy *proxy;
+
+	GHashTable *devices;
+} NMBluez5ManagerPrivate;
+
+#define NM_BLUEZ5_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_BLUEZ5_MANAGER, NMBluez5ManagerPrivate))
+
+G_DEFINE_TYPE (NMBluez5Manager, nm_bluez5_manager, G_TYPE_OBJECT)
+
+enum {
+	BDADDR_ADDED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void device_initialized (NMBluezDevice *device, gboolean success, NMBluez5Manager *self);
+static void device_usable (NMBluezDevice *device, GParamSpec *pspec, NMBluez5Manager *self);
+
+static void
+emit_bdaddr_added (NMBluez5Manager *self, NMBluezDevice *device)
+{
+	g_signal_emit (self, signals[BDADDR_ADDED], 0,
+	               device,
+	               nm_bluez_device_get_address (device),
+	               nm_bluez_device_get_name (device),
+	               nm_bluez_device_get_path (device),
+	               nm_bluez_device_get_capabilities (device));
+}
+
+void
+nm_bluez5_manager_query_devices (NMBluez5Manager *self)
+{
+	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+	NMBluezDevice *device;
+	GHashTableIter iter;
+
+	g_hash_table_iter_init (&iter, priv->devices);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) {
+		if (nm_bluez_device_get_usable (device))
+			emit_bdaddr_added (self, device);
+	}
+}
+
+static void
+remove_device (NMBluez5Manager *self, NMBluezDevice *device)
+{
+	g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_initialized), self);
+	g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_usable), self);
+	if (nm_bluez_device_get_usable (device))
+		g_signal_emit_by_name (device, NM_BLUEZ_DEVICE_REMOVED);
+}
+
+static void
+remove_all_devices (NMBluez5Manager *self)
+{
+	GHashTableIter iter;
+	NMBluezDevice *device;
+	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+
+	g_hash_table_iter_init (&iter, priv->devices);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) {
+		g_hash_table_iter_steal (&iter);
+		remove_device (self, device);
+		g_object_unref (device);
+	}
+}
+
+static void
+device_usable (NMBluezDevice *device, GParamSpec *pspec, NMBluez5Manager *self)
+{
+	gboolean usable = nm_bluez_device_get_usable (device);
+
+	nm_log_dbg (LOGD_BT, "(%s): bluez device now %s",
+	            nm_bluez_device_get_path (device),
+	            usable ? "usable" : "unusable");
+
+	if (usable) {
+		nm_log_dbg (LOGD_BT, "(%s): bluez device address %s",
+				    nm_bluez_device_get_path (device),
+				    nm_bluez_device_get_address (device));
+		emit_bdaddr_added (self, device);
+	} else
+		g_signal_emit_by_name (device, NM_BLUEZ_DEVICE_REMOVED);
+}
+
+static void
+device_initialized (NMBluezDevice *device, gboolean success, NMBluez5Manager *self)
+{
+	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+
+	nm_log_dbg (LOGD_BT, "(%s): bluez device %s",
+	            nm_bluez_device_get_path (device),
+	            success ? "initialized" : "failed to initialize");
+	if (!success)
+		g_hash_table_remove (priv->devices, nm_bluez_device_get_path (device));
+}
+
+static void
+device_added (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *self)
+{
+	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+	NMBluezDevice *device;
+
+	device = nm_bluez_device_new (path, priv->provider, 5);
+	g_signal_connect (device, "initialized", G_CALLBACK (device_initialized), self);
+	g_signal_connect (device, "notify::usable", G_CALLBACK (device_usable), self);
+	g_hash_table_insert (priv->devices, (gpointer) nm_bluez_device_get_path (device), device);
+
+	nm_log_dbg (LOGD_BT, "(%s): new bluez device found", path);
+}
+
+static void
+device_removed (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *self)
+{
+	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+	NMBluezDevice *device;
+
+	nm_log_dbg (LOGD_BT, "(%s): bluez device removed", path);
+
+	device = g_hash_table_lookup (priv->devices, path);
+	if (device) {
+		g_hash_table_steal (priv->devices, nm_bluez_device_get_path (device));
+		remove_device (NM_BLUEZ5_MANAGER (self), device);
+		g_object_unref (device);
+	}
+}
+
+static void
+object_manager_g_signal (GDBusProxy     *proxy,
+                         gchar          *sender_name,
+                         gchar          *signal_name,
+                         GVariant       *parameters,
+                         NMBluez5Manager *self)
+{
+	GVariant *variant;
+	const gchar *path;
+
+	if (!strcmp (signal_name, "InterfacesRemoved")) {
+		const gchar **ifaces;
+		gsize i, length;
+
+		g_variant_get (parameters, "(&o*)", &path, &variant);
+
+		ifaces = g_variant_get_strv (variant, &length);
+
+		for (i = 0; i < length; i++) {
+			if (!strcmp (ifaces[i], BLUEZ5_DEVICE_INTERFACE)) {
+				device_removed (proxy, path, self);
+				break;
+			}
+		}
+
+		g_free (ifaces);
+
+	} else if (!strcmp (signal_name, "InterfacesAdded")) {
+		g_variant_get (parameters, "(&o*)", &path, &variant);
+
+		if (g_variant_lookup_value (variant, BLUEZ5_DEVICE_INTERFACE,
+		                            G_VARIANT_TYPE_DICTIONARY))
+			device_added (proxy, path, self);
+	}
+}
+
+static void
+get_managed_objects_cb (GDBusProxy *proxy,
+                        GAsyncResult *res,
+                        NMBluez5Manager *self)
+{
+	GVariant *variant, *ifaces;
+	GVariantIter i;
+	GError *error = NULL;
+	const char *path;
+
+	variant = g_dbus_proxy_call_finish (proxy, res, &error);
+
+	if (!variant) {
+		if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
+			nm_log_warn (LOGD_BT, "Couldn't get managed objects: not running Bluez5?");
+		else {
+			nm_log_warn (LOGD_BT, "Couldn't get managed objects: %s",
+				         error && error->message ? error->message : "(unknown)");
+		}
+		g_clear_error (&error);
+		return;
+	}
+	g_variant_iter_init (&i, g_variant_get_child_value (variant, 0));
+	while ((g_variant_iter_next (&i, "{&o*}", &path, &ifaces))) {
+		if (g_variant_lookup_value (ifaces, BLUEZ5_DEVICE_INTERFACE,
+		                            G_VARIANT_TYPE_DICTIONARY)) {
+			device_added (proxy, path, self);
+		}
+	}
+
+	g_variant_unref (variant);
+}
+
+static void
+on_proxy_acquired (GObject *object,
+                   GAsyncResult *res,
+                   NMBluez5Manager *self)
+{
+	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+	GError *error = NULL;
+
+	priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+
+	if (!priv->proxy) {
+		nm_log_warn (LOGD_BT, "Couldn't acquire object manager proxy: %s",
+		             error && error->message ? error->message : "(unknown)");
+		g_clear_error (&error);
+		return;
+	}
+
+	/* Get already managed devices. */
+	g_dbus_proxy_call (priv->proxy, "GetManagedObjects",
+	                   NULL,
+	                   G_DBUS_CALL_FLAGS_NONE,
+	                   -1,
+	                   NULL,
+	                   (GAsyncReadyCallback) get_managed_objects_cb,
+	                   self);
+
+	g_signal_connect (priv->proxy, "g-signal",
+	                  G_CALLBACK (object_manager_g_signal), self);
+}
+
+static void
+bluez_connect (NMBluez5Manager *self)
+{
+	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+
+	g_return_if_fail (priv->proxy == NULL);
+
+	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+	                          G_DBUS_PROXY_FLAGS_NONE,
+	                          NULL,
+	                          BLUEZ_SERVICE,
+	                          BLUEZ_MANAGER_PATH,
+	                          OBJECT_MANAGER_INTERFACE,
+	                          NULL,
+	                          (GAsyncReadyCallback) on_proxy_acquired,
+	                          self);
+}
+
+static void
+name_owner_changed_cb (NMDBusManager *dbus_mgr,
+                       const char *name,
+                       const char *old_owner,
+                       const char *new_owner,
+                       gpointer user_data)
+{
+	NMBluez5Manager *self = NM_BLUEZ5_MANAGER (user_data);
+	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+	gboolean old_owner_good = (old_owner && strlen (old_owner));
+	gboolean new_owner_good = (new_owner && strlen (new_owner));
+
+	/* Can't handle the signal if its not from the Bluez */
+	if (strcmp (BLUEZ_SERVICE, name))
+		return;
+
+	if (old_owner_good && !new_owner_good) {
+		if (priv->devices)
+			remove_all_devices (self);
+	}
+}
+
+static void
+bluez_cleanup (NMBluez5Manager *self, gboolean do_signal)
+{
+	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+
+	if (priv->proxy) {
+		g_object_unref (priv->proxy);
+		priv->proxy = NULL;
+	}
+
+	if (do_signal)
+		remove_all_devices (self);
+	else
+		g_hash_table_remove_all (priv->devices);
+}
+
+static void
+dbus_connection_changed_cb (NMDBusManager *dbus_mgr,
+                            DBusGConnection *connection,
+                            gpointer user_data)
+{
+	NMBluez5Manager *self = NM_BLUEZ5_MANAGER (user_data);
+
+	if (!connection)
+		bluez_cleanup (self, TRUE);
+	else
+		bluez_connect (self);
+}
+
+/****************************************************************/
+
+NMBluez5Manager *
+nm_bluez5_manager_new (NMConnectionProvider *provider)
+{
+	NMBluez5Manager *instance = NULL;
+
+	instance = g_object_new (NM_TYPE_BLUEZ5_MANAGER, NULL);
+	NM_BLUEZ5_MANAGER_GET_PRIVATE (instance)->provider = provider;
+	return instance;
+}
+
+static void
+nm_bluez5_manager_init (NMBluez5Manager *self)
+{
+	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+
+	priv->dbus_mgr = nm_dbus_manager_get ();
+	g_assert (priv->dbus_mgr);
+
+	g_signal_connect (priv->dbus_mgr,
+	                  NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
+	                  G_CALLBACK (name_owner_changed_cb),
+	                  self);
+
+	g_signal_connect (priv->dbus_mgr,
+	                  NM_DBUS_MANAGER_DBUS_CONNECTION_CHANGED,
+	                  G_CALLBACK (dbus_connection_changed_cb),
+	                  self);
+
+	bluez_connect (self);
+
+	priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal,
+	                                       NULL, g_object_unref);
+}
+
+static void
+dispose (GObject *object)
+{
+	NMBluez5Manager *self = NM_BLUEZ5_MANAGER (object);
+	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+
+	bluez_cleanup (self, FALSE);
+
+	if (priv->dbus_mgr) {
+		g_signal_handlers_disconnect_by_func (priv->dbus_mgr, name_owner_changed_cb, self);
+		g_signal_handlers_disconnect_by_func (priv->dbus_mgr, dbus_connection_changed_cb, self);
+		priv->dbus_mgr = NULL;
+	}
+
+	G_OBJECT_CLASS (nm_bluez5_manager_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (object);
+
+	g_hash_table_destroy (priv->devices);
+
+	G_OBJECT_CLASS (nm_bluez5_manager_parent_class)->finalize (object);
+}
+
+static void
+nm_bluez5_manager_class_init (NMBluez5ManagerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMBluez5ManagerPrivate));
+
+	/* virtual methods */
+	object_class->dispose = dispose;
+	object_class->finalize = finalize;
+
+	/* Signals */
+	signals[BDADDR_ADDED] =
+		g_signal_new (NM_BLUEZ_MANAGER_BDADDR_ADDED,
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMBluez5ManagerClass, bdaddr_added),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 5, G_TYPE_OBJECT, G_TYPE_STRING,
+		              G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
+}
diff --git a/src/devices/bluetooth/nm-bluez5-manager.h b/src/devices/bluetooth/nm-bluez5-manager.h
new file mode 100644
index 00000000..79f347bc
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez5-manager.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2007 - 2008 Novell, Inc.
+ * Copyright (C) 2007 - 2013 Red Hat, Inc.
+ */
+
+#ifndef NM_BLUEZ5_MANAGER_H
+#define NM_BLUEZ5_MANAGER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include  <config.h>
+#include  "nm-connection-provider.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_BLUEZ5_MANAGER            (nm_bluez5_manager_get_type ())
+#define NM_BLUEZ5_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ5_MANAGER, NMBluez5Manager))
+#define NM_BLUEZ5_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ5_MANAGER, NMBluez5ManagerClass))
+#define NM_IS_BLUEZ5_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ5_MANAGER))
+#define NM_IS_BLUEZ5_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ5_MANAGER))
+#define NM_BLUEZ5_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ5_MANAGER, NMBluez5ManagerClass))
+
+typedef struct {
+	GObject parent;
+} NMBluez5Manager;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+	void (*bdaddr_added) (NMBluez5Manager *manager,
+	                      const char *bdaddr,
+	                      const char *name,
+	                      const char *object_path,
+	                      guint uuids);
+} NMBluez5ManagerClass;
+
+GType nm_bluez5_manager_get_type (void);
+
+NMBluez5Manager *nm_bluez5_manager_new (NMConnectionProvider *provider);
+
+void nm_bluez5_manager_query_devices (NMBluez5Manager *manager);
+
+#endif /* NM_BLUEZ5_MANAGER_H */
+
diff --git a/src/devices/bluetooth/nm-bt-enum-types.c b/src/devices/bluetooth/nm-bt-enum-types.c
new file mode 100644
index 00000000..2595caeb
--- /dev/null
+++ b/src/devices/bluetooth/nm-bt-enum-types.c
@@ -0,0 +1,32 @@
+
+
+
+/* Generated by glib-mkenums. Do not edit */
+
+#include "nm-bt-enum-types.h"
+
+#include "nm-device-bt.h"
+
+GType
+nm_bt_error_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile))
+    {
+      static const GEnumValue values[] = {
+        { NM_BT_ERROR_CONNECTION_NOT_BT, "NM_BT_ERROR_CONNECTION_NOT_BT", "ConnectionNotBt" },
+        { NM_BT_ERROR_CONNECTION_INVALID, "NM_BT_ERROR_CONNECTION_INVALID", "ConnectionInvalid" },
+        { NM_BT_ERROR_CONNECTION_INCOMPATIBLE, "NM_BT_ERROR_CONNECTION_INCOMPATIBLE", "ConnectionIncompatible" },
+        { 0, NULL, NULL }
+      };
+      GType g_define_type_id =
+        g_enum_register_static (g_intern_static_string ("NMBtError"), values);
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+  return g_define_type_id__volatile;
+}
+
+
+
diff --git a/src/devices/bluetooth/nm-bt-enum-types.h b/src/devices/bluetooth/nm-bt-enum-types.h
new file mode 100644
index 00000000..af887929
--- /dev/null
+++ b/src/devices/bluetooth/nm-bt-enum-types.h
@@ -0,0 +1,19 @@
+
+
+
+/* Generated by glib-mkenums. Do not edit */
+
+#ifndef __NM_BT_ENUM_TYPES_H__
+#define __NM_BT_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+GType nm_bt_error_get_type (void) G_GNUC_CONST;
+#define NM_TYPE_BT_ERROR (nm_bt_error_get_type ())
+G_END_DECLS
+
+#endif /* __NM_BT_ENUM_TYPES_H__ */
+
+
+
diff --git a/src/devices/bluetooth/nm-device-bt-glue.h b/src/devices/bluetooth/nm-device-bt-glue.h
new file mode 100644
index 00000000..b6f851b2
--- /dev/null
+++ b/src/devices/bluetooth/nm-device-bt-glue.h
@@ -0,0 +1,73 @@
+/* Generated by dbus-binding-tool; do not edit! */
+
+
+#ifndef __dbus_glib_marshal_nm_device_bt_MARSHAL_H__
+#define __dbus_glib_marshal_nm_device_bt_MARSHAL_H__
+
+#include	<glib-object.h>
+
+G_BEGIN_DECLS
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_schar (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#define g_marshal_value_peek_variant(v)  g_value_get_variant (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_variant(v)  (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+G_END_DECLS
+
+#endif /* __dbus_glib_marshal_nm_device_bt_MARSHAL_H__ */
+
+#include <dbus/dbus-glib.h>
+static const DBusGMethodInfo dbus_glib_nm_device_bt_methods[] = {
+};
+
+const DBusGObjectInfo dbus_glib_nm_device_bt_object_info = {  1,
+  dbus_glib_nm_device_bt_methods,
+  0,
+"\0",
+"org.freedesktop.NetworkManager.Device.Bluetooth\0PropertiesChanged\0\0",
+"org.freedesktop.NetworkManager.Device.Bluetooth\0HwAddress\0hw_address\0read\0org.freedesktop.NetworkManager.Device.Bluetooth\0Name\0name\0read\0org.freedesktop.NetworkManager.Device.Bluetooth\0BtCapabilities\0bt_capabilities\0read\0\0"
+};
+
diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c
new file mode 100644
index 00000000..0bd2f17d
--- /dev/null
+++ b/src/devices/bluetooth/nm-device-bt.c
@@ -0,0 +1,1278 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2009 - 2011 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <net/ethernet.h>
+#include <netinet/ether.h>
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "nm-glib-compat.h"
+#include "nm-bluez-common.h"
+#include "nm-bluez-device.h"
+#include "nm-dbus-manager.h"
+#include "nm-device-bt.h"
+#include "nm-device-private.h"
+#include "nm-logging.h"
+#include "ppp-manager/nm-ppp-manager.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-bluetooth.h"
+#include "nm-setting-cdma.h"
+#include "nm-setting-gsm.h"
+#include "nm-setting-serial.h"
+#include "nm-setting-ppp.h"
+#include "nm-device-bt-glue.h"
+#include "NetworkManagerUtils.h"
+#include "nm-bt-enum-types.h"
+#include "nm-utils.h"
+
+#define MM_OLD_DBUS_SERVICE  "org.freedesktop.ModemManager"
+#define MM_NEW_DBUS_SERVICE  "org.freedesktop.ModemManager1"
+
+G_DEFINE_TYPE (NMDeviceBt, nm_device_bt, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_BT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_BT, NMDeviceBtPrivate))
+
+static gboolean modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *reason);
+
+typedef struct {
+	NMDBusManager *dbus_mgr;
+	guint mm_watch_id;
+	gboolean mm_running;
+
+	NMBluezDevice *bt_device;
+
+	guint8 bdaddr[ETH_ALEN];
+	char *name;
+	guint32 capabilities;
+
+	gboolean connected;
+	gboolean have_iface;
+
+	char *rfcomm_iface;
+	NMModem *modem;
+	guint32 timeout_id;
+
+	guint32 bt_type;  /* BT type of the current connection */
+} NMDeviceBtPrivate;
+
+enum {
+	PROP_0,
+	PROP_BT_NAME,
+	PROP_BT_CAPABILITIES,
+	PROP_BT_DEVICE,
+
+	LAST_PROP
+};
+
+enum {
+	PPP_STATS,
+
+	LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+#define NM_BT_ERROR (nm_bt_error_quark ())
+
+static GQuark
+nm_bt_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("nm-bt-error");
+	return quark;
+}
+
+guint32 nm_device_bt_get_capabilities (NMDeviceBt *self)
+{
+	g_return_val_if_fail (NM_IS_DEVICE_BT (self), NM_BT_CAPABILITY_NONE);
+
+	return NM_DEVICE_BT_GET_PRIVATE (self)->capabilities;
+}
+
+static guint
+get_hw_address_length (NMDevice *device, gboolean *out_permanent)
+{
+	/* HW address is the Bluetooth HW address of the remote device */
+	if (out_permanent)
+		*out_permanent = TRUE;   /* the bdaddr of the remote device will never change */
+	return ETH_ALEN;
+}
+
+static guint32
+get_connection_bt_type (NMConnection *connection)
+{
+	NMSettingBluetooth *s_bt;
+	const char *bt_type;
+
+	s_bt = nm_connection_get_setting_bluetooth (connection);
+	if (!s_bt)
+		return NM_BT_CAPABILITY_NONE;
+
+	bt_type = nm_setting_bluetooth_get_connection_type (s_bt);
+	g_assert (bt_type);
+
+	if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN))
+		return NM_BT_CAPABILITY_DUN;
+	else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU))
+		return NM_BT_CAPABILITY_NAP;
+
+	return NM_BT_CAPABILITY_NONE;
+}
+
+static gboolean
+can_auto_connect (NMDevice *device,
+                  NMConnection *connection,
+                  char **specific_object)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+	guint32 bt_type;
+
+	if (!NM_DEVICE_CLASS (nm_device_bt_parent_class)->can_auto_connect (device, connection, specific_object))
+		return FALSE;
+
+	/* Can't auto-activate a DUN connection without ModemManager */
+	bt_type = get_connection_bt_type (connection);
+	if (bt_type == NM_BT_CAPABILITY_DUN && priv->mm_running == FALSE)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+	NMSettingConnection *s_con;
+	NMSettingBluetooth *s_bt;
+	const GByteArray *array;
+	guint32 bt_type;
+
+	if (!NM_DEVICE_CLASS (nm_device_bt_parent_class)->check_connection_compatible (device, connection))
+		return FALSE;
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+
+	if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_BLUETOOTH_SETTING_NAME))
+		return FALSE;
+
+	s_bt = nm_connection_get_setting_bluetooth (connection);
+	if (!s_bt)
+		return FALSE;
+
+	bt_type = get_connection_bt_type (connection);
+	if (!(bt_type & priv->capabilities))
+		return FALSE;
+
+	array = nm_setting_bluetooth_get_bdaddr (s_bt);
+	if (!array || (array->len != ETH_ALEN))
+		return FALSE;
+
+	if (memcmp (priv->bdaddr, array->data, ETH_ALEN) != 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+check_connection_available (NMDevice *device,
+                            NMConnection *connection,
+                            const char *specific_object)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+	guint32 bt_type;
+
+	bt_type = get_connection_bt_type (connection);
+	if (!(bt_type & priv->capabilities))
+		return FALSE;
+
+	/* DUN connections aren't available without ModemManager */
+	if (bt_type == NM_BT_CAPABILITY_DUN && priv->mm_running == FALSE)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+complete_connection (NMDevice *device,
+                     NMConnection *connection,
+                     const char *specific_object,
+                     const GSList *existing_connections,
+                     GError **error)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+	NMSettingBluetooth *s_bt;
+	const GByteArray *setting_bdaddr;
+	const char *ctype;
+	gboolean is_dun = FALSE, is_pan = FALSE;
+	NMSettingGsm *s_gsm;
+	NMSettingCdma *s_cdma;
+	NMSettingSerial *s_serial;
+	NMSettingPPP *s_ppp;
+	const char *format = NULL, *preferred = NULL;
+
+	s_gsm = nm_connection_get_setting_gsm (connection);
+	s_cdma = nm_connection_get_setting_cdma (connection);
+	s_serial = nm_connection_get_setting_serial (connection);
+	s_ppp = nm_connection_get_setting_ppp (connection);
+
+	s_bt = nm_connection_get_setting_bluetooth (connection);
+	if (!s_bt) {
+		s_bt = (NMSettingBluetooth *) nm_setting_bluetooth_new ();
+		nm_connection_add_setting (connection, NM_SETTING (s_bt));
+	}
+
+	ctype = nm_setting_bluetooth_get_connection_type (s_bt);
+	if (ctype) {
+		if (!strcmp (ctype, NM_SETTING_BLUETOOTH_TYPE_DUN))
+			is_dun = TRUE;
+		else if (!strcmp (ctype, NM_SETTING_BLUETOOTH_TYPE_PANU))
+			is_pan = TRUE;
+	} else {
+		if (s_gsm || s_cdma)
+			is_dun = TRUE;
+		else if (priv->capabilities & NM_BT_CAPABILITY_NAP)
+			is_pan = TRUE;
+	}
+
+	if (is_pan) {
+		/* Make sure the device supports PAN */
+		if (!(priv->capabilities & NM_BT_CAPABILITY_NAP)) {
+			g_set_error_literal (error,
+			                     NM_SETTING_BLUETOOTH_ERROR,
+			                     NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
+			                     "PAN required but Bluetooth device does not support NAP");
+			return FALSE;
+		}
+
+		/* PAN can't use any DUN-related settings */
+		if (s_gsm || s_cdma || s_serial || s_ppp) {
+			g_set_error_literal (error,
+			                     NM_SETTING_BLUETOOTH_ERROR,
+			                     NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
+			                     "PAN incompatible with GSM, CDMA, or serial settings");
+			return FALSE;
+		}
+
+		g_object_set (G_OBJECT (s_bt),
+		              NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU,
+		              NULL);
+
+		format = _("PAN connection %d");
+	} else if (is_dun) {
+		/* Make sure the device supports PAN */
+		if (!(priv->capabilities & NM_BT_CAPABILITY_DUN)) {
+			g_set_error_literal (error,
+			                     NM_SETTING_BLUETOOTH_ERROR,
+			                     NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
+			                     "DUN required but Bluetooth device does not support DUN");
+			return FALSE;
+		}
+
+		/* Need at least a GSM or a CDMA setting */
+		if (!s_gsm && !s_cdma) {
+			g_set_error_literal (error,
+			                     NM_SETTING_BLUETOOTH_ERROR,
+			                     NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
+			                     "Setting requires DUN but no GSM or CDMA setting is present");
+			return FALSE;
+		}
+
+		g_object_set (G_OBJECT (s_bt),
+		              NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_DUN,
+		              NULL);
+
+		if (s_gsm) {
+			format = _("GSM connection %d");
+			if (!nm_setting_gsm_get_number (s_gsm))
+				g_object_set (G_OBJECT (s_gsm), NM_SETTING_GSM_NUMBER, "*99#", NULL);
+		} else if (s_cdma) {
+			format = _("CDMA connection %d");
+			if (!nm_setting_cdma_get_number (s_cdma))
+				g_object_set (G_OBJECT (s_cdma), NM_SETTING_GSM_NUMBER, "#777", NULL);
+		} else
+			format = _("DUN connection %d");
+	} else {
+		g_set_error_literal (error,
+		                     NM_SETTING_BLUETOOTH_ERROR,
+		                     NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
+		                     "Unknown/unhandled Bluetooth connection type");
+		return FALSE;
+	}
+
+	nm_utils_complete_generic (connection,
+	                           NM_SETTING_BLUETOOTH_SETTING_NAME,
+	                           existing_connections,
+	                           format,
+	                           preferred,
+	                           is_dun ? FALSE : TRUE); /* No IPv6 yet for DUN */
+
+	setting_bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
+	if (setting_bdaddr) {
+		/* Make sure the setting BT Address (if any) matches the device's */
+		if (memcmp (setting_bdaddr->data, priv->bdaddr, ETH_ALEN)) {
+			g_set_error_literal (error,
+			                     NM_SETTING_BLUETOOTH_ERROR,
+			                     NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
+			                     NM_SETTING_BLUETOOTH_BDADDR);
+			return FALSE;
+		}
+	} else {
+		GByteArray *bdaddr;
+		const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+
+		/* Lock the connection to this device by default */
+		if (memcmp (priv->bdaddr, null_mac, ETH_ALEN)) {
+			bdaddr = g_byte_array_sized_new (ETH_ALEN);
+			g_byte_array_append (bdaddr, priv->bdaddr, ETH_ALEN);
+			g_object_set (G_OBJECT (s_bt), NM_SETTING_BLUETOOTH_BDADDR, bdaddr, NULL);
+			g_byte_array_free (bdaddr, TRUE);
+		}
+	}
+
+	return TRUE;
+}
+
+/*****************************************************************************/
+/* IP method PPP */
+
+static void
+ppp_stats (NMModem *modem,
+		   guint32 in_bytes,
+		   guint32 out_bytes,
+		   gpointer user_data)
+{
+	g_signal_emit (NM_DEVICE_BT (user_data), signals[PPP_STATS], 0, in_bytes, out_bytes);
+}
+
+static void
+ppp_failed (NMModem *modem, NMDeviceStateReason reason, gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+
+	switch (nm_device_get_state (device)) {
+	case NM_DEVICE_STATE_PREPARE:
+	case NM_DEVICE_STATE_CONFIG:
+	case NM_DEVICE_STATE_NEED_AUTH:
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+		break;
+	case NM_DEVICE_STATE_IP_CONFIG:
+	case NM_DEVICE_STATE_IP_CHECK:
+	case NM_DEVICE_STATE_SECONDARIES:
+	case NM_DEVICE_STATE_ACTIVATED:
+		if (nm_device_activate_ip4_state_in_conf (device))
+			nm_device_activate_schedule_ip4_config_timeout (device);
+		else {
+			nm_device_state_changed (device,
+			                         NM_DEVICE_STATE_FAILED,
+			                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+modem_auth_requested (NMModem *modem, gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+
+	/* Auth requests (PIN, PAP/CHAP passwords, etc) only get handled
+	 * during activation.
+	 */
+	if (!nm_device_is_activating (device))
+		return;
+
+	nm_device_state_changed (device,
+	                         NM_DEVICE_STATE_NEED_AUTH,
+	                         NM_DEVICE_STATE_REASON_NONE);
+}
+
+static void
+modem_auth_result (NMModem *modem, GError *error, gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+
+	if (error) {
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_NO_SECRETS);
+	} else {
+		/* Otherwise, on success for GSM/CDMA secrets we need to schedule modem stage1 again */
+		g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH);
+		if (!modem_stage1 (NM_DEVICE_BT (device), priv->modem, &reason))
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+	}
+}
+
+static void
+modem_prepare_result (NMModem *modem,
+                      gboolean success,
+                      NMDeviceStateReason reason,
+                      gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceState state;
+
+	state = nm_device_get_state (device);
+	g_return_if_fail (state == NM_DEVICE_STATE_CONFIG || state == NM_DEVICE_STATE_NEED_AUTH);
+
+	if (success) {
+		NMActRequest *req;
+		NMActStageReturn ret;
+		NMDeviceStateReason stage2_reason = NM_DEVICE_STATE_REASON_NONE;
+
+		req = nm_device_get_act_request (device);
+		g_assert (req);
+
+		ret = nm_modem_act_stage2_config (modem, req, &stage2_reason);
+		switch (ret) {
+		case NM_ACT_STAGE_RETURN_POSTPONE:
+			break;
+		case NM_ACT_STAGE_RETURN_SUCCESS:
+			nm_device_activate_schedule_stage3_ip_config_start (device);
+			break;
+		case NM_ACT_STAGE_RETURN_FAILURE:
+		default:
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, stage2_reason);
+			break;
+		}
+	} else {
+		if (reason == NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT) {
+			/* If the connect failed because the SIM PIN was wrong don't allow
+			 * the device to be auto-activated anymore, which would risk locking
+			 * the SIM if the incorrect PIN continues to be used.
+			 */
+			g_object_set (G_OBJECT (device), NM_DEVICE_AUTOCONNECT, FALSE, NULL);
+			nm_log_info (LOGD_MB, "(%s): disabling autoconnect due to failed SIM PIN",
+			             nm_device_get_iface (device));
+		}
+
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+	}
+}
+
+static void
+device_state_changed (NMDevice *device,
+                      NMDeviceState new_state,
+                      NMDeviceState old_state,
+                      NMDeviceStateReason reason)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+
+	if (priv->modem)
+		nm_modem_device_state_changed (priv->modem, new_state, old_state, reason);
+}
+
+static void
+modem_ip4_config_result (NMModem *self,
+                         NMIP4Config *config,
+                         GError *error,
+                         gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+
+	g_return_if_fail (nm_device_activate_ip4_state_in_conf (device) == TRUE);
+
+	if (error) {
+		nm_log_warn (LOGD_MB | LOGD_IP4 | LOGD_BT,
+		             "(%s): retrieving IP4 configuration failed: (%d) %s",
+		             nm_device_get_ip_iface (device),
+		             error ? error->code : -1,
+		             error && error->message ? error->message : "(unknown)");
+
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+	} else
+		nm_device_activate_schedule_ip4_config_result (device, config);
+}
+
+static void
+data_port_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+
+	nm_device_set_ip_iface (self, nm_modem_get_data_port (modem));
+}
+
+static gboolean
+modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *reason)
+{
+	NMActRequest *req;
+	NMActStageReturn ret;
+
+	g_return_val_if_fail (reason != NULL, FALSE);
+
+	req = nm_device_get_act_request (NM_DEVICE (self));
+	g_assert (req);
+
+	ret = nm_modem_act_stage1_prepare (modem, req, reason);
+	switch (ret) {
+	case NM_ACT_STAGE_RETURN_POSTPONE:
+	case NM_ACT_STAGE_RETURN_SUCCESS:
+		/* Success, wait for the 'prepare-result' signal */
+		return TRUE;
+	case NM_ACT_STAGE_RETURN_FAILURE:
+	default:
+		break;
+	}
+
+	return FALSE;
+}
+
+/*****************************************************************************/
+
+static void
+modem_cleanup (NMDeviceBt *self)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+
+	if (priv->modem) {
+		g_signal_handlers_disconnect_matched (priv->modem, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
+		g_clear_object (&priv->modem);
+	}
+}
+
+static void
+modem_state_cb (NMModem *modem,
+                NMModemState new_state,
+                NMModemState old_state,
+                gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceState dev_state = nm_device_get_state (device);
+
+	if (new_state <= NM_MODEM_STATE_DISABLING && old_state > NM_MODEM_STATE_DISABLING) {
+		/* Will be called whenever something external to NM disables the
+		 * modem directly through ModemManager.
+		 */
+		if (nm_device_is_activating (device) || dev_state == NM_DEVICE_STATE_ACTIVATED) {
+			nm_device_state_changed (device,
+			                         NM_DEVICE_STATE_DISCONNECTED,
+			                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
+			return;
+		}
+	}
+
+	if (new_state < NM_MODEM_STATE_CONNECTING &&
+	    old_state >= NM_MODEM_STATE_CONNECTING &&
+	    dev_state >= NM_DEVICE_STATE_NEED_AUTH &&
+	    dev_state <= NM_DEVICE_STATE_ACTIVATED) {
+		/* Fail the device if the modem disconnects unexpectedly while the
+		 * device is activating/activated. */
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER);
+		return;
+	}
+}
+
+static void
+modem_removed_cb (NMModem *modem, gpointer user_data)
+{
+	NMDeviceBt *self = NM_DEVICE_BT (user_data);
+	NMDeviceState state;
+
+	/* Fail the device if the modem was removed while active */
+	state = nm_device_get_state (NM_DEVICE (self));
+	if (   state == NM_DEVICE_STATE_ACTIVATED
+	    || nm_device_is_activating (NM_DEVICE (self))) {
+		nm_device_state_changed (NM_DEVICE (self),
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_BT_FAILED);
+	} else
+		modem_cleanup (self);
+}
+
+static gboolean
+component_added (NMDevice *device, GObject *component)
+{
+	NMDeviceBt *self = NM_DEVICE_BT (device);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	NMModem *modem;
+	const gchar *modem_data_port;
+	const gchar *modem_control_port;
+	char *base;
+	NMDeviceState state;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+
+	if (!NM_IS_MODEM (component))
+		return FALSE;
+	modem = NM_MODEM (component);
+
+	modem_data_port = nm_modem_get_data_port (modem);
+	modem_control_port = nm_modem_get_control_port (modem);
+	g_return_val_if_fail (modem_data_port != NULL || modem_control_port != NULL, FALSE);
+
+	if (!priv->rfcomm_iface)
+		return FALSE;
+
+	base = g_path_get_basename (priv->rfcomm_iface);
+	if (g_strcmp0 (base, modem_data_port) && g_strcmp0 (base, modem_control_port)) {
+		g_free (base);
+		return FALSE;
+	}
+	g_free (base);
+
+	/* Got the modem */
+	if (priv->timeout_id) {
+		g_source_remove (priv->timeout_id);
+		priv->timeout_id = 0;
+	}
+
+	/* Can only accept the modem in stage2, but since the interface matched
+	 * what we were expecting, don't let anything else claim the modem either.
+	 */
+	state = nm_device_get_state (NM_DEVICE (self));
+	if (state != NM_DEVICE_STATE_CONFIG) {
+		nm_log_warn (LOGD_BT | LOGD_MB,
+		             "(%s): modem found but device not in correct state (%d)",
+		             nm_device_get_iface (NM_DEVICE (self)),
+		             nm_device_get_state (NM_DEVICE (self)));
+		return TRUE;
+	}
+
+	nm_log_info (LOGD_BT | LOGD_MB,
+	             "Activation (%s/bluetooth) Stage 2 of 5 (Device Configure) modem found.",
+	             nm_device_get_iface (NM_DEVICE (self)));
+
+	if (priv->modem) {
+		g_warn_if_reached ();
+		modem_cleanup (self);
+	}
+
+	priv->modem = g_object_ref (modem);
+	g_signal_connect (modem, NM_MODEM_PPP_STATS, G_CALLBACK (ppp_stats), self);
+	g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self);
+	g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self);
+	g_signal_connect (modem, NM_MODEM_IP4_CONFIG_RESULT, G_CALLBACK (modem_ip4_config_result), self);
+	g_signal_connect (modem, NM_MODEM_AUTH_REQUESTED, G_CALLBACK (modem_auth_requested), self);
+	g_signal_connect (modem, NM_MODEM_AUTH_RESULT, G_CALLBACK (modem_auth_result), self);
+	g_signal_connect (modem, NM_MODEM_STATE_CHANGED, G_CALLBACK (modem_state_cb), self);
+	g_signal_connect (modem, NM_MODEM_REMOVED, G_CALLBACK (modem_removed_cb), self);
+
+	/* In the old ModemManager the data port is known from the very beginning;
+	 * while in the new ModemManager the data port is set afterwards when the bearer gets
+	 * created */
+	if (modem_data_port)
+		nm_device_set_ip_iface (NM_DEVICE (self), modem_data_port);
+	g_signal_connect (modem, "notify::" NM_MODEM_DATA_PORT, G_CALLBACK (data_port_changed_cb), self);
+
+	/* Kick off the modem connection */
+	if (!modem_stage1 (self, modem, &reason))
+		nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, reason);
+
+	return TRUE;
+}
+
+static gboolean
+modem_find_timeout (gpointer user_data)
+{
+	NMDeviceBt *self = NM_DEVICE_BT (user_data);
+
+	NM_DEVICE_BT_GET_PRIVATE (self)->timeout_id = 0;
+	nm_device_state_changed (NM_DEVICE (self),
+	                         NM_DEVICE_STATE_FAILED,
+	                         NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND);
+	return FALSE;
+}
+
+static void
+check_connect_continue (NMDeviceBt *self)
+{
+	NMDevice *device = NM_DEVICE (self);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	gboolean pan = (priv->bt_type == NM_BT_CAPABILITY_NAP);
+	gboolean dun = (priv->bt_type == NM_BT_CAPABILITY_DUN);
+
+	if (!priv->connected || !priv->have_iface)
+		return;
+
+	nm_log_info (LOGD_BT, "Activation (%s %s/bluetooth) Stage 2 of 5 (Device Configure) "
+	             "successful.  Will connect via %s.",
+	             nm_device_get_iface (device),
+	             nm_device_get_ip_iface (device),
+	             dun ? "DUN" : (pan ? "PAN" : "unknown"));
+
+	/* Kill the connect timeout since we're connected now */
+	if (priv->timeout_id) {
+		g_source_remove (priv->timeout_id);
+		priv->timeout_id = 0;
+	}
+
+	if (pan) {
+		/* Bluez says we're connected now.  Start IP config. */
+		nm_device_activate_schedule_stage3_ip_config_start (device);
+	} else if (dun) {
+		/* Wait for ModemManager to find the modem */
+		priv->timeout_id = g_timeout_add_seconds (30, modem_find_timeout, self);
+
+		nm_log_info (LOGD_BT | LOGD_MB, "Activation (%s/bluetooth) Stage 2 of 5 (Device Configure) "
+		             "waiting for modem to appear.",
+		             nm_device_get_iface (device));
+	} else
+		g_assert_not_reached ();
+}
+
+static void
+bluez_connect_cb (GObject *object,
+                  GAsyncResult *res,
+                  void *user_data)
+{
+	NMDeviceBt *self = NM_DEVICE_BT (user_data);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	GError *error = NULL;
+	const char *device;
+
+	device = nm_bluez_device_connect_finish (NM_BLUEZ_DEVICE (object),
+	                                         res, &error);
+
+	if (!device) {
+		nm_log_warn (LOGD_BT, "Error connecting with bluez: %s",
+		             error && error->message ? error->message : "(unknown)");
+		g_clear_error (&error);
+
+		nm_device_state_changed (NM_DEVICE (self),
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_BT_FAILED);
+		return;
+	}
+
+	if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
+		g_free (priv->rfcomm_iface);
+		priv->rfcomm_iface = g_strdup (device);
+	} else if (priv->bt_type == NM_BT_CAPABILITY_NAP) {
+		nm_device_set_ip_iface (NM_DEVICE (self), device);
+	}
+
+	nm_log_dbg (LOGD_BT, "(%s): connect request successful",
+	            nm_device_get_iface (NM_DEVICE (self)));
+
+	/* Stage 3 gets scheduled when Bluez says we're connected */
+	priv->have_iface = TRUE;
+	check_connect_continue (self);
+}
+
+static void
+bluez_connected_changed (NMBluezDevice *bt_device,
+                         GParamSpec *pspec,
+                         NMDevice *device)
+{
+	NMDeviceBt *self = NM_DEVICE_BT (device);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	gboolean connected;
+	NMDeviceState state;
+
+	state = nm_device_get_state (device);
+	connected = nm_bluez_device_get_connected (bt_device);
+	if (connected) {
+		if (state == NM_DEVICE_STATE_CONFIG) {
+			nm_log_dbg (LOGD_BT, "(%s): connected to the device",
+			            nm_device_get_iface (device));
+
+			priv->connected = TRUE;
+			check_connect_continue (self);
+		}
+	} else {
+		gboolean fail = FALSE;
+
+		/* Bluez says we're disconnected from the device.  Suck. */
+
+		if (nm_device_is_activating (device)) {
+			nm_log_info (LOGD_BT,
+			             "Activation (%s/bluetooth): bluetooth link disconnected.",
+			             nm_device_get_iface (device));
+			fail = TRUE;
+		} else if (state == NM_DEVICE_STATE_ACTIVATED) {
+			nm_log_info (LOGD_BT, "(%s): bluetooth link disconnected.",
+			             nm_device_get_iface (device));
+			fail = TRUE;
+		}
+
+		if (fail) {
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CARRIER);
+			priv->connected = FALSE;
+		}
+	}
+}
+
+static gboolean
+bt_connect_timeout (gpointer user_data)
+{
+	NMDeviceBt *self = NM_DEVICE_BT (user_data);
+
+	nm_log_dbg (LOGD_BT, "(%s): initial connection timed out",
+	            nm_device_get_iface (NM_DEVICE (self)));
+
+	NM_DEVICE_BT_GET_PRIVATE (self)->timeout_id = 0;
+	nm_device_state_changed (NM_DEVICE (self),
+	                         NM_DEVICE_STATE_FAILED,
+	                         NM_DEVICE_STATE_REASON_BT_FAILED);
+	return FALSE;
+}
+
+static NMActStageReturn
+act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+	NMConnection *connection;
+
+	connection = nm_device_get_connection (device);
+	g_assert (connection);
+	priv->bt_type = get_connection_bt_type (connection);
+	if (priv->bt_type == NM_BT_CAPABILITY_NONE) {
+		// FIXME: set a reason code
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	if (priv->bt_type == NM_BT_CAPABILITY_DUN && !priv->mm_running) {
+		*reason = NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE;
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	nm_log_dbg (LOGD_BT, "(%s): requesting connection to the device",
+	            nm_device_get_iface (device));
+
+	/* Connect to the BT device */
+	nm_bluez_device_connect_async (priv->bt_device,
+	                               priv->bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP),
+	                               bluez_connect_cb, device);
+
+	if (priv->timeout_id)
+		g_source_remove (priv->timeout_id);
+	priv->timeout_id = g_timeout_add_seconds (30, bt_connect_timeout, device);
+
+	return NM_ACT_STAGE_RETURN_POSTPONE;
+}
+
+static NMActStageReturn
+act_stage3_ip4_config_start (NMDevice *device,
+                             NMIP4Config **out_config,
+                             NMDeviceStateReason *reason)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+	NMActStageReturn ret;
+
+	if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
+		ret = nm_modem_stage3_ip4_config_start (NM_DEVICE_BT_GET_PRIVATE (device)->modem,
+		                                        device,
+		                                        NM_DEVICE_CLASS (nm_device_bt_parent_class),
+		                                        reason);
+	} else
+		ret = NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage3_ip4_config_start (device, out_config, reason);
+
+	return ret;
+}
+
+static NMActStageReturn
+act_stage3_ip6_config_start (NMDevice *device,
+                             NMIP6Config **out_config,
+                             NMDeviceStateReason *reason)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+	NMActStageReturn ret;
+
+	if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
+		ret = nm_modem_stage3_ip6_config_start (NM_DEVICE_BT_GET_PRIVATE (device)->modem,
+		                                        device,
+		                                        NM_DEVICE_CLASS (nm_device_bt_parent_class),
+		                                        reason);
+	} else
+		ret = NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage3_ip6_config_start (device, out_config, reason);
+
+	return ret;
+}
+
+static void
+deactivate (NMDevice *device)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+
+	priv->have_iface = FALSE;
+	priv->connected = FALSE;
+
+	if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
+		if (priv->modem) {
+			nm_modem_deactivate (priv->modem, device);
+
+			/* Since we're killing the Modem object before it'll get the
+			 * state change signal, simulate the state change here.
+			 */
+			nm_modem_device_state_changed (priv->modem,
+			                               NM_DEVICE_STATE_DISCONNECTED,
+			                               NM_DEVICE_STATE_ACTIVATED,
+			                               NM_DEVICE_STATE_REASON_USER_REQUESTED);
+			modem_cleanup (NM_DEVICE_BT (device));
+		}
+	}
+
+	if (priv->bt_type != NM_BT_CAPABILITY_NONE)
+		nm_bluez_device_disconnect (priv->bt_device);
+
+	if (priv->timeout_id) {
+		g_source_remove (priv->timeout_id);
+		priv->timeout_id = 0;
+	}
+
+	priv->bt_type = NM_BT_CAPABILITY_NONE;
+
+	g_free (priv->rfcomm_iface);
+	priv->rfcomm_iface = NULL;
+
+	if (NM_DEVICE_CLASS (nm_device_bt_parent_class)->deactivate)
+		NM_DEVICE_CLASS (nm_device_bt_parent_class)->deactivate (device);
+}
+
+static void
+bluez_device_removed (NMBluezDevice *bdev, gpointer user_data)
+{
+	g_signal_emit_by_name (NM_DEVICE_BT (user_data), NM_DEVICE_REMOVED);
+}
+
+/*****************************************************************************/
+
+static gboolean
+is_available (NMDevice *dev)
+{
+	NMDeviceBt *self = NM_DEVICE_BT (dev);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+
+	/* PAN doesn't need ModemManager, so devices that support it are always available */
+	if (priv->capabilities & NM_BT_CAPABILITY_NAP)
+		return TRUE;
+
+	/* DUN requires ModemManager */
+	return priv->mm_running;
+}
+
+static void
+handle_availability_change (NMDeviceBt *self,
+                            gboolean old_available,
+                            NMDeviceStateReason unavailable_reason)
+{
+	NMDevice *device = NM_DEVICE (self);
+	NMDeviceState state;
+	gboolean available;
+
+	state = nm_device_get_state (device);
+	if (state < NM_DEVICE_STATE_UNAVAILABLE) {
+		nm_log_dbg (LOGD_BT, "(%s): availability blocked by UNMANAGED state",
+		            nm_device_get_iface (device));
+		return;
+	}
+
+	available = nm_device_is_available (device);
+	if (available == old_available)
+		return;
+
+	if (available) {
+		if (state != NM_DEVICE_STATE_UNAVAILABLE)
+			nm_log_warn (LOGD_CORE | LOGD_BT, "not in expected unavailable state!");
+
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_DISCONNECTED,
+		                         NM_DEVICE_STATE_REASON_NONE);
+	} else {
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_UNAVAILABLE,
+		                         unavailable_reason);
+	}
+}
+
+static void
+set_mm_running (NMDeviceBt *self, gboolean running)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	gboolean old_available;
+
+	if (priv->mm_running == running)
+		return;
+
+	nm_log_dbg (LOGD_BT, "(%s): ModemManager now %s",
+	            nm_device_get_iface (NM_DEVICE (self)),
+	            running ? "available" : "unavailable");
+
+	old_available = nm_device_is_available (NM_DEVICE (self));
+	priv->mm_running = running;
+	handle_availability_change (self, old_available, NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE);
+
+	/* Need to recheck available connections whenever MM appears or disappears,
+	 * since the device could be both DUN and NAP capable and thus may not
+	 * change state (which rechecks available connections) when MM comes and goes.
+	 */
+	if (priv->capabilities & NM_BT_CAPABILITY_DUN)
+	    nm_device_recheck_available_connections (NM_DEVICE (self));
+}
+
+static void
+mm_name_owner_changed (NMDBusManager *dbus_mgr,
+                       const char *name,
+                       const char *old_owner,
+                       const char *new_owner,
+                       NMDeviceBt *self)
+{
+	gboolean old_owner_good;
+	gboolean new_owner_good;
+
+	/* Can't handle the signal if its not from the modem service */
+	if (   strcmp (MM_OLD_DBUS_SERVICE, name) != 0
+#if WITH_MODEM_MANAGER_1
+	    && strcmp (MM_NEW_DBUS_SERVICE, name) != 0
+#endif
+	    )
+		return;
+
+	old_owner_good = (old_owner && strlen (old_owner));
+	new_owner_good = (new_owner && strlen (new_owner));
+
+	if (!old_owner_good && new_owner_good)
+		set_mm_running (self, TRUE);
+	else if (old_owner_good && !new_owner_good)
+		set_mm_running (self, FALSE);
+}
+
+/*****************************************************************************/
+
+NMDevice *
+nm_device_bt_new (NMBluezDevice *bt_device,
+                  const char *udi,
+                  const char *bdaddr,
+                  const char *name,
+                  guint32 capabilities)
+{
+	g_return_val_if_fail (udi != NULL, NULL);
+	g_return_val_if_fail (bdaddr != NULL, NULL);
+	g_return_val_if_fail (name != NULL, NULL);
+	g_return_val_if_fail (capabilities != NM_BT_CAPABILITY_NONE, NULL);
+	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (bt_device), NULL);
+
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BT,
+	                                  NM_DEVICE_UDI, udi,
+	                                  NM_DEVICE_IFACE, bdaddr,
+	                                  NM_DEVICE_DRIVER, "bluez",
+	                                  NM_DEVICE_HW_ADDRESS, bdaddr,
+	                                  NM_DEVICE_BT_DEVICE, bt_device,
+	                                  NM_DEVICE_BT_NAME, name,
+	                                  NM_DEVICE_BT_CAPABILITIES, capabilities,
+	                                  NM_DEVICE_TYPE_DESC, "Bluetooth",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BT,
+	                                  NULL);
+}
+
+static void
+nm_device_bt_init (NMDeviceBt *self)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	gboolean mm_running;
+
+	priv->dbus_mgr = nm_dbus_manager_get ();
+
+	priv->mm_watch_id = g_signal_connect (priv->dbus_mgr,
+	                                      NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
+	                                      G_CALLBACK (mm_name_owner_changed),
+	                                      self);
+
+	/* Initial check to see if ModemManager is running */
+	mm_running = nm_dbus_manager_name_has_owner (priv->dbus_mgr, MM_OLD_DBUS_SERVICE);
+#if WITH_MODEM_MANAGER_1
+	if (!mm_running)
+		mm_running = nm_dbus_manager_name_has_owner (priv->dbus_mgr, MM_NEW_DBUS_SERVICE);
+#endif
+	set_mm_running (self, mm_running);
+}
+
+static void
+constructed (GObject *object)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object);
+	const guint8 *my_hwaddr;
+	guint my_hwaddr_len = 0;
+
+	G_OBJECT_CLASS (nm_device_bt_parent_class)->constructed (object);
+
+	my_hwaddr = nm_device_get_hw_address (NM_DEVICE (object), &my_hwaddr_len);
+	g_assert (my_hwaddr);
+	g_assert_cmpint (my_hwaddr_len, ==, ETH_ALEN);
+	memcpy (priv->bdaddr, my_hwaddr, ETH_ALEN);
+
+	/* Watch for BT device property changes */
+	g_signal_connect (priv->bt_device, "notify::" NM_BLUEZ_DEVICE_CONNECTED,
+	                  G_CALLBACK (bluez_connected_changed),
+	                  object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_BT_NAME:
+		/* Construct only */
+		priv->name = g_value_dup_string (value);
+		break;
+	case PROP_BT_CAPABILITIES:
+		/* Construct only */
+		priv->capabilities = g_value_get_uint (value);
+		break;
+	case PROP_BT_DEVICE:
+		/* Construct only */
+		priv->bt_device = g_value_dup_object (value);
+		g_signal_connect (priv->bt_device, "removed", G_CALLBACK (bluez_device_removed), object);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_BT_NAME:
+		g_value_set_string (value, priv->name);
+		break;
+	case PROP_BT_CAPABILITIES:
+		g_value_set_uint (value, priv->capabilities);
+		break;
+	case PROP_BT_DEVICE:
+		g_value_set_object (value, priv->bt_device);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+dispose (GObject *object)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object);
+
+	if (priv->timeout_id) {
+		g_source_remove (priv->timeout_id);
+		priv->timeout_id = 0;
+	}
+
+	g_signal_handlers_disconnect_matched (priv->bt_device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object);
+
+	if (priv->dbus_mgr && priv->mm_watch_id) {
+		g_signal_handler_disconnect (priv->dbus_mgr, priv->mm_watch_id);
+		priv->mm_watch_id = 0;
+	}
+	priv->dbus_mgr = NULL;
+
+	modem_cleanup (NM_DEVICE_BT (object));
+	g_clear_object (&priv->bt_device);
+
+	G_OBJECT_CLASS (nm_device_bt_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object);
+
+	g_free (priv->rfcomm_iface);
+	g_free (priv->name);
+
+	G_OBJECT_CLASS (nm_device_bt_parent_class)->finalize (object);
+}
+
+static void
+nm_device_bt_class_init (NMDeviceBtClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (NMDeviceBtPrivate));
+
+	object_class->constructed = constructed;
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+	object_class->dispose = dispose;
+	object_class->finalize = finalize;
+
+	device_class->get_hw_address_length = get_hw_address_length;
+	device_class->can_auto_connect = can_auto_connect;
+	device_class->deactivate = deactivate;
+	device_class->act_stage2_config = act_stage2_config;
+	device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
+	device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
+	device_class->check_connection_compatible = check_connection_compatible;
+	device_class->check_connection_available = check_connection_available;
+	device_class->complete_connection = complete_connection;
+	device_class->is_available = is_available;
+	device_class->component_added = component_added;
+
+	device_class->state_changed = device_state_changed;
+
+	/* Properties */
+	g_object_class_install_property
+		(object_class, PROP_BT_NAME,
+		 g_param_spec_string (NM_DEVICE_BT_NAME,
+		                      "Bluetooth device name",
+		                      "Bluetooth device name",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_BT_CAPABILITIES,
+		 g_param_spec_uint (NM_DEVICE_BT_CAPABILITIES,
+		                    "Bluetooth device capabilities",
+		                    "Bluetooth device capabilities",
+		                    NM_BT_CAPABILITY_NONE, G_MAXUINT, NM_BT_CAPABILITY_NONE,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_BT_DEVICE,
+		 g_param_spec_object (NM_DEVICE_BT_DEVICE,
+		                      "NMBluezDevice object for the Device",
+		                      "NMBluezDevice object for the Device",
+		                      NM_TYPE_BLUEZ_DEVICE,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/* Signals */
+	signals[PPP_STATS] =
+		g_signal_new ("ppp-stats",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMDeviceBtClass, ppp_stats),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 2,
+		              G_TYPE_UINT, G_TYPE_UINT);
+
+	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
+	                                        G_TYPE_FROM_CLASS (klass),
+	                                        &dbus_glib_nm_device_bt_object_info);
+
+	dbus_g_error_domain_register (NM_BT_ERROR, NULL, NM_TYPE_BT_ERROR);
+}
diff --git a/src/devices/bluetooth/nm-device-bt.h b/src/devices/bluetooth/nm-device-bt.h
new file mode 100644
index 00000000..83732bc0
--- /dev/null
+++ b/src/devices/bluetooth/nm-device-bt.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_BT_H
+#define NM_DEVICE_BT_H
+
+#include <nm-device.h>
+#include "nm-bluez-device.h"
+#include "nm-modem.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_BT		(nm_device_bt_get_type ())
+#define NM_DEVICE_BT(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_BT, NMDeviceBt))
+#define NM_DEVICE_BT_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_BT, NMDeviceBtClass))
+#define NM_IS_DEVICE_BT(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_BT))
+#define NM_IS_DEVICE_BT_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_BT))
+#define NM_DEVICE_BT_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_BT, NMDeviceBtClass))
+
+typedef enum {
+	NM_BT_ERROR_CONNECTION_NOT_BT = 0,   /*< nick=ConnectionNotBt >*/
+	NM_BT_ERROR_CONNECTION_INVALID,      /*< nick=ConnectionInvalid >*/
+	NM_BT_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/
+} NMBtError;
+
+#define NM_DEVICE_BT_NAME         "name"
+#define NM_DEVICE_BT_CAPABILITIES "bt-capabilities"
+#define NM_DEVICE_BT_DEVICE       "bt-device"
+
+typedef struct {
+	NMDevice parent;
+} NMDeviceBt;
+
+typedef struct {
+	NMDeviceClass parent;
+
+	/* Signals */
+	void (*ppp_stats) (NMDeviceBt *device, guint32 in_bytes, guint32 out_bytes);
+} NMDeviceBtClass;
+
+GType nm_device_bt_get_type (void);
+
+NMDevice *nm_device_bt_new (NMBluezDevice *bt_device,
+                            const char *udi,
+                            const char *bdaddr,
+                            const char *name,
+                            guint32 capabilities);
+
+guint32 nm_device_bt_get_capabilities (NMDeviceBt *device);
+
+gboolean nm_device_bt_modem_added (NMDeviceBt *device,
+                                   NMModem *modem,
+                                   const char *driver);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_BT_H */