Merge BlueZ 5.40 to master

Merge BlueZ 5.40 from gfiber-internal/upstream to master.

Change-Id: Ib8ba5c60af9d7d37e025d696bff6f091f39b427e
diff --git a/ChangeLog b/ChangeLog
index ce664b4..dab8141 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+ver 5.40:
+	Fix issue with not storing GATT attributes.
+	Fix issue with optional GATT notifications.
+	Fix issue with reading GATT extended properties.
+	Fix issue with GATT device name properties.
+	Fix issue with previously paired devices.
+	Fix issue with handling device removal.
+	Fix issue with profile connection handling.
+	Add support for TTY monitor protocol.
+
 ver 5.39:
 	Fix issue with missing uHID kernel support.
 	Fix issue with GATT reliable write handling.
diff --git a/HACKING b/HACKING
index a8fb403..d93730d 100644
--- a/HACKING
+++ b/HACKING
@@ -108,6 +108,19 @@
 welcome! In order to ease the inclusion of your patch, it's important to follow
 some rules, otherwise it will likely be rejected by maintainers.
 
+Make sure the author name and email are set properly:
+
+  # git config --global user.name <name>
+  # git config --global user.email <email>
+
+The preferred way to send patches is by email, using git send-email:
+
+  # git config --global sendemail.smtpencryption <tls>
+  # git config --global sendemail.smtpserver <smtp.gmail.com>
+  # git config --global sendemail.smtpuser <yourname@gmail.com>
+  # git config --global sendemail.smtpserverport <587>
+  # git config sendemail.to linux-bluetooth@vger.kernel.org
+
 BlueZ rules for submitting patches follow most of the rules used by Linux kernel
 (https://www.kernel.org/doc/Documentation/SubmittingPatches) with some remarks:
 
@@ -128,3 +141,17 @@
 should be limited to 50 characters and the description should be wrapped at 72
 characters except if it contains quoted information from debug tools like
 backtraces, compiler errors, etc.
+
+6) Prefix the email subject with [PATCH BlueZ]:
+
+  # git config format.subjectprefix "PATCH BlueZ"
+
+7) Add a cover letter when introducing a new feature explaning what problem
+you're trying to solve:
+
+  # git format-patch --cover-letter -M origin/master -o outgoing/
+  # edit outgoing/0000-*
+
+8) Submit:
+
+  # git send-email outgoing/*
diff --git a/Makefile.am b/Makefile.am
index bac371b..5510102 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:11:18
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:12:18
 lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
 endif
 
@@ -118,7 +118,8 @@
 			src/shared/gatt-client.h src/shared/gatt-client.c \
 			src/shared/gatt-server.h src/shared/gatt-server.c \
 			src/shared/gatt-db.h src/shared/gatt-db.c \
-			src/shared/gap.h src/shared/gap.c
+			src/shared/gap.h src/shared/gap.c \
+			src/shared/tty.h
 
 src_libshared_glib_la_SOURCES = $(shared_sources) \
 				src/shared/io-glib.c \
@@ -261,6 +262,8 @@
 EXTRA_DIST += doc/pics-opp.txt doc/pixit-opp.txt \
 		doc/pts-opp.txt
 
+EXTRA_DIST += doc/btsnoop.txt
+
 EXTRA_DIST += tools/magic.btsnoop
 
 AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@
diff --git a/Makefile.tools b/Makefile.tools
index 36d2a1b..af709b8 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -36,7 +36,8 @@
 				monitor/keys.h monitor/keys.c \
 				monitor/analyze.h monitor/analyze.c \
 				monitor/intel.h monitor/intel.c \
-				monitor/broadcom.h monitor/broadcom.c
+				monitor/broadcom.h monitor/broadcom.c \
+				monitor/tty.h
 monitor_btmon_LDADD = lib/libbluetooth-internal.la \
 				src/libshared-mainloop.la @UDEV_LIBS@
 endif
diff --git a/android/bluetoothd-snoop.c b/android/bluetoothd-snoop.c
index 7526782..4b09663 100644
--- a/android/bluetoothd-snoop.c
+++ b/android/bluetoothd-snoop.c
@@ -139,7 +139,7 @@
 
 		flags = get_flags_from_opcode(opcode);
 		if (flags != 0xff)
-			btsnoop_write(snoop, tv, flags, monitor_buf, pktlen);
+			btsnoop_write(snoop, tv, flags, 0, monitor_buf, pktlen);
 	}
 }
 
diff --git a/client/gatt.c b/client/gatt.c
index 7dd3c94..fee1cf9 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -379,9 +379,23 @@
 	rl_hexdump(value, len);
 }
 
+static void read_setup(DBusMessageIter *iter, void *user_data)
+{
+	DBusMessageIter dict;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+	/* TODO: Add offset support */
+	dbus_message_iter_close_container(iter, &dict);
+}
+
 static void read_attribute(GDBusProxy *proxy)
 {
-	if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply,
+	if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup, read_reply,
 							NULL, NULL) == FALSE) {
 		rl_printf("Failed to read\n");
 		return;
@@ -421,12 +435,21 @@
 static void write_setup(DBusMessageIter *iter, void *user_data)
 {
 	struct iovec *iov = user_data;
-	DBusMessageIter array;
+	DBusMessageIter array, dict;
 
 	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
 	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
 						&iov->iov_base, iov->iov_len);
 	dbus_message_iter_close_container(iter, &array);
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+	/* TODO: Add offset support */
+	dbus_message_iter_close_container(iter, &dict);
 }
 
 static void write_attribute(GDBusProxy *proxy, char *arg)
diff --git a/client/main.c b/client/main.c
index 209cf0c..c75b558 100644
--- a/client/main.c
+++ b/client/main.c
@@ -350,7 +350,6 @@
 	rl_set_prompt(desc ? desc : PROMPT_ON);
 	printf("\r");
 	rl_on_new_line();
-	rl_redisplay();
 	g_free(desc);
 }
 
diff --git a/configure.ac b/configure.ac
index 1af8e00..62c82ea 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(bluez, 5.39)
+AC_INIT(bluez, 5.40)
 
 AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
 					tar-pax no-dist-gzip dist-xz])
diff --git a/doc/btsnoop.txt b/doc/btsnoop.txt
new file mode 100644
index 0000000..975a53f
--- /dev/null
+++ b/doc/btsnoop.txt
@@ -0,0 +1,178 @@
+BTSnoop/Monitor protocol formats
+********************************
+
+Opcode definitions
+==================
+
+New Index
+---------
+
+	Code:        0x0000
+	Parameters:  Type (1 Octet
+		     Bus (1 Octet)
+		     BD_Addr (6 Octets)
+		     Name (8 Octets)
+
+	This opcode indicates that a new controller instance with a
+	given index was added. With some protocols, like the TTY-based
+	one there is only a single supported controller, meaning the
+	index is implicitly 0.
+
+Deleted Index
+-------------
+
+	Code:        0x0001
+
+	This opcode indicates that the controller with a specific index
+	was removed.
+
+Command Packet
+--------------
+
+	Code:        0x0002
+
+	HCI command packet.
+
+Event Packet
+------------
+
+	Code:        0x0003
+
+	HCI event packet.
+
+ACL TX Packet
+-------------
+
+	Code:        0x0004
+
+	Outgoing ACL packet.
+
+ACL RX Packet
+-------------
+
+	Code:        0x0005
+
+	Incoming ACL packet.
+
+SCO TX Packet
+--------------
+
+	Code:        0x0006
+
+	Outgoing SCO packet.
+
+SCO RX Packet
+-------------
+
+	Code:        0x0007
+
+	Incomnig SCO packet.
+
+Open Index
+----------
+
+	Code:        0x0008
+
+	The HCI transport for the specified controller has been opened.
+
+Close Index
+-----------
+
+	Code:        0x0009
+
+	The HCI transport for the specified controller has been closed.
+
+Index Information
+-----------------
+
+	Code:        0x000a
+	Parameters:  BD_Addr (6 Octets)
+		     Manufacturer (2 Octets)
+
+	Information about a specific controller.
+
+Vendor Diagnostics
+------------------
+
+	Code:        0x000b
+
+	Vendor diagnostic information.
+
+System Note
+-----------
+
+	Code:        0x000c
+
+	System note.
+
+User Logging
+------------
+
+	Code:        0x000d
+	Parameters:  Priority (1 Octet)
+		     Ident_Length (1 Octet)
+		     Ident (Ident_Length Octets)
+
+	User logging information.
+
+
+TTY-based protocol
+==================
+
+This section covers the protocol that can be parsed by btmon when
+passing it the --tty parameter. The protocol is little endian, packet
+based, and has the following header for each packet:
+
+struct tty_hdr {
+	uint16_t data_len;
+	uint16_t opcode;
+	uint8_t  flags;
+	uint8_t  hdr_len;
+	uint8_t  ext_hdr[0];
+} __attribute__ ((packed));
+
+The actual payload starts at ext_hdr + hdr_len and has the length of
+data_len - 4 - hdr_len. Each field of the header is defined as follows:
+
+data_len:
+	This is the total length of the entire packet, excuding the
+	data_len field itself.
+
+opcode:
+	The BTSnoop opcode
+
+flags:
+	Special flags for the packet. Currently no flags are defined.
+
+hdr_len:
+	Length of the extended header.
+
+ext_hdr:
+	This is a sequence of header extension fields formatted as:
+
+	struct {
+		uint8_t type;
+		uint8_t value[length];
+	}
+
+	The length of the value is dependent on the type. Currently the
+	following types are defined:
+
+	Type                 Length    Meaning
+	----------------------------------------------------------------
+	1  Command drops     1 byte    Dropped HCI command packets
+	2  Event drops       1 byte    Dropped HCI event packets
+	3  ACL TX drops      1 byte    Dropped ACL TX packets
+	4  ACL RX drops      1 byte    Dropped ACL RX packets
+	5  SCO TX drops      1 byte    Dropped SCO TX packets
+	6  SCO RX drops      1 byte    Dropped SCO RX packets
+	7  Other drops       1 byte    Dropped other packets
+	8  32-bit timestamp  4 bytes   Timestamp in 1/10th ms
+
+	The drops fields indicate the number of packets that the
+	implementation had to drop (e.g. due to lack of buffers) since
+	the last reported drop count.
+
+	The fields of the extended header must be sorted by increasing
+	type. This is essential so that unknown types can be ignored and
+	the parser can jump to processing the payload.
diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
index 232ffa0..4992243 100644
--- a/doc/gatt-api.txt
+++ b/doc/gatt-api.txt
@@ -61,23 +61,29 @@
 Interface	org.bluez.GattCharacteristic1 [Experimental]
 Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY
 
-Methods		array{byte} ReadValue()
+Methods		array{byte} ReadValue(dict options)
 
 			Issues a request to read the value of the
 			characteristic and returns the value if the
 			operation was successful.
 
+			Possible options: "offset": uint16 offset
+					  "device": Object Device (Server only)
+
 			Possible Errors: org.bluez.Error.Failed
 					 org.bluez.Error.InProgress
 					 org.bluez.Error.NotPermitted
 					 org.bluez.Error.NotAuthorized
 					 org.bluez.Error.NotSupported
 
-		void WriteValue(array{byte} value)
+		void WriteValue(array{byte} value, dict options)
 
 			Issues a request to write the value of the
 			characteristic.
 
+			Possible options: "offset": Start offset
+					  "device": Device path (Server only)
+
 			Possible Errors: org.bluez.Error.Failed
 					 org.bluez.Error.InProgress
 					 org.bluez.Error.NotPermitted
@@ -119,7 +125,7 @@
 			when a notification or indication is received, upon
 			which a PropertiesChanged signal will be emitted.
 
-		boolean Notifying [read-only]
+		boolean Notifying [read-only, optional]
 
 			True, if notifications or indications on this
 			characteristic are currently enabled.
@@ -144,6 +150,8 @@
 				"encrypt-write"
 				"encrypt-authenticated-read"
 				"encrypt-authenticated-write"
+				"secure-read" (Server only)
+				"secure-write" (Server only)
 
 Characteristic Descriptors hierarchy
 ====================================
@@ -154,23 +162,29 @@
 Interface	org.bluez.GattDescriptor1 [Experimental]
 Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY/descriptorZZZ
 
-Methods		array{byte} ReadValue()
+Methods		array{byte} ReadValue(dict flags)
 
 			Issues a request to read the value of the
 			characteristic and returns the value if the
 			operation was successful.
 
+			Possible options: "offset": Start offset
+					  "device": Device path (Server only)
+
 			Possible Errors: org.bluez.Error.Failed
 					 org.bluez.Error.InProgress
 					 org.bluez.Error.NotPermitted
 					 org.bluez.Error.NotAuthorized
 					 org.bluez.Error.NotSupported
 
-		void WriteValue(array{byte} value)
+		void WriteValue(array{byte} value, dict flags)
 
 			Issues a request to write the value of the
 			characteristic.
 
+			Possible options: "offset": Start offset
+					  "device": Device path (Server only)
+
 			Possible Errors: org.bluez.Error.Failed
 					 org.bluez.Error.InProgress
 					 org.bluez.Error.NotPermitted
@@ -205,9 +219,11 @@
 				"encrypt-write"
 				"encrypt-authenticated-read"
 				"encrypt-authenticated-write"
+				"secure-read" (Server Only)
+				"secure-write" (Server Only)
 
-Profile hierarcy
-================
+GATT Profile hierarcy
+=====================
 
 Local profile (GATT client) instance. By registering this type of object
 an application effectively indicates support for a specific GATT profile
@@ -226,6 +242,10 @@
 			profile, because when this method gets called it has
 			already been unregistered.
 
+Properties	array{string} UUIDs [read-only]
+
+			128-bit GATT service UUIDs to auto connect.
+
 
 GATT Manager hierarchy
 ======================
@@ -294,11 +314,12 @@
 Methods		void RegisterApplication(object application, dict options)
 
 			Registers a local GATT services hierarchy as described
-			above.
+			above (GATT Server) and/or GATT profiles (GATT Client).
 
 			The application object path together with the D-Bus
 			system bus connection ID define the identification of
-			the application registering a GATT based service.
+			the application registering a GATT based
+			service or profile.
 
 			Possible errors: org.bluez.Error.InvalidArguments
 					 org.bluez.Error.AlreadyExists
@@ -312,27 +333,3 @@
 
 			Possible errors: org.bluez.Error.InvalidArguments
 					 org.bluez.Error.DoesNotExist
-
-		void RegisterProfile(object profile, array{string} UUIDs,
-				     dict options)
-
-			Registers a GATT (client role) profile exported
-			under interface GattProfile1. The array of UUIDs
-			specifies the mandatory set of remote service
-			UUIDs that should all be available for the
-			remote device to match this profile. Matching
-			devices will be added to the auto-connection
-			list and connected whenever available.
-
-			Possible errors: org.bluez.Error.InvalidArguments
-					 org.bluez.Error.AlreadyExists
-
-		void UnregisterProfile(object profile)
-
-			This unregisters the profile that has been
-			previously registered. The object path parameter
-			must match the same value that has been used
-			on registration.
-
-			Possible errors: org.bluez.Error.InvalidArguments
-					 org.bluez.Error.DoesNotExist
diff --git a/doc/settings-storage.txt b/doc/settings-storage.txt
index aea50a6..2c34ec4 100644
--- a/doc/settings-storage.txt
+++ b/doc/settings-storage.txt
@@ -175,6 +175,7 @@
     2803:value_handle:properties:uuid
 
   Descriptor:
+    value:uuid
     uuid
 
 Sample Attributes section:
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index 69fbc10..e37385f 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -302,6 +302,15 @@
 			const char *name, const char *format, va_list args);
 void g_dbus_pending_property_error(GDBusPendingReply id, const char *name,
 						const char *format, ...);
+
+/*
+ * Note that when multiple properties for a given object path are changed
+ * in the same mainloop iteration, they will be grouped with the last
+ * property changed. If this behaviour is undesired, use
+ * g_dbus_emit_property_changed_full() with the
+ * G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH flag, causing the signal to ignore
+ * any grouping.
+ */
 void g_dbus_emit_property_changed(DBusConnection *connection,
 				const char *path, const char *interface,
 				const char *name);
diff --git a/gdbus/object.c b/gdbus/object.c
index a220101..afb4587 100644
--- a/gdbus/object.c
+++ b/gdbus/object.c
@@ -635,11 +635,19 @@
 
 static void add_pending(struct generic_data *data)
 {
-	if (data->process_id > 0)
-		return;
+	guint old_id = data->process_id;
 
 	data->process_id = g_idle_add(process_changes, data);
 
+	if (old_id > 0) {
+		/*
+		 * If the element already had an old idler, remove the old one,
+		 * no need to re-add it to the pending list.
+		 */
+		g_source_remove(old_id);
+		return;
+	}
+
 	pending = g_slist_append(pending, data);
 }
 
diff --git a/lib/bluetooth.c b/lib/bluetooth.c
index 5f57f02..fd0b081 100644
--- a/lib/bluetooth.c
+++ b/lib/bluetooth.c
@@ -548,7 +548,7 @@
 	case 142:
 		return "Quintic Corp.";
 	case 143:
-		return "Stollman E+V GmbH";
+		return "Telit Wireless Solutions GmbH (Formerly Stollman E+V GmbH)";
 	case 144:
 		return "Funai Electric Co., Ltd.";
 	case 145:
@@ -1953,6 +1953,112 @@
 		return "HM Electronics, Inc.";
 	case 845:
 		return "TASER International, Inc.";
+	case 846:
+		return "Safe Trust Inc.";
+	case 847:
+		return "Heartland Payment Systems";
+	case 848:
+		return "Bitstrata Systems Inc.";
+	case 849:
+		return "Pieps GmbH";
+	case 850:
+		return "iRiding(Xiamen)Technology Co.,Ltd.";
+	case 851:
+		return "Alpha Audiotronics, Inc.";
+	case 852:
+		return "TOPPAN FORMS CO.,LTD.";
+	case 853:
+		return "Sigma Designs, Inc.";
+	case 854:
+		return "Spectrum Brands, Inc.";
+	case 855:
+		return "Polymap Wireless";
+	case 856:
+		return "MagniWare Ltd.";
+	case 857:
+		return "Novotec Medical GmbH";
+	case 858:
+		return "Medicom Innovation Partner a/s";
+	case 859:
+		return "Matrix Inc.";
+	case 860:
+		return "Eaton Corporation";
+	case 861:
+		return "KYS";
+	case 862:
+		return "Naya Health, Inc.";
+	case 863:
+		return "Acromag";
+	case 864:
+		return "Insulet Corporation";
+	case 865:
+		return "Wellinks Inc.";
+	case 866:
+		return "ON Semiconductor";
+	case 867:
+		return "FREELAP SA";
+	case 868:
+		return "Favero Electronics Srl";
+	case 869:
+		return "BioMech Sensor LLC";
+	case 870:
+		return "BOLTT Sports technologies Private limited";
+	case 871:
+		return "Saphe International";
+	case 872:
+		return "Metormote AB";
+	case 873:
+		return "littleBits";
+	case 874:
+		return "SetPoint Medical";
+	case 875:
+		return "BRControls Products BV";
+	case 876:
+		return "Zipcar";
+	case 877:
+		return "AirBolt Pty Ltd";
+	case 878:
+		return "KeepTruckin Inc";
+	case 879:
+		return "Motiv, Inc.";
+	case 880:
+		return "Wazombi Labs OÜ";
+	case 881:
+		return "ORBCOMM";
+	case 882:
+		return "Nixie Labs, Inc.";
+	case 883:
+		return "AppNearMe Ltd";
+	case 884:
+		return "Holman Industries";
+	case 885:
+		return "Expain AS";
+	case 886:
+		return "Electronic Temperature Instruments Ltd";
+	case 887:
+		return "Plejd AB";
+	case 888:
+		return "Propeller Health";
+	case 889:
+		return "Shenzhen iMCO Electronic Technology Co.,Ltd";
+	case 890:
+		return "Algoria";
+	case 891:
+		return "Apption Labs Inc.";
+	case 892:
+		return "Cronologics Corporation";
+	case 893:
+		return "MICRODIA Ltd.";
+	case 894:
+		return "lulabytes S.L.";
+	case 895:
+		return "Nestec S.A.";
+	case 896:
+		return "LLC \"MEGA-F service\"";
+	case 897:
+		return "Sharp Corporation";
+	case 898:
+		return "Precision Outcomes Ltd";
 	case 65535:
 		return "internal use";
 	default:
diff --git a/lib/bluetooth.h b/lib/bluetooth.h
index 852a6b2..eb27926 100644
--- a/lib/bluetooth.h
+++ b/lib/bluetooth.h
@@ -69,6 +69,7 @@
 #define BT_SECURITY_LOW		1
 #define BT_SECURITY_MEDIUM	2
 #define BT_SECURITY_HIGH	3
+#define BT_SECURITY_FIPS	4
 
 #define BT_DEFER_SETUP	7
 
diff --git a/lib/hci.c b/lib/hci.c
index 97b6ec3..fda1aca 100644
--- a/lib/hci.c
+++ b/lib/hci.c
@@ -156,6 +156,10 @@
 		return "PCI";
 	case HCI_SDIO:
 		return "SDIO";
+	case HCI_SPI:
+		return "SPI";
+	case HCI_I2C:
+		return "I2C";
 	default:
 		return "UNKNOWN";
 	}
diff --git a/lib/hci.h b/lib/hci.h
index cbc55af..f76e205 100644
--- a/lib/hci.h
+++ b/lib/hci.h
@@ -55,6 +55,8 @@
 #define HCI_RS232	4
 #define HCI_PCI		5
 #define HCI_SDIO	6
+#define HCI_SPI		7
+#define HCI_I2C		8
 
 /* HCI controller types */
 #define HCI_BREDR	0x00
diff --git a/monitor/control.c b/monitor/control.c
index b3fc93d..26aecfd 100644
--- a/monitor/control.c
+++ b/monitor/control.c
@@ -35,6 +35,10 @@
 #include <sys/time.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <fcntl.h>
 
 #include "lib/bluetooth.h"
 #include "lib/hci.h"
@@ -48,6 +52,7 @@
 #include "packet.h"
 #include "hcidump.h"
 #include "ellisys.h"
+#include "tty.h"
 #include "control.h"
 
 static struct btsnoop *btsnoop_file = NULL;
@@ -965,7 +970,7 @@
 							data->buf, pktlen);
 			break;
 		case HCI_CHANNEL_MONITOR:
-			btsnoop_write_hci(btsnoop_file, tv, index, opcode,
+			btsnoop_write_hci(btsnoop_file, tv, index, opcode, 0,
 							data->buf, pktlen);
 			ellisys_inject_hci(tv, index, opcode,
 							data->buf, pktlen);
@@ -1058,23 +1063,25 @@
 
 	data->offset += len;
 
-	if (data->offset > MGMT_HDR_SIZE) {
+	while (data->offset >= MGMT_HDR_SIZE) {
 		struct mgmt_hdr *hdr = (struct mgmt_hdr *) data->buf;
 		uint16_t pktlen = le16_to_cpu(hdr->len);
+		uint16_t opcode, index;
 
-		if (data->offset > pktlen + MGMT_HDR_SIZE) {
-			uint16_t opcode = le16_to_cpu(hdr->opcode);
-			uint16_t index = le16_to_cpu(hdr->index);
+		if (data->offset < pktlen + MGMT_HDR_SIZE)
+			return;
 
-			packet_monitor(NULL, NULL, index, opcode,
+		opcode = le16_to_cpu(hdr->opcode);
+		index = le16_to_cpu(hdr->index);
+
+		packet_monitor(NULL, NULL, index, opcode,
 					data->buf + MGMT_HDR_SIZE, pktlen);
 
-			data->offset -= pktlen + MGMT_HDR_SIZE;
+		data->offset -= pktlen + MGMT_HDR_SIZE;
 
-			if (data->offset > 0)
-				memmove(data->buf, data->buf +
-					 MGMT_HDR_SIZE + pktlen, data->offset);
-		}
+		if (data->offset > 0)
+			memmove(data->buf, data->buf + MGMT_HDR_SIZE + pktlen,
+								data->offset);
 	}
 }
 
@@ -1157,6 +1164,202 @@
 	server_fd = fd;
 }
 
+static bool parse_drops(uint8_t **data, uint8_t *len, uint8_t *drops,
+							uint32_t *total)
+{
+	if (*len < 1)
+		return false;
+
+	*drops = **data;
+	*total += *drops;
+	(*data)++;
+	(*len)--;
+
+	return true;
+}
+
+static bool tty_parse_header(uint8_t *hdr, uint8_t len, struct timeval **tv,
+				struct timeval *ctv, uint32_t *drops)
+{
+	uint8_t cmd = 0;
+	uint8_t evt = 0;
+	uint8_t acl_tx = 0;
+	uint8_t acl_rx = 0;
+	uint8_t sco_tx = 0;
+	uint8_t sco_rx = 0;
+	uint8_t other = 0;
+	uint32_t total = 0;
+	uint32_t ts32;
+
+	while (len) {
+		uint8_t type = hdr[0];
+
+		hdr++; len--;
+
+		switch (type) {
+		case TTY_EXTHDR_COMMAND_DROPS:
+			if (!parse_drops(&hdr, &len, &cmd, &total))
+				return false;
+			break;
+		case TTY_EXTHDR_EVENT_DROPS:
+			if (!parse_drops(&hdr, &len, &evt, &total))
+				return false;
+			break;
+		case TTY_EXTHDR_ACL_TX_DROPS:
+			if (!parse_drops(&hdr, &len, &acl_tx, &total))
+				return false;
+			break;
+		case TTY_EXTHDR_ACL_RX_DROPS:
+			if (!parse_drops(&hdr, &len, &acl_rx, &total))
+				return false;
+			break;
+		case TTY_EXTHDR_SCO_TX_DROPS:
+			if (!parse_drops(&hdr, &len, &sco_tx, &total))
+				return false;
+			break;
+		case TTY_EXTHDR_SCO_RX_DROPS:
+			if (!parse_drops(&hdr, &len, &sco_rx, &total))
+				return false;
+			break;
+		case TTY_EXTHDR_OTHER_DROPS:
+			if (!parse_drops(&hdr, &len, &other, &total))
+				return false;
+			break;
+		case TTY_EXTHDR_TS32:
+			if (len < sizeof(ts32))
+				return false;
+			ts32 = get_le32(hdr);
+			hdr += sizeof(ts32); len -= sizeof(ts32);
+			/* ts32 is in units of 1/10th of a millisecond */
+			ctv->tv_sec = ts32 / 10000;
+			ctv->tv_usec = (ts32 % 10000) * 100;
+			*tv = ctv;
+			break;
+		default:
+			printf("Unknown extended header type %u\n", type);
+			return false;
+		}
+	}
+
+	if (total) {
+		*drops += total;
+		printf("* Drops: cmd %u evt %u acl_tx %u acl_rx %u sco_tx %u "
+			"sco_rx %u other %u\n", cmd, evt, acl_tx, acl_rx,
+			sco_tx, sco_rx, other);
+	}
+
+	return true;
+}
+
+static void tty_callback(int fd, uint32_t events, void *user_data)
+{
+	struct control_data *data = user_data;
+	ssize_t len;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_remove_fd(data->fd);
+		return;
+	}
+
+	len = read(data->fd, data->buf + data->offset,
+					sizeof(data->buf) - data->offset);
+	if (len < 0)
+		return;
+
+	data->offset += len;
+
+	while (data->offset >= sizeof(struct tty_hdr)) {
+		struct tty_hdr *hdr = (struct tty_hdr *) data->buf;
+		uint16_t pktlen, opcode, data_len;
+		struct timeval *tv = NULL;
+		struct timeval ctv;
+		uint32_t drops = 0;
+
+		data_len = le16_to_cpu(hdr->data_len);
+
+		if (data->offset < 2 + data_len)
+			return;
+
+		if (data->offset < sizeof(*hdr) + hdr->hdr_len) {
+			fprintf(stderr, "Received corrupted data from TTY\n");
+			memmove(data->buf, data->buf + 2 + data_len,
+								data->offset);
+			return;
+		}
+
+		if (!tty_parse_header(hdr->ext_hdr, hdr->hdr_len,
+							&tv, &ctv, &drops))
+			fprintf(stderr, "Unable to parse extended header\n");
+
+		opcode = le16_to_cpu(hdr->opcode);
+		pktlen = data_len - 4 - hdr->hdr_len;
+
+		btsnoop_write_hci(btsnoop_file, tv, 0, opcode, drops,
+					hdr->ext_hdr + hdr->hdr_len, pktlen);
+		packet_monitor(tv, NULL, 0, opcode,
+					hdr->ext_hdr + hdr->hdr_len, pktlen);
+
+		data->offset -= 2 + data_len;
+
+		if (data->offset > 0)
+			memmove(data->buf, data->buf + 2 + data_len,
+								data->offset);
+	}
+}
+
+int control_tty(const char *path, unsigned int speed)
+{
+	struct control_data *data;
+	struct termios ti;
+	int fd, err;
+
+	fd = open(path, O_RDWR | O_NOCTTY | O_NONBLOCK);
+	if (fd < 0) {
+		err = -errno;
+		perror("Failed to open serial port");
+		return err;
+	}
+
+	if (tcflush(fd, TCIOFLUSH) < 0) {
+		err = -errno;
+		perror("Failed to flush serial port");
+		close(fd);
+		return err;
+	}
+
+	memset(&ti, 0, sizeof(ti));
+	/* Switch TTY to raw mode */
+	cfmakeraw(&ti);
+
+	ti.c_cflag |= (CLOCAL | CREAD);
+	ti.c_cflag &= ~CRTSCTS;
+
+	cfsetspeed(&ti, speed);
+
+	if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+		err = -errno;
+		perror("Failed to set serial port settings");
+		close(fd);
+		return err;
+	}
+
+	printf("--- %s opened ---\n", path);
+
+	data = malloc(sizeof(*data));
+	if (!data) {
+		close(fd);
+		return -ENOMEM;
+	}
+
+	memset(data, 0, sizeof(*data));
+	data->channel = HCI_CHANNEL_MONITOR;
+	data->fd = fd;
+
+	mainloop_add_fd(data->fd, EPOLLIN, tty_callback, data, free_data);
+
+	return 0;
+}
+
 bool control_writer(const char *path)
 {
 	btsnoop_file = btsnoop_create(path, BTSNOOP_FORMAT_MONITOR);
diff --git a/monitor/control.h b/monitor/control.h
index 28f16db..55384db 100644
--- a/monitor/control.h
+++ b/monitor/control.h
@@ -27,6 +27,7 @@
 bool control_writer(const char *path);
 void control_reader(const char *path);
 void control_server(const char *path);
+int control_tty(const char *path, unsigned int speed);
 int control_tracing(void);
 
 void control_message(uint16_t opcode, const void *data, uint16_t size);
diff --git a/monitor/main.c b/monitor/main.c
index 4bf7ca1..a9fd90d 100644
--- a/monitor/main.c
+++ b/monitor/main.c
@@ -33,6 +33,7 @@
 #include <getopt.h>
 
 #include "src/shared/mainloop.h"
+#include "src/shared/tty.h"
 
 #include "packet.h"
 #include "lmp.h"
@@ -63,6 +64,8 @@
 		"\t-s, --server <socket>  Start monitor server socket\n"
 		"\t-p, --priority <level> Show only priority or lower\n"
 		"\t-i, --index <num>      Show only specified controller\n"
+		"\t-d, --tty <tty>        Read data from TTY\n"
+		"\t-B, --tty-speed <rate> Set TTY speed (default 115200)\n"
 		"\t-t, --time             Show time instead of time offset\n"
 		"\t-T, --date             Show time and date information\n"
 		"\t-S, --sco              Dump SCO traffic\n"
@@ -71,6 +74,8 @@
 }
 
 static const struct option main_options[] = {
+	{ "tty",     required_argument, NULL, 'd' },
+	{ "tty-speed", required_argument, NULL, 'B' },
 	{ "read",    required_argument, NULL, 'r' },
 	{ "write",   required_argument, NULL, 'w' },
 	{ "analyze", required_argument, NULL, 'a' },
@@ -94,6 +99,8 @@
 	const char *writer_path = NULL;
 	const char *analyze_path = NULL;
 	const char *ellisys_server = NULL;
+	const char *tty = NULL;
+	unsigned int tty_speed = B115200;
 	unsigned short ellisys_port = 0;
 	const char *str;
 	int exit_status;
@@ -107,12 +114,22 @@
 	for (;;) {
 		int opt;
 
-		opt = getopt_long(argc, argv, "r:w:a:s:p:i:tTSE:vh",
+		opt = getopt_long(argc, argv, "d:r:w:a:s:p:i:tTSE:vh",
 						main_options, NULL);
 		if (opt < 0)
 			break;
 
 		switch (opt) {
+		case 'd':
+			tty= optarg;
+			break;
+		case 'B':
+			tty_speed = tty_get_speed(atoi(optarg));
+			if (!tty_speed) {
+				fprintf(stderr, "Unknown speed: %s\n", optarg);
+				return EXIT_FAILURE;
+			}
+			break;
 		case 'r':
 			reader_path = optarg;
 			break;
@@ -213,7 +230,10 @@
 	if (ellisys_server)
 		ellisys_enable(ellisys_server, ellisys_port);
 
-	if (control_tracing() < 0)
+	if (!tty && control_tracing() < 0)
+		return EXIT_FAILURE;
+
+	if (tty && control_tty(tty, tty_speed) < 0)
 		return EXIT_FAILURE;
 
 	exit_status = mainloop_run();
diff --git a/monitor/packet.c b/monitor/packet.c
index 0b21121..6c642a8 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -1870,7 +1870,9 @@
 
 static void print_pk256(const char *label, const uint8_t *key)
 {
-	print_hex_field(label, key, 64);
+	print_field("%s:", label);
+	print_hex_field("  X", &key[0], 32);
+	print_hex_field("  Y", &key[32], 32);
 }
 
 static void print_dhkey(const uint8_t *dhkey)
diff --git a/monitor/tty.h b/monitor/tty.h
new file mode 100644
index 0000000..f0ba0c5
--- /dev/null
+++ b/monitor/tty.h
@@ -0,0 +1,41 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2016  Intel Corporation
+ *
+ *
+ *  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>
+
+struct tty_hdr {
+	uint16_t data_len;
+	uint16_t opcode;
+	uint8_t  flags;
+	uint8_t  hdr_len;
+	uint8_t  ext_hdr[0];
+} __attribute__ ((packed));
+
+#define TTY_EXTHDR_COMMAND_DROPS  1
+#define TTY_EXTHDR_EVENT_DROPS    2
+#define TTY_EXTHDR_ACL_TX_DROPS   3
+#define TTY_EXTHDR_ACL_RX_DROPS   4
+#define TTY_EXTHDR_SCO_TX_DROPS   5
+#define TTY_EXTHDR_SCO_RX_DROPS   6
+#define TTY_EXTHDR_OTHER_DROPS    7
+#define TTY_EXTHDR_TS32           8
diff --git a/monitor/uuid.c b/monitor/uuid.c
index 54adb0d..1ceaa6f 100644
--- a/monitor/uuid.c
+++ b/monitor/uuid.c
@@ -31,7 +31,7 @@
 
 #include "uuid.h"
 
-static struct {
+static const struct {
 	uint16_t uuid;
 	const char *str;
 } uuid16_table[] = {
@@ -540,6 +540,31 @@
 	{ }
 };
 
+static const struct {
+	const char *uuid;
+	const char *str;
+} uuid128_table[] = {
+	{ "a3c87500-8ed3-4bdf-8a39-a01bebede295",
+		"Eddystone Configuration Service"			},
+	{ "a3c87501-8ed3-4bdf-8a39-a01bebede295", "Capabilities"	},
+	{ "a3c87502-8ed3-4bdf-8a39-a01bebede295", "Active Slot"		},
+	{ "a3c87503-8ed3-4bdf-8a39-a01bebede295",
+		"Advertising Interval"					},
+	{ "a3c87504-8ed3-4bdf-8a39-a01bebede295", "Radio Tx Power"	},
+	{ "a3c87505-8ed3-4bdf-8a39-a01bebede295",
+		"(Advanced) Advertised Tx Power"			},
+	{ "a3c87506-8ed3-4bdf-8a39-a01bebede295", "Lock State"		},
+	{ "a3c87507-8ed3-4bdf-8a39-a01bebede295", "Unlock"		},
+	{ "a3c87508-8ed3-4bdf-8a39-a01bebede295", "Public ECDH Key"	},
+	{ "a3c87509-8ed3-4bdf-8a39-a01bebede295", "EID Identity Key"	},
+	{ "a3c8750a-8ed3-4bdf-8a39-a01bebede295", "ADV Slot Data"	},
+	{ "a3c8750b-8ed3-4bdf-8a39-a01bebede295",
+		"(Advanced) Factory reset"				},
+	{ "a3c8750c-8ed3-4bdf-8a39-a01bebede295",
+		"(Advanced) Remain Connectable"				},
+	{ }
+};
+
 const char *uuid16_to_str(uint16_t uuid)
 {
 	int i;
@@ -568,6 +593,7 @@
 const char *uuidstr_to_str(const char *uuid)
 {
 	uint32_t val;
+	int i;
 
 	if (!uuid)
 		return NULL;
@@ -575,6 +601,11 @@
 	if (strlen(uuid) != 36)
 		return NULL;
 
+	for (i = 0; uuid128_table[i].str; i++) {
+		if (strcasecmp(uuid128_table[i].uuid, uuid) == 0)
+			return uuid128_table[i].str;
+	}
+
 	if (strncasecmp(uuid + 8, "-0000-1000-8000-00805f9b34fb", 28))
 		return "Vendor specific";
 
diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c
index 22bf35b..2a43d32 100644
--- a/profiles/audio/avctp.c
+++ b/profiles/audio/avctp.c
@@ -293,8 +293,9 @@
 static void auth_cb(DBusError *derr, void *user_data);
 static gboolean process_queue(gpointer user_data);
 static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
-					uint8_t subunit, uint8_t *operands,
-					size_t operand_count, void *user_data);
+					uint8_t subunit, uint8_t transaction,
+					uint8_t *operands, size_t operand_count,
+					void *user_data);
 
 static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
 {
@@ -706,8 +707,8 @@
 	if (p->err == 0 || req->func == NULL)
 		goto done;
 
-	req->func(session, AVC_CTYPE_REJECTED, req->subunit, NULL, 0,
-							req->user_data);
+	req->func(session, AVC_CTYPE_REJECTED, req->subunit, p->transaction,
+						NULL, 0, req->user_data);
 
 done:
 	g_free(req->operands);
@@ -829,9 +830,9 @@
 			continue;
 
 		if (req->func && req->func(control->session, avc->code,
-						avc->subunit_type,
-						operands, operand_count,
-						req->user_data))
+					avc->subunit_type, p->transaction,
+					operands, operand_count,
+					req->user_data))
 			return;
 
 		control->processed = g_slist_remove(control->processed, p);
@@ -1724,8 +1725,9 @@
 }
 
 static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
-					uint8_t subunit, uint8_t *operands,
-					size_t operand_count, void *user_data)
+					uint8_t subunit, uint8_t transaction,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
 {
 	if (code != AVC_CTYPE_ACCEPTED)
 		return FALSE;
diff --git a/profiles/audio/avctp.h b/profiles/audio/avctp.h
index 6c19ce4..68a2735 100644
--- a/profiles/audio/avctp.h
+++ b/profiles/audio/avctp.h
@@ -132,8 +132,9 @@
 					uint8_t *subunit, uint8_t *operands,
 					size_t operand_count, void *user_data);
 typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code,
-					uint8_t subunit, uint8_t *operands,
-					size_t operand_count, void *user_data);
+					uint8_t subunit, uint8_t transaction,
+					uint8_t *operands, size_t operand_count,
+					void *user_data);
 typedef gboolean (*avctp_browsing_rsp_cb) (struct avctp *session,
 					uint8_t *operands, size_t operand_count,
 					void *user_data);
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 37bc291..6c8ed81 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2076,8 +2076,8 @@
 	}
 }
 
-static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
-					uint8_t code, uint8_t subunit,
+static gboolean avrcp_get_play_status_rsp(struct avctp *conn, uint8_t code,
+					uint8_t subunit, uint8_t transaction,
 					uint8_t *operands, size_t operand_count,
 					void *user_data)
 {
@@ -2140,8 +2140,8 @@
 	}
 }
 
-static gboolean avrcp_player_value_rsp(struct avctp *conn,
-					uint8_t code, uint8_t subunit,
+static gboolean avrcp_player_value_rsp(struct avctp *conn, uint8_t code,
+					uint8_t subunit, uint8_t transaction,
 					uint8_t *operands, size_t operand_count,
 					void *user_data)
 {
@@ -2210,8 +2210,8 @@
 
 static gboolean avrcp_list_player_attributes_rsp(struct avctp *conn,
 					uint8_t code, uint8_t subunit,
-					uint8_t *operands, size_t operand_count,
-					void *user_data)
+					uint8_t transaction, uint8_t *operands,
+					size_t operand_count, void *user_data)
 {
 	uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
 	struct avrcp *session = user_data;
@@ -2297,6 +2297,7 @@
 
 static gboolean avrcp_get_element_attributes_rsp(struct avctp *conn,
 						uint8_t code, uint8_t subunit,
+						uint8_t transaction,
 						uint8_t *operands,
 						size_t operand_count,
 						void *user_data)
@@ -3112,7 +3113,7 @@
 	struct avrcp *session = user_data;
 	struct avrcp_player *player = session->controller->player;
 	struct media_player *mp = player->user_data;
-	uint32_t num_of_items;
+	uint32_t num_of_items = 0;
 
 	if (pdu == NULL)
 		return -ETIMEDOUT;
@@ -3529,8 +3530,8 @@
 	player->uid_counter = get_be16(&pdu->params[1]);
 }
 
-static gboolean avrcp_handle_event(struct avctp *conn,
-					uint8_t code, uint8_t subunit,
+static gboolean avrcp_handle_event(struct avctp *conn, uint8_t code,
+					uint8_t subunit, uint8_t transaction,
 					uint8_t *operands, size_t operand_count,
 					void *user_data)
 {
@@ -3538,16 +3539,30 @@
 	struct avrcp_header *pdu = (void *) operands;
 	uint8_t event;
 
-	if ((code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED) ||
-								pdu == NULL)
+	if (!pdu)
 		return FALSE;
 
+	if ((code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)) {
+		if (pdu->params[0] == AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED &&
+				code == AVC_CTYPE_REJECTED) {
+			int i;
+
+			/* Lookup event by transaction */
+			for (i = 0; i <= AVRCP_EVENT_LAST; i++) {
+				if (session->transaction_events[i] ==
+								transaction) {
+					event = i;
+					goto changed;
+				}
+			}
+		}
+		return FALSE;
+	}
+
 	event = pdu->params[0];
 
 	if (code == AVC_CTYPE_CHANGED) {
-		session->registered_events ^= (1 << event);
-		avrcp_register_notification(session, event);
-		return FALSE;
+		goto changed;
 	}
 
 	switch (event) {
@@ -3578,8 +3593,15 @@
 	}
 
 	session->registered_events |= (1 << event);
+	session->transaction_events[event] = transaction;
 
 	return TRUE;
+
+changed:
+	session->registered_events ^= (1 << event);
+	session->transaction_events[event] = 0;
+	avrcp_register_notification(session, event);
+	return FALSE;
 }
 
 static void avrcp_register_notification(struct avrcp *session, uint8_t event)
@@ -3611,8 +3633,8 @@
 					avrcp_handle_event, session);
 }
 
-static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
-					uint8_t code, uint8_t subunit,
+static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code,
+					uint8_t subunit, uint8_t transaction,
 					uint8_t *operands, size_t operand_count,
 					void *user_data)
 {
@@ -4126,8 +4148,8 @@
 	player_destroy(player);
 }
 
-static gboolean avrcp_handle_set_volume(struct avctp *conn,
-					uint8_t code, uint8_t subunit,
+static gboolean avrcp_handle_set_volume(struct avctp *conn, uint8_t code,
+					uint8_t subunit, uint8_t transaction,
 					uint8_t *operands, size_t operand_count,
 					void *user_data)
 {
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index 69070bf..23d1561 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -1018,6 +1018,9 @@
 {
 	struct media_player *mp = user_data;
 
+	if (!mp->name)
+		return "Player";
+
 	return mp->name;
 }
 
diff --git a/profiles/deviceinfo/deviceinfo.c b/profiles/deviceinfo/deviceinfo.c
index d1f51a0..0c48f00 100644
--- a/profiles/deviceinfo/deviceinfo.c
+++ b/profiles/deviceinfo/deviceinfo.c
@@ -88,7 +88,7 @@
 	bt_string_to_uuid(&pnpid_uuid, PNPID_UUID);
 
 	if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
-								&uuid)) {
+								NULL, &uuid)) {
 		error("Failed to obtain characteristic data");
 		return;
 	}
diff --git a/profiles/gap/gas.c b/profiles/gap/gas.c
index 877c4fd..35b996c 100644
--- a/profiles/gap/gas.c
+++ b/profiles/gap/gas.c
@@ -181,7 +181,7 @@
 	bt_uuid_t uuid;
 
 	if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
-								&uuid)) {
+								NULL, &uuid)) {
 		error("Failed to obtain characteristic data");
 		return;
 	}
diff --git a/profiles/scanparam/scan.c b/profiles/scanparam/scan.c
index 4015b3f..d3ca762 100644
--- a/profiles/scanparam/scan.c
+++ b/profiles/scanparam/scan.c
@@ -140,7 +140,7 @@
 	bt_uuid_t uuid, scan_interval_wind_uuid, scan_refresh_uuid;
 
 	if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
-								&uuid)) {
+								NULL, &uuid)) {
 		error("Failed to obtain characteristic data");
 		return;
 	}
diff --git a/src/bluetooth.service.in b/src/bluetooth.service.in
index 83e4732..f799f65 100644
--- a/src/bluetooth.service.in
+++ b/src/bluetooth.service.in
@@ -12,6 +12,8 @@
 #Restart=on-failure
 CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
 LimitNPROC=1
+ProtectHome=true
+ProtectSystem=full
 
 [Install]
 WantedBy=bluetooth.target
diff --git a/src/device.c b/src/device.c
index ed8ffba..b1ff1bb 100644
--- a/src/device.c
+++ b/src/device.c
@@ -1277,6 +1277,9 @@
 	if (device->blocked)
 		return 0;
 
+	if (device->disconn_timer > 0)
+		g_source_remove(device->disconn_timer);
+
 	disconnect_all(device);
 
 	while (device->services != NULL) {
@@ -2001,6 +2004,7 @@
 
 struct gatt_saver {
 	struct btd_device *device;
+	uint16_t ext_props;
 	GKeyFile *key_file;
 };
 
@@ -2010,6 +2014,7 @@
 	GKeyFile *key_file = saver->key_file;
 	char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR];
 	const bt_uuid_t *uuid;
+	bt_uuid_t ext_uuid;
 	uint16_t handle_num;
 
 	handle_num = gatt_db_attribute_get_handle(attr);
@@ -2017,7 +2022,13 @@
 
 	uuid = gatt_db_attribute_get_type(attr);
 	bt_uuid_to_string(uuid, uuid_str, sizeof(uuid_str));
-	sprintf(value, "%s", uuid_str);
+
+	bt_uuid16_create(&ext_uuid, GATT_CHARAC_EXT_PROPER_UUID);
+	if (!bt_uuid_cmp(uuid, &ext_uuid) && saver->ext_props)
+		sprintf(value, "%04hx:%s", saver->ext_props, uuid_str);
+	else
+		sprintf(value, "%s", uuid_str);
+
 	g_key_file_set_string(key_file, "Attributes", handle, value);
 }
 
@@ -2031,7 +2042,8 @@
 	bt_uuid_t uuid;
 
 	if (!gatt_db_attribute_get_char_data(attr, &handle_num, &value_handle,
-							&properties, &uuid)) {
+						&properties, &saver->ext_props,
+						&uuid)) {
 		warn("Error storing characteristic - can't get data");
 		return;
 	}
@@ -2742,6 +2754,61 @@
 	return NULL;
 }
 
+static void load_services(struct btd_device *device, char **uuids)
+{
+	char **uuid;
+
+	for (uuid = uuids; *uuid; uuid++) {
+		if (g_slist_find_custom(device->uuids, *uuid, bt_uuid_strcmp))
+			continue;
+
+		device->uuids = g_slist_insert_sorted(device->uuids,
+							g_strdup(*uuid),
+							bt_uuid_strcmp);
+	}
+
+	g_strfreev(uuids);
+}
+
+static void convert_info(struct btd_device *device, GKeyFile *key_file)
+{
+	char filename[PATH_MAX];
+	char adapter_addr[18];
+	char device_addr[18];
+	char **uuids;
+	char *str;
+	gsize length = 0;
+
+	/* Load device profile list from legacy properties */
+	uuids = g_key_file_get_string_list(key_file, "General", "SDPServices",
+								NULL, NULL);
+	if (uuids)
+		load_services(device, uuids);
+
+	uuids = g_key_file_get_string_list(key_file, "General", "GATTServices",
+								NULL, NULL);
+	if (uuids)
+		load_services(device, uuids);
+
+	if (!device->uuids)
+		return;
+
+	/* Remove old entries so they are not loaded again */
+	g_key_file_remove_key(key_file, "General", "SDPServices", NULL);
+	g_key_file_remove_key(key_file, "General", "GATTServices", NULL);
+
+	ba2str(btd_adapter_get_address(device->adapter), adapter_addr);
+	ba2str(&device->bdaddr, device_addr);
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
+			device_addr);
+
+	str = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(filename, str, length, NULL);
+	g_free(str);
+
+	store_device_info(device);
+}
+
 static void load_info(struct btd_device *device, const char *local,
 			const char *peer, GKeyFile *key_file)
 {
@@ -2841,21 +2908,7 @@
 	uuids = g_key_file_get_string_list(key_file, "General", "Services",
 						NULL, NULL);
 	if (uuids) {
-		char **uuid;
-
-		for (uuid = uuids; *uuid; uuid++) {
-			GSList *match;
-
-			match = g_slist_find_custom(device->uuids, *uuid,
-							bt_uuid_strcmp);
-			if (match)
-				continue;
-
-			device->uuids = g_slist_insert_sorted(device->uuids,
-								g_strdup(*uuid),
-								bt_uuid_strcmp);
-		}
-		g_strfreev(uuids);
+		load_services(device, uuids);
 
 		/* Discovered services restored from storage */
 		device->bredr_state.svc_resolved = true;
@@ -2994,24 +3047,43 @@
 	*new_services = g_slist_append(*new_services, prim);
 }
 
+static void load_desc_value(struct gatt_db_attribute *attrib,
+						int err, void *user_data)
+{
+	if (err)
+		warn("loading descriptor value to db failed");
+}
+
 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;
+	uint16_t val;
+	bt_uuid_t uuid, ext_uuid;
 
 	if (sscanf(handle, "%04hx", &handle_int) != 1)
 		return -EIO;
 
-	if (sscanf(value, "%s", uuid_str) != 1)
-		return -EIO;
+	/* Check if there is any value stored, otherwise it is just the UUID */
+	if (sscanf(value, "%04hx:%s", &val, uuid_str) != 2) {
+		if (sscanf(value, "%s", uuid_str) != 1)
+			return -EIO;
+		val = 0;
+	}
+
+	DBG("loading descriptor handle: 0x%04x, value: 0x%04x, uuid: %s",
+				handle_int, val, uuid_str);
 
 	bt_string_to_uuid(&uuid, uuid_str);
+	bt_uuid16_create(&ext_uuid, GATT_CHARAC_EXT_PROPER_UUID);
 
-	DBG("loading descriptor handle: 0x%04x, uuid: %s", handle_int,
-								uuid_str);
+	/* If it is CEP then it must contain the value */
+	if (!bt_uuid_cmp(&uuid, &ext_uuid) && !val) {
+		warn("cannot load CEP descriptor without value");
+		return -EIO;
+	}
 
 	att = gatt_db_service_insert_descriptor(service, handle_int, &uuid,
 							0, NULL, NULL, NULL);
@@ -3020,6 +3092,13 @@
 		return -EIO;
 	}
 
+	if (val) {
+		if (!gatt_db_attribute_write(att, 0, (uint8_t *)&val,
+						sizeof(val), 0, NULL,
+						load_desc_value, NULL))
+			return -EIO;
+	}
+
 	return 0;
 }
 
@@ -3373,9 +3452,6 @@
 
 	store_gatt_db(device);
 
-	g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE,
-								"GattServices");
-
 	return FALSE;
 }
 
@@ -3576,6 +3652,8 @@
 	src = btd_adapter_get_address(adapter);
 	ba2str(src, srcaddr);
 
+	convert_info(device, key_file);
+
 	load_info(device, srcaddr, address, key_file);
 	load_att_info(device, srcaddr, address);
 
@@ -3907,8 +3985,10 @@
 	g_slist_free(device->pending);
 	device->pending = NULL;
 
-	if (btd_device_is_connected(device))
+	if (btd_device_is_connected(device)) {
+		g_source_remove(device->disconn_timer);
 		disconnect_all(device);
+	}
 
 	if (device->store_id > 0) {
 		g_source_remove(device->store_id);
@@ -4584,6 +4664,8 @@
 	btd_gatt_client_ready(device->client_dbus);
 
 	device_svc_resolved(device, device->bdaddr_type, 0);
+
+	store_gatt_db(device);
 }
 
 static void gatt_client_service_changed(uint16_t start_handle,
diff --git a/src/gatt-client.c b/src/gatt-client.c
index a018c8c..0cbacca 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -23,6 +23,7 @@
 
 #include <stdbool.h>
 #include <stdint.h>
+#include <errno.h>
 
 #include <dbus/dbus.h>
 
@@ -71,7 +72,6 @@
 	bt_uuid_t uuid;
 	char *path;
 	struct queue *chrcs;
-	struct queue *pending_ext_props;
 };
 
 struct characteristic {
@@ -192,33 +192,17 @@
 	return ret;
 }
 
-static bool parse_value_arg(DBusMessage *msg, uint8_t **value,
-							size_t *value_len)
+static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len)
 {
-	DBusMessageIter iter, array;
-	uint8_t *val;
-	int len;
+	DBusMessageIter array;
 
-	if (!dbus_message_iter_init(msg, &iter))
-		return false;
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return -EINVAL;
 
-	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
-		return false;
+	dbus_message_iter_recurse(iter, &array);
+	dbus_message_iter_get_fixed_array(&array, value, len);
 
-	dbus_message_iter_recurse(&iter, &array);
-	dbus_message_iter_get_fixed_array(&array, &val, &len);
-	dbus_message_iter_next(&iter);
-
-	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID)
-		return false;
-
-	if (len < 0)
-		return false;
-
-	*value = val;
-	*value_len = len;
-
-	return true;
+	return 0;
 }
 
 typedef bool (*async_dbus_op_complete_t)(void *data);
@@ -391,12 +375,60 @@
 	return;
 }
 
+static int parse_options(DBusMessageIter *iter, uint16_t *offset)
+{
+	DBusMessageIter dict;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return -EINVAL;
+
+	dbus_message_iter_recurse(iter, &dict);
+
+	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+		const char *key;
+		DBusMessageIter value, entry;
+		int var;
+
+		dbus_message_iter_recurse(&dict, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_recurse(&entry, &value);
+
+		var = dbus_message_iter_get_arg_type(&value);
+		if (strcasecmp(key, "offset") == 0) {
+			if (var != DBUS_TYPE_UINT16)
+				return -EINVAL;
+			dbus_message_iter_get_basic(&value, offset);
+		}
+	}
+
+	return 0;
+}
+
+static unsigned int read_value(struct bt_gatt_client *gatt, uint16_t handle,
+				bt_gatt_client_read_callback_t callback,
+				struct async_dbus_op *op)
+{
+	if (op->offset)
+		return bt_gatt_client_read_long_value(gatt, handle, op->offset,
+							callback,
+							async_dbus_op_ref(op),
+							async_dbus_op_unref);
+	else
+		return bt_gatt_client_read_value(gatt, handle, callback,
+							async_dbus_op_ref(op),
+							async_dbus_op_unref);
+}
+
 static DBusMessage *descriptor_read_value(DBusConnection *conn,
 					DBusMessage *msg, void *user_data)
 {
 	struct descriptor *desc = user_data;
 	struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
+	DBusMessageIter iter;
 	struct async_dbus_op *op;
+	uint16_t offset = 0;
 
 	if (!gatt)
 		return btd_error_failed(msg, "Not connected");
@@ -404,14 +436,17 @@
 	if (desc->read_id)
 		return btd_error_in_progress(msg);
 
+	dbus_message_iter_init(msg, &iter);
+
+	if (parse_options(&iter, &offset))
+		return btd_error_invalid_args(msg);
+
 	op = new0(struct async_dbus_op, 1);
 	op->msg = dbus_message_ref(msg);
 	op->data = desc;
+	op->offset = offset;
 
-	desc->read_id = bt_gatt_client_read_value(gatt, desc->handle,
-							desc_read_cb,
-							async_dbus_op_ref(op),
-							async_dbus_op_unref);
+	desc->read_id = read_value(gatt, desc->handle, desc_read_cb, op);
 	if (desc->read_id)
 		return NULL;
 
@@ -451,7 +486,6 @@
 	g_dbus_send_message(btd_get_dbus_connection(), reply);
 }
 
-
 static void write_cb(bool success, uint8_t att_ecode, void *user_data)
 {
 	write_result_cb(success, false, att_ecode, user_data);
@@ -460,7 +494,8 @@
 static unsigned int start_long_write(DBusMessage *msg, uint16_t handle,
 					struct bt_gatt_client *gatt,
 					bool reliable, const uint8_t *value,
-					size_t value_len, void *data,
+					size_t value_len, uint16_t offset,
+					void *data,
 					async_dbus_op_complete_t complete)
 {
 	struct async_dbus_op *op;
@@ -470,9 +505,10 @@
 	op->msg = dbus_message_ref(msg);
 	op->data = data;
 	op->complete = complete;
+	op->offset = offset;
 
-	id = bt_gatt_client_write_long_value(gatt, reliable, handle,
-							0, value, value_len,
+	id = bt_gatt_client_write_long_value(gatt, reliable, handle, offset,
+							value, value_len,
 							write_result_cb, op,
 							async_dbus_op_free);
 
@@ -523,8 +559,10 @@
 {
 	struct descriptor *desc = user_data;
 	struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
+	DBusMessageIter iter;
 	uint8_t *value = NULL;
-	size_t value_len = 0;
+	int value_len = 0;
+	uint16_t offset = 0;
 
 	if (!gatt)
 		return btd_error_failed(msg, "Not connected");
@@ -532,7 +570,12 @@
 	if (desc->write_id)
 		return btd_error_in_progress(msg);
 
-	if (!parse_value_arg(msg, &value, &value_len))
+	dbus_message_iter_init(msg, &iter);
+
+	if (parse_value_arg(&iter, &value, &value_len))
+		return btd_error_invalid_args(msg);
+
+	if (parse_options(&iter, &offset))
 		return btd_error_invalid_args(msg);
 
 	/*
@@ -547,15 +590,15 @@
 	 * Based on the value length and the MTU, either use a write or a long
 	 * write.
 	 */
-	if (value_len <= (unsigned) bt_gatt_client_get_mtu(gatt) - 3)
+	if (value_len <= bt_gatt_client_get_mtu(gatt) - 3 && !offset)
 		desc->write_id = start_write_request(msg, desc->handle,
 							gatt, value,
 							value_len, desc,
 							desc_write_complete);
 	else
-		desc->write_id = start_long_write(msg, desc->handle,
-							gatt, false, value,
-							value_len, desc,
+		desc->write_id = start_long_write(msg, desc->handle, gatt,
+							false, value,
+							value_len, offset, desc,
 							desc_write_complete);
 
 	if (!desc->write_id)
@@ -575,13 +618,15 @@
 };
 
 static const GDBusMethodTable descriptor_methods[] = {
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", NULL,
-						GDBUS_ARGS({ "value", "ay" }),
-						descriptor_read_value) },
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue",
+					GDBUS_ARGS({ "options", "a{sv}" }),
+					GDBUS_ARGS({ "value", "ay" }),
+					descriptor_read_value) },
 	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue",
-						GDBUS_ARGS({ "value", "ay" }),
-						NULL,
-						descriptor_write_value) },
+					GDBUS_ARGS({ "value", "ay" },
+						{ "options", "a{sv}" }),
+					NULL,
+					descriptor_write_value) },
 	{ }
 };
 
@@ -707,6 +752,15 @@
 	return TRUE;
 }
 
+static gboolean
+characteristic_notifying_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct characteristic *chrc = data;
+
+	return (chrc->props & BT_GATT_CHRC_PROP_NOTIFY ||
+				chrc->props & BT_GATT_CHRC_PROP_INDICATE);
+}
+
 struct chrc_prop_data {
 	uint8_t prop;
 	char *str;
@@ -829,7 +883,9 @@
 {
 	struct characteristic *chrc = user_data;
 	struct bt_gatt_client *gatt = chrc->service->client->gatt;
+	DBusMessageIter iter;
 	struct async_dbus_op *op;
+	uint16_t offset = 0;
 
 	if (!gatt)
 		return btd_error_failed(msg, "Not connected");
@@ -837,14 +893,17 @@
 	if (chrc->read_id)
 		return btd_error_in_progress(msg);
 
+	dbus_message_iter_init(msg, &iter);
+
+	if (parse_options(&iter, &offset))
+		return btd_error_invalid_args(msg);
+
 	op = new0(struct async_dbus_op, 1);
 	op->msg = dbus_message_ref(msg);
 	op->data = chrc;
+	op->offset = offset;
 
-	chrc->read_id = bt_gatt_client_read_value(gatt, chrc->value_handle,
-							chrc_read_cb,
-							async_dbus_op_ref(op),
-							async_dbus_op_unref);
+	chrc->read_id = read_value(gatt, chrc->value_handle, chrc_read_cb, op);
 	if (chrc->read_id)
 		return NULL;
 
@@ -871,9 +930,11 @@
 {
 	struct characteristic *chrc = user_data;
 	struct bt_gatt_client *gatt = chrc->service->client->gatt;
+	DBusMessageIter iter;
 	uint8_t *value = NULL;
-	size_t value_len = 0;
+	int value_len = 0;
 	bool supported = false;
+	uint16_t offset = 0;
 
 	if (!gatt)
 		return btd_error_failed(msg, "Not connected");
@@ -881,7 +942,12 @@
 	if (chrc->write_id)
 		return btd_error_in_progress(msg);
 
-	if (!parse_value_arg(msg, &value, &value_len))
+	dbus_message_iter_init(msg, &iter);
+
+	if (parse_value_arg(&iter, &value, &value_len))
+		return btd_error_invalid_args(msg);
+
+	if (parse_options(&iter, &offset))
 		return btd_error_invalid_args(msg);
 
 	/*
@@ -898,7 +964,7 @@
 	if ((chrc->ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE)) {
 		supported = true;
 		chrc->write_id = start_long_write(msg, chrc->value_handle, gatt,
-						true, value, value_len,
+						true, value, value_len, offset,
 						chrc, chrc_write_complete);
 		if (chrc->write_id)
 			return NULL;
@@ -912,7 +978,7 @@
 		if (!mtu)
 			return btd_error_failed(msg, "No ATT transport");
 
-		if (value_len <= (unsigned) mtu - 3)
+		if (value_len <= mtu - 3 && !offset)
 			chrc->write_id = start_write_request(msg,
 						chrc->value_handle,
 						gatt, value, value_len,
@@ -920,7 +986,7 @@
 		else
 			chrc->write_id = start_long_write(msg,
 						chrc->value_handle, gatt,
-						false, value, value_len,
+						false, value, value_len, offset,
 						chrc, chrc_write_complete);
 
 		if (chrc->write_id)
@@ -1225,7 +1291,8 @@
 	{ "Value", "ay", characteristic_get_value, NULL,
 					characteristic_value_exists,
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "Notifying", "b", characteristic_get_notifying, NULL, NULL,
+	{ "Notifying", "b", characteristic_get_notifying, NULL,
+					characteristic_notifying_exists,
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 	{ "Flags", "as", characteristic_get_flags, NULL, NULL,
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
@@ -1233,17 +1300,19 @@
 };
 
 static const GDBusMethodTable characteristic_methods[] = {
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", NULL,
-						GDBUS_ARGS({ "value", "ay" }),
-						characteristic_read_value) },
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue",
+					GDBUS_ARGS({ "options", "a{sv}" }),
+					GDBUS_ARGS({ "value", "ay" }),
+					characteristic_read_value) },
 	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue",
-						GDBUS_ARGS({ "value", "ay" }),
-						NULL,
-						characteristic_write_value) },
+					GDBUS_ARGS({ "value", "ay" },
+						{ "options", "a{sv}" }),
+					NULL,
+					characteristic_write_value) },
 	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("StartNotify", NULL, NULL,
-						characteristic_start_notify) },
+					characteristic_start_notify) },
 	{ GDBUS_EXPERIMENTAL_METHOD("StopNotify", NULL, NULL,
-						characteristic_stop_notify) },
+					characteristic_stop_notify) },
 	{ }
 };
 
@@ -1273,7 +1342,9 @@
 
 	gatt_db_attribute_get_char_data(attr, &chrc->handle,
 							&chrc->value_handle,
-							&chrc->props, &uuid);
+							&chrc->props,
+							&chrc->ext_props,
+							&uuid);
 
 	chrc->attr = gatt_db_get_attribute(service->client->db,
 							chrc->value_handle);
@@ -1387,7 +1458,6 @@
 	struct service *service = data;
 
 	queue_destroy(service->chrcs, NULL);  /* List should be empty here */
-	queue_destroy(service->pending_ext_props, NULL);
 	g_free(service->path);
 	free(service);
 }
@@ -1401,7 +1471,6 @@
 
 	service = new0(struct service, 1);
 	service->chrcs = queue_new();
-	service->pending_ext_props = queue_new();
 	service->client = client;
 
 	gatt_db_attribute_get_service_data(attr, &service->start_handle,
@@ -1473,44 +1542,6 @@
 	queue_push_tail(charac->descs, desc);
 }
 
-static void read_ext_props_cb(bool success, uint8_t att_ecode,
-					const uint8_t *value, uint16_t length,
-					void *user_data)
-{
-	struct characteristic *chrc = user_data;
-	struct service *service = chrc->service;
-
-	if (!success) {
-		error("Failed to obtain extended properties - error: 0x%02x",
-								att_ecode);
-		return;
-	}
-
-	if (!value || length != 2) {
-		error("Malformed extended properties value");
-		return;
-	}
-
-	chrc->ext_props = get_le16(value);
-	if (chrc->ext_props)
-		g_dbus_emit_property_changed(btd_get_dbus_connection(),
-						chrc->path,
-						GATT_CHARACTERISTIC_IFACE,
-						"Flags");
-
-	queue_remove(service->pending_ext_props, chrc);
-}
-
-static void read_ext_props(void *data, void *user_data)
-{
-	struct characteristic *chrc = data;
-
-	bt_gatt_client_read_value(chrc->service->client->gatt,
-							chrc->ext_props_handle,
-							read_ext_props_cb,
-							chrc, NULL);
-}
-
 static bool create_descriptors(struct gatt_db_attribute *attr,
 					struct characteristic *charac)
 {
@@ -1544,9 +1575,6 @@
 
 	queue_push_tail(service->chrcs, charac);
 
-	if (charac->ext_props_handle)
-		queue_push_tail(service->pending_ext_props, charac);
-
 	return;
 
 fail:
@@ -1563,13 +1591,7 @@
 
 	gatt_db_service_foreach_char(attr, export_char, &data);
 
-	if (data.failed)
-		return false;
-
-	/* Obtain extended properties */
-	queue_foreach(service->pending_ext_props, read_ext_props, NULL);
-
-	return true;
+	return !data.failed;
 }
 
 static void export_service(struct gatt_db_attribute *attr, void *user_data)
diff --git a/src/gatt-database.c b/src/gatt-database.c
index b8da955..e287b98 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -54,6 +54,7 @@
 #endif
 
 #define GATT_MANAGER_IFACE	"org.bluez.GattManager1"
+#define GATT_PROFILE_IFACE	"org.bluez.GattProfile1"
 #define GATT_SERVICE_IFACE	"org.bluez.GattService1"
 #define GATT_CHRC_IFACE		"org.bluez.GattCharacteristic1"
 #define GATT_DESC_IFACE		"org.bluez.GattDescriptor1"
@@ -88,6 +89,7 @@
 	DBusMessage *reg;
 	GDBusClient *client;
 	bool failed;
+	struct queue *profiles;
 	struct queue *services;
 	struct queue *proxies;
 };
@@ -103,10 +105,8 @@
 };
 
 struct external_profile {
-	struct btd_gatt_database *database;
-	char *owner;
-	char *path;	/* Path to GattProfile1 */
-	unsigned int id;
+	struct gatt_app *app;
+	GDBusProxy *proxy;
 	struct queue *profiles; /* btd_profile list */
 };
 
@@ -116,6 +116,7 @@
 	GDBusProxy *proxy;
 	uint8_t props;
 	uint8_t ext_props;
+	uint32_t perm;
 	struct gatt_db_attribute *attrib;
 	struct gatt_db_attribute *ccc;
 	struct queue *pending_reads;
@@ -135,6 +136,7 @@
 };
 
 struct pending_op {
+	struct btd_device *device;
 	unsigned int id;
 	struct gatt_db_attribute *attrib;
 	struct queue *owner_queue;
@@ -361,10 +363,47 @@
 	free(service);
 }
 
+static void profile_remove(void *data)
+{
+	struct btd_profile *p = data;
+
+	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);
+
+	free(p);
+}
+
+static void profile_release(struct external_profile *profile)
+{
+	DBG("Releasing \"%s\"", profile->app->owner);
+
+	g_dbus_proxy_method_call(profile->proxy, "Release", NULL, NULL, NULL,
+									NULL);
+}
+
+static void profile_free(void *data)
+{
+	struct external_profile *profile = data;
+
+	queue_destroy(profile->profiles, profile_remove);
+
+	profile_release(profile);
+
+	g_dbus_proxy_unref(profile->proxy);
+
+	free(profile);
+}
+
 static void app_free(void *data)
 {
 	struct gatt_app *app = data;
 
+	queue_destroy(app->profiles, profile_free);
 	queue_destroy(app->services, service_free);
 	queue_destroy(app->proxies, NULL);
 
@@ -385,53 +424,6 @@
 	free(app);
 }
 
-static void profile_remove(void *data)
-{
-	struct btd_profile *p = data;
-
-	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);
-
-	free(p);
-}
-
-static void profile_release(struct external_profile *profile)
-{
-	DBusMessage *msg;
-
-	if (!profile->id)
-		return;
-
-	DBG("Releasing \"%s\"", profile->owner);
-
-	g_dbus_remove_watch(btd_get_dbus_connection(), profile->id);
-
-	msg = dbus_message_new_method_call(profile->owner, profile->path,
-						"org.bluez.GattProfile1",
-						"Release");
-	if (msg)
-		g_dbus_send_message(btd_get_dbus_connection(), msg);
-}
-
-static void profile_free(void *data)
-{
-	struct external_profile *profile = data;
-
-	queue_destroy(profile->profiles, profile_remove);
-
-	profile_release(profile);
-
-	g_free(profile->owner);
-	g_free(profile->path);
-
-	free(profile);
-}
-
 static void gatt_database_free(void *data)
 {
 	struct btd_gatt_database *database = data;
@@ -1122,7 +1114,7 @@
 }
 
 static bool parse_chrc_flags(DBusMessageIter *array, uint8_t *props,
-							uint8_t *ext_props)
+					uint8_t *ext_props, uint32_t *perm)
 {
 	const char *flag;
 
@@ -1136,34 +1128,51 @@
 
 		if (!strcmp("broadcast", flag))
 			*props |= BT_GATT_CHRC_PROP_BROADCAST;
-		else if (!strcmp("read", flag))
+		else if (!strcmp("read", flag)) {
 			*props |= BT_GATT_CHRC_PROP_READ;
-		else if (!strcmp("write-without-response", flag))
+			*perm |= BT_ATT_PERM_READ;
+		} else if (!strcmp("write-without-response", flag)) {
 			*props |= BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP;
-		else if (!strcmp("write", flag))
+			*perm |= BT_ATT_PERM_WRITE;
+		} else if (!strcmp("write", flag)) {
 			*props |= BT_GATT_CHRC_PROP_WRITE;
-		else if (!strcmp("notify", flag))
+			*perm |= BT_ATT_PERM_WRITE;
+		} else if (!strcmp("notify", flag)) {
 			*props |= BT_GATT_CHRC_PROP_NOTIFY;
-		else if (!strcmp("indicate", flag))
+		} else if (!strcmp("indicate", flag)) {
 			*props |= BT_GATT_CHRC_PROP_INDICATE;
-		else if (!strcmp("authenticated-signed-writes", flag))
+		} else if (!strcmp("authenticated-signed-writes", flag)) {
 			*props |= BT_GATT_CHRC_PROP_AUTH;
-		else if (!strcmp("reliable-write", flag))
+			*perm |= BT_ATT_PERM_WRITE;
+		} else if (!strcmp("reliable-write", flag)) {
 			*ext_props |= BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE;
-		else if (!strcmp("writable-auxiliaries", flag))
+			*perm |= BT_ATT_PERM_WRITE;
+		} else if (!strcmp("writable-auxiliaries", flag)) {
 			*ext_props |= BT_GATT_CHRC_EXT_PROP_WRITABLE_AUX;
-		else if (!strcmp("encrypt-read", flag)) {
+		} else if (!strcmp("encrypt-read", flag)) {
 			*props |= BT_GATT_CHRC_PROP_READ;
 			*ext_props |= BT_GATT_CHRC_EXT_PROP_ENC_READ;
+			*perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT;
 		} else if (!strcmp("encrypt-write", flag)) {
 			*props |= BT_GATT_CHRC_PROP_WRITE;
 			*ext_props |= BT_GATT_CHRC_EXT_PROP_ENC_WRITE;
+			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_ENCRYPT;
 		} else if (!strcmp("encrypt-authenticated-read", flag)) {
 			*props |= BT_GATT_CHRC_PROP_READ;
 			*ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_READ;
+			*perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN;
 		} else if (!strcmp("encrypt-authenticated-write", flag)) {
 			*props |= BT_GATT_CHRC_PROP_WRITE;
 			*ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_WRITE;
+			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN;
+		} else if (!strcmp("secure-read", flag)) {
+			*props |= BT_GATT_CHRC_PROP_READ;
+			*ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_READ;
+			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_READ_SECURE;
+		} else if (!strcmp("secure-write", flag)) {
+			*props |= BT_GATT_CHRC_PROP_WRITE;
+			*ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_WRITE;
+			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_SECURE;
 		} else {
 			error("Invalid characteristic flag: %s", flag);
 			return false;
@@ -1200,6 +1209,10 @@
 			*perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN;
 		else if (!strcmp("encrypt-authenticated-write", flag))
 			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN;
+		else if (!strcmp("secure-read", flag))
+			*perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN;
+		else if (!strcmp("secure-write", flag))
+			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN;
 		else {
 			error("Invalid descriptor flag: %s", flag);
 			return false;
@@ -1213,6 +1226,7 @@
 								uint32_t *perm)
 {
 	DBusMessageIter iter, array;
+	const char *iface;
 
 	if (!g_dbus_proxy_get_property(proxy, "Flags", &iter))
 		return false;
@@ -1222,10 +1236,11 @@
 
 	dbus_message_iter_recurse(&iter, &array);
 
-	if (perm)
+	iface = g_dbus_proxy_get_interface(proxy);
+	if (!strcmp(iface, GATT_DESC_IFACE))
 		return parse_desc_flags(&array, perm);
 
-	return parse_chrc_flags(&array, props, ext_props);
+	return parse_chrc_flags(&array, props, ext_props, perm);
 }
 
 static struct external_chrc *chrc_create(struct gatt_app *app,
@@ -1273,7 +1288,7 @@
 	 * are used to determine if any special descriptors should be
 	 * created.
 	 */
-	if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, NULL)) {
+	if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, &chrc->perm)) {
 		error("Failed to parse characteristic properties");
 		goto fail;
 	}
@@ -1592,7 +1607,8 @@
 	free(op);
 }
 
-static struct pending_op *pending_read_new(struct queue *owner_queue,
+static struct pending_op *pending_read_new(struct btd_device *device,
+					struct queue *owner_queue,
 					struct gatt_db_attribute *attrib,
 					unsigned int id)
 {
@@ -1601,6 +1617,7 @@
 	op = new0(struct pending_op, 1);
 
 	op->owner_queue = owner_queue;
+	op->device = device;
 	op->attrib = attrib;
 	op->id = id;
 	queue_push_tail(owner_queue, op);
@@ -1608,33 +1625,75 @@
 	return op;
 }
 
-static void send_read(struct gatt_db_attribute *attrib, GDBusProxy *proxy,
-						struct queue *owner_queue,
-						unsigned int id)
+static void append_options(DBusMessageIter *iter, void *user_data)
+{
+	struct pending_op *op = user_data;
+	const char *path = device_get_path(op->device);
+
+	dict_append_entry(iter, "device", DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static void read_setup_cb(DBusMessageIter *iter, void *user_data)
+{
+	struct pending_op *op = user_data;
+	DBusMessageIter dict;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	append_options(&dict, op);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static struct pending_op *send_read(struct btd_device *device,
+					struct gatt_db_attribute *attrib,
+					GDBusProxy *proxy,
+					struct queue *owner_queue,
+					unsigned int id)
 {
 	struct pending_op *op;
-	uint8_t ecode = BT_ATT_ERROR_UNLIKELY;
 
-	op = pending_read_new(owner_queue, attrib, id);
+	op = pending_read_new(device, owner_queue, attrib, id);
 
-	if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply_cb,
-						op, pending_op_free) == TRUE)
-		return;
+	if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup_cb,
+				read_reply_cb, op, pending_op_free) == TRUE)
+		return op;
 
 	pending_op_free(op);
 
-	gatt_db_attribute_read_result(attrib, id, ecode, NULL, 0);
+	return NULL;
 }
 
 static void write_setup_cb(DBusMessageIter *iter, void *user_data)
 {
 	struct pending_op *op = user_data;
-	DBusMessageIter array;
+	DBusMessageIter array, dict;
 
 	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
 	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
 					&op->data.iov_base, op->data.iov_len);
 	dbus_message_iter_close_container(iter, &array);
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	append_options(&dict, op);
+
+	dbus_message_iter_close_container(iter, &dict);
+
+	if (!op->owner_queue) {
+		gatt_db_attribute_write_result(op->attrib, op->id, 0);
+		pending_op_free(op);
+	}
 }
 
 static void write_reply_cb(DBusMessage *message, void *user_data)
@@ -1673,7 +1732,8 @@
 	gatt_db_attribute_write_result(op->attrib, op->id, ecode);
 }
 
-static struct pending_op *pending_write_new(struct queue *owner_queue,
+static struct pending_op *pending_write_new(struct btd_device *device,
+					struct queue *owner_queue,
 					struct gatt_db_attribute *attrib,
 					unsigned int id,
 					const uint8_t *value,
@@ -1686,6 +1746,7 @@
 	op->data.iov_base = (uint8_t *) value;
 	op->data.iov_len = len;
 
+	op->device = device;
 	op->owner_queue = owner_queue;
 	op->attrib = attrib;
 	op->id = id;
@@ -1694,55 +1755,25 @@
 	return op;
 }
 
-static void send_write(struct gatt_db_attribute *attrib, GDBusProxy *proxy,
+static struct pending_op *send_write(struct btd_device *device,
+					struct gatt_db_attribute *attrib,
+					GDBusProxy *proxy,
 					struct queue *owner_queue,
 					unsigned int id,
 					const uint8_t *value, size_t len)
 {
 	struct pending_op *op;
-	uint8_t ecode = BT_ATT_ERROR_UNLIKELY;
 
-	op = pending_write_new(owner_queue, attrib, id, value, len);
+	op = pending_write_new(device, owner_queue, attrib, id, value, len);
 
 	if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup_cb,
-						write_reply_cb, op,
-						pending_op_free) == TRUE)
-		return;
+					owner_queue ? write_reply_cb : NULL,
+					op, pending_op_free) == TRUE)
+		return op;
 
 	pending_op_free(op);
 
-	gatt_db_attribute_write_result(attrib, id, ecode);
-}
-
-static uint32_t permissions_from_props(uint8_t props, uint8_t ext_props)
-{
-	uint32_t perm = 0;
-
-	if (props & BT_GATT_CHRC_PROP_WRITE ||
-			props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP ||
-			ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE ||
-			ext_props & BT_GATT_CHRC_EXT_PROP_ENC_WRITE ||
-			ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_WRITE)
-		perm |= BT_ATT_PERM_WRITE;
-
-	if (props & BT_GATT_CHRC_PROP_READ ||
-			ext_props & BT_GATT_CHRC_EXT_PROP_ENC_READ ||
-			ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_READ)
-		perm |= BT_ATT_PERM_READ;
-
-	if (ext_props & BT_GATT_CHRC_EXT_PROP_ENC_READ)
-		perm |= BT_ATT_PERM_READ_ENCRYPT;
-
-	if (ext_props & BT_GATT_CHRC_EXT_PROP_ENC_WRITE)
-		perm |= BT_ATT_PERM_WRITE_ENCRYPT;
-
-	if (ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_READ)
-		perm |= BT_ATT_PERM_READ_AUTHEN;
-
-	if (ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_WRITE)
-		perm |= BT_ATT_PERM_WRITE_AUTHEN;
-
-	return perm;
+	return NULL;
 }
 
 static uint8_t ccc_write_cb(uint16_t value, void *user_data)
@@ -1895,19 +1926,65 @@
 	return true;
 }
 
+static struct btd_device *att_get_device(struct bt_att *att)
+{
+	GIOChannel *io = NULL;
+	GError *gerr = NULL;
+	bdaddr_t src, dst;
+	uint8_t dst_type;
+	struct btd_adapter *adapter;
+
+	io = g_io_channel_unix_new(bt_att_get_fd(att));
+	if (!io)
+		return NULL;
+
+	bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src,
+					BT_IO_OPT_DEST_BDADDR, &dst,
+					BT_IO_OPT_DEST_TYPE, &dst_type,
+					BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("bt_io_get: %s", gerr->message);
+		g_error_free(gerr);
+		g_io_channel_unref(io);
+		return NULL;
+	}
+
+	g_io_channel_unref(io);
+
+	adapter = adapter_find(&src);
+	if (!adapter) {
+		error("Unable to find adapter object");
+		return NULL;
+	}
+
+	return btd_adapter_find_device(adapter, &dst, dst_type);
+}
+
 static void desc_read_cb(struct gatt_db_attribute *attrib,
 					unsigned int id, uint16_t offset,
 					uint8_t opcode, struct bt_att *att,
 					void *user_data)
 {
 	struct external_desc *desc = user_data;
+	struct btd_device *device;
 
 	if (desc->attrib != attrib) {
 		error("Read callback called with incorrect attribute");
-		return;
+		goto fail;
 	}
 
-	send_read(attrib, desc->proxy, desc->pending_reads, id);
+	device = att_get_device(att);
+	if (!device) {
+		error("Unable to find device object");
+		goto fail;
+	}
+
+	if (send_read(device, attrib, desc->proxy, desc->pending_reads, id))
+		return;
+
+fail:
+	gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
+								NULL, 0);
 }
 
 static void desc_write_cb(struct gatt_db_attribute *attrib,
@@ -1917,13 +1994,26 @@
 					void *user_data)
 {
 	struct external_desc *desc = user_data;
+	struct btd_device *device;
 
 	if (desc->attrib != attrib) {
 		error("Read callback called with incorrect attribute");
-		return;
+		goto fail;
 	}
 
-	send_write(attrib, desc->proxy, desc->pending_writes, id, value, len);
+	device = att_get_device(att);
+	if (!device) {
+		error("Unable to find device object");
+		goto fail;
+	}
+
+	if (send_write(device, attrib, desc->proxy, desc->pending_writes, id,
+							value, len))
+		return;
+
+fail:
+	gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
+								NULL, 0);
 }
 
 static bool database_add_desc(struct external_service *service,
@@ -1956,43 +2046,25 @@
 					void *user_data)
 {
 	struct external_chrc *chrc = user_data;
+	struct btd_device *device;
 
 	if (chrc->attrib != attrib) {
 		error("Read callback called with incorrect attribute");
-		return;
+		goto fail;
 	}
 
-	send_read(attrib, chrc->proxy, chrc->pending_reads, id);
-}
+	device = att_get_device(att);
+	if (!device) {
+		error("Unable to find device object");
+		goto fail;
+	}
 
-static void write_without_response_setup_cb(DBusMessageIter *iter,
-							void *user_data)
-{
-	struct iovec *iov = user_data;
-	DBusMessageIter array;
+	if (send_read(device, attrib, chrc->proxy, chrc->pending_reads, id))
+		return;
 
-	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
-	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
-						&iov->iov_base, iov->iov_len);
-	dbus_message_iter_close_container(iter, &array);
-}
-
-static void send_write_without_response(struct gatt_db_attribute *attrib,
-					GDBusProxy *proxy, unsigned int id,
-					const uint8_t *value, size_t len)
-{
-	struct iovec iov;
-	uint8_t ecode = 0;
-
-	iov.iov_base = (uint8_t *) value;
-	iov.iov_len = len;
-
-	if (!g_dbus_proxy_method_call(proxy, "WriteValue",
-					write_without_response_setup_cb,
-					NULL, &iov, NULL))
-		ecode = BT_ATT_ERROR_UNLIKELY;
-
-	gatt_db_attribute_write_result(attrib, id, ecode);
+fail:
+	gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
+								NULL, 0);
 }
 
 static void chrc_write_cb(struct gatt_db_attribute *attrib,
@@ -2002,26 +2074,37 @@
 					void *user_data)
 {
 	struct external_chrc *chrc = user_data;
+	struct btd_device *device;
+	struct queue *queue;
 
 	if (chrc->attrib != attrib) {
 		error("Write callback called with incorrect attribute");
-		return;
+		goto fail;
 	}
 
-	if (chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP) {
-		send_write_without_response(attrib, chrc->proxy, id, value,
-									len);
-		return;
+	device = att_get_device(att);
+	if (!device) {
+		error("Unable to find device object");
+		goto fail;
 	}
 
-	send_write(attrib, chrc->proxy, chrc->pending_writes, id, value, len);
+	if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP))
+		queue = chrc->pending_writes;
+	else
+		queue = NULL;
+
+	if (send_write(device, attrib, chrc->proxy, queue, id, value, len))
+		return;
+
+fail:
+	gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
+								NULL, 0);
 }
 
 static bool database_add_chrc(struct external_service *service,
 						struct external_chrc *chrc)
 {
 	bt_uuid_t uuid;
-	uint32_t perm;
 	const struct queue_entry *entry;
 
 	if (!parse_uuid(chrc->proxy, &uuid)) {
@@ -2034,14 +2117,8 @@
 		return false;
 	}
 
-	/*
-	 * TODO: Once shared/gatt-server properly supports permission checks,
-	 * set the permissions based on a D-Bus property of the external
-	 * characteristic.
-	 */
-	perm = permissions_from_props(chrc->props, chrc->ext_props);
 	chrc->attrib = gatt_db_service_add_characteristic(service->attrib,
-						&uuid, perm,
+						&uuid, chrc->perm,
 						chrc->props, chrc_read_cb,
 						chrc_write_cb, chrc);
 	if (!chrc->attrib) {
@@ -2134,9 +2211,6 @@
 {
 	const struct queue_entry *entry;
 
-	if (queue_isempty(app->services))
-		return false;
-
 	entry = queue_get_entries(app->services);
 	while (entry) {
 		if (!database_add_service(entry->data)) {
@@ -2150,6 +2224,131 @@
 	return true;
 }
 
+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;
+
+	p = new0(struct btd_profile, 1);
+
+	/* Assign directly to avoid having extra fields */
+	p->name = (const void *) g_strdup_printf("%s%s/%s", profile->app->owner,
+				g_dbus_proxy_get_path(profile->proxy), uuid);
+	if (!p->name) {
+		free(p);
+		return -ENOMEM;
+	}
+
+	p->remote_uuid = (const void *) g_strdup(uuid);
+	if (!p->remote_uuid) {
+		g_free((void *) p->name);
+		free(p);
+		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);
+
+	DBG("Added \"%s\"", p->name);
+
+	return 0;
+}
+
+static void add_profile(void *data, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	btd_profile_register(data);
+	adapter_add_profile(adapter, data);
+}
+
+static struct external_profile *create_profile(struct gatt_app *app,
+						GDBusProxy *proxy,
+						const char *path)
+{
+	struct external_profile *profile;
+	DBusMessageIter iter, array;
+
+	if (!path || !g_str_has_prefix(path, "/"))
+		return NULL;
+
+	profile = new0(struct external_profile, 1);
+
+	profile->app = app;
+	profile->proxy = g_dbus_proxy_ref(proxy);
+	profile->profiles = queue_new();
+
+	if (!g_dbus_proxy_get_property(proxy, "UUIDs", &iter)) {
+		DBG("UUIDs property not found");
+		goto fail;
+	}
+
+	dbus_message_iter_recurse(&iter, &array);
+
+	while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
+		const char *uuid;
+
+		dbus_message_iter_get_basic(&array, &uuid);
+
+		if (profile_add(profile, uuid) < 0)
+			goto fail;
+
+		dbus_message_iter_next(&array);
+	}
+
+	if (queue_isempty(profile->profiles))
+		goto fail;
+
+	queue_foreach(profile->profiles, add_profile, app->database->adapter);
+	queue_push_tail(app->profiles, profile);
+
+	return profile;
+
+fail:
+	profile_free(profile);
+	return NULL;
+}
+
+static void register_profile(void *data, void *user_data)
+{
+	struct gatt_app *app = user_data;
+	GDBusProxy *proxy = data;
+	const char *iface = g_dbus_proxy_get_interface(proxy);
+	const char *path = g_dbus_proxy_get_path(proxy);
+
+	if (app->failed)
+		return;
+
+	if (g_strcmp0(iface, GATT_PROFILE_IFACE) == 0) {
+		struct external_profile *profile;
+
+		profile = create_profile(app, proxy, path);
+		if (!profile) {
+			app->failed = true;
+			return;
+		}
+	}
+}
+
 static void register_service(void *data, void *user_data)
 {
 	struct gatt_app *app = user_data;
@@ -2232,11 +2431,13 @@
 		goto reply;
 	}
 
+	queue_foreach(app->proxies, register_profile, app);
 	queue_foreach(app->proxies, register_service, app);
 	queue_foreach(app->proxies, register_characteristic, app);
 	queue_foreach(app->proxies, register_descriptor, app);
 
-	if (!app->services || app->failed) {
+	if ((queue_isempty(app->services) && queue_isempty(app->profiles)) ||
+							app->failed) {
 		error("No valid external GATT objects found");
 		fail = true;
 		reply = btd_error_failed(app->reg,
@@ -2289,6 +2490,7 @@
 		goto fail;
 
 	app->services = queue_new();
+	app->profiles = queue_new();
 	app->proxies = queue_new();
 	app->reg = dbus_message_ref(msg);
 
@@ -2375,203 +2577,6 @@
 	return dbus_message_new_method_return(msg);
 }
 
-static void profile_exited(DBusConnection *conn, void *user_data)
-{
-	struct external_profile *profile = user_data;
-
-	DBG("\"%s\" exited", profile->owner);
-
-	profile->id = 0;
-
-	queue_remove(profile->database->profiles, profile);
-
-	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;
-
-	p = new0(struct btd_profile, 1);
-
-	/* Assign directly to avoid having extra fields */
-	p->name = (const void *) g_strdup_printf("%s%s/%s", profile->owner,
-							profile->path, uuid);
-	if (!p->name) {
-		free(p);
-		return -ENOMEM;
-	}
-
-	p->remote_uuid = (const void *) g_strdup(uuid);
-	if (!p->remote_uuid) {
-		g_free((void *) p->name);
-		free(p);
-		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);
-
-	DBG("Added \"%s\"", p->name);
-
-	return 0;
-}
-
-static void add_profile(void *data, void *user_data)
-{
-	struct btd_adapter *adapter = user_data;
-
-	btd_profile_register(data);
-	adapter_add_profile(adapter, data);
-}
-
-static int profile_create(DBusConnection *conn,
-				struct btd_gatt_database *database,
-				const char *sender, const char *path,
-				DBusMessageIter *iter)
-{
-	struct external_profile *profile;
-	DBusMessageIter uuids;
-
-	if (!path || !g_str_has_prefix(path, "/"))
-		return -EINVAL;
-
-	profile = new0(struct external_profile, 1);
-
-	profile->owner = g_strdup(sender);
-	if (!profile->owner)
-		goto fail;
-
-	profile->path = g_strdup(path);
-	if (!profile->path)
-		goto fail;
-
-	profile->profiles = queue_new();
-	profile->database = database;
-	profile->id = g_dbus_add_disconnect_watch(conn, sender, profile_exited,
-								profile, NULL);
-
-	dbus_message_iter_recurse(iter, &uuids);
-
-	while (dbus_message_iter_get_arg_type(&uuids) == DBUS_TYPE_STRING) {
-		const char *uuid;
-
-		dbus_message_iter_get_basic(&uuids, &uuid);
-
-		if (profile_add(profile, uuid) < 0)
-			goto fail;
-
-		dbus_message_iter_next(&uuids);
-	}
-
-	if (queue_isempty(profile->profiles))
-		goto fail;
-
-	queue_foreach(profile->profiles, add_profile, database->adapter);
-	queue_push_tail(database->profiles, profile);
-
-	return 0;
-
-fail:
-	profile_free(profile);
-	return -EINVAL;
-}
-
-static bool match_profile(const void *a, const void *b)
-{
-	const struct external_profile *profile = a;
-	const struct svc_match_data *data = b;
-
-	return g_strcmp0(profile->path, data->path) == 0 &&
-				g_strcmp0(profile->owner, data->sender) == 0;
-}
-
-static DBusMessage *manager_register_profile(DBusConnection *conn,
-					DBusMessage *msg, void *user_data)
-{
-	struct btd_gatt_database *database = user_data;
-	const char *sender = dbus_message_get_sender(msg);
-	DBusMessageIter args;
-	const char *path;
-	struct svc_match_data match_data;
-
-	DBG("sender %s", sender);
-
-	if (!dbus_message_iter_init(msg, &args))
-		return btd_error_invalid_args(msg);
-
-	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
-		return btd_error_invalid_args(msg);
-
-	dbus_message_iter_get_basic(&args, &path);
-
-	match_data.path = path;
-	match_data.sender = sender;
-
-	if (queue_find(database->profiles, match_profile, &match_data))
-		return btd_error_already_exists(msg);
-
-	dbus_message_iter_next(&args);
-	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
-		return btd_error_invalid_args(msg);
-
-	if (profile_create(conn, database, sender, path, &args) < 0)
-		return btd_error_failed(msg, "Failed to register profile");
-
-	return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *manager_unregister_profile(DBusConnection *conn,
-					DBusMessage *msg, void *user_data)
-{
-	struct btd_gatt_database *database = user_data;
-	const char *sender = dbus_message_get_sender(msg);
-	const char *path;
-	DBusMessageIter args;
-	struct external_profile *profile;
-	struct svc_match_data match_data;
-
-	if (!dbus_message_iter_init(msg, &args))
-		return btd_error_invalid_args(msg);
-
-	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
-		return btd_error_invalid_args(msg);
-
-	dbus_message_iter_get_basic(&args, &path);
-
-	match_data.path = path;
-	match_data.sender = sender;
-
-	profile = queue_remove_if(database->profiles, match_profile,
-								&match_data);
-	if (!profile)
-		return btd_error_does_not_exist(msg);
-
-	profile_free(profile);
-
-	return dbus_message_new_method_return(msg);
-}
-
 static const GDBusMethodTable manager_methods[] = {
 	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterApplication",
 			GDBUS_ARGS({ "application", "o" },
@@ -2580,13 +2585,6 @@
 	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterApplication",
 					GDBUS_ARGS({ "application", "o" }),
 					NULL, manager_unregister_app) },
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterProfile",
-			GDBUS_ARGS({ "profile", "o" }, { "UUIDs", "as" },
-			{ "options", "a{sv}" }), NULL,
-			manager_register_profile) },
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterProfile",
-					GDBUS_ARGS({ "profile", "o" }),
-					NULL, manager_unregister_profile) },
 	{ }
 };
 
diff --git a/src/profile.c b/src/profile.c
index 5a4f09c..c81a9f9 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -1047,10 +1047,11 @@
 									conn);
 	}
 
-	if (conn->service && service_accept(conn->service) == 0) {
-		if (send_new_connection(ext, conn))
-			return;
-	}
+	if (conn->service && service_accept(conn->service) < 0)
+		goto drop;
+
+	if (send_new_connection(ext, conn))
+		return;
 
 drop:
 	if (conn->service)
diff --git a/src/shared/att-types.h b/src/shared/att-types.h
index c3062c0..4a9b67f 100644
--- a/src/shared/att-types.h
+++ b/src/shared/att-types.h
@@ -31,6 +31,7 @@
 #define BT_ATT_SECURITY_LOW	1
 #define BT_ATT_SECURITY_MEDIUM	2
 #define BT_ATT_SECURITY_HIGH	3
+#define BT_ATT_SECURITY_FIPS	4
 
 #define BT_ATT_DEFAULT_LE_MTU	23
 #define BT_ATT_MAX_LE_MTU	517
@@ -123,6 +124,10 @@
 					BT_ATT_PERM_WRITE_AUTHEN)
 #define BT_ATT_PERM_AUTHOR		0x40
 #define BT_ATT_PERM_NONE		0x80
+#define BT_ATT_PERM_READ_SECURE		0x0100
+#define BT_ATT_PERM_WRITE_SECURE	0x0200
+#define BT_ATT_PERM_SECURE		(BT_ATT_PERM_READ_SECURE | \
+					BT_ATT_PERM_WRITE_SECURE)
 
 /* GATT Characteristic Properties Bitfield values */
 #define BT_GATT_CHRC_PROP_BROADCAST			0x01
diff --git a/src/shared/btsnoop.c b/src/shared/btsnoop.c
index cec1b21..e20d1b3 100644
--- a/src/shared/btsnoop.c
+++ b/src/shared/btsnoop.c
@@ -198,7 +198,8 @@
 }
 
 bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
-			uint32_t flags, const void *data, uint16_t size)
+			uint32_t flags, uint32_t drops, const void *data,
+			uint16_t size)
 {
 	struct btsnoop_pkt pkt;
 	uint64_t ts;
@@ -212,7 +213,7 @@
 	pkt.size  = htobe32(size);
 	pkt.len   = htobe32(size);
 	pkt.flags = htobe32(flags);
-	pkt.drops = htobe32(0);
+	pkt.drops = htobe32(drops);
 	pkt.ts    = htobe64(ts + 0x00E03AB44A676000ll);
 
 	written = write(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE);
@@ -254,8 +255,8 @@
 }
 
 bool btsnoop_write_hci(struct btsnoop *btsnoop, struct timeval *tv,
-					uint16_t index, uint16_t opcode,
-					const void *data, uint16_t size)
+			uint16_t index, uint16_t opcode, uint32_t drops,
+			const void *data, uint16_t size)
 {
 	uint32_t flags;
 
@@ -283,7 +284,7 @@
 		return false;
 	}
 
-	return btsnoop_write(btsnoop, tv, flags, data, size);
+	return btsnoop_write(btsnoop, tv, flags, drops, data, size);
 }
 
 bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv,
@@ -303,7 +304,7 @@
 		return false;
 	}
 
-	return btsnoop_write(btsnoop, tv, flags, data, size);
+	return btsnoop_write(btsnoop, tv, flags, 0, data, size);
 }
 
 static bool pklg_read_hci(struct btsnoop *btsnoop, struct timeval *tv,
diff --git a/src/shared/btsnoop.h b/src/shared/btsnoop.h
index d94dbae..dab2b2f 100644
--- a/src/shared/btsnoop.h
+++ b/src/shared/btsnoop.h
@@ -62,6 +62,8 @@
 #define BTSNOOP_BUS_RS232	4
 #define BTSNOOP_BUS_PCI		5
 #define BTSNOOP_BUS_SDIO	6
+#define BTSNOOP_BUS_SPI		7
+#define BTSNOOP_BUS_I2C		8
 
 struct btsnoop_opcode_new_index {
 	uint8_t  type;
@@ -99,11 +101,11 @@
 
 uint32_t btsnoop_get_format(struct btsnoop *btsnoop);
 
-bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
-			uint32_t flags, const void *data, uint16_t size);
+bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv, uint32_t flags,
+			uint32_t drops, const void *data, uint16_t size);
 bool btsnoop_write_hci(struct btsnoop *btsnoop, struct timeval *tv,
-					uint16_t index, uint16_t opcode,
-					const void *data, uint16_t size);
+			uint16_t index, uint16_t opcode, uint32_t drops,
+			const void *data, uint16_t size);
 bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv,
 			uint16_t frequency, const void *data, uint16_t size);
 
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 34b6cc7..adabfe3 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -243,9 +243,9 @@
 	if (bt_uuid_cmp(&uuid, gatt_db_attribute_get_type(attr)))
 		return NULL;
 
-	if (!gatt_db_attribute_get_char_data(attr, NULL, NULL,
-							&properties, NULL))
-			return NULL;
+	if (!gatt_db_attribute_get_char_data(attr, NULL, NULL, &properties,
+								NULL, NULL))
+		return NULL;
 
 	chrc = new0(struct notify_chrc, 1);
 
@@ -316,6 +316,7 @@
 	struct queue *pending_svcs;
 	struct queue *pending_chrcs;
 	struct queue *svcs;
+	struct queue *ext_prop_desc;
 	struct gatt_db_attribute *cur_svc;
 	bool success;
 	uint16_t start;
@@ -331,6 +332,7 @@
 	queue_destroy(op->pending_svcs, NULL);
 	queue_destroy(op->pending_chrcs, free);
 	queue_destroy(op->svcs, NULL);
+	queue_destroy(op->ext_prop_desc, NULL);
 	free(op);
 }
 
@@ -360,6 +362,7 @@
 	op->pending_svcs = queue_new();
 	op->pending_chrcs = queue_new();
 	op->svcs = queue_new();
+	op->ext_prop_desc = queue_new();
 	op->client = client;
 	op->complete_func = complete_func;
 	op->failure_func = failure_func;
@@ -604,6 +607,107 @@
 	return false;
 }
 
+static void ext_prop_write_cb(struct gatt_db_attribute *attrib,
+						int err, void *user_data)
+{
+	struct bt_gatt_client *client = user_data;
+
+	util_debug(client->debug_callback, client->debug_data,
+						"Value set status: %d", err);
+}
+
+static void ext_prop_read_cb(bool success, uint8_t att_ecode,
+					const uint8_t *value, uint16_t length,
+					void *user_data);
+
+static bool read_ext_prop_desc(struct discovery_op *op)
+{
+	struct bt_gatt_client *client = op->client;
+	uint16_t handle;
+	struct gatt_db_attribute *attr;
+
+	attr = queue_peek_head(op->ext_prop_desc);
+	if (!attr)
+		return false;
+
+	handle = gatt_db_attribute_get_handle(attr);
+	bt_gatt_client_read_value(client, handle, ext_prop_read_cb,
+							discovery_op_ref(op),
+							discovery_op_unref);
+
+	return true;
+}
+
+static void ext_prop_read_cb(bool success, uint8_t att_ecode,
+					const uint8_t *value, uint16_t length,
+					void *user_data)
+{
+	struct discovery_op *op = user_data;
+	struct bt_gatt_client *client = op->client;
+	bool discovering;
+	struct gatt_db_attribute *desc_attr = NULL;
+	struct gatt_db_attribute *next_srv;
+	uint16_t start, end;
+
+	util_debug(client->debug_callback, client->debug_data,
+				"Ext. prop value: 0x%04x", (uint16_t)value[0]);
+
+	desc_attr = queue_pop_head(op->ext_prop_desc);
+	if (!desc_attr)
+		goto failed;
+
+	if (!gatt_db_attribute_write(desc_attr, 0, value, length, 0, NULL,
+						ext_prop_write_cb, client))
+		goto failed;
+
+	/* Any other descriptor to read? */
+	if (read_ext_prop_desc(op))
+		return;
+
+	/* Continue with discovery */
+	do {
+		if (!discover_descs(op, &discovering))
+			goto failed;
+
+		if (discovering)
+			return;
+
+		/* Done with the current service */
+		gatt_db_service_set_active(op->cur_svc, true);
+
+		next_srv = queue_pop_head(op->svcs);
+		if (!next_srv)
+			goto done;
+
+		if (!gatt_db_attribute_get_service_handles(next_srv, &start,
+									&end))
+			goto failed;
+
+	} while (start == end);
+
+	/* Move on to the next service */
+	op->cur_svc = next_srv;
+
+	client->discovery_req = bt_gatt_discover_characteristics(client->att,
+							start, end,
+							discover_chrcs_cb,
+							discovery_op_ref(op),
+							discovery_op_unref);
+	if (client->discovery_req)
+		return;
+
+	util_debug(client->debug_callback, client->debug_data,
+				"Failed to start characteristic discovery");
+
+	discovery_op_unref(op);
+
+failed:
+	success = false;
+
+done:
+	discovery_op_complete(op, success, att_ecode);
+}
+
 static void discover_descs_cb(bool success, uint8_t att_ecode,
 						struct bt_gatt_result *result,
 						void *user_data)
@@ -618,6 +722,7 @@
 	char uuid_str[MAX_LEN_UUID_STR];
 	unsigned int desc_count;
 	bool discovering;
+	bt_uuid_t ext_prop_uuid;
 
 	discovery_req_clear(client);
 
@@ -640,6 +745,8 @@
 	util_debug(client->debug_callback, client->debug_data,
 					"Descriptors found: %u", desc_count);
 
+	bt_uuid16_create(&ext_prop_uuid, GATT_CHARAC_EXT_PROPER_UUID);
+
 	while (bt_gatt_iter_next_descriptor(&iter, &handle, u128.data)) {
 		bt_uuid128_create(&uuid, u128);
 
@@ -657,8 +764,15 @@
 
 		if (gatt_db_attribute_get_handle(attr) != handle)
 			goto failed;
+
+		if (!bt_uuid_cmp(&ext_prop_uuid, &uuid))
+			queue_push_tail(op->ext_prop_desc, attr);
 	}
 
+	/* If we got extended prop descriptor, lets read it right away */
+	if (read_ext_prop_desc(op))
+		return;
+
 next:
 	if (!discover_descs(op, &discovering))
 		goto failed;
@@ -1631,7 +1745,7 @@
 
 	queue_foreach(client->notify_list, notify_handler, &pdu_data);
 
-	if (opcode == BT_ATT_OP_HANDLE_VAL_IND)
+	if (opcode == BT_ATT_OP_HANDLE_VAL_IND && !client->parent)
 		bt_att_send(client->att, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0,
 							NULL, NULL, NULL);
 
diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index cc49458..513451f 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -52,6 +52,8 @@
 					.value.u16 = GATT_CHARAC_UUID };
 static const bt_uuid_t included_service_uuid = { .type = BT_UUID16,
 					.value.u16 = GATT_INCLUDE_UUID };
+static const bt_uuid_t ext_desc_uuid = { .type = BT_UUID16,
+				.value.u16 = GATT_CHARAC_EXT_PROPER_UUID };
 
 struct gatt_db {
 	int ref_count;
@@ -1456,10 +1458,68 @@
 	return le_to_uuid(decl->value, decl->value_len, uuid);
 }
 
+static void read_ext_prop_value(struct gatt_db_attribute *attrib,
+						int err, const uint8_t *value,
+						size_t length, void *user_data)
+{
+	uint16_t *ext_prop = user_data;
+
+	if (err || (length != sizeof(uint16_t)))
+		return;
+
+	*ext_prop = (uint16_t) value[0];
+}
+
+static void read_ext_prop(struct gatt_db_attribute *attrib,
+							void *user_data)
+{
+	uint16_t *ext_prop = user_data;
+
+	/*
+	 * If ext_prop is set that means extended properties descriptor
+	 * has been already found
+	 */
+	if (*ext_prop != 0)
+		return;
+
+	if (bt_uuid_cmp(&ext_desc_uuid, &attrib->uuid))
+		return;
+
+	gatt_db_attribute_read(attrib, 0, BT_ATT_OP_READ_REQ, NULL,
+						read_ext_prop_value, ext_prop);
+}
+
+static uint8_t get_char_extended_prop(const struct gatt_db_attribute *attrib)
+{
+	uint16_t ext_prop;
+
+	if (!attrib)
+		return 0;
+
+	if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid))
+		return 0;
+
+	/* Check properties first */
+	if (!(attrib->value[0] & BT_GATT_CHRC_PROP_EXT_PROP))
+		return 0;
+
+	ext_prop = 0;
+
+	/*
+	 * Cast needed for foreach function. We do not change attrib during
+	 * this call
+	 */
+	gatt_db_service_foreach_desc((struct gatt_db_attribute *) attrib,
+						read_ext_prop, &ext_prop);
+
+	return ext_prop;
+}
+
 bool gatt_db_attribute_get_char_data(const struct gatt_db_attribute *attrib,
 							uint16_t *handle,
 							uint16_t *value_handle,
 							uint8_t *properties,
+							uint16_t *ext_prop,
 							bt_uuid_t *uuid)
 {
 	if (!attrib)
@@ -1484,6 +1544,9 @@
 	if (properties)
 		*properties = attrib->value[0];
 
+	if (ext_prop)
+		*ext_prop = get_char_extended_prop(attrib);
+
 	if (value_handle)
 		*value_handle = get_le16(attrib->value + 1);
 
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 96cceb9..134ec63 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -199,6 +199,7 @@
 							uint16_t *handle,
 							uint16_t *value_handle,
 							uint8_t *properties,
+							uint16_t *ext_prop,
 							bt_uuid_t *uuid);
 
 bool gatt_db_attribute_get_incl_data(const struct gatt_db_attribute *attrib,
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index f1fca92..79e01c8 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -72,6 +72,8 @@
 	uint16_t handle;
 	uint16_t offset;
 	uint16_t length;
+
+	bool reliable_supported;
 };
 
 static void prep_write_data_destroy(void *user_data)
@@ -396,6 +398,9 @@
 		return 0;
 
 	security = bt_att_get_security(server->att);
+	if (perm & BT_ATT_PERM_SECURE && security < BT_ATT_SECURITY_FIPS)
+		return BT_ATT_ERROR_AUTHENTICATION;
+
 	if (perm & BT_ATT_PERM_AUTHEN && security < BT_ATT_SECURITY_HIGH)
 		return BT_ATT_ERROR_AUTHENTICATION;
 
@@ -1111,6 +1116,23 @@
 	return true;
 }
 
+static bool is_reliable_write_supported(const struct bt_gatt_server  *server,
+							uint16_t handle)
+{
+	struct gatt_db_attribute *attr;
+	uint16_t ext_prop;
+
+	attr = gatt_db_get_attribute(server->db, handle);
+	if (!attr)
+		return false;
+
+	if (!gatt_db_attribute_get_char_data(attr, NULL, NULL, NULL, &ext_prop,
+									NULL))
+		return false;
+
+	return (ext_prop & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE);
+}
+
 static bool prep_data_new(struct bt_gatt_server *server,
 					uint16_t handle, uint16_t offset,
 					uint16_t length, uint8_t *value)
@@ -1128,6 +1150,13 @@
 	prep_data->handle = handle;
 	prep_data->offset = offset;
 
+	/*
+	 * Handle is the value handle. We need characteristic declaration
+	 * handle which in BlueZ is handle_value -1
+	 */
+	prep_data->reliable_supported = is_reliable_write_supported(server,
+								handle - 1);
+
 	queue_push_tail(server->prep_queue, prep_data);
 
 	return true;
@@ -1258,6 +1287,14 @@
 								ehandle, err);
 }
 
+static bool find_no_reliable_characteristic(const void *data,
+						const void *match_data)
+{
+	const struct prep_write_data *prep_data = data;
+
+	return !prep_data->reliable_supported;
+}
+
 static void exec_write_cb(uint8_t opcode, const void *pdu,
 					uint16_t length, void *user_data)
 {
@@ -1265,6 +1302,7 @@
 	uint8_t flags;
 	uint8_t ecode;
 	bool write;
+	uint16_t ehandle = 0;
 
 	if (length != 1) {
 		ecode = BT_ATT_ERROR_INVALID_PDU;
@@ -1293,6 +1331,19 @@
 		return;
 	}
 
+	/* If there is more than one prep request, we are in reliable session */
+	if (queue_length(server->prep_queue) > 1) {
+		struct prep_write_data *prep_data;
+
+		prep_data = queue_find(server->prep_queue,
+					find_no_reliable_characteristic, NULL);
+		if (prep_data) {
+			ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+			ehandle = prep_data->handle;
+			goto error;
+		}
+	}
+
 	exec_next_prep_write(server, 0, 0);
 
 	return;
@@ -1300,7 +1351,7 @@
 error:
 	queue_remove_all(server->prep_queue, NULL, NULL,
 						prep_write_data_destroy);
-	bt_att_send_error_rsp(server->att, opcode, 0, ecode);
+	bt_att_send_error_rsp(server->att, opcode, ehandle, ecode);
 }
 
 static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
diff --git a/src/shared/tty.h b/src/shared/tty.h
new file mode 100644
index 0000000..66ec09f
--- /dev/null
+++ b/src/shared/tty.h
@@ -0,0 +1,80 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2016  Intel Corporation. All rights reserved.
+ *
+ *
+ *  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 <termios.h>
+
+static inline unsigned int tty_get_speed(int speed)
+{
+	switch (speed) {
+	case 9600:
+		return B9600;
+	case 19200:
+		return B19200;
+	case 38400:
+		return B38400;
+	case 57600:
+		return B57600;
+	case 115200:
+		return B115200;
+	case 230400:
+		return B230400;
+	case 460800:
+		return B460800;
+	case 500000:
+		return B500000;
+	case 576000:
+		return B576000;
+	case 921600:
+		return B921600;
+	case 1000000:
+		return B1000000;
+	case 1152000:
+		return B1152000;
+	case 1500000:
+		return B1500000;
+	case 2000000:
+		return B2000000;
+#ifdef B2500000
+	case 2500000:
+		return B2500000;
+#endif
+#ifdef B3000000
+	case 3000000:
+		return B3000000;
+#endif
+#ifdef B3500000
+	case 3500000:
+		return B3500000;
+#endif
+#ifdef B3710000
+	case 3710000:
+		return B3710000;
+#endif
+#ifdef B4000000
+	case 4000000:
+		return B4000000;
+#endif
+	}
+
+	return 0;
+}
diff --git a/test/example-gatt-client b/test/example-gatt-client
index b77a627..4d1df2f 100755
--- a/test/example-gatt-client
+++ b/test/example-gatt-client
@@ -113,7 +113,7 @@
 
 def start_client():
     # Read the Body Sensor Location value and print it asynchronously.
-    body_snsr_loc_chrc[0].ReadValue(reply_handler=body_sensor_val_cb,
+    body_snsr_loc_chrc[0].ReadValue({}, reply_handler=body_sensor_val_cb,
                                     error_handler=generic_error_cb,
                                     dbus_interface=GATT_CHRC_IFACE)
 
diff --git a/test/example-gatt-server b/test/example-gatt-server
index 93cf068..71aeb1b 100755
--- a/test/example-gatt-server
+++ b/test/example-gatt-server
@@ -166,13 +166,15 @@
 
         return self.get_properties[GATT_CHRC_IFACE]
 
-    @dbus.service.method(GATT_CHRC_IFACE, out_signature='ay')
-    def ReadValue(self):
+    @dbus.service.method(GATT_CHRC_IFACE,
+                        in_signature='a{sv}',
+                        out_signature='ay')
+    def ReadValue(self, options):
         print('Default ReadValue called, returning error')
         raise NotSupportedException()
 
-    @dbus.service.method(GATT_CHRC_IFACE, in_signature='ay')
-    def WriteValue(self, value):
+    @dbus.service.method(GATT_CHRC_IFACE, in_signature='aya{sv}')
+    def WriteValue(self, value, options):
         print('Default WriteValue called, returning error')
         raise NotSupportedException()
 
@@ -222,13 +224,15 @@
 
         return self.get_properties[GATT_CHRC_IFACE]
 
-    @dbus.service.method(GATT_DESC_IFACE, out_signature='ay')
-    def ReadValue(self):
+    @dbus.service.method(GATT_DESC_IFACE,
+                        in_signature='a{sv}',
+                        out_signature='ay')
+    def ReadValue(self, options):
         print ('Default ReadValue called, returning error')
         raise NotSupportedException()
 
-    @dbus.service.method(GATT_DESC_IFACE, in_signature='ay')
-    def WriteValue(self, value):
+    @dbus.service.method(GATT_DESC_IFACE, in_signature='aya{sv}')
+    def WriteValue(self, value, options):
         print('Default WriteValue called, returning error')
         raise NotSupportedException()
 
@@ -317,7 +321,7 @@
                 ['read'],
                 service)
 
-    def ReadValue(self):
+    def ReadValue(self, options):
         # Return 'Chest' as the sensor location.
         return [ 0x01 ]
 
@@ -331,7 +335,7 @@
                 ['write'],
                 service)
 
-    def WriteValue(self, value):
+    def WriteValue(self, value, options):
         print('Heart Rate Control Point WriteValue called')
 
         if len(value) != 1:
@@ -393,7 +397,7 @@
         self.notify_battery_level()
         return True
 
-    def ReadValue(self):
+    def ReadValue(self, options):
         print('Battery Level read: ' + repr(self.battery_lvl))
         return [dbus.Byte(self.battery_lvl)]
 
@@ -425,6 +429,7 @@
         Service.__init__(self, bus, index, self.TEST_SVC_UUID, False)
         self.add_characteristic(TestCharacteristic(bus, 0, self))
         self.add_characteristic(TestEncryptCharacteristic(bus, 1, self))
+        self.add_characteristic(TestSecureCharacteristic(bus, 2, self))
 
 class TestCharacteristic(Characteristic):
     """
@@ -445,11 +450,11 @@
         self.add_descriptor(
                 CharacteristicUserDescriptionDescriptor(bus, 1, self))
 
-    def ReadValue(self):
+    def ReadValue(self, options):
         print('TestCharacteristic Read: ' + repr(self.value))
         return self.value
 
-    def WriteValue(self, value):
+    def WriteValue(self, value, options):
         print('TestCharacteristic Write: ' + repr(value))
         self.value = value
 
@@ -468,7 +473,7 @@
                 ['read', 'write'],
                 characteristic)
 
-    def ReadValue(self):
+    def ReadValue(self, options):
         return [
                 dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
         ]
@@ -491,10 +496,10 @@
                 ['read', 'write'],
                 characteristic)
 
-    def ReadValue(self):
+    def ReadValue(self, options):
         return self.value
 
-    def WriteValue(self, value):
+    def WriteValue(self, value, options):
         if not self.writable:
             raise NotPermittedException()
         self.value = value
@@ -517,11 +522,11 @@
         self.add_descriptor(
                 CharacteristicUserDescriptionDescriptor(bus, 3, self))
 
-    def ReadValue(self):
+    def ReadValue(self, options):
         print('TestCharacteristic Read: ' + repr(self.value))
         return self.value
 
-    def WriteValue(self, value):
+    def WriteValue(self, value, options):
         print('TestCharacteristic Write: ' + repr(value))
         self.value = value
 
@@ -539,7 +544,54 @@
                 ['encrypt-read', 'encrypt-write'],
                 characteristic)
 
-    def ReadValue(self):
+    def ReadValue(self, options):
+        return [
+                dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
+        ]
+
+
+class TestSecureCharacteristic(Characteristic):
+    """
+    Dummy test characteristic requiring secure connection.
+
+    """
+    TEST_CHRC_UUID = '12345678-1234-5678-1234-56789abcdef5'
+
+    def __init__(self, bus, index, service):
+        Characteristic.__init__(
+                self, bus, index,
+                self.TEST_CHRC_UUID,
+                ['secure-read', 'secure-write'],
+                service)
+        self.value = []
+        self.add_descriptor(TestEncryptDescriptor(bus, 2, self))
+        self.add_descriptor(
+                CharacteristicUserDescriptionDescriptor(bus, 3, self))
+
+    def ReadValue(self, options):
+        print('TestCharacteristic Read: ' + repr(self.value))
+        return self.value
+
+    def WriteValue(self, value, options):
+        print('TestCharacteristic Write: ' + repr(value))
+        self.value = value
+
+
+class TestSecureDescriptor(Descriptor):
+    """
+    Dummy test descriptor requiring secure connection. Returns a static value.
+
+    """
+    TEST_DESC_UUID = '12345678-1234-5678-1234-56789abcdef6'
+
+    def __init__(self, bus, index, characteristic):
+        Descriptor.__init__(
+                self, bus, index,
+                self.TEST_DESC_UUID,
+                ['secure-read', 'secure-write'],
+                characteristic)
+
+    def ReadValue(self, options):
         return [
                 dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
         ]
diff --git a/test/test-gatt-profile b/test/test-gatt-profile
index ad320b1..995a659 100755
--- a/test/test-gatt-profile
+++ b/test/test-gatt-profile
@@ -15,46 +15,116 @@
   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()
+BLUEZ_SERVICE_NAME = 'org.bluez'
+GATT_MANAGER_IFACE = 'org.bluez.GattManager1'
+DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
+DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
+
+GATT_PROFILE_IFACE = 'org.bluez.GattProfile1'
+
+
+class InvalidArgsException(dbus.exceptions.DBusException):
+    _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
+
+
+class Application(dbus.service.Object):
+    def __init__(self, bus):
+        self.path = '/'
+        self.profiles = []
+        dbus.service.Object.__init__(self, bus, self.path)
+
+    def get_path(self):
+        return dbus.ObjectPath(self.path)
+
+    def add_profile(self, profile):
+        self.profiles.append(profile)
+
+    @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
+    def GetManagedObjects(self):
+        response = {}
+        print('GetManagedObjects')
+
+        for profile in self.profiles:
+            response[profile.get_path()] = profile.get_properties()
+
+        return response
+
+
+class Profile(dbus.service.Object):
+    PATH_BASE = '/org/bluez/example/profile'
+
+    def __init__(self, bus, uuids):
+        self.path = self.PATH_BASE
+        self.bus = bus
+        self.uuids = uuids
+        dbus.service.Object.__init__(self, bus, self.path)
+
+    def get_properties(self):
+        return {
+            GATT_PROFILE_IFACE: {
+                'UUIDs': self.uuids,
+            }
+        }
+
+    def get_path(self):
+        return dbus.ObjectPath(self.path)
+
+    @dbus.service.method(GATT_PROFILE_IFACE,
+                        in_signature="",
+                        out_signature="")
+    def Release(self):
+        print("Release")
+        mainloop.quit()
+
+    @dbus.service.method(DBUS_PROP_IFACE,
+                         in_signature='s',
+                         out_signature='a{sv}')
+    def GetAll(self, interface):
+        if interface != GATT_PROFILE_IFACE:
+            raise InvalidArgsException()
+
+        return self.get_properties[GATT_PROFILE_IFACE]
+
+
+def register_app_cb():
+    print('GATT application registered')
+
+
+def register_app_error_cb(error):
+    print('Failed to register application: ' + str(error))
+    mainloop.quit()
 
 if __name__ == '__main__':
-	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
 
-	bus = dbus.SystemBus()
+    bus = dbus.SystemBus()
 
-	path = bluezutils.find_adapter().object_path
+    path = bluezutils.find_adapter().object_path
 
-	manager = dbus.Interface(bus.get_object("org.bluez", path),
-				"org.bluez.GattManager1")
+    manager = dbus.Interface(bus.get_object("org.bluez", path),
+                            GATT_MANAGER_IFACE)
 
-	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"),
-			]
+    option_list = [make_option("-u", "--uuid", action="store",
+                                type="string", dest="uuid",
+                                default=None),
+    ]
 
-	opts = dbus.Dictionary({ }, signature='sv')
+    opts = dbus.Dictionary({}, signature='sv')
 
-	parser = OptionParser(option_list=option_list)
+    parser = OptionParser(option_list=option_list)
 
-	(options, args) = parser.parse_args()
+    (options, args) = parser.parse_args()
 
-	profile = GattProfile(bus, options.path)
+    mainloop = GObject.MainLoop()
 
-	mainloop = GObject.MainLoop()
+    if not options.uuid:
+        options.uuid = str(uuid.uuid4())
 
-	if not options.uuid:
-		options.uuid = str(uuid.uuid4())
+    app = Application(bus)
+    profile = Profile(bus, [options.uuid])
+    app.add_profile(profile)
+    manager.RegisterApplication(app.get_path(), {},
+                                reply_handler=register_app_cb,
+                                error_handler=register_app_error_cb)
 
-	uuids = { options.uuid }
-	manager.RegisterProfile(options.path, uuids, opts)
-
-	mainloop.run()
+    mainloop.run()
diff --git a/tools/bccmd.c b/tools/bccmd.c
index 4d15f3f..84f1a4a 100644
--- a/tools/bccmd.c
+++ b/tools/bccmd.c
@@ -36,6 +36,8 @@
 #include "lib/hci.h"
 #include "lib/hci_lib.h"
 
+#include "src/shared/tty.h"
+
 #include "csr.h"
 
 #define CSR_TRANSPORT_UNKNOWN	0
@@ -1193,34 +1195,8 @@
 			device = strdup(optarg);
 			break;
 		case 'b':
-			switch (atoi(optarg)) {
-			case 9600: bcsp_rate = B9600; break;
-			case 19200: bcsp_rate = B19200; break;
-			case 38400: bcsp_rate = B38400; break;
-			case 57600: bcsp_rate = B57600; break;
-			case 115200: bcsp_rate = B115200; break;
-			case 230400: bcsp_rate = B230400; break;
-			case 460800: bcsp_rate = B460800; break;
-			case 500000: bcsp_rate = B500000; break;
-			case 576000: bcsp_rate = B576000; break;
-			case 921600: bcsp_rate = B921600; break;
-			case 1000000: bcsp_rate = B1000000; break;
-			case 1152000: bcsp_rate = B1152000; break;
-			case 1500000: bcsp_rate = B1500000; break;
-			case 2000000: bcsp_rate = B2000000; break;
-#ifdef B2500000
-			case 2500000: bcsp_rate = B2500000; break;
-#endif
-#ifdef B3000000
-			case 3000000: bcsp_rate = B3000000; break;
-#endif
-#ifdef B3500000
-			case 3500000: bcsp_rate = B3500000; break;
-#endif
-#ifdef B4000000
-			case 4000000: bcsp_rate = B4000000; break;
-#endif
-			default:
+			bcsp_rate = tty_get_speed(atoi(optarg));
+			if (!bcsp_rate) {
 				printf("Unknown BCSP baud rate specified, defaulting to 38400bps\n");
 				bcsp_rate = B38400;
 			}
diff --git a/tools/bluemoon.c b/tools/bluemoon.c
index 650ab0c..5fc6269 100644
--- a/tools/bluemoon.c
+++ b/tools/bluemoon.c
@@ -620,6 +620,7 @@
 	{ 0x09, "iBT 1.5 (AG610)"	},
 	{ 0x0a, "iBT 2.1 (AG620)"	},
 	{ 0x0b, "iBT 3.0 (LnP)"		},
+	{ 0x0c, "iBT 3.0 (WsP)"		},
 	{ }
 };
 
diff --git a/tools/btattach.c b/tools/btattach.c
index 45962e8..ec63ec9 100644
--- a/tools/btattach.c
+++ b/tools/btattach.c
@@ -46,6 +46,7 @@
 #include "src/shared/mainloop.h"
 #include "src/shared/timeout.h"
 #include "src/shared/util.h"
+#include "src/shared/tty.h"
 #include "src/shared/hci.h"
 
 static int open_serial(const char *path, unsigned int speed)
@@ -219,56 +220,6 @@
 	{ }
 };
 
-static unsigned int get_speed(const char *str)
-{
-	switch (atoi(str)) {
-	case 57600:
-		return B57600;
-	case 115200:
-		return B115200;
-	case 230400:
-		return B230400;
-	case 460800:
-		return B460800;
-	case 500000:
-		return B500000;
-	case 576000:
-		return B576000;
-	case 921600:
-		return B921600;
-	case 1000000:
-		return B1000000;
-	case 1152000:
-		return B1152000;
-	case 1500000:
-		return B1500000;
-	case 2000000:
-		return B2000000;
-#ifdef B2500000
-	case 2500000:
-		return B2500000;
-#endif
-#ifdef B3000000
-	case 3000000:
-		return B3000000;
-#endif
-#ifdef B3500000
-	case 3500000:
-		return B3500000;
-#endif
-#ifdef B3710000
-	case 3710000:
-		return B3710000;
-#endif
-#ifdef B4000000
-	case 4000000:
-		return B4000000;
-#endif
-	}
-
-	return 0;
-}
-
 int main(int argc, char *argv[])
 {
 	const char *bredr_path = NULL, *amp_path = NULL, *proto = NULL;
@@ -296,7 +247,7 @@
 			proto = optarg;
 			break;
 		case 'S':
-			speed = get_speed(optarg);
+			speed = tty_get_speed(atoi(optarg));
 			if (!speed) {
 				fprintf(stderr, "Invalid speed: %s\n", optarg);
 				return EXIT_FAILURE;
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 2153bec..4c8c9dd 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -297,18 +297,20 @@
 {
 	uint16_t handle, value_handle;
 	uint8_t properties;
+	uint16_t ext_prop;
 	bt_uuid_t uuid;
 
 	if (!gatt_db_attribute_get_char_data(attr, &handle,
 								&value_handle,
 								&properties,
+								&ext_prop,
 								&uuid))
 		return;
 
 	printf("\t  " COLOR_YELLOW "charac" COLOR_OFF
-					" - start: 0x%04x, value: 0x%04x, "
-					"props: 0x%02x, uuid: ",
-					handle, value_handle, properties);
+				" - start: 0x%04x, value: 0x%04x, "
+				"props: 0x%02x, ext_props: 0x%04x, uuid: ",
+				handle, value_handle, properties, ext_prop);
 	print_uuid(&uuid);
 
 	gatt_db_service_foreach_desc(attr, print_desc, NULL);
diff --git a/tools/btgatt-server.c b/tools/btgatt-server.c
index 292b584..fadaff2 100644
--- a/tools/btgatt-server.c
+++ b/tools/btgatt-server.c
@@ -419,7 +419,8 @@
 	bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
 	gatt_db_service_add_characteristic(service, &uuid,
 					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
-					BT_GATT_CHRC_PROP_READ,
+					BT_GATT_CHRC_PROP_READ |
+					BT_GATT_CHRC_PROP_EXT_PROP,
 					gap_device_name_read_cb,
 					gap_device_name_write_cb,
 					server);
@@ -931,18 +932,20 @@
 {
 	uint16_t handle, value_handle;
 	uint8_t properties;
+	uint16_t ext_prop;
 	bt_uuid_t uuid;
 
 	if (!gatt_db_attribute_get_char_data(attr, &handle,
 								&value_handle,
 								&properties,
+								&ext_prop,
 								&uuid))
 		return;
 
 	printf("\t  " COLOR_YELLOW "charac" COLOR_OFF
-					" - start: 0x%04x, value: 0x%04x, "
-					"props: 0x%02x, uuid: ",
-					handle, value_handle, properties);
+				" - start: 0x%04x, value: 0x%04x, "
+				"props: 0x%02x, ext_prop: 0x%04x, uuid: ",
+				handle, value_handle, properties, ext_prop);
 	print_uuid(&uuid);
 
 	gatt_db_service_foreach_desc(attr, print_desc, NULL);
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 329d1af..0c78c4d 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -29,6 +29,7 @@
 #include <stdio.h>
 #include <stdbool.h>
 #include <unistd.h>
+#include <string.h>
 #include <sys/signalfd.h>
 
 #include <glib.h>
@@ -127,32 +128,48 @@
 	return desc_read(desc, iter);
 }
 
-static void desc_write(struct descriptor *desc, DBusMessageIter *iter)
+static void desc_write(struct descriptor *desc, const uint8_t *value, int len)
 {
-	DBusMessageIter array;
-	const uint8_t *value;
-	int vlen;
-
-	dbus_message_iter_recurse(iter, &array);
-	dbus_message_iter_get_fixed_array(&array, &value, &vlen);
-
 	g_free(desc->value);
-	desc->value = g_memdup(value, vlen);
-	desc->vlen = vlen;
+	desc->value = g_memdup(value, len);
+	desc->vlen = len;
 
 	g_dbus_emit_property_changed(connection, desc->path,
 					GATT_DESCRIPTOR_IFACE, "Value");
 }
 
+static int parse_value(DBusMessageIter *iter, const uint8_t **value, int *len)
+{
+	DBusMessageIter array;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return -EINVAL;
+
+	dbus_message_iter_recurse(iter, &array);
+	dbus_message_iter_get_fixed_array(&array, value, len);
+
+	return 0;
+}
+
 static void desc_set_value(const GDBusPropertyTable *property,
 				DBusMessageIter *iter,
 				GDBusPendingPropertySet id, void *user_data)
 {
 	struct descriptor *desc = user_data;
+	const uint8_t *value;
+	int len;
 
 	printf("Descriptor(%s): Set(\"Value\", ...)\n", desc->uuid);
 
-	desc_write(desc, iter);
+	if (parse_value(iter, &value, &len)) {
+		printf("Invalid value for Set('Value'...)\n");
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	desc_write(desc, value, len);
 
 	g_dbus_pending_property_success(id);
 }
@@ -249,15 +266,8 @@
 	return TRUE;
 }
 
-static void chr_write(struct characteristic *chr, DBusMessageIter *iter)
+static void chr_write(struct characteristic *chr, const uint8_t *value, int len)
 {
-	DBusMessageIter array;
-	uint8_t *value;
-	int len;
-
-	dbus_message_iter_recurse(iter, &array);
-	dbus_message_iter_get_fixed_array(&array, &value, &len);
-
 	g_free(chr->value);
 	chr->value = g_memdup(value, len);
 	chr->vlen = len;
@@ -271,10 +281,12 @@
 				GDBusPendingPropertySet id, void *user_data)
 {
 	struct characteristic *chr = user_data;
+	const uint8_t *value;
+	int len;
 
 	printf("Characteristic(%s): Set('Value', ...)\n", chr->uuid);
 
-	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+	if (!parse_value(iter, &value, &len)) {
 		printf("Invalid value for Set('Value'...)\n");
 		g_dbus_pending_property_error(id,
 					ERROR_INTERFACE ".InvalidArguments",
@@ -282,7 +294,7 @@
 		return;
 	}
 
-	chr_write(chr, iter);
+	chr_write(chr, value, len);
 
 	g_dbus_pending_property_success(id);
 }
@@ -368,12 +380,53 @@
 	g_free(desc);
 }
 
+static int parse_options(DBusMessageIter *iter, const char **device)
+{
+	DBusMessageIter dict;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return -EINVAL;
+
+	dbus_message_iter_recurse(iter, &dict);
+
+	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+		const char *key;
+		DBusMessageIter value, entry;
+		int var;
+
+		dbus_message_iter_recurse(&dict, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_recurse(&entry, &value);
+
+		var = dbus_message_iter_get_arg_type(&value);
+		if (strcasecmp(key, "device") == 0) {
+			if (var != DBUS_TYPE_OBJECT_PATH)
+				return -EINVAL;
+			dbus_message_iter_get_basic(&value, device);
+			printf("Device: %s\n", *device);
+		}
+	}
+
+	return 0;
+}
+
 static DBusMessage *chr_read_value(DBusConnection *conn, DBusMessage *msg,
 							void *user_data)
 {
 	struct characteristic *chr = user_data;
 	DBusMessage *reply;
 	DBusMessageIter iter;
+	const char *device;
+
+	if (!dbus_message_iter_init(msg, &iter))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
+
+	if (parse_options(&iter, &device))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
 
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
@@ -392,10 +445,21 @@
 {
 	struct characteristic *chr = user_data;
 	DBusMessageIter iter;
+	const uint8_t *value;
+	int len;
+	const char *device;
 
 	dbus_message_iter_init(msg, &iter);
 
-	chr_write(chr, &iter);
+	if (parse_value(&iter, &value, &len))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
+
+	if (parse_options(&iter, &device))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
+
+	chr_write(chr, value, len);
 
 	return dbus_message_new_method_return(msg);
 }
@@ -415,10 +479,12 @@
 }
 
 static const GDBusMethodTable chr_methods[] = {
-	{ GDBUS_ASYNC_METHOD("ReadValue", NULL, GDBUS_ARGS({ "value", "ay" }),
-						chr_read_value) },
-	{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }),
-						NULL, chr_write_value) },
+	{ GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
+					GDBUS_ARGS({ "value", "ay" }),
+					chr_read_value) },
+	{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
+						{ "options", "a{sv}" }),
+					NULL, chr_write_value) },
 	{ GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chr_start_notify) },
 	{ GDBUS_METHOD("StopNotify", NULL, NULL, chr_stop_notify) },
 	{ }
@@ -430,6 +496,15 @@
 	struct descriptor *desc = user_data;
 	DBusMessage *reply;
 	DBusMessageIter iter;
+	const char *device;
+
+	if (!dbus_message_iter_init(msg, &iter))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
+
+	if (parse_options(&iter, &device))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
 
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
@@ -438,6 +513,10 @@
 
 	dbus_message_iter_init_append(reply, &iter);
 
+	if (parse_options(&iter, &device))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
+
 	desc_read(desc, &iter);
 
 	return reply;
@@ -448,20 +527,34 @@
 {
 	struct descriptor *desc = user_data;
 	DBusMessageIter iter;
+	const char *device;
+	const uint8_t *value;
+	int len;
 
-	dbus_message_iter_init(msg, &iter);
+	if (!dbus_message_iter_init(msg, &iter))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
 
-	desc_write(desc, &iter);
+	if (parse_value(&iter, &value, &len))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
+
+	if (parse_options(&iter, &device))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"Invalid arguments");
+
+	desc_write(desc, value, len);
 
 	return dbus_message_new_method_return(msg);
 }
 
 static const GDBusMethodTable desc_methods[] = {
-	{ GDBUS_ASYNC_METHOD("ReadValue", NULL, GDBUS_ARGS({ "value", "ay" }),
-						desc_read_value) },
-	{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }),
-						NULL,
-						desc_write_value) },
+	{ GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
+					GDBUS_ARGS({ "value", "ay" }),
+					desc_read_value) },
+	{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
+						{ "options", "a{sv}" }),
+					NULL, desc_write_value) },
 	{ }
 };
 
diff --git a/tools/hciattach.c b/tools/hciattach.c
index 59a76a7..fad176c 100644
--- a/tools/hciattach.c
+++ b/tools/hciattach.c
@@ -46,6 +46,8 @@
 #include "lib/hci.h"
 #include "lib/hci_lib.h"
 
+#include "src/shared/tty.h"
+
 #include "hciattach.h"
 
 struct uart_t {
@@ -84,68 +86,12 @@
 	exit(1);
 }
 
-int uart_speed(int s)
-{
-	switch (s) {
-	case 9600:
-		return B9600;
-	case 19200:
-		return B19200;
-	case 38400:
-		return B38400;
-	case 57600:
-		return B57600;
-	case 115200:
-		return B115200;
-	case 230400:
-		return B230400;
-	case 460800:
-		return B460800;
-	case 500000:
-		return B500000;
-	case 576000:
-		return B576000;
-	case 921600:
-		return B921600;
-	case 1000000:
-		return B1000000;
-	case 1152000:
-		return B1152000;
-	case 1500000:
-		return B1500000;
-	case 2000000:
-		return B2000000;
-#ifdef B2500000
-	case 2500000:
-		return B2500000;
-#endif
-#ifdef B3000000
-	case 3000000:
-		return B3000000;
-#endif
-#ifdef B3500000
-	case 3500000:
-		return B3500000;
-#endif
-#ifdef B3710000
-	case 3710000:
-		return B3710000;
-#endif
-#ifdef B4000000
-	case 4000000:
-		return B4000000;
-#endif
-	default:
-		return B57600;
-	}
-}
-
 int set_speed(int fd, struct termios *ti, int speed)
 {
-	if (cfsetospeed(ti, uart_speed(speed)) < 0)
+	if (cfsetospeed(ti, tty_get_speed(speed)) < 0)
 		return -errno;
 
-	if (cfsetispeed(ti, uart_speed(speed)) < 0)
+	if (cfsetispeed(ti, tty_get_speed(speed)) < 0)
 		return -errno;
 
 	if (tcsetattr(fd, TCSANOW, ti) < 0)
@@ -646,7 +592,7 @@
 		fprintf(stderr, "Speed %d too high. Remaining at %d baud\n",
 			u->speed, u->init_speed);
 		u->speed = u->init_speed;
-	} else if (u->speed != 57600 && uart_speed(u->speed) == B57600) {
+	} else if (!tty_get_speed(u->speed)) {
 		/* Unknown speed. Why oh why can't we just pass an int to the kernel? */
 		fprintf(stderr, "Speed %d unrecognised. Remaining at %d baud\n",
 			u->speed, u->init_speed);
diff --git a/tools/parse_companies.pl b/tools/parse_companies.pl
index 1746222..f755390 100755
--- a/tools/parse_companies.pl
+++ b/tools/parse_companies.pl
@@ -10,6 +10,8 @@
     'nbsp' => ' ',
     'eacute' => 'é',
     'auml' => 'ä',
+    'uuml' => 'ü',
+    'Uuml' => 'Ü',
 );
 
 # better to use URI::Encode if you have it
diff --git a/tools/test-runner.c b/tools/test-runner.c
index 42c2c1a..9b54426 100644
--- a/tools/test-runner.c
+++ b/tools/test-runner.c
@@ -5,18 +5,18 @@
  *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
  *
  *
- *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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/unit/test-gatt.c b/unit/test-gatt.c
index 0912348..8129719 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -257,7 +257,7 @@
 		raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00,	\
 			0x2a),						\
 		raw_pdu(0x08, 0x13, 0xf0, 0x18, 0xf0, 0x03, 0x28),	\
-		raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x02, 0x15, 0xf0, 0xef,	\
+		raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x82, 0x15, 0xf0, 0xef,	\
 			0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00,	\
 			0x00, 0x00, 0x00, 0x09, 0xB0, 0x00, 0x00),	\
 		raw_pdu(0x08, 0x15, 0xf0, 0x18, 0xf0, 0x03, 0x28),	\
@@ -267,8 +267,10 @@
 		raw_pdu(0x01, 0x08, 0x18, 0xf0, 0x0a),			\
 		raw_pdu(0x04, 0x16, 0xf0, 0x16, 0xf0),			\
 		raw_pdu(0x05, 0x01, 0x16, 0xf0, 0x00, 0x29),		\
+		raw_pdu(0x0a, 0x16, 0xf0),				\
+		raw_pdu(0x0b, 0x01, 0x00),				\
 		raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28),	\
-		raw_pdu(0x09, 0x07, 0x02, 0x00, 0x32, 0x03, 0x00, 0x29,	\
+		raw_pdu(0x09, 0x07, 0x02, 0x00, 0xb2, 0x03, 0x00, 0x29,	\
 			0x2a),						\
 		raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28),	\
 		raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a)
@@ -526,9 +528,9 @@
 	bt_uuid_t a_uuid, b_uuid;
 
 	gatt_db_attribute_get_char_data(a, &a_handle, &a_value_handle,
-							&a_properties, &a_uuid);
+						&a_properties, NULL, &a_uuid);
 	gatt_db_attribute_get_char_data(b, &b_handle, &b_value_handle,
-							&b_properties, &b_uuid);
+						&b_properties, NULL, &b_uuid);
 
 	return a_handle == b_handle && a_value_handle == b_value_handle &&
 						a_properties == b_properties &&
@@ -1556,7 +1558,8 @@
 						BT_ATT_PERM_WRITE,
 						BT_GATT_CHRC_PROP_READ |
 						BT_GATT_CHRC_PROP_NOTIFY |
-						BT_GATT_CHRC_PROP_INDICATE,
+						BT_GATT_CHRC_PROP_INDICATE |
+						BT_GATT_CHRC_PROP_EXT_PROP,
 						"BlueZ"),
 		DESCRIPTOR(GATT_CLIENT_CHARAC_CFG_UUID, BT_ATT_PERM_READ |
 						BT_ATT_PERM_WRITE, 0x00, 0x00),
@@ -1572,7 +1575,8 @@
 							"BlueZ Unit Tester"),
 		CHARACTERISTIC(0000B009-0000-0000-0123-456789abcdef,
 					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
-					BT_GATT_CHRC_PROP_READ, 0x09),
+					BT_GATT_CHRC_PROP_READ |
+					BT_GATT_CHRC_PROP_EXT_PROP, 0x09),
 		DESCRIPTOR(GATT_CHARAC_EXT_PROPER_UUID, BT_ATT_PERM_READ, 0x01,
 									0x00),
 		CHARACTERISTIC(GATT_CHARAC_APPEARANCE, BT_ATT_PERM_READ,
@@ -1617,7 +1621,8 @@
 		PRIMARY_SERVICE(0x0080, "a00b", 7),
 		CHARACTERISTIC(0xb008, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
 					BT_GATT_CHRC_PROP_READ |
-					BT_GATT_CHRC_PROP_WRITE,
+					BT_GATT_CHRC_PROP_WRITE |
+					BT_GATT_CHRC_PROP_EXT_PROP,
 					0x08),
 		DESCRIPTOR(0xb015, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, 0x01),
 		DESCRIPTOR(0xb016, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, 0x02),
@@ -2546,7 +2551,7 @@
 			raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00,
 					0x2a),
 			raw_pdu(0x08, 0x13, 0xf0, 0x18, 0xf0, 0x03, 0x28),
-			raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x02, 0x15, 0xf0, 0xef,
+			raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x82, 0x15, 0xf0, 0xef,
 					0xcd, 0xab, 0x89, 0x67, 0x45, 0x23,
 					0x01, 0x00, 0x00, 0x00, 0x00, 0x09,
 					0xb0, 0x00, 0x00),
@@ -2560,7 +2565,7 @@
 			ts_small_db, NULL,
 			raw_pdu(0x03, 0x00, 0x02),
 			raw_pdu(0x08, 0x01, 0x00, 0x0f, 0x00, 0x03, 0x28),
-			raw_pdu(0x09, 0x07, 0x02, 0x00, 0x32, 0x03, 0x00, 0x29,
+			raw_pdu(0x09, 0x07, 0x02, 0x00, 0xb2, 0x03, 0x00, 0x29,
 					0x2a),
 			raw_pdu(0x08, 0x03, 0x00, 0x0f, 0x00, 0x03, 0x28),
 			raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a));
@@ -2599,7 +2604,7 @@
 			raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00,
 					0x2a),
 			raw_pdu(0x08, 0x13, 0xf0, 0x17, 0xf0, 0x03, 0x28),
-			raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x02, 0x15, 0xf0, 0xef,
+			raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x82, 0x15, 0xf0, 0xef,
 					0xcd, 0xab, 0x89, 0x67, 0x45, 0x23,
 					0x01, 0x00, 0x00, 0x00, 0x00, 0x09,
 					0xb0, 0x00, 0x00),
@@ -2613,7 +2618,7 @@
 			ts_small_db, NULL,
 			raw_pdu(0x03, 0x00, 0x02),
 			raw_pdu(0x08, 0x01, 0x00, 0x0f, 0x00, 0x03, 0x28),
-			raw_pdu(0x09, 0x07, 0x02, 0x00, 0x32, 0x03, 0x00, 0x29,
+			raw_pdu(0x09, 0x07, 0x02, 0x00, 0xb2, 0x03, 0x00, 0x29,
 					0x2a),
 			raw_pdu(0x08, 0x03, 0x00, 0x0f, 0x00, 0x03, 0x28),
 			raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a));
@@ -4434,5 +4439,19 @@
 				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
 			raw_pdu(0x01, 0x16, 0x04, 0x00, 0x03));
 
+	define_test_server("/robustness/no-reliable-characteristic",
+			test_server, ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x82, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x17, 0x82, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x16, 0x25, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x17, 0x25, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x16, 0x82, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+			raw_pdu(0x17, 0x82, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+			raw_pdu(0x16, 0x25, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+			raw_pdu(0x17, 0x25, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+			raw_pdu(0x18, 0x01),
+			raw_pdu(0x01, 0x18, 0x25, 0x00, 0x06));
+
 	return tester_run();
 }