CHROMIUM: Merge latest BlueZ upstream version 5.35+

Merged all changes from the latest BlueZ upstream repo up to change:
"monitor: Add support for showing index information updates" after the
5.35 release.

BUG=chromium:539282
diff --git a/ChangeLog b/ChangeLog
index fbd407a..1ab978d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+ver 5.35:
+	Fix issue with connected devices after discovery.
+	Fix issue with profile support and LTK loading.
+	Fix issue with AVRCP events for volume control.
+	Fix issue with OBEX session owner handling.
+	Fix issue with HID over GATT setup failures.
+	Fix issue with GATT notification registration.
+	Fix issue with GATT cache validation feature.
+	Add support for persistent GATT database.
+	Add support for controller enabling option.
+
+ver 5.34:
+	Fix issue with GATT profiles and auto-connect.
+	Fix issue with missing GoepL2CapPsm SDP data.
+	Fix issue with suspending AVDTP endpoints.
+	Fix issue with audio service state on disconnect.
+	Add support for AVRCP Set Addressed Player feature.
+	Add support for AVRCP Get Folder Items feature.
+	Add support for Android 5.1 HFP WBS callbacks.
+
 ver 5.33:
 	Fix issue with memory leak in GATT database.
 	Fix issue with AVDTP set configuration handling.
diff --git a/Makefile.am b/Makefile.am
index ad4a9ae..d74187a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -80,7 +80,7 @@
 lib_LTLIBRARIES += lib/libbluetooth.la
 
 lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
-lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:6:18
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:8:18
 lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
 endif
 
@@ -257,6 +257,9 @@
 
 EXTRA_DIST += doc/obex-api.txt doc/obex-agent-api.txt
 
+EXTRA_DIST += doc/pics-opp.txt doc/pixit-opp.txt \
+		doc/pts-opp.txt
+
 EXTRA_DIST += tools/magic.btsnoop
 
 AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@
@@ -425,6 +428,12 @@
 AM_TESTS_ENVIRONMENT += dbus-run-session --
 endif
 
+if VALGRIND
+LOG_COMPILER = valgrind --error-exitcode=1 --num-callers=30
+LOG_FLAGS = --trace-children=yes --leak-check=full --show-reachable=no \
+		--suppressions=$(srcdir)/tools/valgrind.supp --quiet
+endif
+
 pkgconfigdir = $(libdir)/pkgconfig
 
 if LIBRARY
diff --git a/Makefile.tools b/Makefile.tools
index af5853e..c313813 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -412,4 +412,5 @@
 		test/test-heartrate test/test-alert test/test-hfp \
 		test/test-cyclingspeed test/opp-client test/ftp-client \
 		test/pbap-client test/map-client test/example-advertisement \
-		test/example-gatt-server test/example-gatt-client
+		test/example-gatt-server test/example-gatt-client \
+		test/test-gatt-profile
diff --git a/TODO b/TODO
index 2945392..8c710ef 100644
--- a/TODO
+++ b/TODO
@@ -61,6 +61,12 @@
   Priority: Low
   Complexity: C2
 
+- Add queueing support for src/agent.c, currently if there is any request
+  pending the code fail with error EBUSY which is very inconvenient.
+
+  Priority: Low
+  Complexity: C2
+
 Low Energy
 ==========
 
diff --git a/android/avdtp.c b/android/avdtp.c
index 7e61280..908c0ca 100644
--- a/android/avdtp.c
+++ b/android/avdtp.c
@@ -1567,7 +1567,7 @@
 	for (i = 0; i < seid_count; i++, seid++) {
 		failed_seid = seid->seid;
 
-		sep = find_local_sep_by_seid(session, req->first_seid.seid);
+		sep = find_local_sep_by_seid(session, seid->seid);
 		if (!sep || !sep->stream) {
 			err = AVDTP_BAD_ACP_SEID;
 			goto failed;
@@ -1678,7 +1678,7 @@
 	for (i = 0; i < seid_count; i++, seid++) {
 		failed_seid = seid->seid;
 
-		sep = find_local_sep_by_seid(session, req->first_seid.seid);
+		sep = find_local_sep_by_seid(session, seid->seid);
 		if (!sep || !sep->stream) {
 			err = AVDTP_BAD_ACP_SEID;
 			goto failed;
diff --git a/android/bluetoothd-snoop.c b/android/bluetoothd-snoop.c
index 035875a..75f58cc 100644
--- a/android/bluetoothd-snoop.c
+++ b/android/bluetoothd-snoop.c
@@ -73,6 +73,9 @@
 	case BTSNOOP_OPCODE_SCO_TX_PKT:
 	case BTSNOOP_OPCODE_SCO_RX_PKT:
 		break;
+	case BTSNOOP_OPCODE_OPEN_INDEX:
+	case BTSNOOP_OPCODE_CLOSE_INDEX:
+		break;
 	}
 
 	return 0xff;
diff --git a/android/hal-handsfree.c b/android/hal-handsfree.c
index 279b26a..3847901 100644
--- a/android/hal-handsfree.c
+++ b/android/hal-handsfree.c
@@ -153,6 +153,16 @@
 #endif
 }
 
+static void handle_wbs(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_handsfree_wbs *ev = buf;
+
+	if (cbs->wbs_cb)
+		cbs->wbs_cb(ev->wbs, (bt_bdaddr_t *) (ev->bdaddr));
+#endif
+}
+
 static void handle_chld(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_handsfree_chld *ev = buf;
@@ -289,6 +299,8 @@
 	/* HAL_EV_HANDSFREE_HSP_KEY_PRESS */
 	{ handle_hsp_key_press, false,
 				sizeof(struct hal_ev_handsfree_hsp_key_press) },
+	/* HAL_EV_HANDSFREE_WBS */
+	{ handle_wbs, false, sizeof(struct hal_ev_handsfree_wbs) },
 };
 
 static uint8_t get_mode(void)
diff --git a/android/hal-ipc-api.txt b/android/hal-ipc-api.txt
index 503b47a..e3b7798 100644
--- a/android/hal-ipc-api.txt
+++ b/android/hal-ipc-api.txt
@@ -1068,6 +1068,14 @@
 
 		Notification parameters: Remote address (6 octets)
 
+	Opcode 0x91 - WBS Command notification
+
+		Notification parameters: WBS types (1 octet)
+		                         Remote address (6 octets)
+
+		Valid WBS types: 0x00 = None
+		                 0x01 = No
+		                 0x02 = Yes
 
 Bluetooth Advanced Audio HAL (ID 6)
 Bluetooth Advanced Audio Sink HAL (ID 13)
diff --git a/android/hal-msg.h b/android/hal-msg.h
index 698f45a..ea79fa7 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -1638,6 +1638,12 @@
 	uint8_t bdaddr[6];
 } __attribute__((packed));
 
+#define HAL_EV_HANDSFREE_WBS		0x91
+struct hal_ev_handsfree_wbs {
+	uint8_t wbs;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
 #define HAL_AVRCP_FEATURE_NONE			0x00
 #define HAL_AVRCP_FEATURE_METADATA		0x01
 #define HAL_AVRCP_FEATURE_ABSOLUTE_VOLUME	0x02
diff --git a/android/handsfree.c b/android/handsfree.c
index f297926..2f8a867 100644
--- a/android/handsfree.c
+++ b/android/handsfree.c
@@ -1067,6 +1067,7 @@
 								void *user_data)
 {
 	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_wbs ev;
 	unsigned int val;
 
 	DBG("");
@@ -1085,6 +1086,12 @@
 			break;
 		}
 
+		ev.wbs = val;
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_WBS, sizeof(ev), &ev);
+
 		dev->proposed_codec = 0;
 		dev->negotiated_codec = val;
 
diff --git a/configure.ac b/configure.ac
index 8ea503c..83a0776 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(bluez, 5.33)
+AC_INIT(bluez, 5.35)
 
 AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
 					tar-pax no-dist-gzip dist-xz])
@@ -35,9 +35,12 @@
 if (test "$USE_MAINTAINER_MODE" = "yes"); then
 	AC_CHECK_PROG(enable_coverage, [lcov], [yes], [no])
 	AC_CHECK_PROG(enable_dbus_run_session, [dbus-run-session], [yes])
+	AC_CHECK_PROG(enable_valgrind, [valgrind], [yes])
+	AC_CHECK_HEADERS(valgrind/memcheck.h)
 fi
 AM_CONDITIONAL(COVERAGE, test "${enable_coverage}" = "yes")
 AM_CONDITIONAL(DBUS_RUN_SESSION, test "${enable_dbus_run_session}" = "yes")
+AM_CONDITIONAL(VALGRIND, test "${enable_valgrind}" = "yes")
 
 MISC_FLAGS
 
diff --git a/doc/media-api.txt b/doc/media-api.txt
index b17a151..b5ad2db 100644
--- a/doc/media-api.txt
+++ b/doc/media-api.txt
@@ -71,43 +71,43 @@
 =======================
 
 Service		org.bluez
-Interface	org.bluez.MediaControl1 [Deprecated]
+Interface	org.bluez.MediaControl1
 Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
 
-Methods		void Play()
+Methods		void Play() [Deprecated]
 
 			Resume playback.
 
-		void Pause()
+		void Pause() [Deprecated]
 
 			Pause playback.
 
-		void Stop()
+		void Stop() [Deprecated]
 
 			Stop playback.
 
-		void Next()
+		void Next() [Deprecated]
 
 			Next item.
 
-		void Previous()
+		void Previous() [Deprecated]
 
 			Previous item.
 
-		void VolumeUp()
+		void VolumeUp() [Deprecated]
 
 			Adjust remote volume one step up
 
-		void VolumeDown()
+		void VolumeDown() [Deprecated]
 
 			Adjust remote volume one step down
 
-		void FastForward()
+		void FastForward() [Deprecated]
 
 			Fast forward playback, this action is only stopped
 			when another method in this interface is called.
 
-		void Rewind()
+		void Rewind() [Deprecated]
 
 			Rewind playback, this action is only stopped
 			when another method in this interface is called.
@@ -116,6 +116,10 @@
 
 		boolean Connected [readonly]
 
+		object Player [readonly, optional]
+
+			Addressed Player object path.
+
 
 MediaPlayer1 hierarchy
 ======================
diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt
index 1ae9835..c89ab42 100644
--- a/doc/mgmt-api.txt
+++ b/doc/mgmt-api.txt
@@ -24,6 +24,7 @@
 Linux kernel v3.17	Version 1.7
 Linux kernel v3.19	Version 1.8
 Linux kernel v4.1	Version 1.9
+Linux kernel v4.2	Version 1.10
 
 Version 1.1 introduces Set Device ID command.
 
diff --git a/doc/pics-opp.txt b/doc/pics-opp.txt
new file mode 100644
index 0000000..fb4746d
--- /dev/null
+++ b/doc/pics-opp.txt
@@ -0,0 +1,187 @@
+OPP PICS for the PTS tool.
+
+PTS version: 6.2.0
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_1_1	True (*)	Role: Object Push Client (C.1)
+TSPC_OPP_1_2	True (*)	Role: Object Push Server (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+		Client Profile Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_1b_1	False		Client supports OPP version 1.1. (C.1)
+TSPC_OPP_1b_2	True (*)	Client supports OPP version 1.2. (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+		Client Application Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_2_1	True		Client: Perform Service Discovery request (M)
+TSPC_OPP_2_2	True		Client: Authentication/PIN exchange supported.
+					(M)
+TSPC_OPP_2_2a	True (*)	Client: Require Authentication/PIN by default.
+					(O)
+TSPC_OPP_2_3	True		Client: Object Push (M)
+TSPC_OPP_2_4	True (*)	Client: vCard 2.1 (C.3)
+TSPC_OPP_2_5	False		Client: vCalender 1.0 (O)
+TSPC_OPP_2_6	False		Client: vMsg as defined in IrMC 1.1 (O)
+TSPC_OPP_2_7	False		Client: vNote as defined in IrMC 1.1 (O)
+TSPC_OPP_2_8	True (*)	Client: Support content formats other than those
+					declared in TSPC_OPP_2_4 through
+					TSPC_OPP_2_7. (O)
+TSPC_OPP_2_8a	False		Client: Support specific set of other content
+					formats. (C.4)
+TSPC_OPP_2_8b	True (*)	Client: Support all content formats. (C.4)
+TSPC_OPP_2_9	True (*)	Client: Push multiple vCard objects. (O)
+TSPC_OPP_2_9a	True (*)	Client: Push multiple vCard objects using
+					different PUT operations. (C.5)
+TSPC_OPP_2_9b	False		Client: Push multiple vCard objects using the
+					same PUT operation. (C.5)
+TSPC_OPP_2_10	True (*)	Client: Push multiple vCalender objects. (O)
+TSPC_OPP_2_10a	True		Client: Push multiple vCalendar objects using
+					different PUT operations. (C.6)
+TSPC_OPP_2_10b	False		Client: Push multiple vCalendar objects using
+					the same PUT operation. (C.6)
+TSPC_OPP_2_11	True (*)	Client: Push multiple vMsg objects. (O)
+TSPC_OPP_2_11a	True (*)	Client: Push multiple vMsg objects using
+					different PUT operations. (C.7)
+TSPC_OPP_2_11b	False		Client: Push multiple vMsg objects using the
+					same PUT operation. (C.7)
+TSPC_OPP_2_12	True (*)	Client: Push multiple vNote objects. (O)
+TSPC_OPP_2_12a	True (*)	Client: Push multiple vNote objects using
+					different PUT operations. (C.8)
+TSPC_OPP_2_12b	False		Client: Push multiple vNote objects using the
+					same PUT operation. (C.8)
+TSPC_OPP_2_13	True (*)	Client: Pull business card (O)
+TSPC_OPP_2_14	True (*)	Client: vCard 2.1 (C.1)
+TSPC_OPP_2_15	True (*)	Client: Exchange business card (O)
+TSPC_OPP_2_16	False		Client: vCard 2.1 (C.2)
+TSPC_OPP_2_17	True (*)	GOEP v2 (C.9)
+TSPC_OPP_2_18	True (*)	GOEP v2 Backward Compatibility (C.9)
+TSPC_OPP_2_19	True (*)	OBEX over L2CAP (C.9)
+TSPC_OPP_2_20	False		OBEX Reliable Session (C.10)
+TSPC_OPP_2_21	False		OBEX SRM (C.10)
+TSPC_OPP_2_22	False		Send OBEX SRMP header (C.10)
+TSPC_OPP_2_23	False		Receive OBEX SRMP header (C.11)
+-------------------------------------------------------------------------------
+C.1: Mandatory to Support IF (TSPC_OPP_2_13) Business Card Pull is supported.
+C.2: Mandatory to Support IF (TSPC_OPP_2_15) Business Card Exchange is
+	supported.
+C.3: vCard 2.1 support is required for devices containing phonebook
+	applications. vCard 2.1 support optional for other devices.
+C.4: Mandatory to support one of TSPC_OPP_2_8a or TSPC_OPP_2_8b if TSPC_OPP_2_8
+	is supported. Otherwise, both items are excluded.
+C.5: Mandatory to support at least one of TSPC_OPP_2_9a and TSPC_OPP_2_9b if
+	TSPC_OPP_2_9 is supported. Otherwise, both items are excluded.
+C.6: Mandatory to support at least one of TSPC_OPP_2_10a and TSPC_OPP_2_10b if
+	TSPC_OPP_2_10 is supported. Otherwise, both items are excluded.
+C.7: Mandatory to support at least one of TSPC_OPP_2_11a and TSPC_OPP_2_11b if
+	TSPC_OPP_2_11 is supported. Otherwise, both items are excluded.
+C.8: Mandatory to support at least one of TSPC_OPP_2_12a and TSPC_OPP_2_12b if
+	TSPC_OPP_2_12 is supported. Otherwise, both items are excluded.
+C.9: Mandatory if TSPC_OPP_1b_2 supported.
+C.10: Optional to support if TSPC_OPP_1b_2 supported else excluded.
+C.11: Mandatory if TSPC_OPP_17 and TSPC_OPP_21 supported else excluded.
+-------------------------------------------------------------------------------
+
+
+		Server Profile Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_2b_1	False		Server supports OPP version 1.1.
+TSPC_OPP_2b_2	True (*)	Server supports OPP version 1.2.
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+		Server Application Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_3_1	True		Server: Provide information on supported
+					contents type on service discovery
+					request. (M)
+TSPC_OPP_3_2	True		Server: Authentication/PIN exchange supported.
+					(M)
+TSPC_OPP_3_3	True		Server: Object Push (M)
+TSPC_OPP_3_3a	True (*)	Server: Receive multiple objects in the same
+					PUT operation. (O)
+TSPC_OPP_3_4	True (*)	Server: vCard 2.1 (C.3)
+TSPC_OPP_3_5	False		Server: vCalender 1.0 format (O)
+TSPC_OPP_3_6	False		Server: vMsg as defined in IrMC 1.1 (O)
+TSPC_OPP_3_7	False		Server: vNote as defined in IrMC 1.1 (O)
+TSPC_OPP_3_8	True (*)	Server: Support content formats other than those
+					declared in TSPC_OPP_3_4 through
+					TSPC_OPP_3_7. (O)
+TSPC_OPP_3_8a	False		Server: Support specific set of other content
+					formats. (C.4)
+TSPC_OPP_3_8b	True (*)	Server: Support all content formats. (C.4)
+TSPC_OPP_3_9	True (*)	Server: Object Push vCard reject. (O)
+TSPC_OPP_3_10	True (*)	Server: Object Push vCal reject. (O)
+TSPC_OPP_3_11	True (*)	Server: Object Push vMsg reject. (O)
+TSPC_OPP_3_12	True (*)	Server: Object Push vNote reject. (O)
+TSPC_OPP_3_13	True (*)	Server: Business card pull (O.1)
+TSPC_OPP_3_14	True (*)	Server: vCard 2.1 (C.1)
+TSPC_OPP_3_15	True (*)	Server: Business card pull reject. (O)
+TSPC_OPP_3_16	True (*)	Server: Business card exchange (O.2)
+TSPC_OPP_3_17	True (*)	Server: vCard 2.1 (C.2)
+TSPC_OPP_3_18	True (*)	Server: Business card exchange reject. (O)
+TSPC_OPP_3_19	True (*)	GOEP v2 (C.5)
+TSPC_OPP_3_20	True (*)	GOEP v2 Backward Compatibility (C.5)
+TSPC_OPP_3_21	True (*)	OBEX over L2CAP (C.5)
+TSPC_OPP_3_22	False		OBEX Reliable Session (C.16)
+TSPC_OPP_3_23	False		OBEX SRM (C.6)
+TSPC_OPP_3_24	False		Send OBEX SRMP header (C.6)
+TSPC_OPP_3_25	False		Receive OBEX SRMP header (C.7)
+-------------------------------------------------------------------------------
+O.1: IF NOT Supported, an error message must be sent on request for Business
+	Card Pull.
+O.2: IF NOT Supported, an error message must be sent on request for Business
+	Card Exchange.
+C.1: Mandatory to Support IF (TSPC_OPP_3_13) Business Card Pull is supported.
+C.2: Mandatory to Support IF (TSPC_OPP_3_16) Business Card Exchange is
+	supported.
+C.3: vCard 2.1 support is required for devices containing phonebook
+	applications. vCard 2.1 support optional for other devices.
+C.4: Mandatory to support one of TSPC_OPP_3_8a or TSPC_OPP_3_8b if TSPC_OPP_3_8
+	is supported. Otherwise, both items are excluded.
+C.5: Mandatory if TSPC_OPP_2b_2 supported.
+C.6: Optional to support if TSPC_OPP_2b_2 supported, else excluded.
+C.7: Mandatory if TSPC_OPP_3_19 and TSPC_OPP_3_23 supported else excluded.
+-------------------------------------------------------------------------------
+
+
+		Additional OPP Capabilities
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_4_1	False		Abort-Push Operation (O)
+TSPC_OPP_4_2	False		Intentionally Left Blank (N/A)
+TSPC_OPP_4_3	True (*)	Multiple vCards transferred as a single vObject
+					(C.1)
+TSPC_OPP_4_4	True (*)	Multiple vCards transfer (C.1)
+TSPC_OPP_4_5	True (*)	vCards with multiple Phone Number Fields (C.1)
+TSPC_OPP_4_6	True (*)	Push vCal to Different Time Zone Server (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_OPP_1_2 is supported, otherwise excluded.
+-------------------------------------------------------------------------------
diff --git a/doc/pixit-opp.txt b/doc/pixit-opp.txt
new file mode 100644
index 0000000..b7461d6
--- /dev/null
+++ b/doc/pixit-opp.txt
@@ -0,0 +1,27 @@
+OPP PIXIT for the PTS tool.
+
+PTS version: 6.2.0
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_supported_extension				bmp
+TSPX_unsupported_extension				pts
+TSPX_client_class_of_device				100104
+TSPX_server_class_of_device				100104
+TSPX_auth_password					0000
+TSPX_auth_user_id					PTS
+TSPX_l2cap_psm						1003
+TSPX_rfcomm_channel					8
+TSPX_no_confirmations					FALSE
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_delete_link_key					FALSE
+TSPX_pin_code						0000
+TSPX_security_enabled					FALSE
+TSPX_time_guard						300000
+TSPX_use_implicit_send					TRUE
+-------------------------------------------------------------------------------
diff --git a/doc/pts-opp.txt b/doc/pts-opp.txt
new file mode 100644
index 0000000..832028c
--- /dev/null
+++ b/doc/pts-opp.txt
@@ -0,0 +1,119 @@
+PTS test results for OPP
+
+PTS version: 6.2.0
+Tested: 26-Aug-2015
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+NONE	test result is none
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_CLIENT_BC_BV_02_I	PASS
+TC_CLIENT_BC_BV_04_I	PASS
+TC_CLIENT_BCE_BV_01_I   PASS
+TC_CLIENT_BCE_BV_03_I   N/A
+TC_CLIENT_BCE_BV_04_I   PASS
+TC_CLIENT_BCE_BV_05_I   PASS
+TC_CLIENT_BCE_BV_06_I   PASS
+TC_CLIENT_BCE_BV_07_I   PASS
+TC_CLIENT_BCP_BV_01_I   PASS
+TC_CLIENT_BCP_BV_02_I   PASS
+TC_CLIENT_BCP_BV_03_I   N/A
+TC_CLIENT_BCP_BV_04_I   PASS
+TC_CLIENT_BCP_BV_05_I   PASS
+TC_CLIENT_CON_BV_01_C	PASS
+TC_CLIENT_OPH_BI_01_C	PASS
+TC_CLIENT_OPH_BV_01_I	PASS
+TC_CLIENT_OPH_BV_02_I	N/A
+TC_CLIENT_OPH_BV_03_I	PASS
+TC_CLIENT_OPH_BV_04_I	PASS
+TC_CLIENT_OPH_BV_05_I	PASS
+TC_CLIENT_OPH_BV_07_I	N/A
+TC_CLIENT_OPH_BV_08_I	PASS
+TC_CLIENT_OPH_BV_09_I	N/A
+TC_CLIENT_OPH_BV_10_I	N/A
+TC_CLIENT_OPH_BV_11_I	N/A
+TC_CLIENT_OPH_BV_12_I	PASS
+TC_CLIENT_OPH_BV_13_I	N/A
+TC_CLIENT_OPH_BV_14_I	N/A
+TC_CLIENT_OPH_BV_15_I	N/A
+TC_CLIENT_OPH_BV_16_I	PASS
+TC_CLIENT_OPH_BV_17_I	N/A
+TC_CLIENT_OPH_BV_18_I	N/A
+TC_CLIENT_OPH_BV_19_I	PASS	Send file other than vCard
+TC_CLIENT_OPH_BV_20_I	PASS
+TC_CLIENT_OPH_BV_22_I	PASS	Send file greater than 2 MB
+TC_CLIENT_OPH_BV_23_I	N/A
+TC_CLIENT_OPH_BV_24_I	N/A
+TC_CLIENT_OPH_BV_25_I	N/A
+TC_CLIENT_OPH_BV_26_I	N/A
+TC_CLIENT_SRM_BV_01_C	N/A
+TC_CLIENT_SRM_BV_03_C	N/A
+TC_CLIENT_SRM_BV_05_C	N/A
+TC_CLIENT_SRM_BV_07_C	N/A
+TC_CLIENT_SRMP_BI_01_C	N/A
+TC_CLIENT_SRMP_BV_01_C	N/A
+TC_CLIENT_SRMP_BV_04_C	N/A
+TC_CLIENT_SRMP_BV_05_C	N/A
+TC_CLIENT_SRMP_BV_06_C	N/A
+TC_SERVER_BC_BV_01_I	PASS
+TC_SERVER_BC_BV_03_I	PASS
+TC_SERVER_BCE_BV_01_I	PASS
+TC_SERVER_BCE_BV_03_I	PASS
+TC_SERVER_BCE_BV_04_I	PASS
+TC_SERVER_BCE_BV_05_I	PASS
+TC_SERVER_BCE_BV_06_I	PASS
+TC_SERVER_BCE_BV_07_I	PASS
+TC_SERVER_BCP_BV_01_I	PASS
+TC_SERVER_BCP_BV_02_I	N/A
+TC_SERVER_BCP_BV_03_I	PASS
+TC_SERVER_BCP_BV_04_I	PASS
+TC_SERVER_BCP_BV_05_I	PASS
+TC_SERVER_CON_BV_02_C	PASS
+TC_SERVER_OPH_BV_01_I	PASS
+TC_SERVER_OPH_BV_02_I	PASS
+TC_SERVER_OPH_BV_03_I	PASS
+TC_SERVER_OPH_BV_04_I	PASS
+TC_SERVER_OPH_BV_05_I	PASS
+TC_SERVER_OPH_BV_07_I	N/A
+TC_SERVER_OPH_BV_08_I	N/A
+TC_SERVER_OPH_BV_09_I	PASS
+TC_SERVER_OPH_BV_10_I	PASS
+TC_SERVER_OPH_BV_11_I	N/A
+TC_SERVER_OPH_BV_12_I	N/A
+TC_SERVER_OPH_BV_13_I	PASS
+TC_SERVER_OPH_BV_14_I	PASS
+TC_SERVER_OPH_BV_15_I	N/A
+TC_SERVER_OPH_BV_16_I	N/A
+TC_SERVER_OPH_BV_17_I	PASS
+TC_SERVER_OPH_BV_18_I	PASS
+TC_SERVER_OPH_BV_19_I	PASS
+TC_SERVER_OPH_BV_21_I	N/A
+TC_SERVER_OPH_BV_22_I	PASS
+TC_SERVER_OPH_BV_23_I	PASS
+TC_SERVER_OPH_BV_24_I	N/A
+TC_SERVER_OPH_BV_25_I	N/A
+TC_SERVER_OPH_BV_26_I	N/A
+TC_SERVER_ROB_BV_01_C	PASS
+TC_SERVER_ROB_BV_02_C	PASS
+TC_SERVER_SRM_BI_02_C	N/A
+TC_SERVER_SRM_BI_03_C	PASS
+TC_SERVER_SRM_BI_05_C	N/A
+TC_SERVER_SRM_BV_04_C	N/A
+TC_SERVER_SRM_BV_08_C	N/A
+TC_SERVER_SRMP_BV_02_C	N/A
+TC_SERVER_SRMP_BV_03_C	N/A
+TC_CLIENT_OPH_BV_27_I	N/A
+TC_CLIENT_OPH_BV_34_I	PASS
+TC_SERVER_OPH_BV_27_I	N/A
+TC_SERVER_OPH_BV_30_I	PASS
+TC_SERVER_OPH_BV_31_I	PASS
+TC_SERVER_OPH_BV_32_I	PASS
+TC_SERVER_OPH_BV_33_I	N/A
+TC_SERVER_OPH_BV_34_I	PASS
+-------------------------------------------------------------------------------
diff --git a/doc/settings-storage.txt b/doc/settings-storage.txt
index 6c9f058..aea50a6 100644
--- a/doc/settings-storage.txt
+++ b/doc/settings-storage.txt
@@ -139,11 +139,15 @@
 ============================
 
 Each file, named by remote device address, may includes multiple groups
-(General and ServiceRecords).
+(General, ServiceRecords, Attributes).
 
 In ServiceRecords, SDP records are stored using their handle as key
 (hexadecimal format).
 
+In "Attributes" group GATT database is stored using attribute handle as key
+(hexadecimal format). Value associated with this handle is serialized form of
+all data required to re-create given attribute. ":" is used to separate fields.
+
 [General] group contains:
 
   Name		String		Remote device friendly name
@@ -155,6 +159,37 @@
   <0x...>	String		SDP record as hexadecimal encoded
 				string
 
+In [Attributes] group value always starts with attribute type, that determines
+how to interpret rest of value:
+
+  Primary service:
+    2800:end_handle:uuid
+
+  Secondary service:
+    2801:end_handle:uuid
+
+  Included service:
+    2802:start_handle:end_handle:uuid
+
+  Characteristic:
+    2803:value_handle:properties:uuid
+
+  Descriptor:
+    uuid
+
+Sample Attributes section:
+  [Attributes]
+  0001=2800:0005:1801
+  0002=2803:0003:20:2a05
+  0014=2800:001c:1800
+  0015=2803:0016:02:2a00
+  0017=2803:0018:02:2a01
+  0019=2803:001a:02:2aa6
+  0028=2800:ffff:0000180d-0000-1000-8000-00805f9b34fb
+  0029=2803:002a:10:00002a37-0000-1000-8000-00805f9b34fb
+  002b=2803:002c:02:00002a38-0000-1000-8000-00805f9b34fb
+  002d=2803:002e:08:00002a39-0000-1000-8000-00805f9b34fb
+
 
 Info file format
 ================
diff --git a/emulator/btdev.c b/emulator/btdev.c
index cd211ef..e8cead8 100644
--- a/emulator/btdev.c
+++ b/emulator/btdev.c
@@ -420,20 +420,33 @@
 	btdev->commands[25] |= 0x01;	/* LE Set Event Mask */
 	btdev->commands[25] |= 0x02;	/* LE Read Buffer Size */
 	btdev->commands[25] |= 0x04;	/* LE Read Local Features */
+	btdev->commands[25] |= 0x10;	/* LE Set Random Address */
 	btdev->commands[25] |= 0x20;	/* LE Set Adv Parameters */
 	btdev->commands[25] |= 0x40;	/* LE Read Adv TX Power */
 	btdev->commands[25] |= 0x80;	/* LE Set Adv Data */
+	btdev->commands[26] |= 0x01;	/* LE Set Scan Response Data */
 	btdev->commands[26] |= 0x02;	/* LE Set Adv Enable */
 	btdev->commands[26] |= 0x04;	/* LE Set Scan Parameters */
 	btdev->commands[26] |= 0x08;	/* LE Set Scan Enable */
+	btdev->commands[26] |= 0x10;	/* LE Create Connection */
 	btdev->commands[26] |= 0x40;	/* LE Read White List Size */
+	btdev->commands[26] |= 0x80;	/* LE Clear White List */
+	btdev->commands[27] |= 0x04;	/* LE Connection Update */
+	btdev->commands[27] |= 0x20;	/* LE Read Remote Used Features */
 	btdev->commands[27] |= 0x40;	/* LE Encrypt */
 	btdev->commands[27] |= 0x80;	/* LE Rand */
+	btdev->commands[28] |= 0x01;	/* LE Start Encryption */
+	btdev->commands[28] |= 0x02;	/* LE Long Term Key Request Reply */
+	btdev->commands[28] |= 0x04;	/* LE Long Term Key Request Neg Reply */
 	btdev->commands[28] |= 0x08;	/* LE Read Supported States */
 	btdev->commands[28] |= 0x10;	/* LE Receiver Test */
 	btdev->commands[28] |= 0x20;	/* LE Transmitter Test */
 	btdev->commands[28] |= 0x40;	/* LE Test End */
 
+	/* Extra LE commands for >= 4.1 adapters */
+	btdev->commands[33] |= 0x10;	/* LE Remote Conn Param Req Reply */
+	btdev->commands[33] |= 0x20;	/* LE Remote Conn Param Req Neg Reply */
+
 	/* Extra LE commands for >= 4.2 adapters */
 	btdev->commands[34] |= 0x02;	/* LE Read Local P-256 Public Key */
 	btdev->commands[34] |= 0x04;	/* LE Generate DHKey */
@@ -553,6 +566,8 @@
 	btdev->max_page = 1;
 
 	btdev->le_features[0] |= 0x01;	/* LE Encryption */
+	btdev->le_features[0] |= 0x02;	/* Connection Parameters Request */
+	btdev->le_features[0] |= 0x08;	/* Slave-initiated Features Exchange */
 }
 
 static void set_amp_features(struct btdev *btdev)
@@ -1086,8 +1101,8 @@
 }
 
 static void le_conn_complete(struct btdev *btdev,
-					const uint8_t *bdaddr, uint8_t bdaddr_type,
-					uint8_t status)
+				const struct bt_hci_cmd_le_create_conn *lecc,
+				uint8_t status)
 {
 	char buf[1 + sizeof(struct bt_hci_evt_le_conn_complete)];
 	struct bt_hci_evt_le_conn_complete *cc = (void *) &buf[1];
@@ -1097,8 +1112,10 @@
 	buf[0] = BT_HCI_EVT_LE_CONN_COMPLETE;
 
 	if (!status) {
-		struct btdev *remote = find_btdev_by_bdaddr_type(bdaddr,
-								bdaddr_type);
+		struct btdev *remote;
+
+		remote = find_btdev_by_bdaddr_type(lecc->peer_addr,
+							lecc->peer_addr_type);
 
 		btdev->conn = remote;
 		btdev->le_adv_enable = 0;
@@ -1114,15 +1131,16 @@
 
 		cc->role = 0x01;
 		cc->handle = cpu_to_le16(42);
+		cc->interval = lecc->max_interval;
+		cc->latency = lecc->latency;
+		cc->supv_timeout = lecc->supv_timeout;
 
 		send_event(remote, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf));
-
-		cc->handle = cpu_to_le16(42);
 	}
 
 	cc->status = status;
-	cc->peer_addr_type = bdaddr_type;
-	memcpy(cc->peer_addr, bdaddr, 6);
+	cc->peer_addr_type = lecc->peer_addr_type;
+	memcpy(cc->peer_addr, lecc->peer_addr, 6);
 	cc->role = 0x00;
 
 	send_event(btdev, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf));
@@ -1164,16 +1182,17 @@
 	return btdev->le_adv_type != 0x03;
 }
 
-static void le_conn_request(struct btdev *btdev, const uint8_t *bdaddr,
-							uint8_t bdaddr_type)
+static void le_conn_request(struct btdev *btdev,
+				const struct bt_hci_cmd_le_create_conn *lecc)
 {
-	struct btdev *remote = find_btdev_by_bdaddr_type(bdaddr, bdaddr_type);
+	struct btdev *remote = find_btdev_by_bdaddr_type(lecc->peer_addr,
+							lecc->peer_addr_type);
 
 	if (remote && adv_connectable(remote) && adv_match(btdev, remote) &&
-					remote->le_adv_own_addr == bdaddr_type)
-		le_conn_complete(btdev, bdaddr, bdaddr_type, 0);
+				remote->le_adv_own_addr == lecc->peer_addr_type)
+		le_conn_complete(btdev, lecc, 0);
 	else
-		le_conn_complete(btdev, bdaddr, bdaddr_type,
+		le_conn_complete(btdev, lecc,
 					BT_HCI_ERR_CONN_FAILED_TO_ESTABLISH);
 }
 
@@ -1194,8 +1213,27 @@
 	}
 }
 
+static void rej_le_conn_update(struct btdev *btdev, uint16_t handle,
+								uint8_t reason)
+{
+	struct btdev *remote = btdev->conn;
+	struct __packed {
+		uint8_t subevent;
+		struct bt_hci_evt_le_conn_update_complete ev;
+	} ev;
+
+	if (!remote)
+		return;
+
+	ev.subevent = BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE;
+	ev.ev.handle = cpu_to_le16(handle);
+	ev.ev.status = cpu_to_le16(reason);
+
+	send_event(remote, BT_HCI_EVT_LE_META_EVENT, &ev, sizeof(ev));
+}
+
 static void le_conn_update(struct btdev *btdev, uint16_t handle,
-				uint16_t max_interval, uint16_t min_interval,
+				uint16_t min_interval, uint16_t max_interval,
 				uint16_t latency, uint16_t supv_timeout,
 				uint16_t min_length, uint16_t max_length)
 {
@@ -1222,6 +1260,30 @@
 		send_event(remote, BT_HCI_EVT_LE_META_EVENT, &ev, sizeof(ev));
 }
 
+static void le_conn_param_req(struct btdev *btdev, uint16_t handle,
+				uint16_t min_interval, uint16_t max_interval,
+				uint16_t latency, uint16_t supv_timeout,
+				uint16_t min_length, uint16_t max_length)
+{
+	struct btdev *remote = btdev->conn;
+	struct __packed {
+		uint8_t subevent;
+		struct bt_hci_evt_le_conn_param_request ev;
+	} ev;
+
+	if (!remote)
+		return;
+
+	ev.subevent = BT_HCI_EVT_LE_CONN_PARAM_REQUEST;
+	ev.ev.handle = cpu_to_le16(handle);
+	ev.ev.min_interval = cpu_to_le16(min_interval);
+	ev.ev.max_interval = cpu_to_le16(max_interval);
+	ev.ev.latency = cpu_to_le16(latency);
+	ev.ev.supv_timeout = cpu_to_le16(supv_timeout);
+
+	send_event(remote, BT_HCI_EVT_LE_META_EVENT, &ev, sizeof(ev));
+}
+
 static void disconnect_complete(struct btdev *btdev, uint16_t handle,
 							uint8_t reason)
 {
@@ -1991,6 +2053,8 @@
 	const struct bt_hci_cmd_le_ltk_req_reply *llrr;
 	const struct bt_hci_cmd_le_encrypt *lenc_cmd;
 	const struct bt_hci_cmd_le_generate_dhkey *dh;
+	const struct bt_hci_cmd_le_conn_param_req_reply *lcprr_cmd;
+	const struct bt_hci_cmd_le_conn_param_req_neg_reply *lcprnr_cmd;
 	const struct bt_hci_cmd_read_local_amp_assoc *rlaa_cmd;
 	const struct bt_hci_cmd_read_rssi *rrssi;
 	const struct bt_hci_cmd_read_tx_power *rtxp;
@@ -2032,6 +2096,8 @@
 	struct bt_hci_rsp_read_local_amp_info rlai;
 	struct bt_hci_rsp_read_local_amp_assoc rlaa_rsp;
 	struct bt_hci_rsp_get_mws_transport_config *gmtc;
+	struct bt_hci_rsp_le_conn_param_req_reply lcprr_rsp;
+	struct bt_hci_rsp_le_conn_param_req_neg_reply lcprnr_rsp;
 	struct bt_hci_rsp_le_read_buffer_size lrbs;
 	struct bt_hci_rsp_le_read_local_features lrlf;
 	struct bt_hci_rsp_le_read_adv_tx_power lratp;
@@ -2939,8 +3005,6 @@
 			status = BT_HCI_ERR_SUCCESS;
 		}
 		cmd_complete(btdev, opcode, &status, sizeof(status));
-		if (status == BT_HCI_ERR_SUCCESS && btdev->le_scan_enable)
-			le_set_scan_enable_complete(btdev);
 		break;
 
 	case BT_HCI_CMD_LE_CREATE_CONN:
@@ -3136,6 +3200,22 @@
 		cmd_complete(btdev, opcode, &lte, sizeof(lte));
 		break;
 
+	case BT_HCI_CMD_LE_CONN_PARAM_REQ_REPLY:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lcprr_cmd = data;
+		lcprr_rsp.handle = lcprr_cmd->handle;
+		lcprr_rsp.status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &lcprr_rsp, sizeof(lcprr_rsp));
+		break;
+	case BT_HCI_CMD_LE_CONN_PARAM_REQ_NEG_REPLY:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lcprnr_cmd = data;
+		lcprnr_rsp.handle = lcprnr_cmd->handle;
+		lcprnr_rsp.status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &lcprnr_rsp, sizeof(lcprnr_rsp));
+		break;
 	default:
 		goto unsupported;
 	}
@@ -3170,6 +3250,9 @@
 	const struct bt_hci_cmd_read_clock_offset *rco;
 	const struct bt_hci_cmd_le_create_conn *lecc;
 	const struct bt_hci_cmd_le_conn_update *lecu;
+	const struct bt_hci_cmd_le_conn_param_req_reply *lcprr;
+	const struct bt_hci_cmd_le_conn_param_req_neg_reply *lcprnr;
+	const struct bt_hci_cmd_le_set_scan_enable *lsse;
 
 	switch (opcode) {
 	case BT_HCI_CMD_INQUIRY:
@@ -3314,21 +3397,57 @@
 			return;
 		lecc = data;
 		btdev->le_scan_own_addr_type = lecc->own_addr_type;
-		le_conn_request(btdev, lecc->peer_addr, lecc->peer_addr_type);
+		le_conn_request(btdev, lecc);
 		break;
 
 	case BT_HCI_CMD_LE_CONN_UPDATE:
 		if (btdev->type == BTDEV_TYPE_BREDR)
 			return;
 		lecu = data;
-		le_conn_update(btdev, le16_to_cpu(lecu->handle),
-				le16_to_cpu(lecu->min_interval),
-				le16_to_cpu(lecu->max_interval),
-				le16_to_cpu(lecu->latency),
-				le16_to_cpu(lecu->supv_timeout),
-				le16_to_cpu(lecu->min_length),
-				le16_to_cpu(lecu->max_length));
+		if (btdev->le_features[0] & 0x02)
+			le_conn_param_req(btdev, le16_to_cpu(lecu->handle),
+					le16_to_cpu(lecu->min_interval),
+					le16_to_cpu(lecu->max_interval),
+					le16_to_cpu(lecu->latency),
+					le16_to_cpu(lecu->supv_timeout),
+					le16_to_cpu(lecu->min_length),
+					le16_to_cpu(lecu->max_length));
+		else
+			le_conn_update(btdev, le16_to_cpu(lecu->handle),
+					le16_to_cpu(lecu->min_interval),
+					le16_to_cpu(lecu->max_interval),
+					le16_to_cpu(lecu->latency),
+					le16_to_cpu(lecu->supv_timeout),
+					le16_to_cpu(lecu->min_length),
+					le16_to_cpu(lecu->max_length));
 		break;
+	case BT_HCI_CMD_LE_CONN_PARAM_REQ_REPLY:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			return;
+		lcprr = data;
+		le_conn_update(btdev, le16_to_cpu(lcprr->handle),
+				le16_to_cpu(lcprr->min_interval),
+				le16_to_cpu(lcprr->max_interval),
+				le16_to_cpu(lcprr->latency),
+				le16_to_cpu(lcprr->supv_timeout),
+				le16_to_cpu(lcprr->min_length),
+				le16_to_cpu(lcprr->max_length));
+		break;
+	case BT_HCI_CMD_LE_CONN_PARAM_REQ_NEG_REPLY:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			return;
+		lcprnr = data;
+		rej_le_conn_update(btdev, le16_to_cpu(lcprnr->handle),
+					le16_to_cpu(lcprnr->reason));
+		break;
+		break;
+	case BT_HCI_CMD_LE_SET_SCAN_ENABLE:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			return;
+		lsse = data;
+		if (btdev->le_scan_enable && lsse->enable)
+			le_set_scan_enable_complete(btdev);
+
 	}
 }
 
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index d99c254..9ece4b0 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -35,6 +35,7 @@
 typedef enum GDBusSignalFlags GDBusSignalFlags;
 typedef enum GDBusPropertyFlags GDBusPropertyFlags;
 typedef enum GDBusSecurityFlags GDBusSecurityFlags;
+typedef enum GDbusPropertyChangedFlags GDbusPropertyChangedFlags;
 
 typedef struct GDBusArgInfo GDBusArgInfo;
 typedef struct GDBusMethodTable GDBusMethodTable;
@@ -115,6 +116,10 @@
 	G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2),
 };
 
+enum GDbusPropertyChangedFlags {
+	G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH = (1 << 0),
+};
+
 struct GDBusArgInfo {
 	const char *name;
 	const char *signature;
@@ -300,6 +305,10 @@
 void g_dbus_emit_property_changed(DBusConnection *connection,
 				const char *path, const char *interface,
 				const char *name);
+void g_dbus_emit_property_changed_full(DBusConnection *connection,
+				const char *path, const char *interface,
+				const char *name,
+				GDbusPropertyChangedFlags flags);
 gboolean g_dbus_get_properties(DBusConnection *connection, const char *path,
 				const char *interface, DBusMessageIter *iter);
 
diff --git a/gdbus/object.c b/gdbus/object.c
index 96db516..4cf2e2f 100644
--- a/gdbus/object.c
+++ b/gdbus/object.c
@@ -1720,9 +1720,10 @@
 	}
 }
 
-void g_dbus_emit_property_changed(DBusConnection *connection,
+void g_dbus_emit_property_changed_full(DBusConnection *connection,
 				const char *path, const char *interface,
-				const char *name)
+				const char *name,
+				GDbusPropertyChangedFlags flags)
 {
 	const GDBusPropertyTable *property;
 	struct generic_data *data;
@@ -1760,7 +1761,16 @@
 	iface->pending_prop = g_slist_prepend(iface->pending_prop,
 						(void *) property);
 
-	add_pending(data);
+	if (flags & G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH)
+		process_property_changes(data);
+	else
+		add_pending(data);
+}
+
+void g_dbus_emit_property_changed(DBusConnection *connection, const char *path,
+				const char *interface, const char *name)
+{
+	g_dbus_emit_property_changed_full(connection, path, interface, name, 0);
 }
 
 gboolean g_dbus_get_properties(DBusConnection *connection, const char *path,
diff --git a/lib/bluetooth.c b/lib/bluetooth.c
index 301e6ab..1142fd3 100644
--- a/lib/bluetooth.c
+++ b/lib/bluetooth.c
@@ -810,7 +810,7 @@
 	case 273:
 		return "Steelseries ApS";
 	case 274:
-		return "vyzybl Inc.";
+		return "Visybl Inc.";
 	case 275:
 		return "Openbrain Technologies, Co., Ltd.";
 	case 276:
@@ -1489,6 +1489,96 @@
 		return "DDS, Inc.";
 	case 613:
 		return "SMK Corporation";
+	case 614:
+		return "Schawbel Technologies LLC";
+	case 615:
+		return "XMI Systems SA";
+	case 616:
+		return "Cerevo";
+	case 617:
+		return "Torrox GmbH & Co KG";
+	case 618:
+		return "Gemalto";
+	case 619:
+		return "DEKA Research & Development Corp.";
+	case 620:
+		return "Domster Tadeusz Szydlowski";
+	case 621:
+		return "Technogym SPA";
+	case 622:
+		return "FLEURBAEY BVBA";
+	case 623:
+		return "Aptcode Solutions";
+	case 624:
+		return "LSI ADL Technology";
+	case 625:
+		return "Animas Corp";
+	case 626:
+		return "Alps Electric Co., Ltd.";
+	case 627:
+		return "OCEASOFT";
+	case 628:
+		return "Motsai Research";
+	case 629:
+		return "Geotab";
+	case 630:
+		return "E.G.O. Elektro-Gertebau GmbH";
+	case 631:
+		return "bewhere inc";
+	case 632:
+		return "Johnson Outdoors Inc";
+	case 633:
+		return "steute Schaltgerate GmbH & Co. KG";
+	case 634:
+		return "Ekomini inc.";
+	case 635:
+		return "DEFA AS";
+	case 636:
+		return "Aseptika Ltd";
+	case 637:
+		return "HUAWEI Technologies Co., Ltd. ( 华为技术有限公司 )";
+	case 638:
+		return "HabitAware, LLC";
+	case 639:
+		return "ruwido austria gmbh";
+	case 640:
+		return "ITEC corporation";
+	case 641:
+		return "StoneL";
+	case 642:
+		return "Sonova AG";
+	case 643:
+		return "Maven Machines, Inc.";
+	case 644:
+		return "Synapse Electronics";
+	case 645:
+		return "Standard Innovation Inc.";
+	case 646:
+		return "RF Code, Inc.";
+	case 647:
+		return "Wally Ventures S.L.";
+	case 648:
+		return "Willowbank Electronics Ltd";
+	case 649:
+		return "SK Telecom";
+	case 650:
+		return "Jetro AS";
+	case 651:
+		return "Code Gears LTD";
+	case 652:
+		return "NANOLINK APS";
+	case 653:
+		return "IF, LLC";
+	case 654:
+		return "RF Digital Corp";
+	case 655:
+		return "Church & Dwight Co., Inc";
+	case 656:
+		return "Multibit Oy";
+	case 657:
+		return "CliniCloud Inc";
+	case 658:
+		return "SwiftSensors";
 	case 65535:
 		return "internal use";
 	default:
diff --git a/lib/sdp.c b/lib/sdp.c
index 2107e28..155eca5 100644
--- a/lib/sdp.c
+++ b/lib/sdp.c
@@ -4906,6 +4906,7 @@
 				length = 0;
 				break;
 			default:
+				sdp_list_free(subseq, free);
 				goto fail;
 			}
 
diff --git a/lib/uuid.c b/lib/uuid.c
index fd61968..046b521 100644
--- a/lib/uuid.c
+++ b/lib/uuid.c
@@ -289,7 +289,12 @@
 
 int bt_uuid_strcmp(const void *a, const void *b)
 {
-	return strcasecmp(a, b);
+	bt_uuid_t u1, u2;
+
+	bt_string_to_uuid(&u1, a);
+	bt_string_to_uuid(&u2, b);
+
+	return bt_uuid_cmp(&u1, &u2);
 }
 
 int bt_uuid_to_le(const bt_uuid_t *src, void *dst)
diff --git a/monitor/analyze.c b/monitor/analyze.c
index eea3c9e..47ae45f 100644
--- a/monitor/analyze.c
+++ b/monitor/analyze.c
@@ -309,6 +309,9 @@
 		case BTSNOOP_OPCODE_SCO_RX_PKT:
 			sco_pkt(&tv, index, buf, pktlen);
 			break;
+		case BTSNOOP_OPCODE_OPEN_INDEX:
+		case BTSNOOP_OPCODE_CLOSE_INDEX:
+			break;
 		default:
 			fprintf(stderr, "Wrong opcode %u\n", opcode);
 			goto done;
diff --git a/monitor/avctp.c b/monitor/avctp.c
index a54b051..d06640e 100644
--- a/monitor/avctp.c
+++ b/monitor/avctp.c
@@ -1818,9 +1818,9 @@
 {
 	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
 	uint16_t id, charset, namelen;
-	uint8_t type, status;
+	uint8_t type, status, i;
 	uint32_t subtype;
-	uint64_t features[2];
+	uint8_t features[16];
 
 	if (!l2cap_frame_get_be16(frame, &id))
 		return false;
@@ -1830,26 +1830,31 @@
 	if (!l2cap_frame_get_u8(frame, &type))
 		return false;
 
-	print_field("%*cPlayerID: 0x%04x (%s)", indent, ' ',
+	print_field("%*cPlayerType: 0x%04x (%s)", indent, ' ',
 						type, playertype2str(type));
 
 	if (!l2cap_frame_get_be32(frame, &subtype))
 		return false;
 
-	print_field("%*cPlayerID: 0x%08x (%s)", indent, ' ',
+	print_field("%*cPlayerSubType: 0x%08x (%s)", indent, ' ',
 					subtype, playersubtype2str(subtype));
 
 	if (!l2cap_frame_get_u8(frame, &status))
 		return false;
 
-	print_field("%*cPlayerID: 0x%02x (%s)", indent, ' ',
+	print_field("%*cPlayStatus: 0x%02x (%s)", indent, ' ',
 						status, playstatus2str(status));
 
-	if (!l2cap_frame_get_be128(frame, &features[0], &features[1]))
-		return false;
+	printf("%*cFeatures: 0x", indent+8, ' ');
 
-	print_field("%*cFeatures: 0x%16" PRIx64 "%16" PRIx64, indent, ' ',
-						features[1], features[0]);
+	for (i = 0; i < 16; i++) {
+		if (!l2cap_frame_get_u8(frame, &features[i]))
+			return false;
+
+		printf("%02x", features[i]);
+	}
+
+	printf("\n");
 
 	if (!l2cap_frame_get_be16(frame, &charset))
 		return false;
@@ -1863,7 +1868,7 @@
 	print_field("%*cNameLength: 0x%04x (%u)", indent, ' ',
 						namelen, namelen);
 
-	printf("%*cName: ", indent, ' ');
+	printf("%*cName: ", indent+8, ' ');
 	for (; namelen > 0; namelen--) {
 		uint8_t c;
 
@@ -1871,6 +1876,7 @@
 			return false;
 		printf("%1c", isprint(c) ? c : '.');
 	}
+	printf("\n");
 
 	return true;
 }
@@ -2296,7 +2302,7 @@
 	if (!l2cap_frame_get_be16(frame, &num))
 		return false;
 
-	print_field("%*cUIDCounter: 0x%04x (%u)", indent, ' ', num, num);
+	print_field("%*cNumOfItems: 0x%04x (%u)", indent, ' ', num, num);
 
 	for (; num > 0; num--) {
 		uint8_t type;
diff --git a/monitor/btsnoop.c b/monitor/btsnoop.c
deleted file mode 100644
index a32335a..0000000
--- a/monitor/btsnoop.c
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2011-2014  Intel Corporation
- *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
- *
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public
- *  License as published by the Free Software Foundation; either
- *  version 2.1 of the License, or (at your option) any later version.
- *
- *  This library 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
- *  Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <arpa/inet.h>
-
-#include "lib/bluetooth.h"
-#include "lib/hci.h"
-
-#include "btsnoop.h"
-
-struct btsnoop_hdr {
-	uint8_t		id[8];		/* Identification Pattern */
-	uint32_t	version;	/* Version Number = 1 */
-	uint32_t	type;		/* Datalink Type */
-} __attribute__ ((packed));
-#define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr))
-
-struct btsnoop_pkt {
-	uint32_t	size;		/* Original Length */
-	uint32_t	len;		/* Included Length */
-	uint32_t	flags;		/* Packet Flags */
-	uint32_t	drops;		/* Cumulative Drops */
-	uint64_t	ts;		/* Timestamp microseconds */
-	uint8_t		data[0];	/* Packet Data */
-} __attribute__ ((packed));
-#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
-
-static const uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e,
-				      0x6f, 0x6f, 0x70, 0x00 };
-
-static const uint32_t btsnoop_version = 1;
-static uint32_t btsnoop_type = 0;
-
-static int btsnoop_fd = -1;
-static uint16_t btsnoop_index = 0xffff;
-
-void btsnoop_create(const char *path, uint32_t type)
-{
-	struct btsnoop_hdr hdr;
-	ssize_t written;
-
-	if (btsnoop_fd >= 0)
-		return;
-
-	btsnoop_fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
-				S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-	if (btsnoop_fd < 0)
-		return;
-
-	btsnoop_type = type;
-
-	memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
-	hdr.version = htobe32(btsnoop_version);
-	hdr.type = htobe32(btsnoop_type);
-
-	written = write(btsnoop_fd, &hdr, BTSNOOP_HDR_SIZE);
-	if (written < 0) {
-		close(btsnoop_fd);
-		btsnoop_fd = -1;
-		return;
-	}
-}
-
-void btsnoop_write(struct timeval *tv, uint32_t flags,
-					const void *data, uint16_t size)
-{
-	struct btsnoop_pkt pkt;
-	uint64_t ts;
-	ssize_t written;
-
-	ts = (tv->tv_sec - 946684800ll) * 1000000ll + tv->tv_usec;
-
-	pkt.size  = htobe32(size);
-	pkt.len   = htobe32(size);
-	pkt.flags = htobe32(flags);
-	pkt.drops = htobe32(0);
-	pkt.ts    = htobe64(ts + 0x00E03AB44A676000ll);
-
-	written = write(btsnoop_fd, &pkt, BTSNOOP_PKT_SIZE);
-	if (written < 0)
-		return;
-
-	if (data && size > 0) {
-		written = write(btsnoop_fd, data, size);
-		if (written < 0)
-			return;
-	}
-}
-
-static uint32_t get_flags_from_opcode(uint16_t opcode)
-{
-	switch (opcode) {
-	case BTSNOOP_OPCODE_NEW_INDEX:
-	case BTSNOOP_OPCODE_DEL_INDEX:
-		break;
-	case BTSNOOP_OPCODE_COMMAND_PKT:
-		return 0x02;
-	case BTSNOOP_OPCODE_EVENT_PKT:
-		return 0x03;
-	case BTSNOOP_OPCODE_ACL_TX_PKT:
-		return 0x00;
-	case BTSNOOP_OPCODE_ACL_RX_PKT:
-		return 0x01;
-	case BTSNOOP_OPCODE_SCO_TX_PKT:
-	case BTSNOOP_OPCODE_SCO_RX_PKT:
-		break;
-	}
-
-	return 0xff;
-}
-
-void btsnoop_write_hci(struct timeval *tv, uint16_t index, uint16_t opcode,
-					const void *data, uint16_t size)
-{
-	uint32_t flags;
-
-	if (!tv)
-		return;
-
-	if (btsnoop_fd < 0)
-		return;
-
-	switch (btsnoop_type) {
-	case BTSNOOP_TYPE_HCI:
-		if (btsnoop_index == 0xffff)
-			btsnoop_index = index;
-
-		if (index != btsnoop_index)
-			return;
-
-		flags = get_flags_from_opcode(opcode);
-		if (flags == 0xff)
-			return;
-		break;
-
-	case BTSNOOP_TYPE_MONITOR:
-		flags = (index << 16) | opcode;
-		break;
-
-	default:
-		return;
-	}
-
-	btsnoop_write(tv, flags, data, size);
-}
-
-void btsnoop_write_phy(struct timeval *tv, uint16_t frequency,
-					const void *data, uint16_t size)
-{
-	uint32_t flags;
-
-	if (!tv)
-		return;
-
-	if (btsnoop_fd < 0)
-		return;
-
-	switch (btsnoop_type) {
-	case BTSNOOP_TYPE_SIMULATOR:
-		flags = (1 << 16) | frequency;
-		break;
-
-	default:
-		return;
-	}
-
-	btsnoop_write(tv, flags, data, size);
-}
-
-int btsnoop_open(const char *path, uint32_t *type)
-{
-	struct btsnoop_hdr hdr;
-	ssize_t len;
-
-	if (btsnoop_fd >= 0) {
-		fprintf(stderr, "Too many open files\n");
-		return -1;
-	}
-
-	btsnoop_fd = open(path, O_RDONLY | O_CLOEXEC);
-	if (btsnoop_fd < 0) {
-		perror("Failed to open file");
-		return -1;
-	}
-
-	len = read(btsnoop_fd, &hdr, BTSNOOP_HDR_SIZE);
-	if (len < 0 || len != BTSNOOP_HDR_SIZE) {
-		perror("Failed to read header");
-		close(btsnoop_fd);
-		btsnoop_fd = -1;
-		return -1;
-	}
-
-	if (memcmp(hdr.id, btsnoop_id, sizeof(btsnoop_id))) {
-		fprintf(stderr, "Invalid btsnoop header\n");
-		close(btsnoop_fd);
-		btsnoop_fd = -1;
-		return -1;
-	}
-
-	if (be32toh(hdr.version) != btsnoop_version) {
-		fprintf(stderr, "Invalid btsnoop version\n");
-		close(btsnoop_fd);
-		btsnoop_fd = -1;
-		return -1;
-	}
-
-	btsnoop_type = be32toh(hdr.type);
-
-	if (type)
-		*type = btsnoop_type;
-
-	return 0;
-}
-
-static uint16_t get_opcode_from_flags(uint8_t type, uint32_t flags)
-{
-	switch (type) {
-	case HCI_COMMAND_PKT:
-		return BTSNOOP_OPCODE_COMMAND_PKT;
-	case HCI_ACLDATA_PKT:
-		if (flags & 0x01)
-			return BTSNOOP_OPCODE_ACL_RX_PKT;
-		else
-			return BTSNOOP_OPCODE_ACL_TX_PKT;
-	case HCI_SCODATA_PKT:
-		if (flags & 0x01)
-			return BTSNOOP_OPCODE_SCO_RX_PKT;
-		else
-			return BTSNOOP_OPCODE_SCO_TX_PKT;
-	case HCI_EVENT_PKT:
-		return BTSNOOP_OPCODE_EVENT_PKT;
-	case 0xff:
-		if (flags & 0x02) {
-			if (flags & 0x01)
-				return BTSNOOP_OPCODE_EVENT_PKT;
-			else
-				return BTSNOOP_OPCODE_COMMAND_PKT;
-		} else {
-			if (flags & 0x01)
-				return BTSNOOP_OPCODE_ACL_RX_PKT;
-			else
-				return BTSNOOP_OPCODE_ACL_TX_PKT;
-		}
-		break;
-	}
-
-	return 0xff;
-}
-
-int btsnoop_read_hci(struct timeval *tv, uint16_t *index, uint16_t *opcode,
-						void *data, uint16_t *size)
-{
-	struct btsnoop_pkt pkt;
-	uint32_t toread, flags;
-	uint64_t ts;
-	uint8_t pkt_type;
-	ssize_t len;
-
-	if (btsnoop_fd < 0)
-		return -1;
-
-	len = read(btsnoop_fd, &pkt, BTSNOOP_PKT_SIZE);
-	if (len == 0)
-		return -1;
-
-	if (len < 0 || len != BTSNOOP_PKT_SIZE) {
-		perror("Failed to read packet");
-		close(btsnoop_fd);
-		btsnoop_fd = -1;
-		return -1;
-	}
-
-	toread = be32toh(pkt.size);
-	if (toread > BTSNOOP_MAX_PACKET_SIZE) {
-		perror("Packet len suspicially big: %u", toread);
-		close(btsnoop_fd);
-		btsnoop_fd = -1;
-		return -1;
-	}
-
-	flags = be32toh(pkt.flags);
-
-	ts = be64toh(pkt.ts) - 0x00E03AB44A676000ll;
-	tv->tv_sec = (ts / 1000000ll) + 946684800ll;
-	tv->tv_usec = ts % 1000000ll;
-
-	switch (btsnoop_type) {
-	case BTSNOOP_TYPE_HCI:
-		*index = 0;
-		*opcode = get_opcode_from_flags(0xff, flags);
-		break;
-
-	case BTSNOOP_TYPE_UART:
-		len = read(btsnoop_fd, &pkt_type, 1);
-		if (len < 0) {
-			perror("Failed to read packet type");
-			close(btsnoop_fd);
-			btsnoop_fd = -1;
-			return -1;
-		}
-		toread--;
-
-		*index = 0;
-		*opcode = get_opcode_from_flags(pkt_type, flags);
-		break;
-
-	case BTSNOOP_TYPE_MONITOR:
-		*index = flags >> 16;
-		*opcode = flags & 0xffff;
-		break;
-
-	default:
-		fprintf(stderr, "Unknown packet type\n");
-		close(btsnoop_fd);
-		btsnoop_fd = -1;
-		return -1;
-	}
-
-	len = read(btsnoop_fd, data, toread);
-	if (len < 0) {
-		perror("Failed to read data");
-		close(btsnoop_fd);
-		btsnoop_fd = -1;
-		return -1;
-	}
-
-	*size = toread;
-
-	return 0;
-}
-
-int btsnoop_read_phy(struct timeval *tv, uint16_t *frequency,
-						void *data, uint16_t *size)
-{
-	struct btsnoop_pkt pkt;
-	uint32_t toread, flags;
-	uint64_t ts;
-	ssize_t len;
-
-	if (btsnoop_fd < 0)
-		return -1;
-
-	len = read(btsnoop_fd, &pkt, BTSNOOP_PKT_SIZE);
-	if (len == 0)
-		return -1;
-
-	if (len < 0 || len != BTSNOOP_PKT_SIZE) {
-		perror("Failed to read packet");
-		close(btsnoop_fd);
-		btsnoop_fd = -1;
-		return -1;
-	}
-
-	toread = be32toh(pkt.size);
-	flags = be32toh(pkt.flags);
-
-	ts = be64toh(pkt.ts) - 0x00E03AB44A676000ll;
-	tv->tv_sec = (ts / 1000000ll) + 946684800ll;
-	tv->tv_usec = ts % 1000000ll;
-
-	switch (btsnoop_type) {
-	case BTSNOOP_TYPE_SIMULATOR:
-		if ((flags >> 16) != 1)
-			break;
-		*frequency = flags & 0xffff;
-		break;
-
-	default:
-		fprintf(stderr, "Unknown packet type\n");
-		close(btsnoop_fd);
-		btsnoop_fd = -1;
-		return -1;
-	}
-
-	len = read(btsnoop_fd, data, toread);
-	if (len < 0) {
-		perror("Failed to read data");
-		close(btsnoop_fd);
-		btsnoop_fd = -1;
-		return -1;
-	}
-
-	*size = toread;
-
-	return 0;
-}
-
-void btsnoop_close(void)
-{
-	if (btsnoop_fd < 0)
-		return;
-
-	close(btsnoop_fd);
-	btsnoop_fd = -1;
-
-	btsnoop_index = 0xffff;
-}
diff --git a/monitor/btsnoop.h b/monitor/btsnoop.h
deleted file mode 100644
index be4e2ed..0000000
--- a/monitor/btsnoop.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2011-2014  Intel Corporation
- *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
- *
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public
- *  License as published by the Free Software Foundation; either
- *  version 2.1 of the License, or (at your option) any later version.
- *
- *  This library 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
- *  Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#include <stdint.h>
-#include <sys/time.h>
-
-#define BTSNOOP_TYPE_HCI		1001
-#define BTSNOOP_TYPE_UART		1002
-#define BTSNOOP_TYPE_BCSP		1003
-#define BTSNOOP_TYPE_3WIRE		1004
-#define BTSNOOP_TYPE_MONITOR		2001
-#define BTSNOOP_TYPE_SIMULATOR		2002
-
-#define BTSNOOP_OPCODE_NEW_INDEX	0
-#define BTSNOOP_OPCODE_DEL_INDEX	1
-#define BTSNOOP_OPCODE_COMMAND_PKT	2
-#define BTSNOOP_OPCODE_EVENT_PKT	3
-#define BTSNOOP_OPCODE_ACL_TX_PKT	4
-#define BTSNOOP_OPCODE_ACL_RX_PKT	5
-#define BTSNOOP_OPCODE_SCO_TX_PKT	6
-#define BTSNOOP_OPCODE_SCO_RX_PKT	7
-
-struct btsnoop_opcode_new_index {
-	uint8_t  type;
-	uint8_t  bus;
-	uint8_t  bdaddr[6];
-	char     name[8];
-} __attribute__((packed));
-
-void btsnoop_create(const char *path, uint32_t type);
-void btsnoop_write(struct timeval *tv, uint32_t flags,
-					const void *data, uint16_t size);
-void btsnoop_write_hci(struct timeval *tv, uint16_t index, uint16_t opcode,
-					const void *data, uint16_t size);
-void btsnoop_write_phy(struct timeval *tv, uint16_t frequency,
-					const void *data, uint16_t size);
-int btsnoop_open(const char *path, uint32_t *type);
-int btsnoop_read_hci(struct timeval *tv, uint16_t *index, uint16_t *opcode,
-						void *data, uint16_t *size);
-int btsnoop_read_phy(struct timeval *tv, uint16_t *frequency,
-						void *data, uint16_t *size);
-void btsnoop_close(void);
diff --git a/monitor/packet.c b/monitor/packet.c
index d6c74e9..d40b69d 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -59,6 +59,9 @@
 
 #define COLOR_NEW_INDEX			COLOR_GREEN
 #define COLOR_DEL_INDEX			COLOR_RED
+#define COLOR_OPEN_INDEX		COLOR_GREEN
+#define COLOR_CLOSE_INDEX		COLOR_RED
+#define COLOR_INDEX_INFO		COLOR_GREEN
 
 #define COLOR_HCI_COMMAND		COLOR_BLUE
 #define COLOR_HCI_COMMAND_UNKNOWN	COLOR_WHITE_BG
@@ -3664,8 +3667,9 @@
 #define MAX_INDEX 16
 
 struct index_data {
-	uint8_t type;
-	uint8_t bdaddr[6];
+	uint8_t  type;
+	uint8_t  bdaddr[6];
+	uint16_t manufacturer;
 };
 
 static struct index_data index_list[MAX_INDEX];
@@ -3674,7 +3678,9 @@
 					const void *data, uint16_t size)
 {
 	const struct btsnoop_opcode_new_index *ni;
+	const struct btsnoop_opcode_index_info *ii;
 	char str[18], extra_str[24];
+	uint16_t manufacturer;
 
 	if (index_filter && index_number != index)
 		return;
@@ -3722,6 +3728,34 @@
 	case BTSNOOP_OPCODE_SCO_RX_PKT:
 		packet_hci_scodata(tv, index, true, data, size);
 		break;
+	case BTSNOOP_OPCODE_OPEN_INDEX:
+		if (index < MAX_INDEX)
+			addr2str(index_list[index].bdaddr, str);
+		else
+			sprintf(str, "00:00:00:00:00:00");
+
+		packet_open_index(tv, index, str);
+		break;
+	case BTSNOOP_OPCODE_CLOSE_INDEX:
+		if (index < MAX_INDEX)
+			addr2str(index_list[index].bdaddr, str);
+		else
+			sprintf(str, "00:00:00:00:00:00");
+
+		packet_close_index(tv, index, str);
+		break;
+	case BTSNOOP_OPCODE_INDEX_INFO:
+		ii = data;
+		manufacturer = le16_to_cpu(ii->manufacturer);
+
+		if (index < MAX_INDEX) {
+			memcpy(index_list[index].bdaddr, ii->bdaddr, 6);
+			index_list[index].manufacturer = manufacturer;
+		}
+
+		addr2str(ii->bdaddr, str);
+		packet_index_info(tv, index, str, manufacturer);
+		break;
 	default:
 		sprintf(extra_str, "(code %d len %d)", opcode, size);
 		print_packet(tv, index, '*', COLOR_ERROR,
@@ -8447,6 +8481,29 @@
 							label, NULL);
 }
 
+void packet_open_index(struct timeval *tv, uint16_t index, const char *label)
+{
+	print_packet(tv, index, '=', COLOR_OPEN_INDEX, "Open Index",
+							label, NULL);
+}
+
+void packet_close_index(struct timeval *tv, uint16_t index, const char *label)
+{
+	print_packet(tv, index, '=', COLOR_CLOSE_INDEX, "Close Index",
+							label, NULL);
+}
+
+void packet_index_info(struct timeval *tv, uint16_t index, const char *label,
+							uint16_t manufacturer)
+{
+	char details[128];
+
+	sprintf(details, "(%s)", bt_compidtostr(manufacturer));
+
+	print_packet(tv, index, '=', COLOR_INDEX_INFO, "Index Info",
+							label, details);
+}
+
 void packet_hci_command(struct timeval *tv, uint16_t index,
 					const void *data, uint16_t size)
 {
diff --git a/monitor/packet.h b/monitor/packet.h
index c39816b..43fabc0 100644
--- a/monitor/packet.h
+++ b/monitor/packet.h
@@ -63,6 +63,10 @@
 void packet_new_index(struct timeval *tv, uint16_t index, const char *label,
 				uint8_t type, uint8_t bus, const char *name);
 void packet_del_index(struct timeval *tv, uint16_t index, const char *label);
+void packet_open_index(struct timeval *tv, uint16_t index, const char *label);
+void packet_close_index(struct timeval *tv, uint16_t index, const char *label);
+void packet_index_info(struct timeval *tv, uint16_t index, const char *label,
+							uint16_t manufacturer);
 
 void packet_hci_command(struct timeval *tv, uint16_t index,
 					const void *data, uint16_t size);
diff --git a/monitor/uuid.c b/monitor/uuid.c
index 1912661..fe79f3f 100644
--- a/monitor/uuid.c
+++ b/monitor/uuid.c
@@ -334,6 +334,130 @@
 	{ 0x2aa1, "Magnetic Flux Density - 3D"			},
 	{ 0x2aa2, "Language"					},
 	{ 0x2aa3, "Barometric Pressure Trend"			},
+	/* vendor defined */
+	{ 0xfeff, "GN Netcom"					},
+	{ 0xfefe, "GN ReSound A/S"				},
+	{ 0xfefd, "Gimbal, Inc."				},
+	{ 0xfefc, "Gimbal, Inc."				},
+	{ 0xfefb, "Stollmann E+V GmbH"				},
+	{ 0xfefa, "PayPal, Inc."				},
+	{ 0xfef9, "PayPal, Inc."				},
+	{ 0xfef8, "Aplix Corporation"				},
+	{ 0xfef7, "Aplix Corporation"				},
+	{ 0xfef6, "Wicentric, Inc."				},
+	{ 0xfef5, "Dialog Semiconductor GmbH"			},
+	{ 0xfef4, "Google"					},
+	{ 0xfef3, "Google"					},
+	{ 0xfef2, "CSR"						},
+	{ 0xfef1, "CSR"						},
+	{ 0xfef0, "Intel"					},
+	{ 0xfeef, "Polar Electro Oy"				},
+	{ 0xfeee, "Polar Electro Oy"				},
+	{ 0xfeed, "Tile, Inc."					},
+	{ 0xfeec, "Tile, Inc."					},
+	{ 0xfeeb, "Swirl Networks, Inc."			},
+	{ 0xfeea, "Swirl Networks, Inc."			},
+	{ 0xfee9, "Quintic Corp."				},
+	{ 0xfee8, "Quintic Corp."				},
+	{ 0xfee7, "Tencent Holdings Limited"			},
+	{ 0xfee6, "Seed Labs, Inc."				},
+	{ 0xfee5, "Nordic Semiconductor ASA"			},
+	{ 0xfee4, "Nordic Semiconductor ASA"			},
+	{ 0xfee3, "Anki, Inc."					},
+	{ 0xfee2, "Anki, Inc."					},
+	{ 0xfee1, "Anhui Huami Information Technology Co."	},
+	{ 0xfee0, "Anhui Huami Information Technology Co."	},
+	{ 0xfedf, "Design SHIFT"				},
+	{ 0xfede, "Coin, Inc."					},
+	{ 0xfedd, "Jawbone"					},
+	{ 0xfedc, "Jawbone"					},
+	{ 0xfedb, "Perka, Inc."					},
+	{ 0xfeda, "ISSC Technologies Corporation"		},
+	{ 0xfed9, "Pebble Technology Corporation"		},
+	{ 0xfed8, "Google"					},
+	{ 0xfed7, "Broadcom Corporation"			},
+	{ 0xfed6, "Broadcom Corporation"			},
+	{ 0xfed5, "Plantronics Inc."				},
+	{ 0xfed4, "Apple, Inc."					},
+	{ 0xfed3, "Apple, Inc."					},
+	{ 0xfed2, "Apple, Inc."					},
+	{ 0xfed1, "Apple, Inc."					},
+	{ 0xfed0, "Apple, Inc."					},
+	{ 0xfecf, "Apple, Inc."					},
+	{ 0xfece, "Apple, Inc."					},
+	{ 0xfecd, "Apple, Inc."					},
+	{ 0xfecc, "Apple, Inc."					},
+	{ 0xfecb, "Apple, Inc."					},
+	{ 0xfeca, "Apple, Inc."					},
+	{ 0xfec9, "Apple, Inc."					},
+	{ 0xfec8, "Apple, Inc."					},
+	{ 0xfec7, "Apple, Inc."					},
+	{ 0xfec6, "Kocomojo, LLC"				},
+	{ 0xfec5, "Realtek Semiconductor Corp."			},
+	{ 0xfec4, "PLUS Location Systems"			},
+	{ 0xfec3, "360fly, Inc."				},
+	{ 0xfec2, "Blue Spark Technologies, Inc."		},
+	{ 0xfec1, "KDDI Corporation"				},
+	{ 0xfec0, "KDDI Corporation"				},
+	{ 0xfebf, "Nod, Inc."					},
+	{ 0xfebe, "Bose Corporation"				},
+	{ 0xfebd, "Clover Network, Inc."			},
+	{ 0xfebc, "Dexcom, Inc."				},
+	{ 0xfebb, "adafruit industries"				},
+	{ 0xfeba, "Tencent Holdings Limited"			},
+	{ 0xfeb9, "LG Electronics"				},
+	{ 0xfeb8, "Facebook, Inc."				},
+	{ 0xfeb7, "Facebook, Inc."				},
+	{ 0xfeb6, "Vencer Co, Ltd"				},
+	{ 0xfeb5, "WiSilica Inc."				},
+	{ 0xfeb4, "WiSilica Inc."				},
+	{ 0xfeb3, "Taobao"					},
+	{ 0xfeb2, "Microsoft Corporation"			},
+	{ 0xfeb1, "Electronics Tomorrow Limited"		},
+	{ 0xfeb0, "Nest Labs Inc."				},
+	{ 0xfeaf, "Nest Labs Inc."				},
+	{ 0xfeae, "Nokia Corporation"				},
+	{ 0xfead, "Nokia Corporation"				},
+	{ 0xfeac, "Nokia Corporation"				},
+	{ 0xfeab, "Nokia Corporation"				},
+	{ 0xfeaa, "Google"					},
+	{ 0xfea9, "Savant Systems LLC"				},
+	{ 0xfea8, "Savant Systems LLC"				},
+	{ 0xfea7, "UTC Fire and Security"			},
+	{ 0xfea6, "GoPro, Inc."					},
+	{ 0xfea5, "GoPro, Inc."					},
+	{ 0xfea4, "Paxton Access Ltd"				},
+	{ 0xfea3, "ITT Industries"				},
+	{ 0xfea2, "Intrepid Control Systems, Inc."		},
+	{ 0xfea1, "Intrepid Control Systems, Inc."		},
+	{ 0xfea0, "Google"					},
+	{ 0xfe9f, "Google"					},
+	{ 0xfe9e, "Dialog Semiconductor B.V."			},
+	{ 0xfe9d, "Mobiquity Networks Inc"			},
+	{ 0xfe9c, "GSI Laboratories, Inc."			},
+	{ 0xfe9b, "Samsara Networks, Inc"			},
+	{ 0xfe9a, "Estimote"					},
+	{ 0xfe99, "Currant, Inc."				},
+	{ 0xfe98, "Currant, Inc."				},
+	{ 0xfe97, "Tesla Motor Inc."				},
+	{ 0xfe96, "Tesla Motor Inc."				},
+	{ 0xfe95, "Xiaomi Inc."					},
+	{ 0xfe94, "OttoQ Inc."					},
+	{ 0xfe93, "OttoQ Inc."					},
+	{ 0xfe92, "Jarden Safety & Security"			},
+	{ 0xfe91, "Shanghai Imilab Technology Co.,Ltd"		},
+	{ 0xfe90, "JUMA"					},
+	{ 0xfe8f, "CSR"						},
+	{ 0xfe8e, "ARM Ltd"					},
+	{ 0xfe8d, "Interaxon Inc."				},
+	{ 0xfe8c, "TRON Forum"					},
+	{ 0xfe8b, "Apple, Inc."					},
+	{ 0xfe8a, "Apple, Inc."					},
+	{ 0xfe89, "B&O Play A/S"				},
+	{ 0xfe88, "SALTO SYSTEMS S.L."				},
+	/* SDO defined */
+	{ 0xfffe, "Alliance for Wireless Power (A4WP)"		},
+	{ 0xfffd, "Fast IDentity Online Alliance (FIDO)"	},
 	{ }
 };
 
diff --git a/obexd/client/session.c b/obexd/client/session.c
index d6da0a7..7248768 100644
--- a/obexd/client/session.c
+++ b/obexd/client/session.c
@@ -112,6 +112,7 @@
 	GQueue *queue;
 	guint process_id;
 	char *folder;
+	struct callback_data *callback;
 };
 
 static GSList *sessions = NULL;
@@ -303,6 +304,16 @@
 	session_free(session);
 }
 
+static void callback_destroy(struct callback_data *callback, GError *err)
+{
+	struct obc_session *session = callback->session;
+
+	callback->func(session, NULL, err, callback->data);
+	g_free(callback);
+	session->callback = NULL;
+	obc_session_unref(session);
+}
+
 static void connect_cb(GObex *obex, GError *err, GObexPacket *rsp,
 							gpointer user_data)
 {
@@ -322,11 +333,9 @@
 				"OBEX Connect failed with 0x%02x", rsp_code);
 
 done:
-	callback->func(callback->session, NULL, gerr, callback->data);
+	callback_destroy(callback, gerr);
 	if (gerr != NULL)
 		g_error_free(gerr);
-	obc_session_unref(callback->session);
-	g_free(callback);
 }
 
 static void session_disconnected(GObex *obex, GError *err, gpointer user_data)
@@ -415,17 +424,28 @@
 
 	return;
 done:
-	callback->func(callback->session, NULL, err, callback->data);
-	obc_session_unref(callback->session);
-	g_free(callback);
+	callback_destroy(callback, err);
 }
 
 static void owner_disconnected(DBusConnection *connection, void *user_data)
 {
 	struct obc_session *session = user_data;
+	GError *err;
 
 	DBG("");
 
+	/*
+	 * If connection still connecting notify the callback to destroy the
+	 * session.
+	 */
+	if (session->callback) {
+		err = g_error_new(OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
+						"Session closed by user");
+		callback_destroy(session->callback, err);
+		g_error_free(err);
+		return;
+	}
+
 	obc_session_shutdown(session);
 }
 
@@ -531,6 +551,8 @@
 		return -EINVAL;
 	}
 
+	session->callback = callback;
+
 	return 0;
 }
 
diff --git a/plugins/policy.c b/plugins/policy.c
index 6bb8268..218a3ed 100644
--- a/plugins/policy.c
+++ b/plugins/policy.c
@@ -80,6 +80,8 @@
 static unsigned int service_id = 0;
 static GSList *devices = NULL;
 
+static bool auto_enable = false;
+
 struct policy_data {
 	struct btd_device *dev;
 
@@ -743,6 +745,20 @@
 	reconnect_set_timer(reconnect);
 }
 
+static int policy_adapter_probe(struct btd_adapter *adapter)
+{
+	DBG("");
+
+	btd_adapter_restore_powered(adapter);
+
+	return 0;
+}
+
+static struct btd_adapter_driver policy_driver = {
+	.name	= "policy",
+	.probe	= policy_adapter_probe,
+};
+
 static int policy_init(void)
 {
 	GError *gerr = NULL;
@@ -758,7 +774,7 @@
 						sizeof(*reconnect_intervals);
 		reconnect_intervals = g_memdup(default_intervals,
 						reconnect_intervals_len);
-		goto add_cb;
+		goto done;
 	}
 
 	reconnect_uuids = g_key_file_get_string_list(conf, "Policy",
@@ -788,12 +804,18 @@
 						reconnect_intervals_len);
 	}
 
-add_cb:
+	auto_enable = g_key_file_get_boolean(conf, "Policy", "AutoEnable",
+									NULL);
+
+done:
 	if (reconnect_uuids && reconnect_uuids[0] && reconnect_attempts) {
 		btd_add_disconnect_cb(disconnect_cb);
 		btd_add_conn_fail_cb(conn_fail_cb);
 	}
 
+	if (auto_enable)
+		btd_register_adapter_driver(&policy_driver);
+
 	return 0;
 }
 
@@ -812,6 +834,9 @@
 	g_slist_free_full(devices, policy_remove);
 
 	btd_service_remove_state_cb(service_id);
+
+	if (auto_enable)
+		btd_unregister_adapter_driver(&policy_driver);
 }
 
 BLUETOOTH_PLUGIN_DEFINE(policy, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 37f7a59..52ee767 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -1710,8 +1710,7 @@
 	for (i = 0; i < seid_count; i++, seid++) {
 		failed_seid = seid->seid;
 
-		sep = find_local_sep_by_seid(session,
-					req->first_seid.seid);
+		sep = find_local_sep_by_seid(session, seid->seid);
 		if (!sep || !sep->stream) {
 			err = AVDTP_BAD_ACP_SEID;
 			goto failed;
@@ -1822,8 +1821,7 @@
 	for (i = 0; i < seid_count; i++, seid++) {
 		failed_seid = seid->seid;
 
-		sep = find_local_sep_by_seid(session,
-					req->first_seid.seid);
+		sep = find_local_sep_by_seid(session, seid->seid);
 		if (!sep || !sep->stream) {
 			err = AVDTP_BAD_ACP_SEID;
 			goto failed;
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index de7590a..9187692 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -103,6 +103,7 @@
 #define AVRCP_REQUEST_CONTINUING	0x40
 #define AVRCP_ABORT_CONTINUING		0x41
 #define AVRCP_SET_ABSOLUTE_VOLUME	0x50
+#define AVRCP_SET_ADDRESSED_PLAYER	0x60
 #define AVRCP_SET_BROWSED_PLAYER	0x70
 #define AVRCP_GET_FOLDER_ITEMS		0x71
 #define AVRCP_CHANGE_PATH		0x72
@@ -136,6 +137,13 @@
 #define AVRCP_CHARSET_UTF8		106
 
 #define AVRCP_BROWSING_TIMEOUT		1
+#define AVRCP_CT_VERSION		0x0106
+#define AVRCP_TG_VERSION		0x0105
+
+#define AVRCP_SCOPE_MEDIA_PLAYER_LIST			0x00
+#define AVRCP_SCOPE_MEDIA_PLAYER_VFS			0x01
+#define AVRCP_SCOPE_SEARCH				0x02
+#define AVRCP_SCOPE_NOW_PLAYING			0x03
 
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 
@@ -175,6 +183,30 @@
 } __attribute__ ((packed));
 #define AVRCP_BROWSING_HEADER_LENGTH 3
 
+struct get_folder_items_rsp {
+	uint8_t status;
+	uint16_t uid_counter;
+	uint16_t num_items;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+struct folder_item {
+	uint8_t type;
+	uint16_t len;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+struct player_item {
+	uint16_t player_id;
+	uint8_t type;
+	uint32_t subtype;
+	uint8_t status;
+	uint8_t features[16];
+	uint16_t charset;
+	uint16_t namelen;
+	char name[0];
+} __attribute__ ((packed));
+
 struct avrcp_server {
 	struct btd_adapter *adapter;
 	uint32_t tg_record_id;
@@ -204,8 +236,10 @@
 	uint64_t uid;
 	uint16_t uid_counter;
 	bool browsed;
+	bool addressed;
 	uint8_t *features;
 	char *path;
+	guint changed_id;
 
 	struct pending_list_items *p;
 	char *change_path;
@@ -258,6 +292,13 @@
 static GSList *servers = NULL;
 static unsigned int avctp_id = 0;
 
+/* Default feature bit mask for media player as per avctp.c:key_map */
+static const uint8_t features[16] = {
+				0xF8, 0xBF, 0xFF, 0xBF, 0x1F,
+				0xFB, 0x3F, 0x60, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00 };
+
 /* Company IDs supported by this device */
 static uint32_t company_ids[] = {
 	IEEEID_BTSIG,
@@ -274,7 +315,7 @@
 	sdp_record_t *record;
 	sdp_data_t *psm[2], *version, *features;
 	uint16_t lp = AVCTP_CONTROL_PSM, ap = AVCTP_BROWSING_PSM;
-	uint16_t avrcp_ver = 0x0106, avctp_ver = 0x0103;
+	uint16_t avctp_ver = 0x0103;
 	uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
 						AVRCP_FEATURE_CATEGORY_2 |
 						AVRCP_FEATURE_CATEGORY_3 |
@@ -329,7 +370,7 @@
 
 	/* Bluetooth Profile Descriptor List */
 	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
-	profile[0].version = avrcp_ver;
+	profile[0].version = AVRCP_CT_VERSION;
 	pfseq = sdp_list_append(NULL, &profile[0]);
 	sdp_set_profile_descs(record, pfseq);
 
@@ -367,7 +408,7 @@
 	sdp_list_t *aproto_browsing, *proto_browsing[2] = {0};
 	uint16_t lp = AVCTP_CONTROL_PSM;
 	uint16_t lp_browsing = AVCTP_BROWSING_PSM;
-	uint16_t avrcp_ver = 0x0104, avctp_ver = 0x0103;
+	uint16_t avctp_ver = 0x0103;
 	uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
 					AVRCP_FEATURE_CATEGORY_2 |
 					AVRCP_FEATURE_CATEGORY_3 |
@@ -417,7 +458,7 @@
 
 	/* Bluetooth Profile Descriptor List */
 	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
-	profile[0].version = avrcp_ver;
+	profile[0].version = AVRCP_TG_VERSION;
 	pfseq = sdp_list_append(NULL, &profile[0]);
 	sdp_set_profile_descs(record, pfseq);
 
@@ -627,6 +668,7 @@
 {
 	uint8_t buf[AVRCP_HEADER_LENGTH + 9];
 	struct avrcp_header *pdu = (void *) buf;
+	uint8_t code;
 	uint16_t size;
 	GSList *l;
 	int attr;
@@ -640,10 +682,19 @@
 	set_company_id(pdu->company_id, IEEEID_BTSIG);
 
 	pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
-	pdu->params[0] = id;
 
 	DBG("id=%u", id);
 
+	if (id != AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED && player->changed_id) {
+		code = AVC_CTYPE_REJECTED;
+		size = 1;
+		pdu->params[0] = AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED;
+		goto done;
+	}
+
+	code = AVC_CTYPE_CHANGED;
+	pdu->params[0] = id;
+
 	switch (id) {
 	case AVRCP_EVENT_STATUS_CHANGED:
 		size = 2;
@@ -674,6 +725,11 @@
 		pdu->params[size++] = attr;
 		pdu->params[size++] = val;
 		break;
+	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		size = 5;
+		memcpy(&pdu->params[1], &player->id, sizeof(uint16_t));
+		memcpy(&pdu->params[3], &player->uid_counter, sizeof(uint16_t));
+		break;
 	case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
 		size = 1;
 		break;
@@ -682,6 +738,7 @@
 		return;
 	}
 
+done:
 	pdu->params_len = htons(size);
 
 	for (l = player->sessions; l; l = l->next) {
@@ -693,8 +750,9 @@
 
 		err = avctp_send_vendordep(session->conn,
 					session->transaction_events[id],
-					AVC_CTYPE_CHANGED, AVC_SUBUNIT_PANEL,
+					code, AVC_SUBUNIT_PANEL,
 					buf, size + AVRCP_HEADER_LENGTH);
+
 		if (err < 0)
 			continue;
 
@@ -1306,6 +1364,22 @@
 	return play_status_to_val(value);
 }
 
+static uint16_t player_get_id(struct avrcp_player *player)
+{
+	if (player == NULL)
+		return 0x0000;
+
+	return player->id;
+}
+
+static uint16_t player_get_uid_counter(struct avrcp_player *player)
+{
+	if (player == NULL)
+		return 0x0000;
+
+	return player->uid_counter;
+}
+
 static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
 						struct avrcp_header *pdu,
 						uint8_t transaction)
@@ -1493,6 +1567,13 @@
 			pdu->params[len++] = val;
 		}
 
+		g_list_free(settings);
+
+		break;
+	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		len = 5;
+		bt_put_be16(player_get_id(player), &pdu->params[1]);
+		bt_put_be16(player_get_uid_counter(player), &pdu->params[3]);
 		break;
 	case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
 		len = 1;
@@ -1595,16 +1676,12 @@
 						struct avrcp_header *pdu,
 						uint8_t transaction)
 {
-	struct avrcp_player *player = session->controller->player;
 	uint16_t len = ntohs(pdu->params_len);
 	uint8_t volume;
 
 	if (len != 1)
 		goto err;
 
-	if (!player)
-		goto err;
-
 	volume = pdu->params[0] & 0x7F;
 
 	media_transport_update_device_volume(session->dev, volume);
@@ -1617,6 +1694,90 @@
 	return AVC_CTYPE_REJECTED;
 }
 
+static struct avrcp_player *find_tg_player(struct avrcp *session, uint16_t id)
+{
+	struct avrcp_server *server = session->server;
+	GSList *l;
+
+	for (l = server->players; l; l = l->next) {
+		struct avrcp_player *player = l->data;
+
+		if (player->id == id)
+			return player;
+	}
+
+	return NULL;
+}
+
+static gboolean notify_addressed_player_changed(gpointer user_data)
+{
+	struct avrcp_player *player = user_data;
+	uint8_t events[6] = { AVRCP_EVENT_STATUS_CHANGED,
+					AVRCP_EVENT_TRACK_CHANGED,
+					AVRCP_EVENT_TRACK_REACHED_START,
+					AVRCP_EVENT_TRACK_REACHED_END,
+					AVRCP_EVENT_SETTINGS_CHANGED,
+					AVRCP_EVENT_PLAYBACK_POS_CHANGED
+				};
+	uint8_t i;
+
+	avrcp_player_event(player, AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED, NULL);
+
+	/*
+	 * TG shall complete all player specific
+	 * notifications with AV/C C-Type REJECTED
+	 * with error code as Addressed Player Changed.
+	 */
+	for (i = 0; i < sizeof(events); i++)
+		avrcp_player_event(player, events[i], NULL);
+
+	player->changed_id = 0;
+
+	return FALSE;
+}
+
+static uint8_t avrcp_handle_set_addressed_player(struct avrcp *session,
+						struct avrcp_header *pdu,
+						uint8_t transaction)
+{
+	struct avrcp_player *player;
+	uint16_t len = ntohs(pdu->params_len);
+	uint16_t player_id = 0;
+	uint8_t status;
+
+	if (len < 1) {
+		status = AVRCP_STATUS_INVALID_PARAM;
+		goto err;
+	}
+
+	player_id = bt_get_be16(&pdu->params[0]);
+	player = find_tg_player(session, player_id);
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+	if (player) {
+		player->addressed = true;
+		status = AVRCP_STATUS_SUCCESS;
+		pdu->params_len = htons(len);
+		pdu->params[0] = status;
+	} else {
+		status = AVRCP_STATUS_INVALID_PLAYER_ID;
+		goto err;
+	}
+
+	/* Don't emit player changed immediately since PTS expect the
+	 * response of SetAddressedPlayer before the event.
+	 */
+	player->changed_id = g_idle_add(notify_addressed_player_changed,
+								player);
+
+	return AVC_CTYPE_ACCEPTED;
+
+err:
+	pdu->params_len = htons(sizeof(status));
+	pdu->params[0] = status;
+	return AVC_CTYPE_REJECTED;
+}
+
 static const struct control_pdu_handler control_handlers[] = {
 		{ AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS,
 					avrcp_handle_get_capabilities },
@@ -1648,6 +1809,8 @@
 					avrcp_handle_request_continuing },
 		{ AVRCP_ABORT_CONTINUING, AVC_CTYPE_CONTROL,
 					avrcp_handle_abort_continuing },
+		{ AVRCP_SET_ADDRESSED_PLAYER, AVC_CTYPE_CONTROL,
+					avrcp_handle_set_addressed_player },
 		{ },
 };
 
@@ -1710,11 +1873,122 @@
 	return AVRCP_HEADER_LENGTH + 1;
 }
 
+static void avrcp_handle_media_player_list(struct avrcp *session,
+				struct avrcp_browsing_header *pdu,
+				uint32_t start_item, uint32_t end_item)
+{
+	struct avrcp_player *player = session->target->player;
+	struct get_folder_items_rsp *rsp;
+	const char *name = NULL;
+	GSList *l;
+
+	rsp = (void *)pdu->params;
+	rsp->status = AVRCP_STATUS_SUCCESS;
+	rsp->uid_counter = htons(player_get_uid_counter(player));
+	rsp->num_items = 0;
+	pdu->param_len = sizeof(*rsp);
+
+	for (l = g_slist_nth(session->server->players, start_item);
+					l; l = g_slist_next(l)) {
+		struct avrcp_player *player = l->data;
+		struct folder_item *folder;
+		struct player_item *item;
+		uint16_t namelen;
+
+		if (rsp->num_items == (end_item - start_item) + 1)
+			break;
+
+		folder = (void *)&pdu->params[pdu->param_len];
+		folder->type = 0x01; /* Media Player */
+
+		pdu->param_len += sizeof(*folder);
+
+		item = (void *)folder->data;
+		item->player_id = htons(player->id);
+		item->type = 0x01; /* Audio */
+		item->subtype = htonl(0x01); /* Audio Book */
+		item->status = player_get_status(player);
+		/* Assign Default Feature Bit Mask */
+		memcpy(&item->features, &features, sizeof(features));
+
+		item->charset = htons(AVRCP_CHARSET_UTF8);
+
+		name = player->cb->get_name(player->user_data);
+		namelen = strlen(name);
+		item->namelen = htons(namelen);
+		memcpy(item->name, name, namelen);
+
+		folder->len = htons(sizeof(*item) + namelen);
+		pdu->param_len += sizeof(*item) + namelen;
+		rsp->num_items++;
+	}
+
+	/* If no player could be found respond with an error */
+	if (!rsp->num_items)
+		goto failed;
+
+	rsp->num_items = htons(rsp->num_items);
+	pdu->param_len = htons(pdu->param_len);
+
+	return;
+
+failed:
+	pdu->params[0] = AVRCP_STATUS_OUT_OF_BOUNDS;
+	pdu->param_len = htons(1);
+}
+
+static void avrcp_handle_get_folder_items(struct avrcp *session,
+				struct avrcp_browsing_header *pdu,
+				uint8_t transaction)
+{
+	uint32_t start_item = 0;
+	uint32_t end_item = 0;
+	uint8_t scope;
+	uint8_t status = AVRCP_STATUS_SUCCESS;
+
+	if (ntohs(pdu->param_len) < 10) {
+		status = AVRCP_STATUS_INVALID_PARAM;
+		goto failed;
+	}
+
+	scope = pdu->params[0];
+	start_item = bt_get_be32(&pdu->params[1]);
+	end_item = bt_get_be32(&pdu->params[5]);
+
+	DBG("scope 0x%02x start_item 0x%08x end_item 0x%08x", scope,
+				start_item, end_item);
+
+	if (end_item < start_item) {
+		status = AVRCP_STATUS_INVALID_PARAM;
+		goto failed;
+	}
+
+	switch (scope) {
+	case AVRCP_SCOPE_MEDIA_PLAYER_LIST:
+		avrcp_handle_media_player_list(session, pdu,
+						start_item, end_item);
+		break;
+	case AVRCP_SCOPE_MEDIA_PLAYER_VFS:
+	case AVRCP_SCOPE_SEARCH:
+	case AVRCP_SCOPE_NOW_PLAYING:
+	default:
+		status = AVRCP_STATUS_INVALID_PARAM;
+		goto failed;
+	}
+
+	return;
+
+failed:
+	pdu->params[0] = status;
+	pdu->param_len = htons(1);
+}
+
 static struct browsing_pdu_handler {
 	uint8_t pdu_id;
 	void (*func) (struct avrcp *session, struct avrcp_browsing_header *pdu,
 							uint8_t transaction);
 } browsing_handlers[] = {
+		{ AVRCP_GET_FOLDER_ITEMS, avrcp_handle_get_folder_items },
 		{ },
 };
 
@@ -2917,6 +3191,15 @@
 	.total_items = ct_get_total_numberofitems,
 };
 
+static void set_ct_player(struct avrcp *session, struct avrcp_player *player)
+{
+	struct btd_service *service;
+
+	session->controller->player = player;
+	service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
+	control_set_player(service, media_player_get_path(player->user_data));
+}
+
 static struct avrcp_player *create_ct_player(struct avrcp *session,
 								uint16_t id)
 {
@@ -2938,7 +3221,7 @@
 	player->destroy = (GDestroyNotify) media_player_destroy;
 
 	if (session->controller->player == NULL)
-		session->controller->player = player;
+		set_ct_player(session, player);
 
 	session->controller->players = g_slist_prepend(
 						session->controller->players,
@@ -3029,6 +3312,9 @@
 	if (player->destroy)
 		player->destroy(player->user_data);
 
+	if (player->changed_id > 0)
+		g_source_remove(player->changed_id);
+
 	g_slist_free(player->sessions);
 	g_free(player->path);
 	g_free(player->change_path);
@@ -3043,10 +3329,15 @@
 
 	for (l = player->sessions; l; l = l->next) {
 		struct avrcp *session = l->data;
+		struct avrcp_data *controller = session->controller;
 
-		session->controller->players = g_slist_remove(
-						session->controller->players,
-						player);
+		controller->players = g_slist_remove(controller->players,
+								player);
+
+		/* Check if current player is being removed */
+		if (controller->player == player)
+			set_ct_player(session, g_slist_nth_data(
+						controller->players, 0));
 	}
 
 	player_destroy(player);
@@ -3097,9 +3388,6 @@
 		i += len;
 	}
 
-	if (g_slist_find(removed, session->controller->player))
-		session->controller->player = NULL;
-
 	g_slist_free_full(removed, player_remove);
 
 	return FALSE;
@@ -3113,6 +3401,8 @@
 	memset(buf, 0, sizeof(buf));
 
 	pdu->pdu_id = AVRCP_GET_FOLDER_ITEMS;
+	put_be32(0, &pdu->params[1]);
+	put_be32(UINT32_MAX, &pdu->params[5]);
 	pdu->param_len = htons(10);
 
 	avctp_send_browsing_req(session->conn, buf, sizeof(buf),
@@ -3222,7 +3512,7 @@
 	}
 
 	player->uid_counter = get_be16(&pdu->params[3]);
-	session->controller->player = player;
+	set_ct_player(session, player);
 
 	if (player->features != NULL)
 		return;
@@ -3521,7 +3811,9 @@
 		return;
 
 	session->supported_events |=
-				(1 << AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED);
+				(1 << AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED) |
+				(1 << AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED) |
+				(1 << AVRCP_EVENT_VOLUME_CHANGED);
 
 	/* Only check capabilities if controller is not supported */
 	if (session->controller == NULL)
@@ -3547,9 +3839,6 @@
 
 	DBG("%p version 0x%04x", controller, controller->version);
 
-	if (controller->version >= 0x0104)
-		session->supported_events |= (1 << AVRCP_EVENT_VOLUME_CHANGED);
-
 	service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
 	btd_service_connecting_complete(service, 0);
 
@@ -3774,12 +4063,14 @@
 	struct avrcp_server *server;
 	struct avrcp_player *player;
 	GSList *l;
+	static uint16_t id = 0;
 
 	server = find_server(servers, adapter);
 	if (!server)
 		return NULL;
 
 	player = g_new0(struct avrcp_player, 1);
+	player->id = ++id;
 	player->server = server;
 	player->cb = cb;
 	player->user_data = user_data;
@@ -3827,9 +4118,10 @@
 			target->player = g_slist_nth_data(server->players, 0);
 	}
 
-	player_destroy(player);
 	avrcp_player_event(player,
 				AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED, NULL);
+
+	player_destroy(player);
 }
 
 static gboolean avrcp_handle_set_volume(struct avctp *conn,
@@ -3854,7 +4146,53 @@
 	return FALSE;
 }
 
-int avrcp_set_volume(struct btd_device *dev, uint8_t volume)
+static int avrcp_event(struct avrcp *session, uint8_t id, const void *data)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH + 2];
+	struct avrcp_header *pdu = (void *) buf;
+	uint8_t code;
+	uint16_t size;
+	int err;
+
+	/* Verify that the event is registered */
+	if (!(session->registered_events & (1 << id)))
+		return -ENOENT;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
+	code = AVC_CTYPE_CHANGED;
+	pdu->params[0] = id;
+
+	DBG("id=%u", id);
+
+	switch (id) {
+	case AVRCP_EVENT_VOLUME_CHANGED:
+		size = 2;
+		memcpy(&pdu->params[1], data, sizeof(uint8_t));
+		break;
+	default:
+		error("Unknown event %u", id);
+		return -EINVAL;
+	}
+
+	pdu->params_len = htons(size);
+
+	err = avctp_send_vendordep(session->conn,
+					session->transaction_events[id],
+					code, AVC_SUBUNIT_PANEL,
+					buf, size + AVRCP_HEADER_LENGTH);
+	if (err < 0)
+		return err;
+
+	/* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
+	session->registered_events ^= 1 << id;
+
+	return err;
+}
+
+int avrcp_set_volume(struct btd_device *dev, uint8_t volume, bool notify)
 {
 	struct avrcp_server *server;
 	struct avrcp *session;
@@ -3866,18 +4204,23 @@
 		return -EINVAL;
 
 	session = find_session(server->sessions, dev);
-	if (session == NULL || session->target == NULL)
+	if (session == NULL)
 		return -ENOTCONN;
 
-	if (session->target->version < 0x0104)
+	if (notify) {
+		if (!session->target)
+			return -ENOTSUP;
+		return avrcp_event(session, AVRCP_EVENT_VOLUME_CHANGED,
+								&volume);
+	}
+
+	if (!session->controller || session->controller->version < 0x0104)
 		return -ENOTSUP;
 
 	memset(buf, 0, sizeof(buf));
 
 	set_company_id(pdu->company_id, IEEEID_BTSIG);
 
-	DBG("volume=%u", volume);
-
 	pdu->pdu_id = AVRCP_SET_ABSOLUTE_VOLUME;
 	pdu->params[0] = volume;
 	pdu->params_len = htons(1);
diff --git a/profiles/audio/avrcp.h b/profiles/audio/avrcp.h
index a9aeb1a..86d310c 100644
--- a/profiles/audio/avrcp.h
+++ b/profiles/audio/avrcp.h
@@ -93,6 +93,7 @@
 	const char *(*get_status) (void *user_data);
 	uint32_t (*get_position) (void *user_data);
 	uint32_t (*get_duration) (void *user_data);
+	const char *(*get_name) (void *user_data);
 	void (*set_volume) (uint8_t volume, struct btd_device *dev,
 							void *user_data);
 	bool (*play) (void *user_data);
@@ -102,7 +103,7 @@
 	bool (*previous) (void *user_data);
 };
 
-int avrcp_set_volume(struct btd_device *dev, uint8_t volume);
+int avrcp_set_volume(struct btd_device *dev, uint8_t volume, bool notify);
 
 struct avrcp_player *avrcp_register_player(struct btd_adapter *adapter,
 						struct avrcp_player_cb *cb,
diff --git a/profiles/audio/control.c b/profiles/audio/control.c
index f4656d8..edc4a98 100644
--- a/profiles/audio/control.c
+++ b/profiles/audio/control.c
@@ -59,6 +59,7 @@
 
 #include "avctp.h"
 #include "control.h"
+#include "player.h"
 
 static GSList *devices = NULL;
 
@@ -68,6 +69,7 @@
 	struct btd_service *target;
 	struct btd_service *remote;
 	unsigned int avctp_id;
+	const char *player;
 };
 
 static void state_changed(struct btd_device *dev, avctp_state_t old_state,
@@ -81,9 +83,12 @@
 	switch (new_state) {
 	case AVCTP_STATE_DISCONNECTED:
 		control->session = NULL;
+		control->player = NULL;
 
 		g_dbus_emit_property_changed(conn, path,
 					AUDIO_CONTROL_INTERFACE, "Connected");
+		g_dbus_emit_property_changed(conn, path,
+					AUDIO_CONTROL_INTERFACE, "Player");
 
 		break;
 	case AVCTP_STATE_CONNECTING:
@@ -215,21 +220,46 @@
 	return TRUE;
 }
 
+static gboolean control_player_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct control *control = data;
+
+	return control->player != NULL;
+}
+
+static gboolean control_get_player(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct control *control = data;
+
+	if (!control->player)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+							&control->player);
+
+	return TRUE;
+}
+
 static const GDBusMethodTable control_methods[] = {
-	{ GDBUS_METHOD("Play", NULL, NULL, control_play) },
-	{ GDBUS_METHOD("Pause", NULL, NULL, control_pause) },
-	{ GDBUS_METHOD("Stop", NULL, NULL, control_stop) },
-	{ GDBUS_METHOD("Next", NULL, NULL, control_next) },
-	{ GDBUS_METHOD("Previous", NULL, NULL, control_previous) },
-	{ GDBUS_METHOD("VolumeUp", NULL, NULL, control_volume_up) },
-	{ GDBUS_METHOD("VolumeDown", NULL, NULL, control_volume_down) },
-	{ GDBUS_METHOD("FastForward", NULL, NULL, control_fast_forward) },
-	{ GDBUS_METHOD("Rewind", NULL, NULL, control_rewind) },
+	{ GDBUS_DEPRECATED_METHOD("Play", NULL, NULL, control_play) },
+	{ GDBUS_DEPRECATED_METHOD("Pause", NULL, NULL, control_pause) },
+	{ GDBUS_DEPRECATED_METHOD("Stop", NULL, NULL, control_stop) },
+	{ GDBUS_DEPRECATED_METHOD("Next", NULL, NULL, control_next) },
+	{ GDBUS_DEPRECATED_METHOD("Previous", NULL, NULL, control_previous) },
+	{ GDBUS_DEPRECATED_METHOD("VolumeUp", NULL, NULL, control_volume_up) },
+	{ GDBUS_DEPRECATED_METHOD("VolumeDown", NULL, NULL,
+							control_volume_down) },
+	{ GDBUS_DEPRECATED_METHOD("FastForward", NULL, NULL,
+							control_fast_forward) },
+	{ GDBUS_DEPRECATED_METHOD("Rewind", NULL, NULL, control_rewind) },
 	{ }
 };
 
 static const GDBusPropertyTable control_properties[] = {
 	{ "Connected", "b", control_property_get_connected },
+	{ "Player", "o", control_get_player, NULL, control_player_exists },
 	{ }
 };
 
@@ -338,3 +368,22 @@
 
 	return 0;
 }
+
+int control_set_player(struct btd_service *service, const char *path)
+{
+	struct control *control = btd_service_get_user_data(service);
+
+	if (!control->session)
+		return -ENOTCONN;
+
+	if (g_strcmp0(control->player, path) == 0)
+		return -EALREADY;
+
+	control->player = path;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					device_get_path(control->dev),
+					AUDIO_CONTROL_INTERFACE, "Player");
+
+	return 0;
+}
diff --git a/profiles/audio/control.h b/profiles/audio/control.h
index 4bda896..aab2631 100644
--- a/profiles/audio/control.h
+++ b/profiles/audio/control.h
@@ -32,3 +32,5 @@
 
 int control_connect(struct btd_service *service);
 int control_disconnect(struct btd_service *service);
+
+int control_set_player(struct btd_service *service, const char *path);
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index ed441d0..69070bf 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -112,6 +112,7 @@
 	bool			next;
 	bool			previous;
 	bool			control;
+	char			*name;
 };
 
 static GSList *adapters = NULL;
@@ -534,7 +535,7 @@
 	struct a2dp_config_data *data = user_data;
 	gboolean *ret_value = ret;
 
-	data->cb(data->setup, *ret_value ? TRUE : FALSE);
+	data->cb(data->setup, ret_value ? *ret_value : FALSE);
 }
 
 static int set_config(struct a2dp_sep *sep, uint8_t *configuration,
@@ -964,6 +965,7 @@
 	g_free(mp->sender);
 	g_free(mp->path);
 	g_free(mp->status);
+	g_free(mp->name);
 	g_free(mp);
 }
 
@@ -1012,6 +1014,13 @@
 	return g_hash_table_lookup(mp->settings, key);
 }
 
+static const char *get_player_name(void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	return mp->name;
+}
+
 static void set_shuffle_setting(DBusMessageIter *iter, const char *value)
 {
 	const char *key = "Shuffle";
@@ -1272,6 +1281,7 @@
 	.get_position = get_position,
 	.get_duration = get_duration,
 	.get_status = get_status,
+	.get_name = get_player_name,
 	.set_volume = set_volume,
 	.play = play,
 	.stop = stop,
@@ -1607,6 +1617,25 @@
 	return TRUE;
 }
 
+static gboolean set_name(struct media_player *mp, DBusMessageIter *iter)
+{
+	const char *value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return FALSE;
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	if (g_strcmp0(mp->name, value) == 0)
+		return TRUE;
+
+	g_free(mp->name);
+
+	mp->name = g_strdup(value);
+
+	return TRUE;
+}
+
 static gboolean set_player_property(struct media_player *mp, const char *key,
 							DBusMessageIter *entry)
 {
@@ -1647,6 +1676,9 @@
 	if (strcasecmp(key, "CanControl") == 0)
 		return set_flag(mp, &var, &mp->control);
 
+	if (strcasecmp(key, "Identity") == 0)
+		return set_name(mp, &var);
+
 	DBG("%s not supported, ignoring", key);
 
 	return TRUE;
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index 147bcbf..4736396 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -1193,6 +1193,11 @@
 	return mp;
 }
 
+const char *media_player_get_path(struct media_player *mp)
+{
+	return mp->path;
+}
+
 void media_player_set_duration(struct media_player *mp, uint32_t duration)
 {
 	char *value, *curval;
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 0871904..4ad8bfe 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -70,6 +70,7 @@
 
 struct media_player *media_player_controller_create(const char *path,
 								uint16_t id);
+const char *media_player_get_path(struct media_player *mp);
 void media_player_destroy(struct media_player *mp);
 void media_player_set_duration(struct media_player *mp, uint32_t duration);
 void media_player_set_position(struct media_player *mp, uint32_t position);
diff --git a/profiles/audio/sink.c b/profiles/audio/sink.c
index 1c36735..ac7237b 100644
--- a/profiles/audio/sink.c
+++ b/profiles/audio/sink.c
@@ -227,6 +227,8 @@
 	struct sink *sink = user_data;
 	int id, perr;
 
+	sink->connect_id = 0;
+
 	if (err) {
 		avdtp_unref(sink->session);
 		sink->session = NULL;
@@ -408,7 +410,7 @@
 	if (sink->connect_id > 0) {
 		a2dp_cancel(sink->connect_id);
 		sink->connect_id = 0;
-		btd_service_connecting_complete(sink->service, -ECANCELED);
+		btd_service_disconnecting_complete(sink->service, 0);
 
 		avdtp_unref(sink->session);
 		sink->session = NULL;
diff --git a/profiles/audio/source.c b/profiles/audio/source.c
index 16a8287..372b132 100644
--- a/profiles/audio/source.c
+++ b/profiles/audio/source.c
@@ -227,6 +227,8 @@
 	struct source *source = user_data;
 	int id, perr;
 
+	source->connect_id = 0;
+
 	if (err) {
 		avdtp_unref(source->session);
 		source->session = NULL;
@@ -400,7 +402,7 @@
 	if (source->connect_id > 0) {
 		a2dp_cancel(source->connect_id);
 		source->connect_id = 0;
-		btd_service_connecting_complete(source->service, -ECANCELED);
+		btd_service_disconnecting_complete(source->service, 0);
 
 		avdtp_unref(source->session);
 		source->session = NULL;
diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index 112ec17..4121e52 100644
--- a/profiles/audio/transport.c
+++ b/profiles/audio/transport.c
@@ -653,6 +653,7 @@
 	struct media_transport *transport = data;
 	struct a2dp_transport *a2dp = transport->data;
 	uint16_t volume;
+	bool notify;
 
 	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) {
 		g_dbus_pending_property_error(id,
@@ -670,12 +671,21 @@
 		return;
 	}
 
-	if (a2dp->volume != volume)
-		avrcp_set_volume(transport->device, volume);
+	g_dbus_pending_property_success(id);
+
+	if (a2dp->volume == volume)
+		return;
 
 	a2dp->volume = volume;
 
-	g_dbus_pending_property_success(id);
+	notify = transport->source_watch ? true : false;
+	if (notify)
+		g_dbus_emit_property_changed(btd_get_dbus_connection(),
+						transport->path,
+						MEDIA_TRANSPORT_INTERFACE,
+						"Volume");
+
+	avrcp_set_volume(transport->device, volume, notify);
 }
 
 static const GDBusMethodTable transport_methods[] = {
@@ -817,7 +827,6 @@
 	transport->destroy = destroy_a2dp;
 
 	a2dp->volume = 127;
-	avrcp_set_volume(transport->device, a2dp->volume);
 	transport->source_watch = source_add_state_cb(service,
 							source_state_changed,
 							transport);
diff --git a/profiles/input/hog.c b/profiles/input/hog.c
index 4be9fd2..e006add 100644
--- a/profiles/input/hog.c
+++ b/profiles/input/hog.c
@@ -633,6 +633,8 @@
 	int i, err;
 	GSList *l;
 
+	DBG("HoG inspecting report map");
+
 	if (status != 0) {
 		error("Report Map read failed: %s", att_ecode2str(status));
 		return;
@@ -703,6 +705,7 @@
 	bt_uhid_register(hogdev->uhid, UHID_GET_REPORT, get_report, hogdev);
 
 	hogdev->uhid_created = TRUE;
+	DBG("HoG created uHID device");
 
 	for (l = hogdev->reports; l; l = l->next) {
 		struct report *r = l->data;
@@ -780,6 +783,8 @@
 	GSList *l;
 	uint16_t info_handle = 0, proto_mode_handle = 0;
 
+	DBG("HoG inspecting characteristics");
+
 	if (status != 0) {
 		const char *str = att_ecode2str(status);
 		DBG("Discover all characteristics failed: %s", str);
@@ -816,6 +821,7 @@
 								report);
 			discover_descriptor(hogdev->attrib, start, end, report);
 		} else if (bt_uuid_cmp(&uuid, &report_map_uuid) == 0) {
+			DBG("HoG discovering report map");
 			gatt_read_char(hogdev->attrib, chr->value_handle,
 						report_map_read_cb, hogdev);
 			discover_descriptor(hogdev->attrib, start, end, hogdev);
@@ -838,6 +844,18 @@
 									hogdev);
 }
 
+static void report_free(void *data)
+{
+	struct report *report = data;
+	struct hog_device *hogdev = report->hogdev;
+
+	if (hogdev->attrib)
+		g_attrib_unregister(hogdev->attrib, report->notifyid);
+
+	g_free(report->decl);
+	g_free(report);
+}
+
 static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
 {
 	struct hog_device *hogdev = user_data;
@@ -846,9 +864,16 @@
 
 	DBG("HoG connected");
 
+	if (!hogdev->uhid_created && hogdev->reports) {
+		DBG("HoG init failed previously, preparing for re-init");
+		g_slist_free_full(hogdev->reports, report_free);
+		hogdev->reports = NULL;
+	}
+
 	hogdev->attrib = g_attrib_ref(attrib);
 
 	if (hogdev->reports == NULL) {
+		DBG("HoG discovering characteristics");
 		gatt_discover_char(hogdev->attrib, prim->range.start,
 						prim->range.end, NULL,
 						char_discovered_cb, hogdev);
@@ -894,18 +919,6 @@
 	return hogdev;
 }
 
-static void report_free(void *data)
-{
-	struct report *report = data;
-	struct hog_device *hogdev = report->hogdev;
-
-	if (hogdev->attrib)
-		g_attrib_unregister(hogdev->attrib, report->notifyid);
-
-	g_free(report->decl);
-	g_free(report);
-}
-
 static void hog_free_device(struct hog_device *hogdev)
 {
 	btd_device_unref(hogdev->device);
diff --git a/src/adapter.c b/src/adapter.c
index 47e901b..d3c515e 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -1722,7 +1722,7 @@
 
 		next = g_slist_next(l);
 
-		if (device_is_temporary(dev))
+		if (device_is_temporary(dev) && !btd_device_is_connected(dev))
 			btd_adapter_remove_device(adapter, dev);
 	}
 
@@ -3474,6 +3474,13 @@
 	return addr_type;
 }
 
+static void probe_devices(void *user_data)
+{
+	struct btd_device *device = user_data;
+
+	device_probe_profiles(device, btd_device_get_uuids(device));
+}
+
 static void load_devices(struct btd_adapter *adapter)
 {
 	char dirname[PATH_MAX];
@@ -3482,6 +3489,7 @@
 	GSList *ltks = NULL;
 	GSList *irks = NULL;
 	GSList *params = NULL;
+	GSList *added_devices = NULL;
 	DIR *dir;
 	struct dirent *entry;
 
@@ -3551,9 +3559,7 @@
 
 		/* TODO: register services from pre-loaded list of primaries */
 
-		list = btd_device_get_uuids(device);
-		if (list)
-			device_probe_profiles(device, list);
+		added_devices = g_slist_append(added_devices, device);
 
 device_exist:
 		if (key_info) {
@@ -3581,6 +3587,8 @@
 	g_slist_free_full(irks, g_free);
 	load_conn_params(adapter, params);
 	g_slist_free_full(params, g_free);
+
+	g_slist_free_full(added_devices, probe_devices);
 }
 
 int btd_adapter_block_address(struct btd_adapter *adapter,
diff --git a/src/device.c b/src/device.c
index 9aebb0f..3d23b34 100644
--- a/src/device.c
+++ b/src/device.c
@@ -87,6 +87,11 @@
 
 #define RSSI_THRESHOLD		2
 
+#define GATT_PRIM_SVC_UUID_STR "2800"
+#define GATT_SND_SVC_UUID_STR  "2801"
+#define GATT_INCLUDE_UUID_STR "2802"
+#define GATT_CHARAC_UUID_STR "2803"
+
 static DBusConnection *dbus_conn = NULL;
 static unsigned service_state_cb_id;
 
@@ -1962,6 +1967,153 @@
 	g_key_file_free(key_file);
 }
 
+struct gatt_saver {
+	struct btd_device *device;
+	GKeyFile *key_file;
+};
+
+static void store_desc(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct gatt_saver *saver = user_data;
+	GKeyFile *key_file = saver->key_file;
+	char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR];
+	const bt_uuid_t *uuid;
+	uint16_t handle_num;
+
+	handle_num = gatt_db_attribute_get_handle(attr);
+	sprintf(handle, "%04hx", handle_num);
+
+	uuid = gatt_db_attribute_get_type(attr);
+	bt_uuid_to_string(uuid, uuid_str, sizeof(uuid_str));
+	sprintf(value, "%s", uuid_str);
+	g_key_file_set_string(key_file, "Attributes", handle, value);
+}
+
+static void store_chrc(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct gatt_saver *saver = user_data;
+	GKeyFile *key_file = saver->key_file;
+	char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR];
+	uint16_t handle_num, value_handle;
+	uint8_t properties;
+	bt_uuid_t uuid;
+
+	if (!gatt_db_attribute_get_char_data(attr, &handle_num, &value_handle,
+							&properties, &uuid)) {
+		warn("Error storing characteristic - can't get data");
+		return;
+	}
+
+	sprintf(handle, "%04hx", handle_num);
+	bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+	sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hx:%s", value_handle,
+							properties, uuid_str);
+	g_key_file_set_string(key_file, "Attributes", handle, value);
+
+	gatt_db_service_foreach_desc(attr, store_desc, saver);
+}
+
+static void store_incl(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct gatt_saver *saver = user_data;
+	GKeyFile *key_file = saver->key_file;
+	struct gatt_db_attribute *service;
+	char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR];
+	uint16_t handle_num, start, end;
+	bt_uuid_t uuid;
+
+	if (!gatt_db_attribute_get_incl_data(attr, &handle_num, &start, &end)) {
+		warn("Error storing included service - can't get data");
+		return;
+	}
+
+	service = gatt_db_get_attribute(saver->device->db, start);
+	if (!service) {
+		warn("Error storing included service - can't find it");
+		return;
+	}
+
+	sprintf(handle, "%04hx", handle_num);
+
+	bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+	sprintf(value, GATT_INCLUDE_UUID_STR ":%04hx:%04hx:%s", start,
+								end, uuid_str);
+
+	g_key_file_set_string(key_file, "Attributes", handle, value);
+}
+
+static void store_service(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct gatt_saver *saver = user_data;
+	GKeyFile *key_file = saver->key_file;
+	char uuid_str[MAX_LEN_UUID_STR], handle[6], value[256];
+	uint16_t start, end;
+	bt_uuid_t uuid;
+	bool primary;
+	char *type;
+
+	if (!gatt_db_attribute_get_service_data(attr, &start, &end, &primary,
+								&uuid)) {
+		warn("Error storing service - can't get data");
+		return;
+	}
+
+	sprintf(handle, "%04hx", start);
+
+	bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+
+	if (primary)
+		type = GATT_PRIM_SVC_UUID_STR;
+	else
+		type = GATT_SND_SVC_UUID_STR;
+
+	sprintf(value, "%s:%04hx:%s", type, end, uuid_str);
+
+	g_key_file_set_string(key_file, "Attributes", handle, value);
+
+	gatt_db_service_foreach_incl(attr, store_incl, saver);
+	gatt_db_service_foreach_char(attr, store_chrc, saver);
+}
+
+static void store_gatt_db(struct btd_device *device)
+{
+	struct btd_adapter *adapter = device->adapter;
+	char filename[PATH_MAX];
+	char src_addr[18], dst_addr[18];
+	GKeyFile *key_file;
+	char *data;
+	gsize length = 0;
+	struct gatt_saver saver;
+
+	if (device_address_is_private(device)) {
+		warn("Can't store GATT db for private addressed device %s",
+								device->path);
+		return;
+	}
+
+	ba2str(btd_adapter_get_address(adapter), src_addr);
+	ba2str(&device->bdaddr, dst_addr);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", src_addr,
+								dst_addr);
+	create_file(filename, S_IRUSR | S_IWUSR);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	saver.key_file = key_file;
+	saver.device = device;
+
+	gatt_db_foreach_service(device->db, NULL, store_service, &saver);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(filename, data, length, NULL);
+
+	g_free(data);
+	g_key_file_free(key_file);
+}
+
+
 static void browse_request_complete(struct browse_req *req, uint8_t bdaddr_type,
 									int err)
 {
@@ -2788,6 +2940,256 @@
 	*new_services = g_slist_append(*new_services, prim);
 }
 
+static int load_desc(char *handle, char *value,
+					struct gatt_db_attribute *service)
+{
+	char uuid_str[MAX_LEN_UUID_STR];
+	struct gatt_db_attribute *att;
+	uint16_t handle_int;
+	bt_uuid_t uuid;
+
+	if (sscanf(handle, "%04hx", &handle_int) != 1)
+		return -EIO;
+
+	if (sscanf(value, "%s", uuid_str) != 1)
+		return -EIO;
+
+	bt_string_to_uuid(&uuid, uuid_str);
+
+	DBG("loading descriptor handle: 0x%04x, uuid: %s", handle_int,
+								uuid_str);
+
+	att = gatt_db_service_insert_descriptor(service, handle_int, &uuid,
+							0, NULL, NULL, NULL);
+	if (!att || gatt_db_attribute_get_handle(att) != handle_int) {
+		warn("loading descriptor to db failed");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int load_chrc(char *handle, char *value,
+					struct gatt_db_attribute *service)
+{
+	uint16_t properties, value_handle, handle_int;
+	char uuid_str[MAX_LEN_UUID_STR];
+	struct gatt_db_attribute *att;
+	bt_uuid_t uuid;
+
+	if (sscanf(handle, "%04hx", &handle_int) != 1)
+		return -EIO;
+
+	if (sscanf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hx:%s", &value_handle,
+						&properties, uuid_str) != 3)
+		return -EIO;
+
+	bt_string_to_uuid(&uuid, uuid_str);
+
+	/* Log debug message. */
+	DBG("loading characteristic handle: 0x%04x, value handle: 0x%04x,"
+				" properties 0x%04x uuid: %s", handle_int,
+				value_handle, properties, uuid_str);
+
+	att = gatt_db_service_insert_characteristic(service, value_handle,
+							&uuid, 0, properties,
+							NULL, NULL, NULL);
+	if (!att || gatt_db_attribute_get_handle(att) != value_handle) {
+		warn("saving characteristic to db failed");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int load_incl(struct gatt_db *db, char *handle, char *value,
+					struct gatt_db_attribute *service)
+{
+	char uuid_str[MAX_LEN_UUID_STR];
+	struct gatt_db_attribute *att;
+	uint16_t start, end;
+
+	if (sscanf(handle, "%04hx", &start) != 1)
+		return -EIO;
+
+	if (sscanf(value, GATT_INCLUDE_UUID_STR ":%04hx:%04hx:%s", &start, &end,
+								uuid_str) != 3)
+		return -EIO;
+
+	/* Log debug message. */
+	DBG("loading included service: 0x%04x, end: 0x%04x, uuid: %s", start,
+								end, uuid_str);
+
+	att = gatt_db_get_attribute(db, start);
+	if (!att) {
+		warn("saving included service to db failed - no such service");
+		return -EIO;
+	}
+
+	att = gatt_db_service_add_included(service, att);
+	if (!att) {
+		warn("saving included service to db failed");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int load_service(struct gatt_db *db, char *handle, char *value)
+{
+	struct gatt_db_attribute *att;
+	uint16_t start, end;
+	char type[MAX_LEN_UUID_STR], uuid_str[MAX_LEN_UUID_STR];
+	bt_uuid_t uuid;
+	bool primary;
+
+	if (sscanf(handle, "%04hx", &start) != 1)
+		return -EIO;
+
+	if (sscanf(value, "%[^:]:%04hx:%s", type, &end, uuid_str) != 3)
+		return -EIO;
+
+	if (g_str_equal(type, GATT_PRIM_SVC_UUID_STR))
+		primary = true;
+	else if (g_str_equal(type, GATT_SND_SVC_UUID_STR))
+		primary = false;
+	else
+		return -EIO;
+
+	bt_string_to_uuid(&uuid, uuid_str);
+
+	/* Log debug message. */
+	DBG("loading service: 0x%04x, end: 0x%04x, uuid: %s",
+							start, end, uuid_str);
+
+	att = gatt_db_insert_service(db, start, &uuid, primary,
+							end - start + 1);
+	if (!att) {
+		DBG("ERROR saving service to db!");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int load_gatt_db_impl(GKeyFile *key_file, char **keys,
+							struct gatt_db *db)
+{
+	struct gatt_db_attribute *current_service;
+	char **handle, *value, type[MAX_LEN_UUID_STR];
+	int ret;
+
+	/* first load service definitions */
+	for (handle = keys; *handle; handle++) {
+		value = g_key_file_get_string(key_file, "Attributes", *handle,
+									NULL);
+
+		if (sscanf(value, "%[^:]:", type) != 1) {
+			warn("Missing Type in attribute definition");
+			g_free(value);
+			return -EIO;
+		}
+
+		if (g_str_equal(type, GATT_PRIM_SVC_UUID_STR) ||
+				g_str_equal(type, GATT_SND_SVC_UUID_STR)) {
+			ret = load_service(db, *handle, value);
+			if (ret) {
+				g_free(value);
+				return ret;
+			}
+		}
+
+		g_free(value);
+	}
+
+	current_service = NULL;
+	/* then fill them with data*/
+	for (handle = keys; *handle; handle++) {
+		value = g_key_file_get_string(key_file, "Attributes", *handle,
+									NULL);
+
+		if (sscanf(value, "%[^:]:", type) != 1) {
+			warn("Missing Type in attribute definition");
+			g_free(value);
+			return -EIO;
+		}
+
+		if (g_str_equal(type, GATT_PRIM_SVC_UUID_STR) ||
+				g_str_equal(type, GATT_SND_SVC_UUID_STR)) {
+			uint16_t tmp;
+			uint16_t start, end;
+			bool primary;
+			bt_uuid_t uuid;
+			char uuid_str[MAX_LEN_UUID_STR];
+
+			if (sscanf(*handle, "%04hx", &tmp) != 1) {
+				warn("Unable to parse attribute handle");
+				g_free(value);
+				return -EIO;
+			}
+
+			if (current_service)
+				gatt_db_service_set_active(current_service,
+									true);
+
+			current_service = gatt_db_get_attribute(db, tmp);
+
+			gatt_db_attribute_get_service_data(current_service,
+							&start, &end,
+							&primary, &uuid);
+
+			bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+		} else if (g_str_equal(type, GATT_INCLUDE_UUID_STR)) {
+			ret = load_incl(db, *handle, value, current_service);
+		} else if (g_str_equal(type, GATT_CHARAC_UUID_STR)) {
+			ret = load_chrc(*handle, value, current_service);
+		} else {
+			ret = load_desc(*handle, value, current_service);
+		}
+
+		g_free(value);
+		if (ret)
+			return ret;
+	}
+
+	if (current_service)
+		gatt_db_service_set_active(current_service, true);
+
+	return 0;
+}
+
+static void load_gatt_db(struct btd_device *device, const char *local,
+							const char *peer)
+{
+	char **keys, filename[PATH_MAX];
+	GKeyFile *key_file;
+
+	DBG("Restoring %s gatt database from file", peer);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+	keys = g_key_file_get_keys(key_file, "Attributes", NULL, NULL);
+
+	if (!keys) {
+		warn("No cache for %s", peer);
+		g_key_file_free(key_file);
+		return;
+	}
+
+	if (load_gatt_db_impl(key_file, keys, device->db))
+		warn("Unable to load gatt db from file for %s", peer);
+
+	g_strfreev(keys);
+	g_key_file_free(key_file);
+
+	g_slist_free_full(device->primaries, g_free);
+	device->primaries = NULL;
+	gatt_db_foreach_service(device->db, NULL, add_primary,
+							&device->primaries);
+}
+
 static void device_add_uuids(struct btd_device *device, GSList *uuids)
 {
 	GSList *l;
@@ -2810,14 +3212,6 @@
 						DEVICE_INTERFACE, "UUIDs");
 }
 
-struct gatt_probe_data {
-	struct btd_device *dev;
-	bool all_services;
-	GSList *uuids;
-	struct gatt_db_attribute *cur_attr;
-	char cur_uuid[MAX_LEN_UUID_STR];
-};
-
 static bool device_match_profile(struct btd_device *device,
 					struct btd_profile *profile,
 					GSList *uuids)
@@ -2832,89 +3226,42 @@
 	return true;
 }
 
-static void dev_probe_gatt(struct btd_profile *p, void *user_data)
+static void probe_gatt_profile(struct gatt_db_attribute *attr, void *user_data)
 {
-	struct gatt_probe_data *data = user_data;
+	struct btd_device *device = user_data;
 	struct btd_service *service;
-
-	if (p->device_probe == NULL)
-		return;
-
-	if (!p->remote_uuid || bt_uuid_strcmp(p->remote_uuid, data->cur_uuid))
-		return;
-
-	service = service_create(data->dev, p);
-	if (!service)
-		return;
-
-	if (service_probe(service) < 0) {
-		btd_service_unref(service);
-		return;
-	}
-
-	/* Mark service as active to skip discovering it again */
-	gatt_db_service_set_active(data->cur_attr, true);
-
-	/* Mark service as claimed */
-	gatt_db_service_set_claimed(data->cur_attr, true);
-
-	data->dev->services = g_slist_append(data->dev->services, service);
-}
-
-static void dev_probe_gatt_profile(struct gatt_db_attribute *attr,
-							void *user_data)
-{
-	struct gatt_probe_data *data = user_data;
+	struct btd_profile *profile;
 	bt_uuid_t uuid;
-	GSList *l = NULL;
+	char uuid_str[MAX_LEN_UUID_STR];
+	GSList *l;
 
 	gatt_db_attribute_get_service_uuid(attr, &uuid);
-	bt_uuid_to_string(&uuid, data->cur_uuid, sizeof(data->cur_uuid));
+	bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
 
-	data->cur_attr = attr;
+	/* Add UUID and probe service */
+	btd_device_add_uuid(device, uuid_str);
 
-	/*
-	 * If we're probing for all services, store the UUID since device->uuids
-	 * was cleared.
-	 */
-	if (data->all_services)
-		data->uuids = g_slist_append(data->uuids,
-						g_strdup(data->cur_uuid));
-
-	/* Don't probe the profiles if a matching service already exists. */
-	if (find_service_with_uuid(data->dev->services, data->cur_uuid)) {
-		/* Mark service as active to skip discovering it again */
-		gatt_db_service_set_active(data->cur_attr, true);
-		/* Mark the service as claimed by the existing profile. */
-		gatt_db_service_set_claimed(data->cur_attr, true);
-		return;
-	}
-
-	btd_profile_foreach(dev_probe_gatt, data);
-
-	if (data->all_services)
+	/* Check if service was probed */
+	l = find_service_with_uuid(device->services, uuid_str);
+	if (!l)
 		return;
 
-	l = g_slist_append(l, g_strdup(data->cur_uuid));
-	device_add_uuids(data->dev, l);
-}
+	/* Mark service as active to skip discovering it again */
+	gatt_db_service_set_active(attr, true);
 
-static void device_probe_gatt_profile(struct btd_device *device,
-						struct gatt_db_attribute *attr)
-{
-	struct gatt_probe_data data;
+	service = l->data;
+	profile = btd_service_get_profile(service);
 
-	memset(&data, 0, sizeof(data));
+	/* Don't claim attributes of external profiles */
+	if (profile->external)
+		return;
 
-	data.dev = device;
-
-	dev_probe_gatt_profile(attr, &data);
-	g_slist_free_full(data.uuids, g_free);
+	/* Mark the service as claimed by the existing profile. */
+	gatt_db_service_set_claimed(attr, true);
 }
 
 static void device_probe_gatt_profiles(struct btd_device *device)
 {
-	struct gatt_probe_data data;
 	char addr[18];
 
 	ba2str(&device->bdaddr, addr);
@@ -2924,16 +3271,7 @@
 		return;
 	}
 
-	memset(&data, 0, sizeof(data));
-
-	data.dev = device;
-	data.all_services = true;
-
-	gatt_db_foreach_service(device->db, NULL, dev_probe_gatt_profile,
-									&data);
-
-	device_add_uuids(device, data.uuids);
-	g_slist_free_full(data.uuids, g_free);
+	gatt_db_foreach_service(device->db, NULL, probe_gatt_profile, device);
 }
 
 static void device_accept_gatt_profiles(struct btd_device *device)
@@ -2969,6 +3307,8 @@
 {
 	struct btd_device *device = user_data;
 
+	store_gatt_db(device);
+
 	g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE,
 								"GattServices");
 
@@ -3014,9 +3354,7 @@
 			service_accept(l->data);
 	}
 
-	device_probe_gatt_profile(device, attr);
-
-	store_device_info(device);
+	btd_device_add_uuid(device, uuid_str);
 
 	btd_gatt_client_service_added(device->client_dbus, attr);
 
@@ -3631,24 +3969,40 @@
 	GSList *uuids;
 };
 
+static struct btd_service *probe_service(struct btd_device *device,
+						struct btd_profile *profile,
+						GSList *uuids)
+{
+	struct btd_service *service;
+
+	if (profile->device_probe == NULL)
+		return NULL;
+
+	if (!device_match_profile(device, profile, uuids))
+		return NULL;
+
+	service = service_create(device, profile);
+
+	if (service_probe(service)) {
+		btd_service_unref(service);
+		return NULL;
+	}
+
+	if (profile->auto_connect)
+		device_set_auto_connect(device, TRUE);
+
+	return service;
+}
+
 static void dev_probe(struct btd_profile *p, void *user_data)
 {
 	struct probe_data *d = user_data;
 	struct btd_service *service;
 
-	if (p->device_probe == NULL)
+	service = probe_service(d->dev, p, d->uuids);
+	if (!service)
 		return;
 
-	if (!device_match_profile(d->dev, p, d->uuids))
-		return;
-
-	service = service_create(d->dev, p);
-
-	if (service_probe(service) < 0) {
-		btd_service_unref(service);
-		return;
-	}
-
 	d->dev->services = g_slist_append(d->dev->services, service);
 }
 
@@ -3658,19 +4012,10 @@
 	struct btd_profile *profile = b;
 	struct btd_service *service;
 
-	if (profile->device_probe == NULL)
+	service = probe_service(device, profile, device->uuids);
+	if (!service)
 		return;
 
-	if (!device_match_profile(device, profile, device->uuids))
-		return;
-
-	service = service_create(device, profile);
-
-	if (service_probe(service) < 0) {
-		btd_service_unref(service);
-		return;
-	}
-
 	device->services = g_slist_append(device->services, service);
 
 	if (!profile->auto_connect || !device->general_connect)
@@ -4253,6 +4598,8 @@
 	}
 
 	device->gatt_cache_used = !gatt_db_isempty(device->db);
+
+	btd_gatt_client_connected(device->client_dbus);
 }
 
 static void gatt_server_init(struct btd_device *device, struct gatt_db *db)
@@ -4307,6 +4654,8 @@
 	uint16_t mtu;
 	uint16_t cid;
 	struct btd_gatt_database *database;
+	const bdaddr_t *src, *dst;
+	char srcaddr[18], dstaddr[18];
 
 	bt_io_get(io, &gerr, BT_IO_OPT_SEC_LEVEL, &sec_level,
 						BT_IO_OPT_IMTU, &mtu,
@@ -4358,6 +4707,15 @@
 
 	database = btd_adapter_get_database(dev->adapter);
 
+	src = btd_adapter_get_address(dev->adapter);
+	ba2str(src, srcaddr);
+
+	dst = device_get_address(dev);
+	ba2str(dst, dstaddr);
+
+	if (gatt_db_isempty(dev->db))
+		load_gatt_db(dev, srcaddr, dstaddr);
+
 	gatt_client_init(dev);
 	gatt_server_init(dev, btd_gatt_database_get_db(database));
 
@@ -4953,6 +5311,10 @@
 	 * request
 	 */
 	if (state->svc_resolved && bonding) {
+		/* Attept to store services for this device failed because it
+		 * was not paired. Now that we're paired retry. */
+		store_gatt_db(device);
+
 		g_dbus_send_reply(dbus_conn, bonding->msg, DBUS_TYPE_INVALID);
 		bonding_request_free(bonding);
 		return;
diff --git a/src/gatt-client.c b/src/gatt-client.c
index 9e6e47b..0408e17 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -520,7 +520,7 @@
 {
 	struct descriptor *desc = data;
 
-	desc->write_id = false;
+	desc->write_id = 0;
 
 	/*
 	 * The descriptor might have been unregistered during the read. Return
@@ -773,8 +773,10 @@
 	if (err)
 		return;
 
-	g_dbus_emit_property_changed(btd_get_dbus_connection(), chrc->path,
-					GATT_CHARACTERISTIC_IFACE, "Value");
+	g_dbus_emit_property_changed_full(btd_get_dbus_connection(),
+				chrc->path, GATT_CHARACTERISTIC_IFACE,
+				"Value", G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH);
+
 }
 
 static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
@@ -867,7 +869,7 @@
 {
 	struct characteristic *chrc = data;
 
-	chrc->write_id = false;
+	chrc->write_id = 0;
 
 	/*
 	 * The characteristic might have been unregistered during the read.
@@ -941,11 +943,11 @@
 		goto fail;
 
 	supported = true;
-	chrc->write_id = bt_gatt_client_write_without_response(gatt,
+
+	if (bt_gatt_client_write_without_response(gatt,
 					chrc->value_handle,
 					chrc->props & BT_GATT_CHRC_PROP_AUTH,
-					value, value_len);
-	if (chrc->write_id)
+					value, value_len))
 		return dbus_message_new_method_return(msg);
 
 fail:
@@ -1521,6 +1523,9 @@
 	/* Set service active so we can skip discovering next time */
 	gatt_db_service_set_active(attr, true);
 
+	/* Mark the service as claimed since it going to be exported */
+	gatt_db_service_set_claimed(attr, true);
+
 	return service;
 }
 
@@ -1805,30 +1810,36 @@
 
 void btd_gatt_client_ready(struct btd_gatt_client *client)
 {
-	struct bt_gatt_client *gatt;
-
 	if (!client)
 		return;
 
+	if (!client->gatt) {
+		error("GATT client not initialized");
+		return;
+	}
+
+	client->ready = true;
+
+	DBG("GATT client ready");
+
+	create_services(client);
+}
+
+void btd_gatt_client_connected(struct btd_gatt_client *client)
+{
+	struct bt_gatt_client *gatt;
+
 	gatt = btd_device_get_gatt_client(client->device);
 	if (!gatt) {
 		error("GATT client not initialized");
 		return;
 	}
 
+	DBG("Device connected.");
+
 	bt_gatt_client_unref(client->gatt);
 	client->gatt = bt_gatt_client_ref(gatt);
 
-	client->ready = true;
-
-	DBG("GATT client ready");
-
-	if (queue_isempty(client->services)) {
-		DBG("Exporting services");
-		create_services(client);
-		return;
-	}
-
 	/*
 	 * Services have already been created before. Re-enable notifications
 	 * for any pre-registered notification sessions.
diff --git a/src/gatt-client.h b/src/gatt-client.h
index a18db17..92a9255 100644
--- a/src/gatt-client.h
+++ b/src/gatt-client.h
@@ -23,6 +23,7 @@
 void btd_gatt_client_destroy(struct btd_gatt_client *client);
 
 void btd_gatt_client_ready(struct btd_gatt_client *client);
+void btd_gatt_client_connected(struct btd_gatt_client *client);
 void btd_gatt_client_service_added(struct btd_gatt_client *client,
 					struct gatt_db_attribute *attrib);
 void btd_gatt_client_service_removed(struct btd_gatt_client *client,
diff --git a/src/gatt-database.c b/src/gatt-database.c
index 41282e7..a0e4ac0 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -43,6 +43,7 @@
 #include "gatt-database.h"
 #include "dbus-common.h"
 #include "profile.h"
+#include "service.h"
 
 #ifndef ATT_CID
 #define ATT_CID 4
@@ -383,6 +384,7 @@
 	DBG("Removed \"%s\"", p->name);
 
 	adapter_foreach(adapter_remove_profile, p);
+	btd_profile_unregister(p);
 
 	g_free((void *) p->name);
 	g_free((void *) p->remote_uuid);
@@ -1073,7 +1075,7 @@
 	service_remove_helper(user_data);
 }
 
-static void service_remove(void *data)
+static void remove_service(void *data)
 {
 	struct external_service *service = data;
 
@@ -1452,7 +1454,7 @@
 
 	DBG("Proxy removed - removing service: %s", service->path);
 
-	service_remove(service);
+	remove_service(service);
 }
 
 static bool parse_uuid(GDBusProxy *proxy, bt_uuid_t *uuid)
@@ -1783,22 +1785,16 @@
 		return 0;
 	}
 
-	/*
-	 * TODO: All of the errors below should fall into the so called
-	 * "Application Error" range. Since there is no well defined error for
-	 * these, we return a generic ATT protocol error for now.
-	 */
-
 	if (chrc->ntfy_cnt == UINT_MAX) {
 		/* Maximum number of per-device CCC descriptors configured */
-		return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+		return BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
 	}
 
 	/* Don't support undefined CCC values yet */
 	if (value > 2 ||
 		(value == 1 && !(chrc->props & BT_GATT_CHRC_PROP_NOTIFY)) ||
 		(value == 2 && !(chrc->props & BT_GATT_CHRC_PROP_INDICATE)))
-		return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+		return BT_ERROR_CCC_IMPROPERLY_CONFIGURED;
 
 	/*
 	 * Always call StartNotify for an incoming enable and ignore the return
@@ -1807,7 +1803,7 @@
 	if (g_dbus_proxy_method_call(chrc->proxy,
 						"StartNotify", NULL, NULL,
 						NULL, NULL) == FALSE)
-		return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+		return BT_ATT_ERROR_UNLIKELY;
 
 	__sync_fetch_and_add(&chrc->ntfy_cnt, 1);
 
@@ -2147,10 +2143,10 @@
 	service->reg = NULL;
 
 	if (fail)
-		service_remove(service);
+		remove_service(service);
 }
 
-static struct external_service *service_create(DBusConnection *conn,
+static struct external_service *create_service(DBusConnection *conn,
 					DBusMessage *msg, const char *path)
 {
 	struct external_service *service;
@@ -2228,7 +2224,7 @@
 	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
 		return btd_error_invalid_args(msg);
 
-	service = service_create(conn, msg, path);
+	service = create_service(conn, msg, path);
 	if (!service)
 		return btd_error_failed(msg, "Failed to register service");
 
@@ -2284,6 +2280,22 @@
 	profile_free(profile);
 }
 
+static int profile_device_probe(struct btd_service *service)
+{
+	struct btd_profile *p = btd_service_get_profile(service);
+
+	DBG("%s probed", p->name);
+
+	return 0;
+}
+
+static void profile_device_remove(struct btd_service *service)
+{
+	struct btd_profile *p = btd_service_get_profile(service);
+
+	DBG("%s removed", p->name);
+}
+
 static int profile_add(struct external_profile *profile, const char *uuid)
 {
 	struct btd_profile *p;
@@ -2307,7 +2319,10 @@
 		return -ENOMEM;
 	}
 
+	p->device_probe = profile_device_probe;
+	p->device_remove = profile_device_remove;
 	p->auto_connect = true;
+	p->external = true;
 
 	queue_push_tail(profile->profiles, p);
 
@@ -2320,6 +2335,7 @@
 {
 	struct btd_adapter *adapter = user_data;
 
+	btd_profile_register(data);
 	adapter_add_profile(adapter, data);
 }
 
diff --git a/src/main.conf b/src/main.conf
index 11db383..372fd8c 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -1,10 +1,8 @@
 [General]
 
 # Default adaper name
-# %h - substituted for hostname
-# %d - substituted for adapter id
-# Defaults to 'BlueZ'
-#Name = %h-%d
+# Defaults to 'BlueZ X.YZ'
+#Name = BlueZ
 
 # Default device class. Only the major and minor device class bits are
 # considered. Defaults to '0x000000'.
@@ -84,3 +82,8 @@
 # If the number of attempts defined in ReconnectAttempts is bigger than the
 # set of intervals the last interval is repeated until the last attempt.
 #ReconnectIntervals=1, 2, 4, 8, 16, 32, 64
+
+# AutoEnable defines option to enable all controllers when they are found.
+# This includes adapters present on start as well as adapters that are plugged
+# in later on. Defaults to 'false'.
+#AutoEnable=false
diff --git a/src/profile.c b/src/profile.c
index 4b73026..70ee4c1 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -435,6 +435,9 @@
 		<attribute id=\"0x0317\">				\
 			<uint32 value=\"0x00000003\"/>			\
 		</attribute>						\
+		<attribute id=\"0x0200\">				\
+			<uint16 value=\"%u\" name=\"psm\"/>		\
+		</attribute>						\
 	</record>"
 
 #define MAS_RECORD							\
@@ -484,6 +487,9 @@
 		<attribute id=\"0x0317\">				\
 			<uint32 value=\"0x0000007f\"/>			\
 		</attribute>						\
+		<attribute id=\"0x0200\">				\
+			<uint16 value=\"%u\" name=\"psm\"/>		\
+		</attribute>						\
 	</record>"
 
 #define MNS_RECORD							\
@@ -524,12 +530,12 @@
 		<attribute id=\"0x0100\">				\
 			<text value=\"%s\"/>				\
 		</attribute>						\
-		<attribute id=\"0x0200\">				\
-			<uint16 value=\"%u\" name=\"psm\"/>		\
-		</attribute>						\
 		<attribute id=\"0x0317\">				\
 			<uint32 value=\"0x0000007f\"/>			\
 		</attribute>						\
+		<attribute id=\"0x0200\">				\
+			<uint16 value=\"%u\" name=\"psm\"/>		\
+		</attribute>						\
 	</record>"
 
 #define SYNC_RECORD							\
@@ -713,13 +719,19 @@
 
 int btd_profile_register(struct btd_profile *profile)
 {
-	profiles = g_slist_append(profiles, profile);
+	if (profile->external)
+		ext_profiles = g_slist_append(ext_profiles, profile);
+	else
+		profiles = g_slist_append(profiles, profile);
 	return 0;
 }
 
 void btd_profile_unregister(struct btd_profile *profile)
 {
-	profiles = g_slist_remove(profiles, profile);
+	if (profile->external)
+		ext_profiles = g_slist_remove(ext_profiles, profile);
+	else
+		profiles = g_slist_remove(profiles, profile);
 }
 
 static struct ext_profile *find_ext_profile(const char *owner,
@@ -1792,15 +1804,29 @@
 static char *get_pse_record(struct ext_profile *ext, struct ext_io *l2cap,
 							struct ext_io *rfcomm)
 {
-	return g_strdup_printf(PSE_RECORD, rfcomm->chan, ext->version,
-								ext->name);
+	uint16_t psm = 0;
+	uint8_t chan = 0;
+
+	if (l2cap)
+		psm = l2cap->psm;
+	if (rfcomm)
+		chan = rfcomm->chan;
+
+	return g_strdup_printf(PSE_RECORD, chan, ext->version, ext->name, psm);
 }
 
 static char *get_mas_record(struct ext_profile *ext, struct ext_io *l2cap,
 							struct ext_io *rfcomm)
 {
-	return g_strdup_printf(MAS_RECORD, rfcomm->chan, ext->version,
-								ext->name);
+	uint16_t psm = 0;
+	uint8_t chan = 0;
+
+	if (l2cap)
+		psm = l2cap->psm;
+	if (rfcomm)
+		chan = rfcomm->chan;
+
+	return g_strdup_printf(MAS_RECORD, chan, ext->version, ext->name, psm);
 }
 
 static char *get_mns_record(struct ext_profile *ext, struct ext_io *l2cap,
@@ -2001,6 +2027,8 @@
 		.uuid		= OBEX_PSE_UUID,
 		.name		= "Phone Book Access",
 		.channel	= PBAP_DEFAULT_CHANNEL,
+		.psm		= BTD_PROFILE_PSM_AUTO,
+		.mode		= BT_IO_MODE_ERTM,
 		.authorize	= true,
 		.get_record	= get_pse_record,
 		.version	= 0x0101,
@@ -2015,6 +2043,8 @@
 		.uuid		= OBEX_MAS_UUID,
 		.name		= "Message Access",
 		.channel	= MAS_DEFAULT_CHANNEL,
+		.psm		= BTD_PROFILE_PSM_AUTO,
+		.mode		= BT_IO_MODE_ERTM,
 		.authorize	= true,
 		.get_record	= get_mas_record,
 		.version	= 0x0100
@@ -2267,6 +2297,7 @@
 	p->name = ext->name;
 	p->local_uuid = ext->service ? ext->service : ext->uuid;
 	p->remote_uuid = ext->remote_uuid;
+	p->external = true;
 
 	if (ext->enable_server) {
 		p->adapter_probe = ext_adapter_probe;
diff --git a/src/profile.h b/src/profile.h
index f5a3ded..4448a2a 100644
--- a/src/profile.h
+++ b/src/profile.h
@@ -35,6 +35,7 @@
 	const char *remote_uuid;
 
 	bool auto_connect;
+	bool external;
 
 	int (*device_probe) (struct btd_service *service);
 	void (*device_remove) (struct btd_service *service);
diff --git a/src/shared/ad.c b/src/shared/ad.c
index 485bd7b..6259eb9 100644
--- a/src/shared/ad.c
+++ b/src/shared/ad.c
@@ -5,15 +5,19 @@
  *  Copyright (C) 2015  Google Inc.
  *
  *
- *  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 library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  This library 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.
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 
diff --git a/src/shared/ad.h b/src/shared/ad.h
index 4b313ab..709563d 100644
--- a/src/shared/ad.h
+++ b/src/shared/ad.h
@@ -5,15 +5,19 @@
  *  Copyright (C) 2015  Google Inc.
  *
  *
- *  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 library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  This library 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.
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 
diff --git a/src/shared/att.c b/src/shared/att.c
index 70f5d72..d88169e 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -82,7 +82,6 @@
 	void *debug_data;
 
 	struct bt_crypto *crypto;
-	bool ext_signed;
 
 	struct sign_info *local_sign;
 	struct sign_info *remote_sign;
@@ -294,7 +293,7 @@
 	if (pdu_len > 1)
 		memcpy(op->pdu + 1, pdu, length);
 
-	if (!sign || !(op->opcode & ATT_OP_SIGNED_MASK))
+	if (!sign || !(op->opcode & ATT_OP_SIGNED_MASK) || !att->crypto)
 		return true;
 
 	if (!sign->counter(&sign_cnt, sign->user_data))
@@ -782,7 +781,7 @@
 	const struct queue_entry *entry;
 	bool found;
 
-	if ((opcode & ATT_OP_SIGNED_MASK) && !att->ext_signed) {
+	if ((opcode & ATT_OP_SIGNED_MASK) && !att->crypto) {
 		if (!handle_signed(att, opcode, pdu, pdu_len))
 			return;
 		pdu_len -= BT_ATT_SIGNATURE_LEN;
@@ -962,7 +961,6 @@
 		return NULL;
 
 	att->fd = fd;
-	att->ext_signed = ext_signed;
 	att->mtu = BT_ATT_DEFAULT_LE_MTU;
 	att->buf = malloc(att->mtu);
 	if (!att->buf)
diff --git a/src/shared/btsnoop.c b/src/shared/btsnoop.c
index 3592c2e..2aa1b8d 100644
--- a/src/shared/btsnoop.c
+++ b/src/shared/btsnoop.c
@@ -242,6 +242,9 @@
 	case BTSNOOP_OPCODE_SCO_TX_PKT:
 	case BTSNOOP_OPCODE_SCO_RX_PKT:
 		break;
+	case BTSNOOP_OPCODE_OPEN_INDEX:
+	case BTSNOOP_OPCODE_CLOSE_INDEX:
+		break;
 	}
 
 	return 0xff;
diff --git a/src/shared/btsnoop.h b/src/shared/btsnoop.h
index 9675980..91b5922 100644
--- a/src/shared/btsnoop.h
+++ b/src/shared/btsnoop.h
@@ -43,6 +43,9 @@
 #define BTSNOOP_OPCODE_ACL_RX_PKT	5
 #define BTSNOOP_OPCODE_SCO_TX_PKT	6
 #define BTSNOOP_OPCODE_SCO_RX_PKT	7
+#define BTSNOOP_OPCODE_OPEN_INDEX	8
+#define BTSNOOP_OPCODE_CLOSE_INDEX	9
+#define BTSNOOP_OPCODE_INDEX_INFO	10
 
 #define BTSNOOP_MAX_PACKET_SIZE		(1486 + 4)
 
@@ -53,6 +56,11 @@
 	char     name[8];
 } __attribute__((packed));
 
+struct btsnoop_opcode_index_info {
+	uint8_t  bdaddr[6];
+	uint16_t manufacturer;
+} __attribute__((packed));
+
 struct btsnoop;
 
 struct btsnoop *btsnoop_open(const char *path, unsigned long flags);
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 903afa7..d3e17e1 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -1596,8 +1596,7 @@
 						!notify_data->chrc->ccc_handle)
 		goto done;
 
-	if (notify_data_write_ccc(notify_data, false, disable_ccc_callback))
-		return;
+	notify_data_write_ccc(notify_data, false, disable_ccc_callback);
 
 done:
 	notify_data_unref(notify_data);
@@ -2990,7 +2989,7 @@
 	if (!client || !client->db || !chrc_value_handle || !callback)
 		return 0;
 
-	if (!bt_gatt_client_is_ready(client) || client->in_svc_chngd)
+	if (client->in_svc_chngd)
 		return 0;
 
 	return register_notify(client, chrc_value_handle, callback, notify,
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index c773df7..008b8bc 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -1490,6 +1490,7 @@
 			return;
 
 		success = false;
+		goto done;
 	}
 
 	success = true;
diff --git a/src/shared/tester.c b/src/shared/tester.c
index 30857e0..c3120fb 100644
--- a/src/shared/tester.c
+++ b/src/shared/tester.c
@@ -35,6 +35,10 @@
 
 #include <glib.h>
 
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
 #include "src/shared/util.h"
 #include "src/shared/tester.h"
 
@@ -339,6 +343,10 @@
 	print_progress(test->name, COLOR_MAGENTA, "teardown");
 	test->teardown_func(test->test_data);
 
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+	VALGRIND_DO_ADDED_LEAK_CHECK;
+#endif
+
 	return FALSE;
 }
 
diff --git a/test/simple-player b/test/simple-player
index a8ae0b1..02754c2 100755
--- a/test/simple-player
+++ b/test/simple-player
@@ -43,6 +43,7 @@
 
 			self.properties = dbus.Dictionary({
 					"PlaybackStatus" : "playing",
+					"Identity" : "SimplePlayer",
 					"LoopStatus" : "None",
 					"Rate" : dbus.Double(1.0),
 					"Shuffle" : dbus.Boolean(False),
diff --git a/test/test-gatt-profile b/test/test-gatt-profile
new file mode 100755
index 0000000..ad320b1
--- /dev/null
+++ b/test/test-gatt-profile
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser, make_option
+import os
+import sys
+import uuid
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+import bluezutils
+
+class GattProfile(dbus.service.Object):
+	@dbus.service.method("org.bluez.GattProfile1",
+					in_signature="", out_signature="")
+	def Release(self):
+		print("Release")
+		mainloop.quit()
+
+if __name__ == '__main__':
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+
+	path = bluezutils.find_adapter().object_path
+
+	manager = dbus.Interface(bus.get_object("org.bluez", path),
+				"org.bluez.GattManager1")
+
+	option_list = [
+			make_option("-u", "--uuid", action="store",
+					type="string", dest="uuid",
+					default=None),
+			make_option("-p", "--path", action="store",
+					type="string", dest="path",
+					default="/foo/bar/profile"),
+			]
+
+	opts = dbus.Dictionary({ }, signature='sv')
+
+	parser = OptionParser(option_list=option_list)
+
+	(options, args) = parser.parse_args()
+
+	profile = GattProfile(bus, options.path)
+
+	mainloop = GObject.MainLoop()
+
+	if not options.uuid:
+		options.uuid = str(uuid.uuid4())
+
+	uuids = { options.uuid }
+	manager.RegisterProfile(options.path, uuids, opts)
+
+	mainloop.run()
diff --git a/tools/btattach.c b/tools/btattach.c
index a9b892f..a025bb0 100644
--- a/tools/btattach.c
+++ b/tools/btattach.c
@@ -154,7 +154,8 @@
 		}
 
 		bt_hci_send(hci, BT_HCI_CMD_READ_LOCAL_VERSION, NULL, 0,
-					local_version_callback, NULL, NULL);
+					local_version_callback, hci,
+					(bt_hci_destroy_func_t) bt_hci_unref);
 	}
 
 	return fd;
@@ -212,6 +213,7 @@
 	{ "ath3k", HCI_UART_ATH3K },
 	{ "intel", HCI_UART_INTEL },
 	{ "bcm",   HCI_UART_BCM   },
+	{ "qca",   HCI_UART_QCA   },
 	{ }
 };
 
diff --git a/tools/hciattach.c b/tools/hciattach.c
index 4dc5be5..59a76a7 100644
--- a/tools/hciattach.c
+++ b/tools/hciattach.c
@@ -1358,6 +1358,12 @@
 			dev[0] = 0;
 			if (!strchr(opt, '/'))
 				strcpy(dev, "/dev/");
+
+			if (strlen(opt) > PATH_MAX - (strlen(dev) + 1)) {
+				fprintf(stderr, "Invalid serial device\n");
+				exit(1);
+			}
+
 			strcat(dev, opt);
 			break;
 
diff --git a/tools/hciattach.h b/tools/hciattach.h
index 909ada8..4279a33 100644
--- a/tools/hciattach.h
+++ b/tools/hciattach.h
@@ -41,6 +41,7 @@
 #define HCI_UART_ATH3K  5
 #define HCI_UART_INTEL	6
 #define HCI_UART_BCM	7
+#define HCI_UART_QCA	8
 
 #define HCI_UART_RAW_DEVICE	0
 #define HCI_UART_RESET_ON_INIT	1
diff --git a/tools/hciconfig.c b/tools/hciconfig.c
index 0e0b790..eac96b0 100644
--- a/tools/hciconfig.c
+++ b/tools/hciconfig.c
@@ -963,6 +963,8 @@
 			printf("%s, %s\n", major_devices[cls[1] & 0x1f],
 				get_minor_device_name(cls[1] & 0x1f, cls[0] >> 2));
 	}
+
+	hci_close_dev(hdev);
 }
 
 static void cmd_voice(int ctl, int hdev, char *opt)
@@ -1023,6 +1025,8 @@
 		}
 		printf("\tAir Coding Format: %s\n", acf[vs & 0x03]);
 	}
+
+	hci_close_dev(s);
 }
 
 static void cmd_delkey(int ctl, int hdev, char *opt)
@@ -1462,6 +1466,8 @@
 		printf("\tInquiry interval: %u slots (%.2f ms), window: %u slots (%.2f ms)\n",
 				interval, (float)interval * 0.625, window, (float)window * 0.625);
 	}
+
+	hci_close_dev(s);
 }
 
 static void cmd_page_parms(int ctl, int hdev, char *opt)
@@ -1533,6 +1539,8 @@
 			interval, (float)interval * 0.625,
 			window, (float)window * 0.625);
 	}
+
+	hci_close_dev(s);
 }
 
 static void cmd_page_to(int ctl, int hdev, char *opt)
@@ -1597,6 +1605,8 @@
 		printf("\tPage timeout: %u slots (%.2f ms)\n",
 				timeout, (float)timeout * 0.625);
 	}
+
+	hci_close_dev(s);
 }
 
 static void cmd_afh_mode(int ctl, int hdev, char *opt)
@@ -1630,6 +1640,8 @@
 		print_dev_hdr(&di);
 		printf("\tAFH mode: %s\n", mode == 1 ? "Enabled" : "Disabled");
 	}
+
+	hci_close_dev(dd);
 }
 
 static void cmd_ssp_mode(int ctl, int hdev, char *opt)
@@ -1664,6 +1676,8 @@
 		printf("\tSimple Pairing mode: %s\n",
 			mode == 1 ? "Enabled" : "Disabled");
 	}
+
+	hci_close_dev(dd);
 }
 
 static void print_rev_ericsson(int dd)
@@ -1790,6 +1804,9 @@
 		printf("\tUnsupported manufacturer\n");
 		break;
 	}
+
+	hci_close_dev(dd);
+
 	return;
 }
 
diff --git a/tools/hcidump.c b/tools/hcidump.c
index 8839eb0..af8f592 100644
--- a/tools/hcidump.c
+++ b/tools/hcidump.c
@@ -145,7 +145,8 @@
 	struct frame frm;
 	struct pollfd fds[2];
 	int nfds = 0;
-	char *buf, *ctrl;
+	char *buf;
+	char ctrl[100];
 	int len, hdr_size = HCIDUMP_HDR_SIZE;
 
 	if (sock < 0)
@@ -167,13 +168,6 @@
 	dp = (void *) buf;
 	frm.data = buf + hdr_size;
 
-	ctrl = malloc(100);
-	if (!ctrl) {
-		free(buf);
-		perror("Can't allocate control buffer");
-		return -1;
-	}
-
 	if (dev == HCI_DEV_NONE)
 		printf("system: ");
 	else
@@ -538,13 +532,13 @@
 	opt = 1;
 	if (setsockopt(sk, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) {
 		perror("Can't enable data direction info");
-		return -1;
+		goto fail;
 	}
 
 	opt = 1;
 	if (setsockopt(sk, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) {
 		perror("Can't enable time stamp");
-		return -1;
+		goto fail;
 	}
 
 	/* Setup filter */
@@ -553,7 +547,7 @@
 	hci_filter_all_events(&flt);
 	if (setsockopt(sk, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
 		perror("Can't set filter");
-		return -1;
+		goto fail;
 	}
 
 	/* Bind socket to the HCI device */
@@ -563,10 +557,14 @@
 	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 		printf("Can't attach to device hci%d. %s(%d)\n",
 					dev, strerror(errno), errno);
-		return -1;
+		goto fail;
 	}
 
 	return sk;
+
+fail:
+	close(sk);
+	return -1;
 }
 
 static struct {
diff --git a/tools/l2test.c b/tools/l2test.c
index 386823f..d4e3ae6 100644
--- a/tools/l2test.c
+++ b/tools/l2test.c
@@ -592,6 +592,7 @@
 
 	if (socktype == SOCK_DGRAM) {
 		handler(sk);
+		close(sk);
 		return;
 	}
 
diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c
index 9ace20a..0a1a7c2 100644
--- a/tools/mgmt-tester.c
+++ b/tools/mgmt-tester.c
@@ -4707,6 +4707,30 @@
 	.adv_data = adv_data_invalid_significant_len,
 };
 
+static const uint8_t adv_data_invalid_field_len[] = { 0x02, 0x01, 0x01,
+		0x05, 0x09, 0x74, 0x65, 0x73, 0x74,
+		0xa0, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05};
+
+static const char device_found_valid2[] = { 0x00, 0x00, 0x01, 0x01, 0xaa, 0x00,
+		0x01, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x02, 0x01,
+		0x01, 0x05, 0x09, 0x74, 0x65, 0x73, 0x74};
+
+static const struct generic_data device_found_invalid_field = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_START_DISCOVERY,
+	.send_param = start_discovery_le_param,
+	.send_len = sizeof(start_discovery_le_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = start_discovery_le_param,
+	.expect_len = sizeof(start_discovery_le_param),
+	.expect_alt_ev = MGMT_EV_DEVICE_FOUND,
+	.expect_alt_ev_param = device_found_valid2,
+	.expect_alt_ev_len = sizeof(device_found_valid2),
+	.set_adv = true,
+	.adv_data_len = sizeof(adv_data_invalid_field_len),
+	.adv_data = adv_data_invalid_field_len,
+};
+
 static const struct generic_data read_local_oob_not_powered_test = {
 	.send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
 	.expect_status = MGMT_STATUS_NOT_POWERED,
@@ -6957,9 +6981,12 @@
 				&read_local_oob_success_sc_test,
 				NULL, test_command_generic);
 
-	test_bredrle("Device Found - Invalid remote ADV_DATA",
+	test_bredrle("Device Found - Advertising data - Zero padded",
 				&device_found_gtag,
 				NULL, test_device_found);
+	test_bredrle("Device Found - Advertising data - Invalid field",
+				&device_found_invalid_field,
+				NULL, test_device_found);
 
 	return tester_run();
 }
diff --git a/tools/mpris-proxy.c b/tools/mpris-proxy.c
index 693055e..bf8148f 100644
--- a/tools/mpris-proxy.c
+++ b/tools/mpris-proxy.c
@@ -406,7 +406,7 @@
 done:
 	dbus_message_unref(copy);
 
-	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
 static struct player *find_player_by_bus_name(const char *name)
diff --git a/tools/rctest.c b/tools/rctest.c
index 9169e3c..6d84e07 100644
--- a/tools/rctest.c
+++ b/tools/rctest.c
@@ -102,7 +102,7 @@
 static uint8_t get_channel(const char *svr, uint16_t uuid)
 {
 	sdp_session_t *sdp;
-	sdp_list_t *srch, *attrs, *rsp;
+	sdp_list_t *srch, *attrs, *rsp, *protos;
 	uuid_t svclass;
 	uint16_t attr;
 	bdaddr_t dst;
@@ -128,7 +128,6 @@
 
 	for (; rsp; rsp = rsp->next) {
 		sdp_record_t *rec = (sdp_record_t *) rsp->data;
-		sdp_list_t *protos;
 
 		if (!sdp_get_access_protos(rec, &protos)) {
 			channel = sdp_get_proto_port(protos, RFCOMM_UUID);
@@ -137,7 +136,11 @@
 		}
 	}
 
+	sdp_list_free(protos, NULL);
+
 done:
+	sdp_list_free(srch, NULL);
+	sdp_list_free(attrs, NULL);
 	sdp_close(sdp);
 
 	return channel;
@@ -593,6 +596,7 @@
 		syslog(LOG_INFO, "Close failed: %m");
 	else
 		syslog(LOG_INFO, "Done");
+	close(sk);
 }
 
 static void reconnect_mode(char *svr)
diff --git a/tools/sco-tester.c b/tools/sco-tester.c
index d55d51a..651fbe0 100644
--- a/tools/sco-tester.c
+++ b/tools/sco-tester.c
@@ -160,6 +160,7 @@
 	if (!data->hciemu) {
 		tester_warn("Failed to setup HCI emulation");
 		tester_pre_setup_failed();
+		return;
 	}
 
 	tester_print("New hciemu instance created");
diff --git a/tools/sdptool.c b/tools/sdptool.c
index cbe8814..b1cbcfd 100644
--- a/tools/sdptool.c
+++ b/tools/sdptool.c
@@ -3312,6 +3312,7 @@
 	sdp_record_t record;
 	sdp_list_t *root, *svclass;
 	uuid_t root_uuid, svclass_uuid;
+	int err;
 
 	memset(&record, 0, sizeof(record));
 	record.handle = si->handle;
@@ -3324,7 +3325,12 @@
 	svclass = sdp_list_append(NULL, &svclass_uuid);
 	sdp_set_service_classes(&record, svclass);
 
-	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+	err = sdp_device_record_register(session, &interface, &record,
+							SDP_RECORD_PERSIST);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass, NULL);
+
+	if (err < 0) {
 		printf("Service Record registration failed\n");
 		return -1;
 	}
diff --git a/tools/valgrind.supp b/tools/valgrind.supp
new file mode 100644
index 0000000..bf28bcd
--- /dev/null
+++ b/tools/valgrind.supp
@@ -0,0 +1,14 @@
+{
+   ecb_bind
+   Memcheck:Param
+   socketcall.bind(my_addr.sa_data)
+   fun:bind
+   fun:ecb_aes_setup
+}
+{
+   cmac_bind
+   Memcheck:Param
+   socketcall.bind(my_addr.sa_data)
+   fun:bind
+   fun:cmac_aes_setup
+}
diff --git a/unit/test-gdbus-client.c b/unit/test-gdbus-client.c
index b25289f..dd17c00 100644
--- a/unit/test-gdbus-client.c
+++ b/unit/test-gdbus-client.c
@@ -861,6 +861,10 @@
 	context->timeout_source = g_timeout_add_seconds(2, timeout_test,
 								context);
 
+	g_dbus_detach_object_manager(conn);
+
+	g_dbus_unregister_interface(conn, SERVICE_PATH, SERVICE_NAME1);
+
 	dbus_connection_flush(conn);
 	dbus_connection_close(conn);
 	dbus_connection_unref(conn);
diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index 66966ce..f2b9622 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -29,9 +29,9 @@
 
 #include <glib.h>
 #include "src/shared/hfp.h"
+#include "src/shared/tester.h"
 
 struct context {
-	GMainLoop *main_loop;
 	guint watch_id;
 	int fd_server;
 	int fd_client;
@@ -97,7 +97,7 @@
 		data.test_name = g_strdup(name);			\
 		data.pdu_list = g_memdup(pdus, sizeof(pdus));		\
 		data.result_func = result_function;			\
-		g_test_add_data_func(name, &data, function);		\
+		tester_add(name, &data, NULL, function, NULL);		\
 		data.test_handler = test_handler;			\
 	} while (0)
 
@@ -112,15 +112,10 @@
 		data.pdu_list = g_memdup(pdus, sizeof(pdus));		\
 		data.hf_result_func = result_func;			\
 		data.response_func = response_function;			\
-		g_test_add_data_func(name, &data, function);		\
+		tester_add(name, &data, NULL, function, NULL);		\
 		data.test_handler = test_hf_handler;			\
 	} while (0)
 
-static void context_quit(struct context *context)
-{
-	g_main_loop_quit(context->main_loop);
-}
-
 static void test_free(gconstpointer user_data)
 {
 	const struct test_data *data = user_data;
@@ -129,6 +124,34 @@
 	g_free(data->pdu_list);
 }
 
+static void destroy_context(struct context *context)
+{
+	if (context->watch_id)
+		g_source_remove(context->watch_id);
+
+	test_free(context->data);
+
+	if (context->hfp)
+		hfp_gw_unref(context->hfp);
+
+	if (context->hfp_hf)
+		hfp_hf_unref(context->hfp_hf);
+
+	g_free(context);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+	struct context *context = user_data;
+
+	if (context == NULL)
+		return FALSE;
+
+	destroy_context(context);
+	tester_test_passed();
+	return FALSE;
+}
+
 static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
 							gpointer user_data)
 {
@@ -138,10 +161,10 @@
 	pdu = &context->data->pdu_list[context->pdu_offset++];
 
 	g_assert(!pdu->valid);
-	context_quit(context);
-
 	context->watch_id = 0;
 
+	context_quit(context);
+
 	return FALSE;
 }
 
@@ -184,9 +207,10 @@
 	return TRUE;
 
 done:
-	context_quit(context);
 	context->watch_id = 0;
 
+	context_quit(context);
+
 	return FALSE;
 }
 
@@ -224,9 +248,6 @@
 	int err, sv[2];
 	const struct test_data *d = data;
 
-	context->main_loop = g_main_loop_new(NULL, FALSE);
-	g_assert(context->main_loop);
-
 	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
 	g_assert(err == 0);
 
@@ -251,26 +272,6 @@
 	return context;
 }
 
-static void execute_context(struct context *context)
-{
-	g_main_loop_run(context->main_loop);
-
-	if (context->watch_id)
-		g_source_remove(context->watch_id);
-
-	g_main_loop_unref(context->main_loop);
-
-	test_free(context->data);
-
-	if (context->hfp)
-		hfp_gw_unref(context->hfp);
-
-	if (context->hfp_hf)
-		hfp_hf_unref(context->hfp_hf);
-
-	g_free(context);
-}
-
 static void test_init(gconstpointer data)
 {
 	struct context *context = create_context(data);
@@ -283,7 +284,7 @@
 	hfp_gw_unref(context->hfp);
 	context->hfp = NULL;
 
-	execute_context(context);
+	context_quit(context);
 }
 
 static void test_command_handler(gconstpointer data)
@@ -308,7 +309,7 @@
 	len = write(context->fd_server, pdu->data, pdu->size);
 	g_assert_cmpint(len, ==, pdu->size);
 
-	execute_context(context);
+	context_quit(context);
 }
 
 static void test_register(gconstpointer data)
@@ -337,7 +338,7 @@
 	len = write(context->fd_server, pdu->data, pdu->size);
 	g_assert_cmpint(len, ==, pdu->size);
 
-	execute_context(context);
+	context_quit(context);
 }
 
 static void test_fragmented(gconstpointer data)
@@ -352,8 +353,6 @@
 	g_assert(ret);
 
 	g_idle_add(send_pdu, context);
-
-	execute_context(context);
 }
 
 static void test_send_and_close(gconstpointer data)
@@ -372,7 +371,7 @@
 	hfp_gw_unref(context->hfp);
 	context->hfp = NULL;
 
-	execute_context(context);
+	context_quit(context);
 }
 
 static void check_ustring_1(struct hfp_context *result,
@@ -494,7 +493,7 @@
 	hfp_hf_unref(context->hfp_hf);
 	context->hfp_hf = NULL;
 
-	execute_context(context);
+	context_quit(context);
 }
 
 static bool unsolicited_resp = false;
@@ -567,7 +566,7 @@
 		g_assert(ret);
 	}
 
-	execute_context(context);
+	context_quit(context);
 }
 static void hf_chld_result_handler(struct hfp_context *hf_context,
 							void *user_data)
@@ -667,8 +666,6 @@
 	}
 
 	send_pdu(context);
-
-	execute_context(context);
 }
 
 static void test_hf_robustness(gconstpointer data)
@@ -687,12 +684,12 @@
 	hfp_hf_unref(context->hfp_hf);
 	context->hfp_hf = NULL;
 
-	execute_context(context);
+	context_quit(context);
 }
 
 int main(int argc, char *argv[])
 {
-	g_test_init(&argc, &argv, NULL);
+	tester_init(&argc, &argv);
 
 	define_test("/hfp/test_init", test_init, NULL, data_end());
 	define_test("/hfp/test_cmd_handler_1", test_command_handler, NULL,
@@ -860,5 +857,5 @@
 			frg_pdu('1', ',', '2', 'x', '\r', '\n'),
 			data_end());
 
-	return g_test_run();
+	return tester_run();
 }
diff --git a/unit/test-sdp.c b/unit/test-sdp.c
index b4ef4d1..ac921a9 100644
--- a/unit/test-sdp.c
+++ b/unit/test-sdp.c
@@ -37,6 +37,7 @@
 #include "lib/sdp_lib.h"
 
 #include "src/shared/util.h"
+#include "src/shared/tester.h"
 #include "src/log.h"
 #include "src/sdpd.h"
 
@@ -78,7 +79,7 @@
 		static struct test_data data;				\
 		data.mtu = _mtu;					\
 		data.pdu_list = g_memdup(pdus, sizeof(pdus));		\
-		g_test_add_data_func(name, &data, test_sdp);		\
+		tester_add(name, &data, NULL, test_sdp, NULL);		\
 	} while (0)
 
 #define define_ss(name, args...) define_test("/TP/SERVER/SS/" name, 48, args)
@@ -105,32 +106,48 @@
 		data.input_data = input;				\
 		data.input_size = sizeof(input);			\
 		data.expected = exp;					\
-		g_test_add_data_func("/sdp/DE/ATTR/" name, &data,	\
-						test_sdp_de_attr);	\
+		tester_add("/sdp/DE/ATTR/" name, &data,	NULL,		\
+					test_sdp_de_attr, NULL);	\
 	} while (0)
 
 struct context {
-	GMainLoop *main_loop;
 	guint server_source;
 	guint client_source;
 	int fd;
-	int mtu;
 	uint8_t cont_data[16];
 	uint8_t cont_size;
 	unsigned int pdu_offset;
-	const struct sdp_pdu *pdu_list;
+	const struct test_data *data;
 };
 
 static void sdp_debug(const char *str, void *user_data)
 {
 	const char *prefix = user_data;
 
-	g_print("%s%s\n", prefix, str);
+	tester_debug("%s%s\n", prefix, str);
 }
 
-static void context_quit(struct context *context)
+static void destroy_context(struct context *context)
 {
-	g_main_loop_quit(context->main_loop);
+	sdp_svcdb_collect_all(context->fd);
+	sdp_svcdb_reset();
+
+	g_source_remove(context->server_source);
+	g_source_remove(context->client_source);
+
+	g_free(context);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+	struct context *context = user_data;
+	if (context == NULL)
+		return FALSE;
+
+	destroy_context(context);
+	tester_test_passed();
+
+	return FALSE;
 }
 
 static gboolean server_handler(GIOChannel *channel, GIOCondition cond,
@@ -169,10 +186,9 @@
 		return FALSE;
 	}
 
-	if (g_test_verbose() == TRUE)
-		util_hexdump('<', buf, len, sdp_debug, "SDP: ");
+	util_hexdump('<', buf, len, sdp_debug, "SDP: ");
 
-	handle_internal_request(fd, context->mtu, buf, len);
+	handle_internal_request(fd, context->data->mtu, buf, len);
 
 	return TRUE;
 }
@@ -185,7 +201,7 @@
 	unsigned char *buf;
 	ssize_t len;
 
-	req_pdu = &context->pdu_list[context->pdu_offset];
+	req_pdu = &context->data->pdu_list[context->pdu_offset];
 
 	pdu_len = req_pdu->raw_size + context->cont_size;
 
@@ -210,7 +226,7 @@
 {
 	context->pdu_offset += 2;
 
-	if (!context->pdu_list[context->pdu_offset].valid) {
+	if (!context->data->pdu_list[context->pdu_offset].valid) {
 		context_quit(context);
 		return;
 	}
@@ -227,7 +243,7 @@
 	ssize_t len;
 	int fd;
 
-	rsp_pdu = &context->pdu_list[context->pdu_offset + 1];
+	rsp_pdu = &context->data->pdu_list[context->pdu_offset + 1];
 
 	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
 		return FALSE;
@@ -238,8 +254,7 @@
 	if (len < 0)
 		return FALSE;
 
-	if (g_test_verbose() == TRUE)
-		util_hexdump('>', buf, len, sdp_debug, "SDP: ");
+	util_hexdump('>', buf, len, sdp_debug, "SDP: ");
 
 	g_assert(len > 0);
 	g_assert((size_t) len == rsp_pdu->raw_size + rsp_pdu->cont_len);
@@ -638,15 +653,12 @@
 	update_db_timestamp();
 }
 
-static struct context *create_context(void)
+static struct context *create_context(gconstpointer data)
 {
 	struct context *context = g_new0(struct context, 1);
 	GIOChannel *channel;
 	int err, sv[2];
 
-	context->main_loop = g_main_loop_new(NULL, FALSE);
-	g_assert(context->main_loop);
-
 	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
 	g_assert(err == 0);
 
@@ -677,6 +689,7 @@
 	g_io_channel_unref(channel);
 
 	context->fd = sv[1];
+	context->data = data;
 
 	set_fixed_db_timestamp(0x496f0654);
 
@@ -695,34 +708,11 @@
 	return context;
 }
 
-static void execute_context(struct context *context)
-{
-	g_main_loop_run(context->main_loop);
-
-	sdp_svcdb_collect_all(context->fd);
-	sdp_svcdb_reset();
-
-	g_source_remove(context->server_source);
-	g_source_remove(context->client_source);
-
-	g_main_loop_unref(context->main_loop);
-
-	g_free(context);
-}
-
 static void test_sdp(gconstpointer data)
 {
-	const struct test_data *test = data;
-	struct context *context = create_context();
-
-	context->mtu = test->mtu;
-	context->pdu_list = test->pdu_list;
+	struct context *context = create_context(data);
 
 	g_idle_add(send_pdu, context);
-
-	execute_context(context);
-
-	g_free(test->pdu_list);
 }
 
 static void test_sdp_de_attr(gconstpointer data)
@@ -737,8 +727,7 @@
 	g_assert_cmpuint(test->input_size, ==, size);
 	g_assert_cmpuint(test->expected.dtd, ==, d->dtd);
 
-	if (g_test_verbose() == TRUE)
-		g_print("DTD=0x%02x\n", d->dtd);
+	tester_debug("DTD=0x%02x\n", d->dtd);
 
 	switch (d->dtd) {
 	case SDP_TEXT_STR8:
@@ -785,14 +774,14 @@
 	}
 
 	sdp_data_free(d);
+	tester_test_passed();
 }
 
 int main(int argc, char *argv[])
 {
-	g_test_init(&argc, &argv, NULL);
+	tester_init(&argc, &argv);
 
-	if (g_test_verbose())
-		__btd_log_init("*", 0);
+	__btd_log_init("*", 0);
 
 	/*
 	 * Service Search Request
@@ -2821,5 +2810,5 @@
 						0x00, 0x00, 0x00, 0x00, 0x00,
 						0x00, 0x00, 0x00, 0x00, 0x00)));
 
-	return g_test_run();
+	return tester_run();
 }
diff --git a/unit/test-uhid.c b/unit/test-uhid.c
index b48e0fa..320cd54 100644
--- a/unit/test-uhid.c
+++ b/unit/test-uhid.c
@@ -38,6 +38,8 @@
 #include "src/shared/uhid.h"
 #include "src/shared/util.h"
 
+#include "src/shared/tester.h"
+
 struct test_pdu {
 	bool valid;
 	const uint8_t *data;
@@ -50,7 +52,6 @@
 };
 
 struct context {
-	GMainLoop *main_loop;
 	struct bt_uhid *uhid;
 	guint source;
 	guint process;
@@ -74,14 +75,14 @@
 		static struct test_data data;				\
 		data.test_name = g_strdup(name);			\
 		data.pdu_list = g_memdup(pdus, sizeof(pdus));		\
-		g_test_add_data_func(name, &data, function);		\
+		tester_add(name, &data, NULL, function, NULL);		\
 	} while (0)
 
 static void test_debug(const char *str, void *user_data)
 {
 	const char *prefix = user_data;
 
-	g_print("%s%s\n", prefix, str);
+	tester_debug("%s%s\n", prefix, str);
 }
 
 static void test_free(gconstpointer user_data)
@@ -92,14 +93,29 @@
 	g_free(data->pdu_list);
 }
 
+static void destroy_context(struct context *context)
+{
+	if (context->source > 0)
+		g_source_remove(context->source);
+
+	bt_uhid_unref(context->uhid);
+
+	test_free(context->data);
+	g_free(context);
+}
+
 static gboolean context_quit(gpointer user_data)
 {
 	struct context *context = user_data;
 
+	if (context == NULL)
+		return FALSE;
+
 	if (context->process > 0)
 		g_source_remove(context->process);
 
-	g_main_loop_quit(context->main_loop);
+	destroy_context(context);
+	tester_test_passed();
 
 	return FALSE;
 }
@@ -114,8 +130,8 @@
 
 	len = write(context->fd, pdu->data, pdu->size);
 
-	if (g_test_verbose())
-		util_hexdump('<', pdu->data, len, test_debug, "uHID: ");
+
+	util_hexdump('<', pdu->data, len, test_debug, "uHID: ");
 
 	g_assert_cmpint(len, ==, pdu->size);
 
@@ -156,8 +172,7 @@
 
 	g_assert(len > 0);
 
-	if (g_test_verbose())
-		util_hexdump('>', buf, len, test_debug, "uHID: ");
+	util_hexdump('>', buf, len, test_debug, "uHID: ");
 
 	g_assert_cmpint(len, ==, pdu->size);
 
@@ -174,9 +189,6 @@
 	GIOChannel *channel;
 	int err, sv[2];
 
-	context->main_loop = g_main_loop_new(NULL, FALSE);
-	g_assert(context->main_loop);
-
 	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
 	g_assert(err == 0);
 
@@ -202,26 +214,6 @@
 	return context;
 }
 
-static void destroy_context(struct context *context)
-{
-	if (context->source > 0)
-		g_source_remove(context->source);
-
-	bt_uhid_unref(context->uhid);
-
-	g_main_loop_unref(context->main_loop);
-
-	test_free(context->data);
-	g_free(context);
-}
-
-static void execute_context(struct context *context)
-{
-	g_main_loop_run(context->main_loop);
-
-	destroy_context(context);
-}
-
 static const struct uhid_event ev_create = {
 	.type = UHID_CREATE,
 };
@@ -263,7 +255,7 @@
 	if (g_str_equal(context->data->test_name, "/uhid/command/input"))
 		bt_uhid_send(context->uhid, &ev_input);
 
-	execute_context(context);
+	context_quit(context);
 }
 
 static void handle_output(struct uhid_event *ev, void *user_data)
@@ -288,13 +280,11 @@
 	bt_uhid_register(context->uhid, UHID_FEATURE, handle_feature, context);
 
 	g_idle_add(send_pdu, context);
-
-	execute_context(context);
 }
 
 int main(int argc, char *argv[])
 {
-	g_test_init(&argc, &argv, NULL);
+	tester_init(&argc, &argv);
 
 	define_test("/uhid/command/create", test_client, event(&ev_create));
 	define_test("/uhid/command/destroy", test_client, event(&ev_destroy));
@@ -305,5 +295,5 @@
 	define_test("/uhid/event/output", test_server, event(&ev_output));
 	define_test("/uhid/event/feature", test_server, event(&ev_feature));
 
-	return g_test_run();
+	return tester_run();
 }