summaryrefslogtreecommitdiff
path: root/src/devices/bluetooth
diff options
context:
space:
mode:
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 000000000..639a1ad7a
--- /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 000000000..3145c52da
--- /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 000000000..d2c451244
--- /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 000000000..f80cfc2e3
--- /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 000000000..4c448a376
--- /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 000000000..0bf7d898b
--- /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 000000000..04ffb0a47
--- /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 000000000..68d6dbe5e
--- /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 000000000..ad1786f02
--- /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 000000000..454ca557e
--- /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 000000000..2660cbd92
--- /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 000000000..19b1c65a1
--- /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 000000000..63006b3ab
--- /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 000000000..79f347bce
--- /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 000000000..2595caeb7
--- /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 000000000..af887929b
--- /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 000000000..b6f851b25
--- /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 000000000..0bd2f17d1
--- /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 000000000..83732bc09
--- /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 */