Merge BlueZ 5.20 into master

Change-Id: I2d69df1b233a4e0135fd5137bc792a7040897f2a
diff --git a/.gitignore b/.gitignore
index 803758b..2e08c21 100644
--- a/.gitignore
+++ b/.gitignore
@@ -86,7 +86,9 @@
 unit/test-sdp
 unit/test-lib
 unit/test-mgmt
+unit/test-uhid
 unit/test-hfp
+unit/test-crypto
 tools/mgmt-tester
 tools/smp-tester
 tools/gap-tester
@@ -128,3 +130,7 @@
 android/test-ipc
 android/test-*.log
 android/test-*.trs
+
+cscope.in.out
+cscope.out
+cscope.po.out
diff --git a/ChangeLog b/ChangeLog
index 1c9fd1a..25ce5ca 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+ver 5.20:
+	Fix issue with LED handling of PS3 controllers.
+	Add support for Android Bluetooth GATT server interface.
+	Add support for Android Bluetooth HID over GATT feature.
+	Add support for Android Bluetooth multi-profile feature.
+	Add support for Android Bluetooth aptX audio integration.
+
+	Note: aptX codec not included
+
 ver 5.19:
 	Fix issue with OBEX Put-Delete and Create-Empty methods.
 	Fix issue with AVRCP browsable/searchable player properties.
diff --git a/Makefile.am b/Makefile.am
index 14e735d..178b0a0 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 20:9:17
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:10:17
 lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
 endif
 
@@ -98,7 +98,8 @@
 attrib_sources = attrib/att.h attrib/att-database.h attrib/att.c \
 		attrib/gatt.h attrib/gatt.c \
 		attrib/gattrib.h attrib/gattrib.c \
-		attrib/gatt-service.h attrib/gatt-service.c
+		attrib/gatt-service.h attrib/gatt-service.c \
+		src/shared/crypto.h src/shared/crypto.c
 
 btio_sources = btio/btio.h btio/btio.c
 
@@ -226,7 +227,8 @@
 AM_CPPFLAGS = -I$(builddir)/lib -I$(srcdir)/gdbus
 
 
-unit_tests += unit/test-eir unit/test-uuid unit/test-textfile unit/test-crc
+unit_tests += unit/test-eir unit/test-uuid unit/test-textfile unit/test-crc \
+							 unit/test-crypto
 
 unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/uuid-helper.c
 unit_test_eir_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
@@ -240,6 +242,10 @@
 unit_test_crc_SOURCES = unit/test-crc.c monitor/crc.h monitor/crc.c
 unit_test_crc_LDADD = @GLIB_LIBS@
 
+unit_test_crypto_SOURCES = unit/test-crypto.c \
+				src/shared/crypto.h src/shared/crypto.c
+unit_test_crypto_LDADD = @GLIB_LIBS@
+
 unit_tests += unit/test-ringbuf unit/test-queue
 
 unit_test_ringbuf_SOURCES = unit/test-ringbuf.c \
@@ -261,6 +267,15 @@
 				src/shared/mgmt.h src/shared/mgmt.c
 unit_test_mgmt_LDADD = @GLIB_LIBS@
 
+unit_tests += unit/test-uhid
+
+unit_test_uhid_SOURCES = unit/test-uhid.c \
+				src/shared/uhid.h src/shared/uhid.c \
+				src/shared/io.h src/shared/io-glib.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/queue.h src/shared/queue.c
+unit_test_uhid_LDADD = @GLIB_LIBS@
+
 unit_tests += unit/test-sdp
 
 unit_test_sdp_SOURCES = unit/test-sdp.c \
diff --git a/Makefile.plugins b/Makefile.plugins
index c6b92d4..dbcb491 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -59,7 +59,8 @@
 builtin_sources += profiles/input/manager.c \
 			profiles/input/server.h profiles/input/server.c \
 			profiles/input/device.h profiles/input/device.c \
-			profiles/input/uhid_copy.h profiles/input/hidp_defs.h
+			src/shared/uhid.h src/shared/uhid.c \
+			profiles/input/hidp_defs.h
 
 builtin_modules += hog
 builtin_sources += profiles/input/hog.c profiles/input/uhid_copy.h \
diff --git a/Makefile.tools b/Makefile.tools
index 5d610a7..966dd64 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -365,7 +365,9 @@
 				attrib/gattrib.c btio/btio.c \
 				attrib/gatttool.h attrib/interactive.c \
 				attrib/utils.c src/log.c client/display.c \
-				client/display.h
+				client/display.h \
+				src/shared/crypto.h src/shared/crypto.c
+
 attrib_gatttool_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ -lreadline
 
 tools_obex_client_tool_SOURCES = $(gobex_sources) $(btio_sources) \
diff --git a/android/Android.mk b/android/Android.mk
index 4235a7c..fc7b59d 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -3,12 +3,15 @@
 # Retrieve BlueZ version from configure.ac file
 BLUEZ_VERSION := `grep "^AC_INIT" $(LOCAL_PATH)/bluez/configure.ac | sed -e "s/.*,.\(.*\))/\1/"`
 
+ANDROID_VERSION := `echo $(PLATFORM_VERSION) | awk -F. '{ printf "0x%02d%02d%02d",$$1,$$2,$$3 }'`
+
 # Specify pathmap for glib and sbc
 pathmap_INCL += glib:external/bluetooth/glib \
 		sbc:external/bluetooth/sbc \
 
 # Specify common compiler flags
 BLUEZ_COMMON_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\" \
+			-DANDROID_VERSION=$(ANDROID_VERSION) \
 			-DANDROID_STORAGEDIR=\"/data/misc/bluetooth\" \
 
 # Enable warnings enabled in autotools build
@@ -32,6 +35,7 @@
 LOCAL_SRC_FILES := \
 	bluez/android/main.c \
 	bluez/android/bluetooth.c \
+	bluez/android/hog.c \
 	bluez/android/hidhost.c \
 	bluez/android/socket.c \
 	bluez/android/ipc.c \
@@ -53,6 +57,8 @@
 	bluez/src/shared/hfp.c \
 	bluez/src/shared/gatt-db.c \
 	bluez/src/shared/io-glib.c \
+	bluez/src/shared/crypto.c \
+	bluez/src/shared/uhid.c \
 	bluez/src/sdpd-database.c \
 	bluez/src/sdpd-service.c \
 	bluez/src/sdpd-request.c \
@@ -252,9 +258,14 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := bluez/android/hal-audio.c
+LOCAL_SRC_FILES := \
+	bluez/src/shared/queue.c \
+	bluez/android/hal-audio.c \
+	bluez/android/hal-audio-sbc.c \
+	bluez/android/hal-audio-aptx.c \
 
 LOCAL_C_INCLUDES = \
+	$(LOCAL_PATH)/bluez \
 	$(call include-path-for, system-core) \
 	$(call include-path-for, libhardware) \
 	$(call include-path-for, sbc) \
@@ -264,6 +275,7 @@
 	libsbc \
 
 LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+LOCAL_LDFLAGS := -ldl
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
 LOCAL_MODULE_TAGS := optional
@@ -581,3 +593,30 @@
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(include_path)
 
 include $(BUILD_STATIC_LIBRARY)
+
+#
+# avtest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/avinfo.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := avinfo
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
diff --git a/android/Makefile.am b/android/Makefile.am
index e663790..51c8253 100644
--- a/android/Makefile.am
+++ b/android/Makefile.am
@@ -1,4 +1,7 @@
 if ANDROID
+
+AM_CFLAGS += -DANDROID_VERSION=0xFFFFFF
+
 android_plugindir = $(abs_top_srcdir)/android/.libs
 
 noinst_PROGRAMS += android/system-emulator
@@ -31,8 +34,11 @@
 				src/shared/ringbuf.h src/shared/ringbuf.c \
 				src/shared/hfp.h src/shared/hfp.c \
 				src/shared/gatt-db.h src/shared/gatt-db.c \
+				src/shared/crypto.h src/shared/crypto.c \
+				src/shared/uhid.h src/shared/uhid.c \
 				android/bluetooth.h android/bluetooth.c \
 				android/hidhost.h android/hidhost.c \
+				android/hog.h android/hog.c \
 				android/ipc-common.h \
 				android/ipc.h android/ipc.c \
 				android/avdtp.h android/avdtp.c \
@@ -165,7 +171,10 @@
 
 android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \
 					android/hal-msg.h \
+					android/hal-audio.h \
 					android/hal-audio.c \
+					android/hal-audio-sbc.c \
+					android/hal-audio-aptx.c \
 					android/hardware/audio.h \
 					android/hardware/audio_effect.h \
 					android/hardware/hardware.h \
@@ -201,7 +210,8 @@
 
 plugin_LTLIBRARIES += android/audio.a2dp.default.la
 
-android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
+					@SBC_CFLAGS@
 
 android_audio_a2dp_default_la_LIBADD = @SBC_LIBS@
 
@@ -214,6 +224,7 @@
 				android/init.bluetooth.rc \
 				android/hal-ipc-api.txt \
 				android/audio-ipc-api.txt \
+				android/cts.txt \
 				android/pics-rfcomm.txt \
 				android/pics-spp.txt \
 				android/pics-sdp.txt \
@@ -236,6 +247,7 @@
 				android/pics-iopt.txt \
 				android/pics-sm.txt \
 				android/pics-mps.txt \
+				android/pics-hogp.txt \
 				android/pixit-l2cap.txt \
 				android/pixit-gap.txt \
 				android/pixit-did.txt \
@@ -255,6 +267,7 @@
 				android/pixit-iopt.txt \
 				android/pixit-sm.txt \
 				android/pixit-mps.txt \
+				android/pixit-hogp.txt \
 				android/bite-rfcomm.txt \
 				android/bite-spp.txt \
 				android/pts-l2cap.txt \
@@ -275,4 +288,5 @@
 				android/pts-hdp.txt \
 				android/pts-mcap.txt \
 				android/pts-mps.txt \
-				android/pts-sm.txt
+				android/pts-sm.txt \
+				android/pts-hogp.txt
diff --git a/android/README b/android/README
index 0895b71..435cf56 100644
--- a/android/README
+++ b/android/README
@@ -102,6 +102,10 @@
 CONFIG_BT_BNEP_PROTO_FILTER
 CONFIG_BRIDGE
 CONFIG_UHID
+CONFIG_CRYPTO_CMAC
+CONFIG_CRYPTO_USER_API
+CONFIG_CRYPTO_USER_API_HASH
+CONFIG_CRYPTO_USER_API_SKCIPHER
 
 Also BT chip driver needs to be enabled e.g:
 CONFIG_BT_HCIBTUSB
@@ -218,9 +222,9 @@
 ---------------------------------------
 core          bluetooth.h        complete
 a2dp          bt_av.h            complete
-gatt          bt_gatt.h          partial
-              bt_gatt_client.h   partial
-              bt_gatt_server.h   initial
+gatt          bt_gatt.h          complete
+              bt_gatt_client.h   complete
+              bt_gatt_server.h   complete
 handsfree     bt_hf.h            complete
 hidhost       bt_hh.h            complete
 health        bt_hl.h            initial
@@ -289,6 +293,14 @@
 set_player_app_value_cb            NULL JNI implementation
 
 
+HAL GATT
+--------
+
+methods:
+client->set_adv_data               missing kernel support for vendor data
+client->connect                    is_direct parameter is ignored
+
+
 Known Android issues
 ====================
 
@@ -320,3 +332,4 @@
 HFP		In-band Ring Tone		+BSIR
 AVRCP		Player Settings			HAL API present but not used
 AVRCP		Browsing			No HAL API
+GATT		Read multiple characteristics	No HAL API
diff --git a/android/a2dp.c b/android/a2dp.c
index 452fdab..dd8bcb8 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -325,15 +325,101 @@
 	return 0;
 }
 
+static int aac_check_config(void *caps, uint8_t caps_len, void *conf,
+							uint8_t conf_len)
+{
+	a2dp_aac_t *cap, *config;
+
+	if (conf_len != caps_len || conf_len != sizeof(a2dp_aac_t)) {
+		error("AAC: Invalid configuration size (%u)", conf_len);
+		return -EINVAL;
+	}
+
+	cap = caps;
+	config = conf;
+
+	if (!(cap->object_type & config->object_type)) {
+		error("AAC: Unsupported object type (%u) by endpoint",
+							config->object_type);
+		return -EINVAL;
+	}
+
+	if (!(AAC_GET_FREQUENCY(*cap) & AAC_GET_FREQUENCY(*config))) {
+		error("AAC: Unsupported frequency (%u) by endpoint",
+						AAC_GET_FREQUENCY(*config));
+		return -EINVAL;
+	}
+
+	if (!(cap->channels & config->channels)) {
+		error("AAC: Unsupported channels (%u) by endpoint",
+							config->channels);
+		return -EINVAL;
+	}
+
+	/* VBR support in SNK is mandatory but let's make sure we don't try to
+	 * have VBR on remote which for some reason does not support it
+	 */
+	if (!cap->vbr && config->vbr) {
+		error("AAC: Unsupported VBR (%u) by endpoint",
+							config->vbr);
+		return -EINVAL;
+	}
+
+	if (AAC_GET_BITRATE(*cap) < AAC_GET_BITRATE(*config))
+		return -ERANGE;
+
+	return 0;
+}
+
+static int aptx_check_config(void *caps, uint8_t caps_len, void *conf,
+							uint8_t conf_len)
+{
+	a2dp_aptx_t *cap, *config;
+
+	if (conf_len != caps_len || conf_len != sizeof(a2dp_aptx_t)) {
+		error("APTX: Invalid configuration size (%u)", conf_len);
+		return -EINVAL;
+	}
+
+	cap = caps;
+	config = conf;
+
+	if (!(cap->frequency & config->frequency)) {
+		error("APTX: Unsupported frequenct (%u) by endpoint",
+							config->frequency);
+		return -EINVAL;
+	}
+
+	if (!(cap->channel_mode & config->channel_mode)) {
+		error("APTX: Unsupported channel mode (%u) by endpoint",
+							config->channel_mode);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int check_capabilities(struct a2dp_preset *preset,
 				struct avdtp_media_codec_capability *codec,
 				uint8_t codec_len)
 {
+	a2dp_vendor_codec_t *vndcodec;
+
 	/* Codec specific */
 	switch (codec->media_codec_type) {
 	case A2DP_CODEC_SBC:
 		return sbc_check_config(codec->data, codec_len, preset->data,
 								preset->len);
+	case A2DP_CODEC_MPEG24:
+		return aac_check_config(codec->data, codec_len, preset->data,
+								preset->len);
+	case A2DP_CODEC_VENDOR:
+		vndcodec = (void *) codec->data;
+		if (btohl(vndcodec->vendor_id) == APTX_VENDOR_ID &&
+				btohs(vndcodec->codec_id) == APTX_CODEC_ID)
+			return aptx_check_config(codec->data, codec_len,
+						preset->data, preset->len);
+		return -EINVAL;
 	default:
 		return -EINVAL;
 	}
@@ -358,6 +444,26 @@
 	return p;
 }
 
+static struct a2dp_preset *aac_select_range(void *caps, uint8_t caps_len,
+						void *conf, uint8_t conf_len)
+{
+	struct a2dp_preset *p;
+	a2dp_aac_t *cap, *config;
+	uint32_t bitrate;
+
+	cap = caps;
+	config = conf;
+
+	bitrate = MIN(AAC_GET_BITRATE(*cap), AAC_GET_BITRATE(*config));
+	AAC_SET_BITRATE(*config, bitrate);
+
+	p = g_new0(struct a2dp_preset, 1);
+	p->len = conf_len;
+	p->data = g_memdup(conf, p->len);
+
+	return p;
+}
+
 static struct a2dp_preset *select_preset_range(struct a2dp_preset *preset,
 				struct avdtp_media_codec_capability *codec,
 				uint8_t codec_len)
@@ -367,6 +473,9 @@
 	case A2DP_CODEC_SBC:
 		return sbc_select_range(codec->data, codec_len, preset->data,
 								preset->len);
+	case A2DP_CODEC_MPEG24:
+		return aac_select_range(codec->data, codec_len, preset->data,
+								preset->len);
 	default:
 		return NULL;
 	}
@@ -541,14 +650,12 @@
 
 	avdtp_add_disconnect_cb(dev->session, disconnect_cb, dev);
 
+	/* Proceed to stream setup if initiator */
 	if (dev->io) {
+		int perr;
+
 		g_io_channel_unref(dev->io);
 		dev->io = NULL;
-	}
-
-	/* Proceed to stream setup if initiator */
-	if (dev->state == HAL_A2DP_STATE_CONNECTING) {
-		int perr;
 
 		perr = avdtp_discover(dev->session, discover_cb, dev);
 		if (perr < 0) {
@@ -739,6 +846,7 @@
 	}
 
 	dev = a2dp_device_new(&dst);
+	bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING);
 	signaling_connect_cb(chan, err, dev);
 }
 
@@ -1227,6 +1335,14 @@
 	endpoint->caps = presets->data;
 	endpoint->presets = g_slist_copy(g_slist_nth(presets, 1));
 
+	if (endpoint->codec == A2DP_CODEC_VENDOR) {
+		a2dp_vendor_codec_t *vndcodec = (void *) endpoint->caps->data;
+
+		avdtp_sep_set_vendor_codec(endpoint->sep,
+						btohl(vndcodec->vendor_id),
+						btohs(vndcodec->codec_id));
+	}
+
 	endpoints = g_slist_append(endpoints, endpoint);
 
 	return endpoint->id;
@@ -1354,7 +1470,10 @@
 
 	DBG("");
 
-	setup = find_setup(cmd->id);
+	if (cmd->id)
+		setup = find_setup(cmd->id);
+	else
+		setup = setups ? setups->data : NULL;
 	if (!setup) {
 		error("Unable to find stream for endpoint %u", cmd->id);
 		ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
@@ -1373,6 +1492,7 @@
 	len = sizeof(struct audio_rsp_open_stream) +
 			sizeof(struct audio_preset) + setup->preset->len;
 	rsp = g_malloc0(len);
+	rsp->id = setup->endpoint->id;
 	rsp->mtu = omtu;
 	rsp->preset->len = setup->preset->len;
 	memcpy(rsp->preset->data, setup->preset->data, setup->preset->len);
diff --git a/android/android-tester.c b/android/android-tester.c
index f04f8ac..bf39517 100644
--- a/android/android-tester.c
+++ b/android/android-tester.c
@@ -1408,9 +1408,13 @@
 
 static bt_bdaddr_t enable_done_bdaddr_val = { {0x00} };
 static const char enable_done_bdname_val[] = "BlueZ for Android";
-static bt_uuid_t enable_done_uuids_val = {
-	.uu = { 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
-					0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb},
+static const char enable_done_uuids_val[] = {
+	/* Multi profile UUID */
+	0x00, 0x00, 0x11, 0x3b, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+					0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
+	/* Device identification profile UUID */
+	0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+					0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
 };
 static bt_device_type_t enable_done_tod_val = BT_DEVICE_DEVTYPE_DUAL;
 static bt_scan_mode_t enable_done_scanmode_val = BT_SCAN_MODE_NONE;
@@ -1771,9 +1775,13 @@
 	.expected_adapter_status = BT_STATUS_SUCCESS,
 };
 
-static bt_uuid_t getprop_uuids = {
-	.uu = { 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
-					0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB },
+static const char getprop_uuids[] = {
+	/* Multi profile UUID */
+	0x00, 0x00, 0x11, 0x3b, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+					0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
+	/* Device identification profile UUID */
+	0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+					0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
 };
 
 static struct priority_property getprop_uuids_props[] = {
diff --git a/android/audio-msg.h b/android/audio-msg.h
index 5981355..7b9553b 100644
--- a/android/audio-msg.h
+++ b/android/audio-msg.h
@@ -61,6 +61,7 @@
 } __attribute__((packed));
 
 struct audio_rsp_open_stream {
+	uint16_t id;
 	uint16_t mtu;
 	struct audio_preset preset[0];
 } __attribute__((packed));
diff --git a/android/avctp.c b/android/avctp.c
index be3fed1..a70ed52 100644
--- a/android/avctp.c
+++ b/android/avctp.c
@@ -76,7 +76,6 @@
 	uint8_t transaction:4;
 	uint16_t pid;
 } __attribute__ ((packed));
-#define AVCTP_HEADER_LENGTH 3
 
 struct avc_header {
 	uint8_t code:4;
@@ -85,7 +84,6 @@
 	uint8_t subunit_type:5;
 	uint8_t opcode;
 } __attribute__ ((packed));
-#define AVC_HEADER_LENGTH 3
 
 #elif __BYTE_ORDER == __BIG_ENDIAN
 
@@ -96,7 +94,6 @@
 	uint8_t ipid:1;
 	uint16_t pid;
 } __attribute__ ((packed));
-#define AVCTP_HEADER_LENGTH 3
 
 struct avc_header {
 	uint8_t _hdr0:4;
@@ -105,7 +102,6 @@
 	uint8_t subunit_id:3;
 	uint8_t opcode;
 } __attribute__ ((packed));
-#define AVC_HEADER_LENGTH 3
 
 #else
 #error "Unknown byte order"
diff --git a/android/avctp.h b/android/avctp.h
index 2b33858..f0da2b3 100644
--- a/android/avctp.h
+++ b/android/avctp.h
@@ -25,7 +25,11 @@
 #define AVCTP_CONTROL_PSM		23
 #define AVCTP_BROWSING_PSM		27
 
-#define AVC_MTU 512
+#define AVCTP_HEADER_LENGTH		3
+#define AVC_HEADER_LENGTH		3
+
+#define AVC_DATA_OFFSET			AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH
+#define AVC_DATA_MTU			512
 
 /* ctype entries */
 #define AVC_CTYPE_CONTROL		0x0
diff --git a/android/avdtp.c b/android/avdtp.c
index 3d309b9..89f4860 100644
--- a/android/avdtp.c
+++ b/android/avdtp.c
@@ -39,8 +39,10 @@
 
 #include <glib.h>
 
+#include "lib/bluetooth.h"
 #include "src/log.h"
 #include "avdtp.h"
+#include "../profiles/audio/a2dp-codecs.h"
 
 #define AVDTP_PSM 25
 
@@ -317,6 +319,8 @@
 	struct avdtp_stream *stream;
 	struct seid_info info;
 	uint8_t codec;
+	uint32_t vndcodec_vendor;
+	uint16_t vndcodec_codec;
 	gboolean delay_reporting;
 	GSList *caps;
 	struct avdtp_sep_ind *ind;
@@ -1089,6 +1093,18 @@
 		if (codec_data->media_codec_type != lsep->codec)
 			continue;
 
+		/* FIXME: Add Vendor Specific Codec match to SEP callback */
+		if (lsep->codec == A2DP_CODEC_VENDOR) {
+			a2dp_vendor_codec_t *vndcodec =
+						(void *) codec_data->data;
+
+			if (btohl(vndcodec->vendor_id) != lsep->vndcodec_vendor)
+				continue;
+
+			if (btohs(vndcodec->codec_id) != lsep->vndcodec_codec)
+				continue;
+		}
+
 		if (sep->stream == NULL)
 			return sep;
 	}
@@ -3349,6 +3365,13 @@
 	return sep;
 }
 
+void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
+							uint16_t codec_id)
+{
+	sep->vndcodec_vendor = vendor_id;
+	sep->vndcodec_codec = codec_id;
+}
+
 int avdtp_unregister_sep(struct avdtp_local_sep *sep)
 {
 	if (!sep)
diff --git a/android/avdtp.h b/android/avdtp.h
index 7fdf597..9d683c4 100644
--- a/android/avdtp.h
+++ b/android/avdtp.h
@@ -268,6 +268,8 @@
 						struct avdtp_sep_ind *ind,
 						struct avdtp_sep_cfm *cfm,
 						void *user_data);
+void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
+							uint16_t codec_id);
 
 /* Find a matching pair of local and remote SEP ID's */
 struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
index 036867e..3d318e7 100644
--- a/android/avrcp-lib.c
+++ b/android/avrcp-lib.c
@@ -96,6 +96,11 @@
 			uint16_t params_len, uint8_t *params, void *user_data);
 };
 
+struct avrcp_continuing {
+	uint8_t pdu_id;
+	struct iovec pdu;
+};
+
 struct avrcp {
 	struct avctp *conn;
 	struct avrcp_player *player;
@@ -103,6 +108,9 @@
 	const struct avrcp_control_handler *control_handlers;
 	void *control_data;
 	unsigned int control_id;
+	uint16_t control_mtu;
+
+	struct avrcp_continuing *continuing;
 
 	const struct avrcp_passthrough_handler *passthrough_handlers;
 	void *passthrough_data;
@@ -135,6 +143,12 @@
 	dst[2] = (src & 0x0000ff);
 }
 
+static void continuing_free(struct avrcp_continuing *continuing)
+{
+	g_free(continuing->pdu.iov_base);
+	g_free(continuing);
+}
+
 void avrcp_shutdown(struct avrcp *session)
 {
 	if (session->conn) {
@@ -153,6 +167,9 @@
 	if (session->destroy)
 		session->destroy(session->destroy_data);
 
+	if (session->continuing)
+		continuing_free(session->continuing);
+
 	g_free(session->player);
 	g_free(session);
 }
@@ -340,6 +357,14 @@
 							AVC_OP_VENDORDEP,
 							handle_vendordep_pdu,
 							session);
+	session->control_mtu = omtu - AVC_DATA_OFFSET;
+
+	/*
+	 * 27.1.2 AV/C Command Frame
+	 * An AV/C command frame contains up to 512 octets of data
+	 */
+	if (session->control_mtu > AVC_DATA_MTU)
+		session->control_mtu = AVC_DATA_MTU;
 
 	avctp_set_destroy_cb(session->conn, disconnect_cb, session);
 
@@ -544,18 +569,22 @@
 			if (values[i] < AVRCP_EQUALIZER_OFF ||
 						values[i] > AVRCP_EQUALIZER_ON)
 				return false;
+			break;
 		case AVRCP_ATTRIBUTE_REPEAT_MODE:
 			if (values[i] < AVRCP_REPEAT_MODE_OFF ||
 					values[i] > AVRCP_REPEAT_MODE_GROUP)
 				return false;
+			break;
 		case AVRCP_ATTRIBUTE_SHUFFLE:
 			if (values[i] < AVRCP_SHUFFLE_OFF ||
 					values[i] > AVRCP_SHUFFLE_GROUP)
 				return false;
+			break;
 		case AVRCP_ATTRIBUTE_SCAN:
 			if (values[i] < AVRCP_SCAN_OFF ||
 					values[i] > AVRCP_SCAN_GROUP)
 				return false;
+			break;
 		}
 	}
 
@@ -751,6 +780,158 @@
 							player->user_data);
 }
 
+static void continuing_new(struct avrcp *session, uint8_t pdu_id,
+					const struct iovec *iov, int iov_cnt,
+					size_t offset)
+{
+	struct avrcp_continuing *continuing;
+	int i;
+	size_t len = 0;
+
+	continuing = g_new0(struct avrcp_continuing, 1);
+	continuing->pdu_id = pdu_id;
+
+	for (i = 0; i < iov_cnt; i++) {
+		if (i == 0 && offset) {
+			len += iov[i].iov_len - offset;
+			continue;
+		}
+
+		len += iov[i].iov_len;
+	}
+
+	continuing->pdu.iov_base = g_malloc0(len);
+
+	DBG("len %zu", len);
+
+	for (i = 0; i < iov_cnt; i++) {
+		if (i == 0 && offset) {
+			memcpy(continuing->pdu.iov_base,
+						iov[i].iov_base + offset,
+						iov[i].iov_len - offset);
+			continuing->pdu.iov_len += iov[i].iov_len - offset;
+			continue;
+		}
+
+		memcpy(continuing->pdu.iov_base + continuing->pdu.iov_len,
+					iov[i].iov_base, iov[i].iov_len);
+		continuing->pdu.iov_len += iov[i].iov_len;
+	}
+
+	session->continuing = continuing;
+}
+
+static int avrcp_send_internal(struct avrcp *session, uint8_t transaction,
+					uint8_t code, uint8_t subunit,
+					uint8_t pdu_id, uint8_t type,
+					const struct iovec *iov, int iov_cnt)
+{
+	struct iovec pdu[iov_cnt + 1];
+	struct avrcp_header hdr;
+	int i;
+
+	/*
+	 * If a receiver receives a start fragment or non-fragmented AVRCP
+	 * Specific AV/C message when it already has an incomplete fragment
+	 * from that sender then the receiver shall consider the first PDU
+	 * aborted.
+	 */
+	if (session->continuing) {
+		continuing_free(session->continuing);
+		session->continuing = NULL;
+	}
+
+	memset(&hdr, 0, sizeof(hdr));
+
+	pdu[0].iov_base = &hdr;
+	pdu[0].iov_len = sizeof(hdr);
+
+	hdr.packet_type = type;
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i + 1].iov_base = iov[i].iov_base;
+
+		if (pdu[0].iov_len + hdr.params_len + iov[i].iov_len <=
+							session->control_mtu) {
+			pdu[i + 1].iov_len = iov[i].iov_len;
+			hdr.params_len += iov[i].iov_len;
+			if (hdr.packet_type != AVRCP_PACKET_TYPE_SINGLE)
+				hdr.packet_type = AVRCP_PACKET_TYPE_END;
+			continue;
+		}
+
+		/*
+		 * Only send what can fit and store the remaining in the
+		 * continuing iovec
+		 */
+		pdu[i + 1].iov_len = session->control_mtu -
+					(pdu[0].iov_len + hdr.params_len);
+		hdr.params_len += pdu[i + 1].iov_len;
+
+		continuing_new(session, pdu_id, &iov[i], iov_cnt - i,
+							pdu[i + 1].iov_len);
+
+		hdr.packet_type = hdr.packet_type != AVRCP_PACKET_TYPE_SINGLE ?
+						AVRCP_PACKET_TYPE_CONTINUING :
+						AVRCP_PACKET_TYPE_START;
+		break;
+	}
+
+	hton24(hdr.company_id, IEEEID_BTSIG);
+	hdr.pdu_id = pdu_id;
+	hdr.params_len = htons(hdr.params_len);
+
+	return avctp_send_vendor(session->conn, transaction, code, subunit,
+							pdu, iov_cnt + 1);
+}
+
+static ssize_t request_continuing(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct iovec iov;
+	int err;
+
+	DBG("");
+
+	if (!params || params_len != 1 || !session->continuing ||
+				session->continuing->pdu_id != params[0])
+		return -EINVAL;
+
+	iov.iov_base = session->continuing->pdu.iov_base;
+	iov.iov_len = session->continuing->pdu.iov_len;
+
+	DBG("len %zu", iov.iov_len);
+
+	session->continuing->pdu.iov_base = NULL;
+
+	err = avrcp_send_internal(session, transaction, AVC_CTYPE_STABLE,
+					AVC_SUBUNIT_PANEL, params[0],
+					AVRCP_PACKET_TYPE_CONTINUING, &iov, 1);
+
+	g_free(iov.iov_base);
+
+	if (err < 0)
+		return -EINVAL;
+
+	return -EAGAIN;
+}
+
+static ssize_t abort_continuing(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	DBG("");
+
+	if (!params || params_len != 1 || !session->continuing)
+		return -EINVAL;
+
+	continuing_free(session->continuing);
+	session->continuing = NULL;
+
+	return 0;
+}
+
 static const struct avrcp_control_handler player_handlers[] = {
 		{ AVRCP_GET_CAPABILITIES,
 					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
@@ -788,6 +969,12 @@
 		{ AVRCP_SET_ADDRESSED_PLAYER,
 					AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
 					set_addressed },
+		{ AVRCP_REQUEST_CONTINUING,
+					AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+					request_continuing },
+		{ AVRCP_ABORT_CONTINUING,
+					AVC_CTYPE_CONTROL, AVC_CTYPE_ACCEPTED,
+					abort_continuing },
 		{ },
 };
 
@@ -1049,28 +1236,8 @@
 					uint8_t subunit, uint8_t pdu_id,
 					const struct iovec *iov, int iov_cnt)
 {
-	struct iovec pdu[iov_cnt + 1];
-	struct avrcp_header hdr;
-	int i;
-
-	memset(&hdr, 0, sizeof(hdr));
-
-	pdu[0].iov_base = &hdr;
-	pdu[0].iov_len = sizeof(hdr);
-
-	for (i = 0; i < iov_cnt; i++) {
-		pdu[i + 1].iov_base = iov[i].iov_base;
-		pdu[i + 1].iov_len = iov[i].iov_len;
-		hdr.params_len += iov[i].iov_len;
-	}
-
-	hton24(hdr.company_id, IEEEID_BTSIG);
-	hdr.pdu_id = pdu_id;
-	hdr.packet_type = AVRCP_PACKET_TYPE_SINGLE;
-	hdr.params_len = htons(hdr.params_len);
-
-	return avctp_send_vendor(session->conn, transaction, code, subunit,
-							pdu, iov_cnt + 1);
+	return avrcp_send_internal(session, transaction, code, subunit, pdu_id,
+					AVRCP_PACKET_TYPE_SINGLE, iov, iov_cnt);
 }
 
 static int status2errno(uint8_t status)
@@ -1659,7 +1826,7 @@
 	 */
 	if (*number > AVRCP_ATTRIBUTE_LAST ||
 					1 + *number * 2 != pdu->params_len) {
-		number = 0;
+		*number = 0;
 		return -EPROTO;
 	}
 
diff --git a/android/bluetooth.c b/android/bluetooth.c
index 379d8ea..fc5d529 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -53,6 +53,40 @@
 #include "utils.h"
 #include "bluetooth.h"
 
+/*
+ * bits in bitmask as defined in table 6.3 of Multi Profile Specification
+ * HFP AG + A2DP SRC + AVRCP TG + PAN (NAP/PANU) + PBAP PSE
+ */
+#define MPS_DEFAULT_MPSD ((1ULL << 0) | (1ULL << 2) | (1ULL << 4) | \
+				(1ULL << 6) | (1ULL << 8) | (1ULL << 10) | \
+				(1ULL << 12) | (1ULL << 26) | (1ULL << 28) | \
+				(1ULL << 30) | (1ULL << 32) | (1ULL << 34) | \
+				(1ULL << 36))
+
+/*
+ * bits in bitmask as defined in table 6.4 of Multi Profile Specification
+ * HFP AG + A2DP SRC + AVRCP TG + PAN (NAP/PANU) + PBAP PSE
+ */
+#define MPS_DEFAULT_MPMD ((1ULL << 1) | (1ULL << 3) | (1ULL << 5) | \
+				(1ULL << 6) | (1ULL << 8) | (1ULL << 10) | \
+				(1ULL << 12) | (1ULL << 15) | (1ULL << 18))
+
+/*
+ * bits in bitmask as defined in table 6.5 of Multi Profile Specification
+ * Note that in this table spec starts bit positions from 1 (bit 0 unused?)
+ */
+#define MPS_DEFAULT_DEPS ((1 << 1) | (1 << 2) | (1 << 3))
+
+/* MPSD bit dependent on HFP AG support */
+#define MPS_MPSD_HFP_AG_DEP ((1ULL << 0) | (1ULL << 2) | (1ULL << 4) | \
+				(1ULL << 6) | (1ULL << 8) | (1ULL << 10) | \
+				(1ULL << 12) | (1ULL << 14) | (1ULL << 16) | \
+				(1ULL << 18) | (1ULL << 26) | (1ULL << 28) | \
+				(1ULL << 30))
+
+/* MPMD bit dependent on HFP AG support */
+#define MPS_MPMD_HFP_AG_DEP (1ULL << 6)
+
 #define DEFAULT_ADAPTER_NAME "BlueZ for Android"
 
 #define DUT_MODE_FILE "/sys/kernel/debug/bluetooth/hci%u/dut_mode"
@@ -116,6 +150,15 @@
 
 	bool found; /* if device is found in current discovery session */
 	unsigned int confirm_id; /* mgtm command id if command pending */
+
+	bool valid_remote_csrk;
+	uint8_t remote_csrk[16];
+	uint32_t remote_sign_cnt;
+
+	bool valid_local_csrk;
+	uint8_t local_csrk[16];
+	uint32_t local_sign_cnt;
+	uint16_t gatt_ccc;
 };
 
 struct browse_req {
@@ -136,6 +179,7 @@
 	uint32_t current_settings;
 	uint32_t supported_settings;
 
+	bool le_scanning;
 	uint8_t cur_discovery_type;
 	uint8_t exp_discovery_type;
 	uint32_t discoverable_timeout;
@@ -603,6 +647,51 @@
 	/* TODO: Gatt attrib set*/
 }
 
+void bt_store_gatt_ccc(const bdaddr_t *dst, uint16_t value)
+{
+	struct device *dev;
+	GKeyFile *key_file;
+	gsize length = 0;
+	char addr[18];
+	char *data;
+
+	dev = find_device(dst);
+	if (!dev)
+		return;
+
+	key_file = g_key_file_new();
+
+	if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	ba2str(dst, addr);
+
+	DBG("%s Gatt CCC %d", addr, value);
+
+	g_key_file_set_integer(key_file, addr, "GattCCC", value);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(DEVICES_FILE, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+
+	dev->gatt_ccc = value;
+}
+
+uint16_t bt_get_gatt_ccc(const bdaddr_t *addr)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return 0;
+
+	return dev->gatt_ccc;
+}
+
 static void store_link_key(const bdaddr_t *dst, const uint8_t *key,
 					uint8_t type, uint8_t pin_length)
 {
@@ -707,6 +796,12 @@
 	} else if (!paired && !dev->bredr_paired) {
 		bonded_devices = g_slist_remove(bonded_devices, dev);
 		remove_device_info(dev, DEVICES_FILE);
+		dev->valid_local_csrk = false;
+		dev->valid_remote_csrk = false;
+		dev->local_sign_cnt = 0;
+		dev->remote_sign_cnt = 0;
+		memset(dev->local_csrk, 0, sizeof(dev->local_csrk));
+		memset(dev->remote_csrk, 0, sizeof(dev->remote_csrk));
 		cache_device(dev);
 	}
 
@@ -751,14 +846,14 @@
 		send_bond_state_change(&dev->bdaddr, status, new_bond);
 }
 
-static  void send_device_property(const bdaddr_t *bdaddr, uint8_t type,
+static void send_device_property(struct device *dev, uint8_t type,
 						uint16_t len, const void *val)
 {
 	uint8_t buf[BASELEN_REMOTE_DEV_PROP + len];
 	struct hal_ev_remote_device_props *ev = (void *) buf;
 
 	ev->status = HAL_STATUS_SUCCESS;
-	bdaddr2android(bdaddr, ev->bdaddr);
+	bdaddr2android(&dev->bdaddr, ev->bdaddr);
 	ev->num_props = 1;
 	ev->props[0].type = type;
 	ev->props[0].len = len;
@@ -779,8 +874,7 @@
 		ptr += sizeof(uint128_t);
 	}
 
-	send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_UUIDS, sizeof(buf),
-									buf);
+	send_device_property(dev, HAL_PROP_DEVICE_UUIDS, sizeof(buf), buf);
 }
 
 static void set_device_uuids(struct device *dev, GSList *uuids)
@@ -970,7 +1064,7 @@
 
 static uint8_t get_device_name(struct device *dev)
 {
-	send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_NAME,
+	send_device_property(dev, HAL_PROP_DEVICE_NAME,
 						strlen(dev->name), dev->name);
 
 	return HAL_STATUS_SUCCESS;
@@ -1018,6 +1112,8 @@
 {
 	struct hal_ev_ssp_request ev;
 
+	memset(&ev, 0, sizeof(ev));
+
 	bdaddr2android(&dev->bdaddr, ev.bdaddr);
 	memcpy(ev.name, dev->name, strlen(dev->name));
 	ev.class_of_dev = dev->class;
@@ -1201,8 +1297,11 @@
 
 	adapter.cur_discovery_type = type;
 
-	if (ev->discovering)
+	if (ev->discovering) {
+		adapter.exp_discovery_type = adapter.le_scanning ?
+						SCAN_TYPE_LE : SCAN_TYPE_NONE;
 		return;
+	}
 
 	/* One shot notification about discovery stopped */
 	if (gatt_discovery_stopped_cb) {
@@ -1211,10 +1310,8 @@
 	}
 
 	type = adapter.exp_discovery_type;
-	adapter.exp_discovery_type = SCAN_TYPE_NONE;
-
-	if (type == SCAN_TYPE_NONE && gatt_device_found_cb)
-		type = SCAN_TYPE_LE;
+	adapter.exp_discovery_type = adapter.le_scanning ? SCAN_TYPE_LE :
+								SCAN_TYPE_NONE;
 
 	if (type != SCAN_TYPE_NONE)
 		start_discovery(type);
@@ -1296,6 +1393,17 @@
 	return get_device_android_type(dev);
 }
 
+bool bt_is_device_le(const bdaddr_t *addr)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return false;
+
+	return dev->le;
+}
+
 const char *bt_get_adapter_name(void)
 {
 	return adapter.name;
@@ -1309,6 +1417,19 @@
 	return false;
 }
 
+bool bt_device_set_uuids(const bdaddr_t *addr, GSList *uuids)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return false;
+
+	set_device_uuids(dev, uuids);
+
+	return true;
+}
+
 static bool rssi_above_threshold(int old, int new)
 {
 	/* only 8 dBm or more */
@@ -1503,7 +1624,8 @@
 		discoverable = eir.flags & (EIR_LIM_DISC | EIR_GEN_DISC);
 
 		gatt_device_found_cb(bdaddr, bdaddr_type, rssi, data_len, data,
-								discoverable);
+								discoverable,
+								dev->le_bonded);
 	}
 
 	if (!dev->bredr_paired && !dev->le_paired)
@@ -1796,6 +1918,142 @@
 	/* TODO browse services here? */
 }
 
+static void store_csrk(struct device *dev)
+{
+	GKeyFile *key_file;
+	char key_str[33];
+	char addr[18];
+	int i;
+	gsize length = 0;
+	char *data;
+
+	ba2str(&dev->bdaddr, addr);
+
+	key_file = g_key_file_new();
+	if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	if (dev->valid_local_csrk) {
+		for (i = 0; i < 16; i++)
+			sprintf(key_str + (i * 2), "%2.2X",
+							dev->local_csrk[i]);
+
+		g_key_file_set_string(key_file, addr, "LocalCSRK", key_str);
+	}
+
+	if (dev->valid_remote_csrk) {
+		for (i = 0; i < 16; i++)
+			sprintf(key_str + (i * 2), "%2.2X",
+							dev->remote_csrk[i]);
+
+		g_key_file_set_string(key_file, addr, "RemoteCSRK", key_str);
+	}
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(DEVICES_FILE, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+static void new_csrk_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_csrk *ev = param;
+	struct device *dev;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small csrk event (%u bytes)", length);
+		return;
+	}
+
+	ba2str(&ev->key.addr.bdaddr, dst);
+	dev = find_device(&ev->key.addr.bdaddr);
+	if (!dev)
+		return;
+
+	switch (ev->key.master) {
+	case 0x00:
+		memcpy(dev->local_csrk, ev->key.val, 16);
+		dev->local_sign_cnt = 0;
+		dev->valid_local_csrk = true;
+		break;
+	case 0x01:
+		memcpy(dev->remote_csrk, ev->key.val, 16);
+		dev->remote_sign_cnt = 0;
+		dev->valid_remote_csrk = true;
+		break;
+	default:
+		error("Unknown CSRK key type 02%02x", ev->key.master);
+		return;
+	}
+
+	update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
+							true, !!ev->store_hint);
+
+	if (ev->store_hint)
+		store_csrk(dev);
+}
+
+static void store_irk(struct device *dev, const uint8_t *val)
+{
+	GKeyFile *key_file;
+	char key_str[33];
+	char addr[18];
+	int i;
+	gsize length = 0;
+	char *data;
+
+	ba2str(&dev->bdaddr, addr);
+
+	key_file = g_key_file_new();
+	if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	for (i = 0; i < 16; i++)
+		sprintf(key_str + (i * 2), "%2.2X", val[i]);
+
+	g_key_file_set_string(key_file, addr, "IdentityResolvingKey", key_str);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(DEVICES_FILE, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+static void new_irk_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_irk *ev = param;
+	const struct mgmt_addr_info *addr = &ev->key.addr;
+	struct device *dev;
+	char dst[18], rpa[18];
+
+	if (length < sizeof(*ev)) {
+		error("To small New Irk Event (%u bytes)", length);
+		return;
+	}
+
+	ba2str(&ev->key.addr.bdaddr, dst);
+	ba2str(&ev->rpa, rpa);
+
+	DBG("new IRK for %s, RPA %s", dst, rpa);
+
+	/* TODO: handle new Identity to RPA mapping */
+	dev = find_device(&addr->bdaddr);
+	if (!dev)
+		return;
+
+	if (ev->store_hint)
+		store_irk(dev, ev->key.val);
+}
+
 static void register_mgmt_handlers(void)
 {
 	mgmt_register(mgmt_if, MGMT_EV_NEW_SETTINGS, adapter.index,
@@ -1845,6 +2103,12 @@
 
 	mgmt_register(mgmt_if, MGMT_EV_NEW_LONG_TERM_KEY, adapter.index,
 					new_long_term_key_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_NEW_CSRK, adapter.index,
+						new_csrk_callback, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_NEW_IRK, adapter.index, new_irk_callback,
+								NULL, NULL);
 }
 
 static void load_link_keys_complete(uint8_t status, uint16_t length,
@@ -1937,6 +2201,33 @@
 	g_free(cp);
 }
 
+static void load_irks(GSList *irks)
+{
+	struct mgmt_cp_load_irks *cp;
+	struct mgmt_irk_info *irk;
+	size_t irk_count, cp_size;
+	GSList *l;
+
+	irk_count = g_slist_length(irks);
+
+	DBG("irks %zu", irk_count);
+
+	cp_size = sizeof(*cp) + (irk_count * sizeof(*irk));
+
+	cp = g_malloc0(cp_size);
+
+	cp->irk_count = htobs(irk_count);
+
+	for (l = irks, irk = cp->irks; l != NULL; l = g_slist_next(l), irk++)
+		memcpy(irk, irks->data, sizeof(*irk));
+
+	if (mgmt_send(mgmt_if, MGMT_OP_LOAD_IRKS, adapter.index, cp_size, cp,
+							NULL, NULL, NULL) == 0)
+		error("Failed to load IRKs");
+
+	g_free(cp);
+}
+
 static uint8_t get_adapter_uuids(void)
 {
 	struct hal_ev_adapter_props_changed *ev;
@@ -2254,6 +2545,40 @@
 		dev->le_bonded = true;
 	}
 
+	str = g_key_file_get_string(key_file, peer, "LocalCSRK", NULL);
+	if (str) {
+		int i;
+
+		dev->valid_local_csrk = true;
+		for (i = 0; i < 16; i++)
+			sscanf(str + (i * 2), "%02hhX", &dev->local_csrk[i]);
+
+		g_free(str);
+
+		dev->local_sign_cnt = g_key_file_get_integer(key_file, peer,
+						"LocalCSRKSignCounter", NULL);
+	}
+
+	str = g_key_file_get_string(key_file, peer, "RemoteCSRK", NULL);
+	if (str) {
+		int i;
+
+		dev->valid_remote_csrk = true;
+		for (i = 0; i < 16; i++)
+			sscanf(str + (i * 2), "%02hhX", &dev->remote_csrk[i]);
+
+		g_free(str);
+
+		dev->remote_sign_cnt = g_key_file_get_integer(key_file, peer,
+						"RemoteCSRKSignCounter", NULL);
+	}
+
+	str = g_key_file_get_string(key_file, peer, "GattCCC", NULL);
+	if (str) {
+		dev->gatt_ccc = atoi(str);
+		g_free(str);
+	}
+
 	str = g_key_file_get_string(key_file, peer, "Name", NULL);
 	if (str) {
 		g_free(dev->name);
@@ -2312,8 +2637,6 @@
 
 	str2ba(peer, &info->addr.bdaddr);
 
-	info->addr.type = g_key_file_get_integer(key_file, peer, "Type", NULL);
-
 	for (i = 0; i < sizeof(info->val); i++)
 		sscanf(str + (i * 2), "%02hhX", &info->val[i]);
 
@@ -2350,7 +2673,8 @@
 
 	str2ba(peer, &info->addr.bdaddr);
 
-	info->addr.type = g_key_file_get_integer(key_file, peer, "Type", NULL);
+	info->addr.type = g_key_file_get_integer(key_file, peer, "AddressType",
+									NULL);
 
 	for (i = 0; i < sizeof(info->val); i++)
 		sscanf(key + (i * 2), "%02hhX", &info->val[i]);
@@ -2374,6 +2698,33 @@
 	return info;
 }
 
+static struct mgmt_irk_info *get_irk_info(GKeyFile *key_file, const char *peer)
+{
+	struct mgmt_irk_info *info = NULL;
+	unsigned int i;
+	char *str;
+
+	str = g_key_file_get_string(key_file, peer, "IdentityResolvingKey",
+									NULL);
+	if (!str || strlen(str) != 32)
+		goto failed;
+
+	info = g_new0(struct mgmt_irk_info, 1);
+
+	str2ba(peer, &info->addr.bdaddr);
+
+	info->addr.type = g_key_file_get_integer(key_file, peer, "AddressType",
+									NULL);
+
+	for (i = 0; i < sizeof(info->val); i++)
+		sscanf(str + (i * 2), "%02hhX", &info->val[i]);
+
+failed:
+	g_free(str);
+
+	return info;
+}
+
 static time_t device_timestamp(const struct device *dev)
 {
 	if (dev->bredr && dev->le) {
@@ -2431,6 +2782,7 @@
 	unsigned int i;
 	GSList *keys = NULL;
 	GSList *ltks = NULL;
+	GSList *irks = NULL;
 
 	key_file = g_key_file_new();
 
@@ -2441,10 +2793,12 @@
 	for (i = 0; i < len; i++) {
 		struct mgmt_link_key_info *key_info;
 		struct mgmt_ltk_info *ltk_info;
+		struct mgmt_irk_info *irk_info;
 		struct mgmt_ltk_info *slave_ltk_info;
 		struct device *dev;
 
 		key_info = get_key_info(key_file, devs[i]);
+		irk_info = get_irk_info(key_file, devs[i]);
 		ltk_info = get_ltk_info(key_file, devs[i], true);
 		slave_ltk_info = get_ltk_info(key_file, devs[i], false);
 
@@ -2457,6 +2811,9 @@
 		if (key_info)
 			keys = g_slist_prepend(keys, key_info);
 
+		if (irk_info)
+			irks = g_slist_prepend(irks, irk_info);
+
 		if (ltk_info)
 			ltks = g_slist_prepend(ltks, ltk_info);
 
@@ -2471,6 +2828,9 @@
 	load_ltks(ltks);
 	g_slist_free_full(ltks, g_free);
 
+	load_irks(irks);
+	g_slist_free_full(irks, g_free);
+
 	load_link_keys(keys, cb);
 	g_slist_free_full(keys, g_free);
 
@@ -2498,6 +2858,77 @@
 	error("Failed to set class of device");
 }
 
+static sdp_record_t *mps_record(void)
+{
+	sdp_data_t *mpsd_features, *mpmd_features, *dependencies;
+	sdp_list_t *svclass_id, *pfseq, *root;
+	uuid_t root_uuid, svclass_uuid;
+	sdp_profile_desc_t profile;
+	sdp_record_t *record;
+	uint64_t mpsd_feat, mpmd_feat;
+	uint16_t deps;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&svclass_uuid, MPS_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &svclass_uuid);
+	sdp_set_service_classes(record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, MPS_PROFILE_ID);
+	profile.version = 0x0100;
+	pfseq = sdp_list_append(NULL, &profile);
+	sdp_set_profile_descs(record, pfseq);
+
+	mpsd_feat = MPS_DEFAULT_MPSD;
+	mpmd_feat = MPS_DEFAULT_MPMD;
+
+	/* TODO should be configurable based on HFP AG support */
+	if (false) {
+		mpsd_feat &= MPS_MPSD_HFP_AG_DEP;
+		mpmd_feat &= MPS_MPMD_HFP_AG_DEP;
+	}
+
+	mpsd_features = sdp_data_alloc(SDP_UINT64, &mpsd_feat);
+	sdp_attr_add(record, SDP_ATTR_MPSD_SCENARIOS, mpsd_features);
+
+	mpmd_features = sdp_data_alloc(SDP_UINT64, &mpmd_feat);
+	sdp_attr_add(record, SDP_ATTR_MPMD_SCENARIOS, mpmd_features);
+
+	deps = MPS_DEFAULT_DEPS;
+	dependencies = sdp_data_alloc(SDP_UINT16, &deps);
+	sdp_attr_add(record, SDP_ATTR_MPS_DEPENDENCIES, dependencies);
+
+	sdp_set_info_attr(record, "Multi Profile", 0, 0);
+
+	sdp_list_free(pfseq, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static void add_mps_record(void)
+{
+	sdp_record_t *rec;
+
+	rec = mps_record();
+	if (!rec) {
+		error("Failed to allocate MPS record");
+		return;
+	}
+
+	if (bt_adapter_add_record(rec, 0) < 0) {
+		error("Failed to register MPS record");
+		sdp_record_free(rec);
+	}
+}
+
 static void read_info_complete(uint8_t status, uint16_t length,
 					const void *param, void *user_data)
 {
@@ -2558,6 +2989,7 @@
 
 	set_io_capability();
 	set_device_id();
+	add_mps_record();
 
 	missing_settings = adapter.current_settings ^
 						adapter.supported_settings;
@@ -3103,42 +3535,57 @@
 	return false;
 }
 
+bool bt_le_register(bt_le_device_found cb)
+{
+	if (gatt_device_found_cb)
+		return false;
+
+	gatt_device_found_cb = cb;
+
+	return true;
+}
+
+void bt_le_unregister(void)
+{
+	gatt_device_found_cb = NULL;
+}
+
 bool bt_le_discovery_stop(bt_le_discovery_stopped cb)
 {
+	if (!(adapter.current_settings & MGMT_SETTING_POWERED))
+		return false;
+
+	adapter.le_scanning = false;
+
 	if (adapter.cur_discovery_type != SCAN_TYPE_LE) {
 		if (cb)
 			cb();
 
-		gatt_device_found_cb = NULL;
-
 		return true;
 	}
 
 	if (!stop_discovery(SCAN_TYPE_LE))
 		return false;
 
-	gatt_device_found_cb = NULL;
 	gatt_discovery_stopped_cb = cb;
 	adapter.exp_discovery_type = SCAN_TYPE_NONE;
 
 	return true;
 }
 
-bool bt_le_discovery_start(bt_le_device_found cb)
+bool bt_le_discovery_start(void)
 {
 	if (!(adapter.current_settings & MGMT_SETTING_POWERED))
 		return false;
 
-	/* If core is discovering, don't bother */
-	if (adapter.cur_discovery_type != SCAN_TYPE_NONE) {
-		gatt_device_found_cb = cb;
-		return true;
-	}
+	adapter.le_scanning = true;
 
-	if (start_discovery(SCAN_TYPE_LE)) {
-		gatt_device_found_cb = cb;
+	/* If core is discovering, don't bother */
+	if (adapter.cur_discovery_type != SCAN_TYPE_NONE)
 		return true;
-	}
+
+	if (start_discovery(SCAN_TYPE_LE))
+		return true;
 
 	return false;
 }
@@ -3199,6 +3646,76 @@
 	return true;
 }
 
+bool bt_get_csrk(const bdaddr_t *addr, enum bt_csrk_type type, uint8_t key[16],
+							uint32_t *sign_cnt)
+{
+	struct device *dev;
+	bool local = (type == LOCAL_CSRK);
+
+	dev = find_device(addr);
+	if (!dev)
+		return false;
+
+	if (local && dev->valid_local_csrk) {
+		memcpy(key, dev->local_csrk, 16);
+		*sign_cnt = dev->local_sign_cnt;
+	} else if (!local && dev->valid_remote_csrk) {
+		memcpy(key, dev->remote_csrk, 16);
+		*sign_cnt = dev->remote_sign_cnt;
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+static void store_sign_counter(struct device *dev, enum bt_csrk_type type)
+{
+	const char *sign_cnt_s;
+	uint32_t sign_cnt;
+	GKeyFile *key_file;
+	bool local = (type == LOCAL_CSRK);
+
+	gsize length = 0;
+	char addr[18];
+	char *data;
+
+	key_file = g_key_file_new();
+	if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	ba2str(&dev->bdaddr, addr);
+
+	sign_cnt_s = local ? "LocalCSRKSignCounter" : "RemoteCSRKSignCounter";
+	sign_cnt = local ? dev->local_sign_cnt : dev->remote_sign_cnt;
+
+	g_key_file_set_integer(key_file, addr, sign_cnt_s, sign_cnt);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(DEVICES_FILE, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+void bt_update_sign_counter(const bdaddr_t *addr, enum bt_csrk_type type)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return;
+
+	if (type == LOCAL_CSRK)
+		dev->local_sign_cnt++;
+	else
+		dev->remote_sign_cnt++;
+
+	store_sign_counter(dev, type);
+}
+
 static uint8_t set_adapter_scan_mode(const void *buf, uint16_t len)
 {
 	const uint8_t *mode = buf;
@@ -3636,7 +4153,7 @@
 
 static uint8_t get_device_class(struct device *dev)
 {
-	send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_CLASS,
+	send_device_property(dev, HAL_PROP_DEVICE_CLASS,
 					sizeof(dev->class), &dev->class);
 
 	return HAL_STATUS_SUCCESS;
@@ -3646,8 +4163,7 @@
 {
 	uint8_t type = get_device_android_type(dev);
 
-	send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_TYPE,
-							sizeof(type), &type);
+	send_device_property(dev, HAL_PROP_DEVICE_TYPE, sizeof(type), &type);
 
 	return HAL_STATUS_SUCCESS;
 }
@@ -3666,7 +4182,7 @@
 	if (!dev->friendly_name)
 		return HAL_STATUS_FAILED;
 
-	send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_FRIENDLY_NAME,
+	send_device_property(dev, HAL_PROP_DEVICE_FRIENDLY_NAME,
 				strlen(dev->friendly_name), dev->friendly_name);
 
 	return HAL_STATUS_SUCCESS;
@@ -3677,7 +4193,7 @@
 	if (!dev->rssi)
 		return HAL_STATUS_FAILED;
 
-	send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_RSSI,
+	send_device_property(dev, HAL_PROP_DEVICE_RSSI,
 						sizeof(dev->rssi), &dev->rssi);
 
 	return HAL_STATUS_SUCCESS;
@@ -3698,7 +4214,7 @@
 
 	timestamp = device_timestamp(dev);
 
-	send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_TIMESTAMP,
+	send_device_property(dev, HAL_PROP_DEVICE_TIMESTAMP,
 						sizeof(timestamp), &timestamp);
 
 	return HAL_STATUS_SUCCESS;
diff --git a/android/bluetooth.h b/android/bluetooth.h
index 6a3e766..eb50fe1 100644
--- a/android/bluetooth.h
+++ b/android/bluetooth.h
@@ -21,6 +21,11 @@
  *
  */
 
+enum bt_csrk_type {
+	LOCAL_CSRK,
+	REMOTE_CSRK,
+};
+
 typedef void (*bt_bluetooth_ready)(int err, const bdaddr_t *addr);
 bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb);
 
@@ -37,8 +42,12 @@
 
 typedef void (*bt_le_device_found)(const bdaddr_t *addr, uint8_t addr_type,
 					int rssi, uint16_t eir_len,
-					const void *eir, bool discoverable);
-bool bt_le_discovery_start(bt_le_device_found cb);
+					const void *eir, bool discoverable,
+					bool bonded);
+bool bt_le_register(bt_le_device_found cb);
+void bt_le_unregister(void);
+
+bool bt_le_discovery_start(void);
 
 typedef void (*bt_le_discovery_stopped)(void);
 bool bt_le_discovery_stop(bt_le_discovery_stopped cb);
@@ -48,10 +57,22 @@
 							void *user_data);
 
 uint8_t bt_get_device_android_type(const bdaddr_t *addr);
+bool bt_is_device_le(const bdaddr_t *addr);
+
 const char *bt_get_adapter_name(void);
 bool bt_device_is_bonded(const bdaddr_t *bdaddr);
+bool bt_device_set_uuids(const bdaddr_t *bdaddr, GSList *uuids);
 
 typedef void (*bt_read_device_rssi_done)(uint8_t status, const bdaddr_t *addr,
 						int8_t rssi, void *user_data);
 bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb,
 							void *user_data);
+
+bool bt_get_csrk(const bdaddr_t *addr, enum bt_csrk_type type,
+					uint8_t key[16], uint32_t *sign_cnt);
+
+void bt_update_sign_counter(const bdaddr_t *addr, enum bt_csrk_type type);
+
+void bt_store_gatt_ccc(const bdaddr_t *addr, uint16_t value);
+
+uint16_t bt_get_gatt_ccc(const bdaddr_t *addr);
diff --git a/android/client/if-gatt.c b/android/client/if-gatt.c
index 252e89d..4b1c549 100644
--- a/android/client/if-gatt.c
+++ b/android/client/if-gatt.c
@@ -40,10 +40,11 @@
 #define MAX_READ_PARAMS_STR_LEN (MAX_SRVC_ID_STR_LEN + MAX_CHAR_ID_STR_LEN \
 		+ MAX_UUID_STR_LEN + MAX_HEX_VAL_STR_LEN + 80)
 
+/* Hex arguments must have "0x" or "0X" prefix */
 #define VERIFY_INT_ARG(n, v, err) \
 	do { \
 		if (n < argc) \
-			v = atoi(argv[n]); \
+			v = strtol(argv[n], NULL, 0); \
 		else { \
 			haltest_error(err); \
 			return;\
@@ -67,6 +68,7 @@
 #define VERIFY_TRANS_ID(n, v) VERIFY_INT_ARG(n, v, "No trans_id specified\n")
 #define VERIFY_STATUS(n, v) VERIFY_INT_ARG(n, v, "No status specified\n")
 #define VERIFY_OFFSET(n, v) VERIFY_INT_ARG(n, v, "No offset specified\n")
+#define VERIFY_TEST_ARG(n, v) VERIFY_INT_ARG(n, v, "No test arg specified\n")
 #define VERIFY_HANDLE(n, v) VERIFY_HEX_ARG(n, v, "No "#v" specified\n")
 #define VERIFY_SERVICE_HANDLE(n, v) VERIFY_HANDLE(n, v)
 
@@ -454,7 +456,7 @@
 	char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
 	char char_id_buf[MAX_CHAR_ID_STR_LEN];
 
-	haltest_info("%s: conn_id=%d status=%d srvc_id=%s char_id=%s, char_prop=%x\n",
+	haltest_info("%s: conn_id=%d status=%d srvc_id=%s char_id=%s, char_prop=0x%x\n",
 			__func__, conn_id, status,
 			btgatt_srvc_id_t2str(srvc_id, srvc_id_buf),
 			btgatt_gatt_id_t2str(char_id, char_id_buf), char_prop);
@@ -644,7 +646,7 @@
 
 	snprintf(server_if_str, sizeof(server_if_str), "%d", server_if);
 
-	haltest_info("%s: status=%d server_if=%d srvc_id=%s handle=%x\n",
+	haltest_info("%s: status=%d server_if=%d srvc_id=%s handle=0x%x\n",
 			__func__, status, server_if,
 			btgatt_srvc_id_t2str(srvc_id, buf), srvc_handle);
 }
@@ -654,7 +656,7 @@
 							int srvc_handle,
 							int incl_srvc_handle)
 {
-	haltest_info("%s: status=%d server_if=%d srvc_handle=%x inc_srvc_handle=%x\n",
+	haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x inc_srvc_handle=0x%x\n",
 						__func__, status, server_if,
 						srvc_handle, incl_srvc_handle);
 }
@@ -667,7 +669,7 @@
 {
 	char buf[MAX_SRVC_ID_STR_LEN];
 
-	haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=%x char_handle=%x\n",
+	haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=0x%x char_handle=0x%x\n",
 			__func__, status, server_if, gatt_uuid_t2str(uuid, buf),
 			srvc_handle, char_handle);
 }
@@ -679,7 +681,7 @@
 {
 	char buf[MAX_SRVC_ID_STR_LEN];
 
-	haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=%x descr_handle=%x\n",
+	haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=0x%x descr_handle=0x%x\n",
 			__func__, status, server_if, gatt_uuid_t2str(uuid, buf),
 			srvc_handle, descr_handle);
 }
@@ -687,21 +689,21 @@
 /* Callback invoked in response to start_service */
 static void gatts_service_started_cb(int status, int server_if, int srvc_handle)
 {
-	haltest_info("%s: status=%d server_if=%d srvc_handle=%x\n",
+	haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n",
 				__func__, status, server_if, srvc_handle);
 }
 
 /* Callback invoked in response to stop_service */
 static void gatts_service_stopped_cb(int status, int server_if, int srvc_handle)
 {
-	haltest_info("%s: status=%d server_if=%d srvc_handle=%x\n",
+	haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n",
 				__func__, status, server_if, srvc_handle);
 }
 
 /* Callback triggered when a service has been deleted */
 static void gatts_service_deleted_cb(int status, int server_if, int srvc_handle)
 {
-	haltest_info("%s: status=%d server_if=%d srvc_handle=%x\n",
+	haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n",
 				__func__, status, server_if, srvc_handle);
 }
 
@@ -715,7 +717,7 @@
 {
 	char buf[MAX_ADDR_STR_LEN];
 
-	haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=%x offset=%d is_long=%d\n",
+	haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=0x%x offset=%d is_long=%d\n",
 			__func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf),
 			attr_handle, offset, is_long);
 }
@@ -732,7 +734,7 @@
 	char buf[MAX_ADDR_STR_LEN];
 	char valbuf[100];
 
-	haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=%x offset=%d length=%d need_rsp=%d is_prep=%d value=%s\n",
+	haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=0x%x offset=%d length=%d need_rsp=%d is_prep=%d value=%s\n",
 			__func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf),
 			attr_handle, offset, length, need_rsp, is_prep,
 			array2str(value, length, valbuf, sizeof(valbuf)));
@@ -755,7 +757,7 @@
  */
 static void gatts_response_confirmation_cb(int status, int handle)
 {
-	haltest_info("%s: status=%d handle=%x\n", __func__, status, handle);
+	haltest_info("%s: status=%d handle=0x%x\n", __func__, status, handle);
 }
 
 static const btgatt_server_callbacks_t btgatt_server_callbacks = {
@@ -1405,7 +1407,7 @@
 	VERIFY_UUID(4, &uuid);
 
 	for (i = 5; i < argc; i++)
-		*u++ = atoi(argv[i]);
+		VERIFY_TEST_ARG(i, *u++);
 
 	EXEC(if_gatt->client->test_command, command, &params);
 }
@@ -1750,6 +1752,51 @@
 							len, confirm, data);
 }
 
+/*
+ * convert hex string to uint8_t array
+ */
+static int fill_buffer(const char *str, uint8_t *out, int out_size)
+{
+	int str_len;
+	int i, j;
+	char c;
+	uint8_t b;
+
+	str_len = strlen(str);
+
+	for (i = 0, j = 0; i < out_size && j < str_len; i++, j++) {
+		c = str[j];
+
+		if (c >= 'a' && c <= 'f')
+			c += 'A' - 'a';
+
+		if (c >= '0' && c <= '9')
+			b = c - '0';
+		else if (c >= 'A' && c <= 'F')
+			b = 10 + c - 'A';
+		else
+			return 0;
+
+		j++;
+
+		c = str[j];
+
+		if (c >= 'a' && c <= 'f')
+			c += 'A' - 'a';
+
+		if (c >= '0' && c <= '9')
+			b = b * 16 + c - '0';
+		else if (c >= 'A' && c <= 'F')
+			b = b * 16 + 10 + c - 'A';
+		else
+			return 0;
+
+		out[i] = b;
+	}
+
+	return i;
+}
+
 /* send_response */
 
 static void gatts_send_response_p(int argc, const char *argv[])
@@ -1772,15 +1819,23 @@
 	data.attr_value.auth_req = 0;
 	data.attr_value.len = 0;
 
-	if (argc <= 7) {
-		haltest_error("No data specified\n");
-		return;
-	}
+	if (argc > 7) {
+		const char *str;
 
-	data.attr_value.len = strlen(argv[7]);
-	scan_field(argv[7], data.attr_value.len, data.attr_value.value,
+		if (strncmp(argv[7], "0X", 2) && strncmp(argv[7], "0x", 2)) {
+			haltest_error("Value must be hex string");
+			return;
+		}
+
+		str = argv[7] + 2;
+
+		data.attr_value.len = fill_buffer(str, data.attr_value.value,
 						sizeof(data.attr_value.value));
-
+		if (data.attr_value.len == 0) {
+			haltest_error("Failed to parse response value");
+			return;
+		}
+	}
 
 	haltest_info("conn_id %d, trans_id %d, status %d", conn_id, trans_id,
 									status);
diff --git a/android/cts.txt b/android/cts.txt
new file mode 100644
index 0000000..bf351b1
--- /dev/null
+++ b/android/cts.txt
@@ -0,0 +1,32 @@
+Android Compatibility Test Suite results
+
+Tested: 10-June-2014
+Android version: 4.4.2
+
+-------------------------------------------------------------------------------
+android.bluetooth.cts.BasicAdapterTest (automated tests)
+Test Name				Result	Notes
+-------------------------------------------------------------------------------
+testAndroidTestCaseSetupProperly	PASS
+test_checkBluetoothAddress		PASS
+test_enableDisable			PASS
+test_getAddress				PASS
+test_getBondedDevices			PASS
+test_getDefaultAdapter			PASS
+test_getName				PASS
+test_getRemoteDevice			PASS
+test_listenUsingRfcommWithServiceRecord	PASS
+-------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------
+com.android.cts.verifier (manual tests)
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+Toggle Bluetooth	PASS
+Insecure Client		PASS
+Insecure Server		PASS	Required kernel patch commit ID:
+				6453462e99e8be13a2c8f2b3535f340b742d8192
+Secure Client		PASS
+Secure Server		PASS
+-------------------------------------------------------------------------------
diff --git a/android/cutils/properties.h b/android/cutils/properties.h
index 8096b19..ef40f01 100644
--- a/android/cutils/properties.h
+++ b/android/cutils/properties.h
@@ -22,6 +22,7 @@
  */
 
 #include <unistd.h>
+#include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/socket.h>
diff --git a/android/gatt.c b/android/gatt.c
index 8e0d72a..32853fa 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -57,6 +57,19 @@
 
 #define BASE_UUID16_OFFSET     12
 
+#define GATT_PERM_READ			0x00000001
+#define GATT_PERM_READ_ENCRYPTED	0x00000002
+#define GATT_PERM_READ_MITM		0x00000004
+#define GATT_PERM_READ_AUTHORIZATION	0x00000008
+#define GATT_PERM_WRITE			0x00000100
+#define GATT_PERM_WRITE_ENCRYPTED	0x00000200
+#define GATT_PERM_WRITE_MITM		0x00000400
+#define GATT_PERM_WRITE_AUTHORIZATION	0x00000800
+#define GATT_PERM_WRITE_SIGNED		0x00010000
+#define GATT_PERM_WRITE_SIGNED_MITM	0x00020000
+
+#define GATT_CONN_TIMEOUT 2
+
 static const uint8_t BLUETOOTH_UUID[] = {
 	0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
 	0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
@@ -76,11 +89,6 @@
 	"CONNECTED",
 };
 
-typedef enum {
-	APP_CLIENT,
-	APP_SERVER,
-} gatt_app_type_t;
-
 struct pending_trans_data {
 	unsigned int id;
 	uint8_t opcode;
@@ -90,13 +98,12 @@
 	int32_t id;
 	uint8_t uuid[16];
 
-	gatt_app_type_t type;
+	gatt_type_t type;
 
 	/* Valid for client applications */
 	struct queue *notifications;
 
-	/* Transaction data valid for server application */
-	struct pending_trans_data trans_id;
+	gatt_conn_cb_t func;
 };
 
 struct element_id {
@@ -149,8 +156,6 @@
 	struct queue *services;
 	bool partial_srvc_search;
 
-	bool notify_services_changed;
-
 	guint watch_id;
 	guint server_id;
 
@@ -163,7 +168,12 @@
 struct app_connection {
 	struct gatt_device *device;
 	struct gatt_app *app;
+	struct queue *transactions;
 	int32_t id;
+
+	guint timeout_id;
+
+	bool wait_execute_write;
 };
 
 static struct ipc *hal_ipc = NULL;
@@ -178,8 +188,18 @@
 static struct queue *listen_apps = NULL;
 static struct gatt_db *gatt_db = NULL;
 
+static uint16_t service_changed_handle = 0;
+
 static GIOChannel *listening_io = NULL;
 
+static struct bt_crypto *crypto = NULL;
+
+static int test_client_if = 0;
+static const uint8_t TEST_UUID[] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04
+};
+
 static void bt_le_discovery_stop_cb(void);
 
 static bool is_bluetooth_uuid(const uint8_t *uuid)
@@ -364,8 +384,14 @@
 
 static struct app_connection *find_connection_by_id(int32_t conn_id)
 {
-	return queue_find(app_connections, match_connection_by_id,
+	struct app_connection *conn;
+
+	conn = queue_find(app_connections, match_connection_by_id,
 							INT_TO_PTR(conn_id));
+	if (conn && conn->device->state == DEVICE_CONNECTED)
+		return conn;
+
+	return NULL;
 }
 
 static bool match_connection_by_device(const void *data, const void *user_data)
@@ -386,7 +412,7 @@
 
 static struct gatt_device *find_device_by_addr(const bdaddr_t *addr)
 {
-	return queue_find(gatt_devices, match_device_by_bdaddr, (void *)addr);
+	return queue_find(gatt_devices, match_device_by_bdaddr, addr);
 }
 
 static struct gatt_device *find_pending_device()
@@ -553,11 +579,12 @@
 		device->att_io = NULL;
 	}
 
-	if (device->server_id > 0)
-		g_attrib_unregister(device->attrib, device->server_id);
-
 	if (device->attrib) {
 		GAttrib *attrib = device->attrib;
+
+		if (device->server_id > 0)
+			g_attrib_unregister(device->attrib, device->server_id);
+
 		device->attrib = NULL;
 		g_attrib_cancel_all(attrib);
 		g_attrib_unref(attrib);
@@ -591,7 +618,7 @@
 	 * too. So remove all elements and then destroy queue.
 	 */
 
-	if (app->type == APP_CLIENT)
+	if (app->type == GATT_CLIENT)
 		while (queue_peek_head(app->notifications)) {
 			struct notification_data *notification;
 
@@ -604,160 +631,11 @@
 	free(app);
 }
 
-static int register_app(const uint8_t *uuid, gatt_app_type_t app_type)
-{
-	static int32_t application_id = 1;
-	struct gatt_app *app;
-
-	if (queue_find(gatt_apps, match_app_by_uuid, (void *) uuid)) {
-		error("gatt: app uuid is already on list");
-		return 0;
-	}
-
-	app = new0(struct gatt_app, 1);
-	if (!app) {
-		error("gatt: Cannot allocate memory for registering app");
-		return 0;
-	}
-
-	app->type = app_type;
-
-	if (app->type == APP_CLIENT) {
-		app->notifications = queue_new();
-		if (!app->notifications) {
-			error("gatt: couldn't allocate notifications queue");
-			destroy_gatt_app(app);
-			return 0;
-		}
-	}
-
-	memcpy(app->uuid, uuid, sizeof(app->uuid));
-
-	app->id = application_id++;
-
-	if (!queue_push_head(gatt_apps, app)) {
-		error("gatt: Cannot push app on the list");
-		destroy_gatt_app(app);
-		return 0;
-	}
-
-	if ((app->type == APP_SERVER) &&
-			!queue_push_tail(listen_apps, INT_TO_PTR(app->id))) {
-		error("gatt: Cannot push server on the list");
-		destroy_gatt_app(app);
-		return 0;
-	}
-
-	return app->id;
-}
-
-static void handle_client_register(const void *buf, uint16_t len)
-{
-	const struct hal_cmd_gatt_client_register *cmd = buf;
-	struct hal_ev_gatt_client_register_client ev;
-
-	DBG("");
-
-	memset(&ev, 0, sizeof(ev));
-
-	ev.client_if = register_app(cmd->uuid, APP_CLIENT);
-
-	if (ev.client_if)
-		ev.status = GATT_SUCCESS;
-	else
-		ev.status = GATT_FAILURE;
-
-	/* We should send notification with given in cmd UUID */
-	memcpy(ev.app_uuid, cmd->uuid, sizeof(ev.app_uuid));
-
-	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
-			HAL_EV_GATT_CLIENT_REGISTER_CLIENT, sizeof(ev), &ev);
-
-	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER,
-							HAL_STATUS_SUCCESS);
-}
-
-static void send_client_disconnection_notify(struct app_connection *connection,
-								int32_t status)
-{
-	struct hal_ev_gatt_client_disconnect ev;
-
-	ev.client_if = connection->app->id;
-	ev.conn_id = connection->id;
-	ev.status = status;
-
-	bdaddr2android(&connection->device->bdaddr, &ev.bda);
-
-	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
-				HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev);
-}
-
-static void send_client_connection_notify(struct app_connection *connection,
-								int32_t status)
-{
-	struct hal_ev_gatt_client_connect ev;
-
-	ev.client_if = connection->app->id;
-	ev.conn_id = connection->id;
-	ev.status = status;
-
-	bdaddr2android(&connection->device->bdaddr, &ev.bda);
-
-	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_CONNECT,
-							sizeof(ev), &ev);
-}
-
-static void send_server_connection_notify(struct app_connection *connection,
-								bool connected)
-{
-	struct hal_ev_gatt_server_connection ev;
-
-	ev.server_if = connection->app->id;
-	ev.conn_id = connection->id;
-	ev.connected = connected;
-
-	bdaddr2android(&connection->device->bdaddr, &ev.bdaddr);
-
-	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
-				HAL_EV_GATT_SERVER_CONNECTION, sizeof(ev), &ev);
-}
-
-static void send_app_disconnect_notify(struct app_connection *connection,
-								int32_t status)
-{
-	if (connection->app->type == APP_CLIENT)
-		send_client_disconnection_notify(connection, status);
-	else
-		send_server_connection_notify(connection, !!status);
-}
-
-static void send_app_connect_notify(struct app_connection *connection,
-								int32_t status)
-{
-	if (connection->app->type == APP_CLIENT)
-		send_client_connection_notify(connection, status);
-	else
-		send_server_connection_notify(connection, !status);
-}
-
-static void disconnect_notify_by_device(void *data, void *user_data)
-{
-	struct app_connection *conn = data;
-	struct gatt_device *dev = user_data;
-
-	if (dev != conn->device)
-		return;
-
-	if (dev->state == DEVICE_CONNECTED)
-		send_app_disconnect_notify(conn, GATT_SUCCESS);
-	else if (dev->state == DEVICE_CONNECT_INIT ||
-					dev->state == DEVICE_CONNECT_READY)
-		send_app_connect_notify(conn, GATT_FAILURE);
-}
-
-#define READ_INIT -3
-#define READ_PENDING -2
-#define READ_FAILED -1
+enum pend_req_state {
+	REQUEST_INIT,
+	REQUEST_PENDING,
+	REQUEST_DONE,
+};
 
 struct pending_request {
 	uint16_t handle;
@@ -767,6 +645,9 @@
 
 	uint8_t *filter_value;
 	uint16_t filter_vlen;
+
+	enum pend_req_state state;
+	uint8_t error;
 };
 
 static void destroy_pending_request(void *data)
@@ -812,10 +693,152 @@
 	destroy_device(device);
 }
 
+static struct gatt_device *create_device(const bdaddr_t *addr)
+{
+	struct gatt_device *dev;
+
+	dev = new0(struct gatt_device, 1);
+	if (!dev)
+		return NULL;
+
+	bacpy(&dev->bdaddr, addr);
+
+	dev->services = queue_new();
+	if (!dev->services) {
+		error("gatt: Failed to allocate memory for client");
+		destroy_device(dev);
+		return NULL;
+	}
+
+
+	dev->pending_requests = queue_new();
+	if (!dev->pending_requests) {
+		error("gatt: Failed to allocate memory for client");
+		destroy_device(dev);
+		return NULL;
+	}
+
+	if (!queue_push_head(gatt_devices, dev)) {
+		error("gatt: Cannot push device to queue");
+		destroy_device(dev);
+		return NULL;
+	}
+
+	return device_ref(dev);
+}
+
+static void send_client_connection_notify(struct app_connection *connection,
+								int32_t status)
+{
+	struct hal_ev_gatt_client_connect ev;
+
+	if (connection->app->func) {
+		connection->app->func(&connection->device->bdaddr,
+					status == GATT_SUCCESS ? 0 : -ENOTCONN,
+					connection->device->attrib);
+		return;
+	}
+
+	ev.client_if = connection->app->id;
+	ev.conn_id = connection->id;
+	ev.status = status;
+
+	bdaddr2android(&connection->device->bdaddr, &ev.bda);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_CONNECT,
+							sizeof(ev), &ev);
+}
+
+static void send_server_connection_notify(struct app_connection *connection,
+								bool connected)
+{
+	struct hal_ev_gatt_server_connection ev;
+
+	if (connection->app->func) {
+		connection->app->func(&connection->device->bdaddr,
+					connected ? 0 : -ENOTCONN,
+					connection->device->attrib);
+		return;
+	}
+
+	ev.server_if = connection->app->id;
+	ev.conn_id = connection->id;
+	ev.connected = connected;
+
+	bdaddr2android(&connection->device->bdaddr, &ev.bdaddr);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_EV_GATT_SERVER_CONNECTION, sizeof(ev), &ev);
+}
+
+static void send_client_disconnection_notify(struct app_connection *connection,
+								int32_t status)
+{
+	struct hal_ev_gatt_client_disconnect ev;
+
+	if (connection->app->func) {
+		connection->app->func(&connection->device->bdaddr, -ENOTCONN,
+						connection->device->attrib);
+		return;
+	}
+
+	ev.client_if = connection->app->id;
+	ev.conn_id = connection->id;
+	ev.status = status;
+
+	bdaddr2android(&connection->device->bdaddr, &ev.bda);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev);
+
+}
+
+static void send_app_disconnect_notify(struct app_connection *connection,
+								int32_t status)
+{
+	if (!connection->app)
+		return;
+
+	if (connection->app->type == GATT_CLIENT)
+		send_client_disconnection_notify(connection, status);
+	else
+		send_server_connection_notify(connection, !!status);
+}
+
+static void send_app_connect_notify(struct app_connection *connection,
+								int32_t status)
+{
+	if (!connection->app)
+		return;
+
+	if (connection->app->type == GATT_CLIENT)
+		send_client_connection_notify(connection, status);
+	else if (connection->app->type == GATT_SERVER)
+		send_server_connection_notify(connection, !status);
+}
+
+static void disconnect_notify_by_device(void *data, void *user_data)
+{
+	struct app_connection *conn = data;
+	struct gatt_device *dev = user_data;
+
+	if (dev != conn->device || !conn->app)
+		return;
+
+	if (dev->state == DEVICE_CONNECTED)
+		send_app_disconnect_notify(conn, GATT_SUCCESS);
+	else if (dev->state == DEVICE_CONNECT_INIT ||
+					dev->state == DEVICE_CONNECT_READY)
+		send_app_connect_notify(conn, GATT_FAILURE);
+}
+
 static void destroy_connection(void *data)
 {
 	struct app_connection *conn = data;
 
+	if (conn->timeout_id > 0)
+		g_source_remove(conn->timeout_id);
+
 	if (!queue_find(gatt_devices, match_by_value, conn->device))
 		goto cleanup;
 
@@ -824,6 +847,7 @@
 		connection_cleanup(conn->device);
 
 cleanup:
+	queue_destroy(conn->transactions, free);
 	device_unref(conn->device);
 	free(conn);
 }
@@ -838,23 +862,158 @@
 							destroy_connection);
 }
 
-static void send_client_primary_notify(void *data, void *user_data)
+static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
 {
-	struct hal_ev_gatt_client_search_result ev;
-	struct service *p = data;
-	int32_t conn_id = PTR_TO_INT(user_data);
+	struct gatt_device *dev = user_data;
+	int sock, err = 0;
+	socklen_t len;
 
-	/* In service queue we will have also included services */
-	if (!p->primary)
+	sock = g_io_channel_unix_get_fd(io);
+	len = sizeof(err);
+	if (!getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len))
+		DBG("%s (%d)", strerror(err), err);
+
+	device_disconnect_clients(dev);
+
+	return FALSE;
+}
+
+static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data);
+
+static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	struct gatt_device *device = user_data;
+	GIOChannel *io;
+	GError *gerr = NULL;
+	uint16_t rmtu, mtu, imtu;
+
+	if (status) {
+		error("gatt: MTU exchange: %s", att_ecode2str(status));
+		goto failed;
+	}
+
+	if (!dec_mtu_resp(pdu, plen, &rmtu)) {
+		error("gatt: MTU exchange: protocol error");
+		goto failed;
+	}
+
+	if (rmtu < ATT_DEFAULT_LE_MTU) {
+		error("gatt: MTU exchange: mtu error");
+		goto failed;
+	}
+
+	io = g_attrib_get_channel(device->attrib);
+
+	bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("gatt: Could not get imtu: %s", gerr->message);
+		g_error_free(gerr);
+
+		return;
+	}
+
+	mtu = MIN(rmtu, imtu);
+	if (mtu != imtu && !g_attrib_set_mtu(device->attrib, mtu)) {
+		error("gatt: MTU exchange failed");
+		goto failed;
+	}
+
+	DBG("MTU exchange succeeded: rmtu:%d, old mtu:%d, new mtu:%d", rmtu,
+								imtu, mtu);
+
+failed:
+	device_unref(device);
+}
+
+static void send_exchange_mtu_request(struct gatt_device *device)
+{
+	GIOChannel *io;
+	GError *gerr = NULL;
+	uint16_t imtu;
+
+	io = g_attrib_get_channel(device->attrib);
+
+	bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("gatt: Could not get imtu: %s", gerr->message);
+		g_error_free(gerr);
+
+		return;
+	}
+
+	if (!gatt_exchange_mtu(device->attrib, imtu, exchange_mtu_cb,
+							device_ref(device)))
+		device_unref(device);
+}
+
+static void notify_att_range_change(struct gatt_device *dev,
+							struct att_range *range)
+{
+	uint16_t length = 0;
+	uint16_t ccc;
+	uint8_t *pdu;
+	size_t mtu;
+
+	ccc = bt_get_gatt_ccc(&dev->bdaddr);
+	if (!ccc)
 		return;
 
-	ev.conn_id  = conn_id;
-	element_id_to_hal_srvc_id(&p->id, 1, &ev.srvc_id);
+	pdu = g_attrib_get_buffer(dev->attrib, &mtu);
 
-	uuid2android(&p->id.uuid, ev.srvc_id.uuid);
+	switch (ccc) {
+	case 0x0001:
+		length = enc_notification(service_changed_handle,
+						(uint8_t *) range,
+						sizeof(*range), pdu, mtu);
+		break;
+	case 0x0002:
+		length = enc_indication(service_changed_handle,
+					(uint8_t *) range, sizeof(*range), pdu,
+					mtu);
+		break;
+	default:
+		/* 0xfff4 reserved for future use */
+		break;
+	}
 
-	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
-			HAL_EV_GATT_CLIENT_SEARCH_RESULT, sizeof(ev), &ev);
+	if (length)
+		g_attrib_send(dev->attrib, 0, pdu, length, NULL, NULL, NULL);
+}
+
+static struct app_connection *create_connection(struct gatt_device *device,
+						struct gatt_app *app)
+{
+	struct app_connection *new_conn;
+	static int32_t last_conn_id = 1;
+
+	/* Check if already connected */
+	new_conn = new0(struct app_connection, 1);
+	if (!new_conn)
+		return NULL;
+
+	/* Make connection id unique to connection record (app, device) pair */
+	new_conn->app = app;
+	new_conn->id = last_conn_id++;
+
+	new_conn->transactions = queue_new();
+	if (!new_conn->transactions) {
+		free(new_conn);
+		return NULL;
+	}
+
+	if (!queue_push_head(app_connections, new_conn)) {
+		error("gatt: Cannot push client on the client queue!?");
+		queue_destroy(new_conn->transactions, free);
+		free(new_conn);
+		return NULL;
+	}
+
+	new_conn->device = device_ref(device);
+	new_conn->device->conn_cnt++;
+
+	return new_conn;
 }
 
 static struct service *create_service(uint8_t id, bool primary, char *uuid,
@@ -904,64 +1063,252 @@
 	return s;
 }
 
-static void le_device_found_handler(const bdaddr_t *addr, uint8_t addr_type,
-						int rssi, uint16_t eir_len,
-							const void *eir,
-							bool discoverable)
+static void send_client_primary_notify(void *data, void *user_data)
 {
-	uint8_t buf[IPC_MTU];
-	struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
-	struct gatt_device *dev;
-	char bda[18];
+	struct hal_ev_gatt_client_search_result ev;
+	struct service *p = data;
+	int32_t conn_id = PTR_TO_INT(user_data);
 
-	if (!scanning || !discoverable)
-		goto connect;
-
-	ba2str(addr, bda);
-	DBG("LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi, !!eir);
-
-	bdaddr2android(addr, ev->bda);
-	ev->rssi = rssi;
-	ev->len = eir_len;
-
-	memcpy(ev->adv_data, eir, ev->len);
-
-	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
-						HAL_EV_GATT_CLIENT_SCAN_RESULT,
-						sizeof(*ev) + ev->len, ev);
-
-connect:
-	dev = find_device_by_addr(addr);
-	if (!dev || (dev->state != DEVICE_CONNECT_INIT))
+	/* In service queue we will have also included services */
+	if (!p->primary)
 		return;
 
-	device_set_state(dev, DEVICE_CONNECT_READY);
-	dev->bdaddr_type = addr_type;
+	ev.conn_id  = conn_id;
+	element_id_to_hal_srvc_id(&p->id, 1, &ev.srvc_id);
 
-	/*
-	 * We are ok to perform connect now. Stop discovery
-	 * and once it is stopped continue with creating ACL
-	 */
-	bt_le_discovery_stop(bt_le_discovery_stop_cb);
+	uuid2android(&p->id.uuid, ev.srvc_id.uuid);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_SEARCH_RESULT, sizeof(ev), &ev);
 }
 
-static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond,
-							gpointer user_data)
+static void send_client_search_complete_notify(int32_t status, int32_t conn_id)
 {
-	struct gatt_device *dev = user_data;
-	int sock, err = 0;
-	socklen_t len;
+	struct hal_ev_gatt_client_search_complete ev;
 
-	sock = g_io_channel_unix_get_fd(io);
-	len = sizeof(err);
-	if (!getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len))
-		DBG("%s (%d)", strerror(err), err);
+	ev.status = status;
+	ev.conn_id = conn_id;
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_SEARCH_COMPLETE, sizeof(ev), &ev);
+}
 
-	device_disconnect_clients(dev);
+struct discover_srvc_data {
+	bt_uuid_t uuid;
+	struct app_connection *conn;
+};
+
+static void discover_srvc_by_uuid_cb(uint8_t status, GSList *ranges,
+								void *user_data)
+{
+	struct discover_srvc_data *cb_data = user_data;
+	struct gatt_primary prim;
+	struct service *s;
+	int32_t gatt_status;
+	struct gatt_device *dev = cb_data->conn->device;
+	uint8_t instance_id = queue_length(dev->services);
+
+	DBG("Status %d", status);
+
+	if (status) {
+		error("gatt: Discover pri srvc filtered by uuid failed: %s",
+							att_ecode2str(status));
+		gatt_status = GATT_FAILURE;
+		goto reply;
+	}
+
+	if (!ranges) {
+		info("gatt: No primary services searched by uuid found");
+		gatt_status = GATT_SUCCESS;
+		goto reply;
+	}
+
+	bt_uuid_to_string(&cb_data->uuid, prim.uuid, sizeof(prim.uuid));
+	/*
+	 * If multiple instances of the same service (as identified by UUID)
+	 * exist, the first instance of the service is returned.
+	 */
+	memcpy(&prim.range, ranges->data, sizeof(prim.range));
+
+	s = create_service(instance_id++, true, prim.uuid, &prim);
+	if (!s) {
+		gatt_status = GATT_FAILURE;
+		goto reply;
+	}
+
+	if (!queue_push_tail(dev->services, s)) {
+		error("gatt: Cannot push primary service to the list");
+		gatt_status = GATT_FAILURE;
+		goto reply;
+	}
+
+	send_client_primary_notify(s, INT_TO_PTR(cb_data->conn->id));
+
+	DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
+		prim.range.start, prim.range.end, prim.uuid);
+
+	/* Partial search service scanning was performed */
+	dev->partial_srvc_search = true;
+	gatt_status = GATT_SUCCESS;
+
+reply:
+	send_client_search_complete_notify(gatt_status, cb_data->conn->id);
+	free(cb_data);
+}
+
+static void discover_srvc_all_cb(uint8_t status, GSList *services,
+								void *user_data)
+{
+	struct discover_srvc_data *cb_data = user_data;
+	struct gatt_device *dev = cb_data->conn->device;
+	int32_t gatt_status;
+	GSList *l;
+	/*
+	 * There might be multiply services with same uuid. Therefore make sure
+	 * each primary service one has unique instance_id
+	 */
+	uint8_t instance_id = queue_length(dev->services);
+
+	DBG("Status %d", status);
+
+	if (status) {
+		error("gatt: Discover all primary services failed: %s",
+							att_ecode2str(status));
+		gatt_status = GATT_FAILURE;
+		goto reply;
+	}
+
+	if (!services) {
+		info("gatt: No primary services found");
+		gatt_status = GATT_SUCCESS;
+		goto reply;
+	}
+
+	for (l = services; l; l = l->next) {
+		struct gatt_primary *prim = l->data;
+		struct service *p;
+
+		if (queue_find(dev->services, match_srvc_by_range,
+								&prim->range))
+			continue;
+
+		p = create_service(instance_id++, true, prim->uuid, prim);
+		if (!p)
+			continue;
+
+		if (!queue_push_tail(dev->services, p)) {
+			error("gatt: Cannot push primary service to the list");
+			free(p);
+			continue;
+		}
+
+		DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
+			prim->range.start, prim->range.end, prim->uuid);
+	}
+
+	/*
+	 * Send all found services notifications - first cache,
+	 * then send notifies
+	 */
+	queue_foreach(dev->services, send_client_primary_notify,
+						INT_TO_PTR(cb_data->conn->id));
+
+	/* Full search service scanning was performed */
+	dev->partial_srvc_search = false;
+	gatt_status = GATT_SUCCESS;
+
+reply:
+	send_client_search_complete_notify(gatt_status, cb_data->conn->id);
+	free(cb_data);
+}
+static gboolean connection_timeout(void *user_data)
+{
+	struct app_connection *conn = user_data;
+
+	conn->timeout_id = 0;
+
+	queue_remove(app_connections, conn);
+	destroy_connection(conn);
 
 	return FALSE;
 }
 
+static void discover_primary_cb(uint8_t status, GSList *services,
+								void *user_data)
+{
+	struct discover_srvc_data *cb_data = user_data;
+	struct app_connection *conn = cb_data->conn;
+	struct gatt_device *dev = conn->device;
+	GSList *l, *uuids = NULL;
+
+	DBG("Status %d", status);
+
+	if (status) {
+		error("gatt: Discover all primary services failed: %s",
+							att_ecode2str(status));
+		free(cb_data);
+
+		return;
+	}
+
+	if (!services) {
+		info("gatt: No primary services found");
+		free(cb_data);
+
+		return;
+	}
+
+	for (l = services; l; l = l->next) {
+		struct gatt_primary *prim = l->data;
+		uint8_t *new_uuid;
+		bt_uuid_t uuid;
+
+		DBG("uuid: %s", prim->uuid);
+
+		if (bt_string_to_uuid(&uuid, prim->uuid) < 0) {
+			error("gatt: Cannot convert string to uuid");
+			continue;
+		}
+
+		new_uuid = g_memdup(&uuid.value.u128, sizeof(uuid.value.u128));
+
+		uuids = g_slist_prepend(uuids, new_uuid);
+	}
+
+	bt_device_set_uuids(&dev->bdaddr, uuids);
+
+	free(cb_data);
+
+	conn->timeout_id = g_timeout_add_seconds(GATT_CONN_TIMEOUT,
+						connection_timeout, conn);
+}
+
+static guint search_dev_for_srvc(struct app_connection *conn, bt_uuid_t *uuid)
+{
+	struct discover_srvc_data *cb_data =
+					new0(struct discover_srvc_data, 1);
+
+	if (!cb_data) {
+		error("gatt: Cannot allocate cb data");
+		return 0;
+	}
+
+	cb_data->conn = conn;
+
+	if (uuid) {
+		memcpy(&cb_data->uuid, uuid, sizeof(cb_data->uuid));
+		return gatt_discover_primary(conn->device->attrib, uuid,
+					discover_srvc_by_uuid_cb, cb_data);
+	}
+
+
+	if (conn->app)
+		return gatt_discover_primary(conn->device->attrib, NULL,
+						discover_srvc_all_cb, cb_data);
+
+	return gatt_discover_primary(conn->device->attrib, NULL,
+						discover_primary_cb, cb_data);
+}
+
 struct connect_data {
 	struct gatt_device *dev;
 	int32_t status;
@@ -976,12 +1323,11 @@
 		send_app_connect_notify(conn, con_data->status);
 }
 
-static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data);
-
 static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
 {
 	struct gatt_device *dev = user_data;
 	struct connect_data data;
+	struct att_range range;
 	uint32_t status;
 	GAttrib *attrib;
 
@@ -991,8 +1337,10 @@
 		return;
 	}
 
-	g_io_channel_unref(dev->att_io);
-	dev->att_io = NULL;
+	if (dev->att_io) {
+		g_io_channel_unref(dev->att_io);
+		dev->att_io = NULL;
+	}
 
 	if (gerr) {
 		error("gatt: connection failed %s", gerr->message);
@@ -1021,9 +1369,42 @@
 
 	device_set_state(dev, DEVICE_CONNECTED);
 
+	/* Send exchange mtu request as we assume being client and server */
+	/* TODO: Dont exchange mtu if no client apps */
+	send_exchange_mtu_request(dev);
+
+	/*
+	 * Service Changed Characteristic and CCC Descriptor handles
+	 * should not change if there are bonded devices. We have them
+	 * constant all the time, thus they should be excluded from
+	 * range indicating changes.
+	 */
+	range.start = service_changed_handle + 2;
+	range.end = 0xffff;
+
+	/*
+	 * If there is ccc stored for that device we were acting as server for
+	 * it, and as we dont have last connect and last services (de)activation
+	 * timestamps we should always assume something has changed.
+	 */
+	notify_att_range_change(dev, &range);
+
 	status = GATT_SUCCESS;
 
 reply:
+	if (!dev->conn_cnt) {
+		struct app_connection *conn;
+
+		if (!dev->attrib)
+			return;
+
+		conn = create_connection(dev, NULL);
+		if (!conn)
+			return;
+
+		search_dev_for_srvc(conn, NULL);
+	}
+
 	data.dev = dev;
 	data.status = status;
 	queue_foreach(app_connections, send_app_connect_notifications, &data);
@@ -1031,7 +1412,7 @@
 
 	/* Check if we should restart scan */
 	if (scanning)
-		bt_le_discovery_start(le_device_found_handler);
+		bt_le_discovery_start();
 
 	/* FIXME: What to do if discovery won't start here. */
 }
@@ -1053,8 +1434,8 @@
 
 	DBG("Connection attempt to: %s", addr);
 
-	/* TODO: If we are bonded then we should use higier sec level */
-	sec_level = BT_IO_SEC_LOW;
+	sec_level = bt_device_is_bonded(&dev->bdaddr) ? BT_IO_SEC_MEDIUM :
+								BT_IO_SEC_LOW;
 
 	/*
 	 * This connection will help us catch any PDUs that comes before
@@ -1082,6 +1463,142 @@
 	return 0;
 }
 
+static int connect_next_dev(void)
+{
+	struct gatt_device *dev;
+
+	DBG("");
+
+	dev = find_device_by_state(DEVICE_CONNECT_READY);
+	if (!dev)
+		return -ENODEV;
+
+	return connect_le(dev);
+}
+
+static void le_device_found_handler(const bdaddr_t *addr, uint8_t addr_type,
+						int rssi, uint16_t eir_len,
+						const void *eir,
+						bool discoverable, bool bonded)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
+	struct gatt_device *dev;
+	char bda[18];
+
+	if (!scanning || (!discoverable && !bonded))
+		goto connect;
+
+	ba2str(addr, bda);
+	DBG("LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi, !!eir);
+
+	bdaddr2android(addr, ev->bda);
+	ev->rssi = rssi;
+	ev->len = eir_len;
+
+	memcpy(ev->adv_data, eir, ev->len);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+						HAL_EV_GATT_CLIENT_SCAN_RESULT,
+						sizeof(*ev) + ev->len, ev);
+
+connect:
+	dev = find_device_by_addr(addr);
+	if (!dev) {
+		if (!bonded)
+			return;
+
+		dev = create_device(addr);
+	}
+
+	if (!dev || dev->state != DEVICE_CONNECT_INIT)
+		return;
+
+	device_set_state(dev, DEVICE_CONNECT_READY);
+	dev->bdaddr_type = addr_type;
+
+	/*
+	 * We are ok to perform connect now. Stop discovery
+	 * and once it is stopped continue with creating ACL
+	 */
+	bt_le_discovery_stop(bt_le_discovery_stop_cb);
+}
+
+static struct gatt_app *register_app(const uint8_t *uuid, gatt_type_t type)
+{
+	static int32_t application_id = 1;
+	struct gatt_app *app;
+
+	if (queue_find(gatt_apps, match_app_by_uuid, uuid)) {
+		error("gatt: app uuid is already on list");
+		return NULL;
+	}
+
+	app = new0(struct gatt_app, 1);
+	if (!app) {
+		error("gatt: Cannot allocate memory for registering app");
+		return 0;
+	}
+
+	app->type = type;
+
+	if (app->type == GATT_CLIENT) {
+		app->notifications = queue_new();
+		if (!app->notifications) {
+			error("gatt: couldn't allocate notifications queue");
+			destroy_gatt_app(app);
+			return NULL;
+		}
+	}
+
+	memcpy(app->uuid, uuid, sizeof(app->uuid));
+
+	app->id = application_id++;
+
+	if (!queue_push_head(gatt_apps, app)) {
+		error("gatt: Cannot push app on the list");
+		destroy_gatt_app(app);
+		return NULL;
+	}
+
+	if ((app->type == GATT_SERVER) &&
+			!queue_push_tail(listen_apps, INT_TO_PTR(app->id))) {
+		error("gatt: Cannot push server on the list");
+		destroy_gatt_app(app);
+		return NULL;
+	}
+
+	return app;
+}
+
+static void handle_client_register(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_register *cmd = buf;
+	struct hal_ev_gatt_client_register_client ev;
+	struct gatt_app *app;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	app = register_app(cmd->uuid, GATT_CLIENT);
+
+	if (app) {
+		ev.client_if = app->id;
+		ev.status = GATT_SUCCESS;
+	} else
+		ev.status = GATT_FAILURE;
+
+	/* We should send notification with given in cmd UUID */
+	memcpy(ev.app_uuid, cmd->uuid, sizeof(ev.app_uuid));
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_REGISTER_CLIENT, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER,
+							HAL_STATUS_SUCCESS);
+}
+
 static void handle_client_scan(const void *buf, uint16_t len)
 {
 	const struct hal_cmd_gatt_client_scan *cmd = buf;
@@ -1117,7 +1634,7 @@
 	}
 
 	/* Turn on scan */
-	if (!bt_le_discovery_start(le_device_found_handler)) {
+	if (!bt_le_discovery_start()) {
 		error("gatt: LE scan switch failed");
 		status = HAL_STATUS_FAILED;
 		goto reply;
@@ -1130,87 +1647,13 @@
 									status);
 }
 
-static int connect_next_dev(void)
-{
-	struct gatt_device *dev;
-
-	DBG("");
-
-	dev = find_device_by_state(DEVICE_CONNECT_READY);
-	if (!dev)
-		return -ENODEV;
-
-	return connect_le(dev);
-}
-
 static void bt_le_discovery_stop_cb(void)
 {
 	DBG("");
 
 	/* Check now if there is any device ready to connect */
 	if (connect_next_dev() < 0)
-		bt_le_discovery_start(le_device_found_handler);
-}
-
-static struct gatt_device *create_device(const bdaddr_t *addr)
-{
-	struct gatt_device *dev;
-
-	dev = new0(struct gatt_device, 1);
-	if (!dev)
-		return NULL;
-
-	bacpy(&dev->bdaddr, addr);
-
-	dev->services = queue_new();
-	if (!dev->services) {
-		error("gatt: Failed to allocate memory for client");
-		destroy_device(dev);
-		return NULL;
-	}
-
-	dev->pending_requests = queue_new();
-	if (!dev->pending_requests) {
-		error("gatt: Failed to allocate memory for client");
-		destroy_device(dev);
-		return NULL;
-	}
-
-	if (!queue_push_head(gatt_devices, dev)) {
-		error("gatt: Cannot push device to queue");
-		destroy_device(dev);
-		return NULL;
-	}
-
-	return device_ref(dev);
-}
-
-static struct app_connection *create_connection(struct gatt_device *device,
-						struct gatt_app *app)
-{
-	struct app_connection *new_conn;
-	static int32_t last_conn_id = 1;
-
-	/* Check if already connected */
-	new_conn = new0(struct app_connection, 1);
-	if (!new_conn)
-		return NULL;
-
-	/* Make connection id unique to connection record (app, device) pair */
-	new_conn->app = app;
-	new_conn->id = last_conn_id++;
-
-	if (!queue_push_head(app_connections, new_conn)) {
-		error("gatt: Cannot push client on the client queue!?");
-
-		free(new_conn);
-		return NULL;
-	}
-
-	new_conn->device = device_ref(device);
-	new_conn->device->conn_cnt++;
-
-	return new_conn;
+		bt_le_discovery_start();
 }
 
 static void trigger_disconnection(struct app_connection *connection)
@@ -1252,7 +1695,7 @@
 
 	/* after state change trigger discovering */
 	if (!scanning && (connection->device->state == DEVICE_CONNECT_INIT))
-		if (!bt_le_discovery_start(le_device_found_handler)) {
+		if (!bt_le_discovery_start()) {
 			error("gatt: Could not start scan");
 
 			return false;
@@ -1261,28 +1704,32 @@
 	return true;
 }
 
+static uint8_t unregister_app(int client_if)
+{
+	struct gatt_app *cl;
+
+	cl = queue_remove_if(gatt_apps, match_app_by_id, INT_TO_PTR(client_if));
+	if (!cl) {
+		error("gatt: client_if=%d not found", client_if);
+
+		return HAL_STATUS_FAILED;
+	}
+
+	/* Destroy app connections with proper notifications for this app. */
+	app_disconnect_devices(cl);
+	destroy_gatt_app(cl);
+
+	return HAL_STATUS_SUCCESS;
+}
+
 static void handle_client_unregister(const void *buf, uint16_t len)
 {
 	const struct hal_cmd_gatt_client_unregister *cmd = buf;
 	uint8_t status;
-	struct gatt_app *cl;
 
 	DBG("");
 
-	cl = queue_remove_if(gatt_apps, match_app_by_id,
-						INT_TO_PTR(cmd->client_if));
-	if (!cl) {
-		error("gatt: client_if=%d not found", cmd->client_if);
-		status = HAL_STATUS_FAILED;
-	} else {
-		/*
-		 * Check if there is any connect request or connected device
-		 * for this client. If so, remove this client from those lists.
-		 */
-		app_disconnect_devices(cl);
-		destroy_gatt_app(cl);
-		status = HAL_STATUS_SUCCESS;
-	}
+	status = unregister_app(cmd->client_if);
 
 	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
 					HAL_OP_GATT_CLIENT_UNREGISTER, status);
@@ -1556,167 +2003,6 @@
 									status);
 }
 
-struct discover_srvc_data {
-	bt_uuid_t uuid;
-	struct app_connection *conn;
-};
-
-static void send_client_search_complete_notify(int32_t status, int32_t conn_id)
-{
-	struct hal_ev_gatt_client_search_complete ev;
-
-	ev.status = status;
-	ev.conn_id = conn_id;
-	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
-			HAL_EV_GATT_CLIENT_SEARCH_COMPLETE, sizeof(ev), &ev);
-}
-
-static void discover_srvc_all_cb(uint8_t status, GSList *services,
-								void *user_data)
-{
-	struct discover_srvc_data *cb_data = user_data;
-	struct gatt_device *dev = cb_data->conn->device;
-	int32_t gatt_status;
-	GSList *l;
-	/*
-	 * There might be multiply services with same uuid. Therefore make sure
-	 * each primary service one has unique instance_id
-	 */
-	uint8_t instance_id = queue_length(dev->services);
-
-	DBG("Status %d", status);
-
-	if (status) {
-		error("gatt: Discover all primary services failed: %s",
-							att_ecode2str(status));
-		gatt_status = GATT_FAILURE;
-		goto reply;
-	}
-
-	if (!services) {
-		info("gatt: No primary services found");
-		gatt_status = GATT_SUCCESS;
-		goto reply;
-	}
-
-	for (l = services; l; l = l->next) {
-		struct gatt_primary *prim = l->data;
-		struct service *p;
-
-		if (queue_find(dev->services, match_srvc_by_range,
-								&prim->range))
-			continue;
-
-		p = create_service(instance_id++, true, prim->uuid, prim);
-		if (!p)
-			continue;
-
-		if (!queue_push_tail(dev->services, p)) {
-			error("gatt: Cannot push primary service to the list");
-			free(p);
-			continue;
-		}
-
-		DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
-			prim->range.start, prim->range.end, prim->uuid);
-	}
-
-	/*
-	 * Send all found services notifications - first cache,
-	 * then send notifies
-	 */
-	queue_foreach(dev->services, send_client_primary_notify,
-						INT_TO_PTR(cb_data->conn->id));
-
-	/* Full search service scanning was performed */
-	dev->partial_srvc_search = false;
-	gatt_status = GATT_SUCCESS;
-
-reply:
-	send_client_search_complete_notify(gatt_status, cb_data->conn->id);
-	free(cb_data);
-}
-
-static void discover_srvc_by_uuid_cb(uint8_t status, GSList *ranges,
-								void *user_data)
-{
-	struct discover_srvc_data *cb_data = user_data;
-	struct gatt_primary prim;
-	struct service *s;
-	int32_t gatt_status;
-	struct gatt_device *dev = cb_data->conn->device;
-	uint8_t instance_id = queue_length(dev->services);
-
-	DBG("Status %d", status);
-
-	if (status) {
-		error("gatt: Discover pri srvc filtered by uuid failed: %s",
-							att_ecode2str(status));
-		gatt_status = GATT_FAILURE;
-		goto reply;
-	}
-
-	if (!ranges) {
-		info("gatt: No primary services searched by uuid found");
-		gatt_status = GATT_SUCCESS;
-		goto reply;
-	}
-
-	bt_uuid_to_string(&cb_data->uuid, prim.uuid, sizeof(prim.uuid));
-	/*
-	 * If multiple instances of the same service (as identified by UUID)
-	 * exist, the first instance of the service is returned.
-	 */
-	memcpy(&prim.range, ranges->data, sizeof(prim.range));
-
-	s = create_service(instance_id++, true, prim.uuid, &prim);
-	if (!s) {
-		gatt_status = GATT_FAILURE;
-		goto reply;
-	}
-
-	if (!queue_push_tail(dev->services, s)) {
-		error("gatt: Cannot push primary service to the list");
-		gatt_status = GATT_FAILURE;
-		goto reply;
-	}
-
-	send_client_primary_notify(s, INT_TO_PTR(cb_data->conn->id));
-
-	DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
-		prim.range.start, prim.range.end, prim.uuid);
-
-	/* Partial search service scanning was performed */
-	dev->partial_srvc_search = true;
-	gatt_status = GATT_SUCCESS;
-
-reply:
-	send_client_search_complete_notify(gatt_status, cb_data->conn->id);
-	free(cb_data);
-}
-
-static guint search_dev_for_srvc(struct app_connection *conn, bt_uuid_t *uuid)
-{
-	struct discover_srvc_data *cb_data =
-					new0(struct discover_srvc_data, 1);
-
-	if (!cb_data) {
-		error("gatt: Cannot allocate cb data");
-		return 0;
-	}
-
-	cb_data->conn = conn;
-
-	if (uuid) {
-		memcpy(&cb_data->uuid, uuid, sizeof(cb_data->uuid));
-		return gatt_discover_primary(conn->device->attrib, uuid,
-					discover_srvc_by_uuid_cb, cb_data);
-	}
-
-	return gatt_discover_primary(conn->device->attrib, NULL,
-						discover_srvc_all_cb, cb_data);
-}
-
 static void handle_client_search_service(const void *buf, uint16_t len)
 {
 	const struct hal_cmd_gatt_client_search_service *cmd = buf;
@@ -1781,8 +2067,10 @@
 		if (s) {
 			send_client_primary_notify(s, INT_TO_PTR(conn->id));
 		} else {
-			if (!search_dev_for_srvc(conn, &uuid))
+			if (!search_dev_for_srvc(conn, &uuid)) {
 				status = HAL_STATUS_FAILED;
+				goto reply;
+			}
 
 			status = HAL_STATUS_SUCCESS;
 			goto reply;
@@ -2080,6 +2368,9 @@
 							srvc->incl.range.end;
 		}
 
+		DBG("attr handle = 0x%04x, end handle = 0x%04x uuid: %s",
+				ch->ch.handle, ch->end_handle, ch->ch.uuid);
+
 		if (!queue_push_tail(srvc->chars, ch)) {
 			error("gatt: Error while caching characteristic");
 			destroy_characteristic(ch);
@@ -2236,6 +2527,8 @@
 		descr->id.instance = i++;
 		descr->handle = desc->handle;
 
+		DBG("attr handle = 0x%04x, uuid: %s", desc->handle, desc->uuid);
+
 		if (!queue_push_tail(ch->descriptors, descr))
 			free(descr);
 	}
@@ -2426,6 +2719,65 @@
 	free(data);
 }
 
+static int get_sec_level(struct gatt_device *dev)
+{
+	GIOChannel *io;
+	int sec_level;
+
+	io = g_attrib_get_channel(dev->attrib);
+
+	if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
+							BT_IO_OPT_INVALID)) {
+		error("gatt: Failed to get sec_level");
+		return -1;
+	}
+
+	return sec_level;
+}
+
+static bool set_security(struct gatt_device *device, int auth_type)
+{
+	int req_sec_level, sec_level;
+	GError *gerr = NULL;
+	GIOChannel *io;
+
+	switch (auth_type) {
+	case HAL_GATT_AUTHENTICATION_MITM:
+		req_sec_level = BT_SECURITY_HIGH;
+		break;
+	case HAL_GATT_AUTHENTICATION_NO_MITM:
+		req_sec_level = BT_SECURITY_MEDIUM;
+		break;
+	case HAL_GATT_AUTHENTICATION_NONE:
+		req_sec_level = BT_SECURITY_LOW;
+		break;
+	default:
+		error("gatt: Invalid auth_type value: %d", auth_type);
+		return false;
+	}
+
+	sec_level = get_sec_level(device);
+	if (sec_level < 0)
+		return false;
+
+	if (req_sec_level <= sec_level)
+		return true;
+
+	io = g_attrib_get_channel(device->attrib);
+	if (!io)
+		return false;
+
+	bt_io_set(io, &gerr, BT_IO_OPT_SEC_LEVEL, req_sec_level,
+							BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("gatt: Failed to set security level: %s", gerr->message);
+		g_error_free(gerr);
+		return false;
+	}
+
+	return true;
+}
+
 static void handle_client_read_characteristic(const void *buf, uint16_t len)
 {
 	const struct hal_cmd_gatt_client_read_characteristic *cmd = buf;
@@ -2466,6 +2818,13 @@
 		goto failed;
 	}
 
+	if (!set_security(conn->device, cmd->auth_req)) {
+		error("gatt: Failed to set security %d", cmd->auth_req);
+		status = HAL_STATUS_FAILED;
+		free(cb_data);
+		goto failed;
+	}
+
 	if (!gatt_read_char(conn->device->attrib, ch->ch.value_handle,
 						read_char_cb, cb_data)) {
 		error("gatt: Cannot read characteristic with inst_id: %d",
@@ -2522,6 +2881,30 @@
 	free(data);
 }
 
+static bool signed_write_cmd(struct gatt_device *dev, uint16_t handle,
+					const uint8_t *value, uint16_t vlen)
+{
+	uint8_t csrk[16];
+	uint32_t sign_cnt;
+
+	memset(csrk, 0, 16);
+
+	if (!bt_get_csrk(&dev->bdaddr, LOCAL_CSRK, csrk, &sign_cnt)) {
+		error("gatt: Could not get csrk key");
+		return false;
+	}
+
+	if (!gatt_signed_write_cmd(dev->attrib, handle, value, vlen, crypto,
+					csrk, sign_cnt, NULL, NULL)) {
+		error("gatt: Could write signed cmd");
+		return false;
+	}
+
+	bt_update_sign_counter(&dev->bdaddr, LOCAL_CSRK);
+
+	return true;
+}
+
 static void handle_client_write_characteristic(const void *buf, uint16_t len)
 {
 	const struct hal_cmd_gatt_client_write_characteristic *cmd = buf;
@@ -2569,6 +2952,12 @@
 		}
 	}
 
+	if (!set_security(conn->device, cmd->auth_req)) {
+		error("gatt: Failed to set security %d", cmd->auth_req);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
 	switch (cmd->write_type) {
 	case GATT_WRITE_TYPE_NO_RESPONSE:
 		res = gatt_write_cmd(conn->device->attrib, ch->ch.value_handle,
@@ -2586,6 +2975,16 @@
 							cmd->value, cmd->len,
 							write_char_cb, cb_data);
 		break;
+	case GATT_WRITE_TYPE_SIGNED:
+		if (get_sec_level(conn->device) != BT_SECURITY_LOW) {
+			error("gatt: Cannot write signed on encrypted link");
+			res = HAL_STATUS_FAILED;
+		} else {
+			res = signed_write_cmd(conn->device,
+							ch->ch.value_handle,
+							cmd->value, cmd->len);
+		}
+		break;
 	default:
 		error("gatt: Write type %d unsupported", cmd->write_type);
 		status = HAL_STATUS_UNSUPPORTED;
@@ -2760,6 +3159,13 @@
 		goto failed;
 	}
 
+	if (!set_security(conn->device, cmd->auth_req)) {
+		error("gatt: Failed to set security %d", cmd->auth_req);
+		status = HAL_STATUS_FAILED;
+		free(cb_data);
+		goto failed;
+	}
+
 	if (!gatt_read_char(conn->device->attrib, descr->handle, read_desc_cb,
 								cb_data)) {
 		free(cb_data);
@@ -2885,6 +3291,12 @@
 		}
 	}
 
+	if (!set_security(conn->device, cmd->auth_req)) {
+		error("gatt: Failed to set security %d", cmd->auth_req);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
 	switch (cmd->write_type) {
 	case GATT_WRITE_TYPE_NO_RESPONSE:
 		res = gatt_write_cmd(conn->device->attrib, descr->handle,
@@ -3004,20 +3416,6 @@
 	ev->len = len - data_offset;
 	memcpy(ev->value, pdu + data_offset, len - data_offset);
 
-	if (!ev->is_notify) {
-		uint8_t *res;
-		uint16_t len;
-		size_t plen;
-
-		res = g_attrib_get_buffer(
-				notification->conn->device->attrib,
-				&plen);
-		len = enc_confirmation(res, plen);
-		if (len > 0)
-			g_attrib_send(notification->conn->device->attrib,
-						0, res, len, NULL, NULL, NULL);
-	}
-
 	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_NOTIFY,
 						sizeof(*ev) + ev->len, ev);
 }
@@ -3279,19 +3677,18 @@
 		return;
 	}
 
-	DBG("scan_rsp=%u name=%u tx=%u min=%d max=%d app=%d manufacturer=%u",
+	DBG("scan_rsp=%u name=%u tx=%u min=%d max=%d app=%d",
 		cmd->set_scan_rsp, cmd->include_name, cmd->include_txpower,
-		cmd->min_interval, cmd->max_interval, cmd->appearance,
-		cmd->manufacturer_len);
+		cmd->min_interval, cmd->max_interval, cmd->appearance);
 
-	/*
-	 * TODO
-	 * Currently kernel is setting all except for vendor data.
-	 * This should be implemented when kernel supports it.
-	 */
+	DBG("manufacturer=%u service_data=%u service_uuid=%u",
+				cmd->manufacturer_len, cmd->service_data_len,
+				cmd->service_uuid_len);
 
-	if (cmd->manufacturer_len) {
-		error("gatt: Manufacturer advertising data not supported");
+	/* TODO This should be implemented when kernel supports it */
+	if (cmd->manufacturer_len || cmd->service_data_len ||
+							cmd->service_uuid_len) {
+		error("gatt: Extra advertising data not supported");
 		status = HAL_STATUS_FAILED;
 		goto failed;
 	}
@@ -3303,29 +3700,168 @@
 				HAL_OP_GATT_CLIENT_SET_ADV_DATA, status);
 }
 
+static uint8_t test_read_write(bdaddr_t *bdaddr, bt_uuid_t *uuid, uint16_t op,
+						uint16_t u2,uint16_t u3,
+						uint16_t u4, uint16_t u5)
+{
+	guint16 length = 0;
+	struct gatt_device *dev;
+	uint8_t *pdu;
+	size_t mtu;
+
+	dev = find_device_by_addr(bdaddr);
+	if (!dev || dev->state != DEVICE_CONNECTED)
+		return HAL_STATUS_FAILED;
+
+	pdu = g_attrib_get_buffer(dev->attrib, &mtu);
+	if (!pdu)
+		return HAL_STATUS_FAILED;
+
+	switch (op) {
+	case ATT_OP_READ_REQ:
+		length = enc_read_req(u2, pdu, mtu);
+		break;
+	case ATT_OP_READ_BY_TYPE_REQ:
+		length = enc_read_by_type_req(u2, u3, uuid, pdu, mtu);
+		break;
+	case ATT_OP_READ_BLOB_REQ:
+		length = enc_read_blob_req(u2, u3, pdu, mtu);
+		break;
+	case ATT_OP_READ_BY_GROUP_REQ:
+		length = enc_read_by_grp_req(u2, u3, uuid, pdu, mtu);
+		break;
+	case ATT_OP_READ_MULTI_REQ:
+		return HAL_STATUS_UNSUPPORTED;
+	case ATT_OP_WRITE_REQ:
+		length = enc_write_req(u2, (uint8_t *) &u3, sizeof(u3), pdu,
+									mtu);
+		break;
+	case ATT_OP_WRITE_CMD:
+		length = enc_write_cmd(u2, (uint8_t *) &u3, sizeof(u3), pdu,
+									mtu);
+		break;
+	case ATT_OP_PREP_WRITE_REQ:
+		length = enc_prep_write_req(u2, u3, (uint8_t *) &u4, sizeof(u4),
+								pdu, mtu);
+		break;
+	case ATT_OP_EXEC_WRITE_REQ:
+		length = enc_exec_write_req(u2, pdu, mtu);
+		break;
+	case ATT_OP_SIGNED_WRITE_CMD:
+		if (signed_write_cmd(dev, u2, (uint8_t *) &u3, sizeof(u3)))
+			return HAL_STATUS_SUCCESS;
+		else
+			return HAL_STATUS_FAILED;
+	default:
+		error("gatt: Unknown operation type");
+
+		return HAL_STATUS_UNSUPPORTED;
+	}
+
+	if (!length)
+		return HAL_STATUS_FAILED;
+
+	g_attrib_send(dev->attrib, 0, pdu, length, NULL, NULL, NULL);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t test_increase_security(bdaddr_t *bdaddr, uint16_t u1)
+{
+	struct gatt_device *device;
+
+	device = find_device_by_addr(bdaddr);
+	if (!device)
+		return HAL_STATUS_FAILED;
+
+	if (!set_security(device, u1))
+		return HAL_STATUS_FAILED;
+
+	return HAL_STATUS_SUCCESS;
+}
+
 static void handle_client_test_command(const void *buf, uint16_t len)
 {
+	const struct hal_cmd_gatt_client_test_command *cmd = buf;
+	struct gatt_app *app;
+	bdaddr_t bdaddr;
+	bt_uuid_t uuid;
+	uint8_t status;
+
 	DBG("");
 
+	android2bdaddr(cmd->bda1, &bdaddr);
+	android2uuid(cmd->uuid1, &uuid);
+
+	switch (cmd->command) {
+	case GATT_CLIENT_TEST_CMD_ENABLE:
+		if (cmd->u1) {
+			if (!test_client_if) {
+				app = register_app(TEST_UUID, GATT_CLIENT);
+				if (app)
+					test_client_if = app->id;
+			}
+
+			if (test_client_if)
+				status = HAL_STATUS_SUCCESS;
+			else
+				status = HAL_STATUS_FAILED;
+		} else {
+			status = unregister_app(test_client_if);
+			test_client_if = 0;
+		}
+		break;
+	case GATT_CLIENT_TEST_CMD_CONNECT:
+		/* TODO u1 holds device type, for now assume BLE */
+		status = handle_connect(test_client_if, &bdaddr);
+		break;
+	case GATT_CLIENT_TEST_CMD_DISCONNECT:
+		app = queue_find(gatt_apps, match_app_by_id,
+						INT_TO_PTR(test_client_if));
+		if (app)
+			app_disconnect_devices(app);
+
+		status = HAL_STATUS_SUCCESS;
+		break;
+	case GATT_CLIENT_TEST_CMD_DISCOVER:
+		status = HAL_STATUS_FAILED;
+		break;
+	case GATT_CLIENT_TEST_CMD_READ:
+	case GATT_CLIENT_TEST_CMD_WRITE:
+		status = test_read_write(&bdaddr, &uuid, cmd->u1, cmd->u2,
+						cmd->u3, cmd->u4, cmd->u5);
+		break;
+	case GATT_CLIENT_TEST_CMD_INCREASE_SECURITY:
+		status = test_increase_security(&bdaddr, cmd->u1);
+		break;
+	case GATT_CLIENT_TEST_CMD_PAIRING_CONFIG:
+	default:
+		status = HAL_STATUS_FAILED;
+		break;
+	}
+
 	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
-			HAL_OP_GATT_CLIENT_TEST_COMMAND, HAL_STATUS_FAILED);
+				HAL_OP_GATT_CLIENT_TEST_COMMAND, status);
 }
 
 static void handle_server_register(const void *buf, uint16_t len)
 {
 	const struct hal_cmd_gatt_server_register *cmd = buf;
 	struct hal_ev_gatt_server_register ev;
+	struct gatt_app *app;
 
 	DBG("");
 
 	memset(&ev, 0, sizeof(ev));
 
-	ev.server_if = register_app(cmd->uuid, APP_SERVER);
+	app = register_app(cmd->uuid, GATT_SERVER);
 
-	if (ev.server_if)
+	if (app) {
+		ev.server_if = app->id;
 		ev.status = GATT_SUCCESS;
-	else
+	} else {
 		ev.status = GATT_FAILURE;
+	}
 
 	memcpy(ev.uuid, cmd->uuid, sizeof(ev.uuid));
 
@@ -3340,21 +3876,11 @@
 {
 	const struct hal_cmd_gatt_server_unregister *cmd = buf;
 	uint8_t status;
-	struct gatt_app *server;
 
 	DBG("");
 
-	server = find_app_by_id(cmd->server_if);
-	if (!server) {
-		error("gatt: server_if=%d not found", cmd->server_if);
-		status = HAL_STATUS_FAILED;
-		goto failed;
-	}
+	status = unregister_app(cmd->server_if);
 
-	destroy_gatt_app(server);
-	status = HAL_STATUS_SUCCESS;
-
-failed:
 	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
 					HAL_OP_GATT_SERVER_UNREGISTER, status);
 }
@@ -3490,14 +4016,28 @@
 	return false;
 }
 
-static void send_dev_pending_response(struct gatt_device *device,
+static bool match_pending_dev_request(const void *data, const void *user_data)
+{
+	const struct pending_request *pending_request = data;
+
+	return pending_request->state == REQUEST_PENDING;
+}
+
+static void send_dev_complete_response(struct gatt_device *device,
 								uint8_t opcode)
 {
-	uint8_t rsp[ATT_DEFAULT_LE_MTU];
+	size_t mtu;
+	uint8_t *rsp = g_attrib_get_buffer(device->attrib, &mtu);
 	struct pending_request *val;
 	uint16_t len = 0;
 	uint8_t error = 0;
 
+	if (queue_find(device->pending_requests, match_pending_dev_request,
+									NULL)) {
+		DBG("Still pending requests");
+		return;
+	}
+
 	switch (opcode) {
 	case ATT_OP_READ_BY_TYPE_REQ: {
 		struct att_data_list *adl;
@@ -3505,7 +4045,6 @@
 		int length;
 		struct queue *temp;
 
-
 		temp = queue_new();
 		if (!temp)
 			goto done;
@@ -3517,9 +4056,15 @@
 			goto done;
 		}
 
+		if (val->error) {
+			queue_destroy(temp, NULL);
+			error = val->error;
+			goto done;
+		}
+
 		length = val->length;
 
-		while (val && val->length == length) {
+		while (val && val->length == length && val->error == 0) {
 			queue_push_tail(temp, val);
 			val = queue_pop_head(device->pending_requests);
 		}
@@ -3538,7 +4083,7 @@
 			val = queue_pop_head(temp);
 		}
 
-		len = enc_read_by_type_resp(adl, rsp, sizeof(rsp));
+		len = enc_read_by_type_resp(adl, rsp, mtu);
 
 		att_data_list_free(adl);
 		queue_destroy(temp, destroy_pending_request);
@@ -3547,23 +4092,23 @@
 	}
 	case ATT_OP_READ_BLOB_REQ:
 		val = queue_pop_head(device->pending_requests);
-		if (!val || val->length < 0) {
-			error = ATT_ECODE_ATTR_NOT_FOUND;
+		if (val->error) {
+			error = val->error;
 			goto done;
 		}
 
 		len = enc_read_blob_resp(val->value, val->length, val->offset,
-							rsp, sizeof(rsp));
+								rsp, mtu);
 		destroy_pending_request(val);
 		break;
 	case ATT_OP_READ_REQ:
 		val = queue_pop_head(device->pending_requests);
-		if (!val || val->length < 0) {
-			error = ATT_ECODE_ATTR_NOT_FOUND;
+		if (val->error) {
+			error = val->error;
 			goto done;
 		}
 
-		len = enc_read_resp(val->value, val->length, rsp, sizeof(rsp));
+		len = enc_read_resp(val->value, val->length, rsp, mtu);
 		destroy_pending_request(val);
 		break;
 	case ATT_OP_READ_BY_GROUP_REQ: {
@@ -3609,7 +4154,7 @@
 			val = queue_pop_head(temp);
 		}
 
-		len = enc_read_by_grp_resp(adl, rsp, sizeof(rsp));
+		len = enc_read_by_grp_resp(adl, rsp, mtu);
 
 		att_data_list_free(adl);
 		queue_destroy(temp, destroy_pending_request);
@@ -3657,7 +4202,7 @@
 		}
 
 		if (list && !error)
-			len = enc_find_by_type_resp(list, rsp, sizeof(rsp));
+			len = enc_find_by_type_resp(list, rsp, mtu);
 		else
 			error = ATT_ECODE_ATTR_NOT_FOUND;
 
@@ -3667,9 +4212,9 @@
 	}
 	case ATT_OP_EXEC_WRITE_REQ:
 		val = queue_pop_head(device->pending_requests);
-		if (!val) {
-			error = ATT_ECODE_ATTR_NOT_FOUND;
-			break;
+		if (val->error) {
+			error = val->error;
+			goto done;
 		}
 
 		len = enc_exec_write_resp(rsp);
@@ -3677,9 +4222,9 @@
 		break;
 	case ATT_OP_WRITE_REQ:
 		val = queue_pop_head(device->pending_requests);
-		if (!val) {
-			error = ATT_ECODE_ATTR_NOT_FOUND;
-			break;
+		if (val->error) {
+			error = val->error;
+			goto done;
 		}
 
 		len = enc_write_resp(rsp);
@@ -3687,13 +4232,13 @@
 		break;
 	case ATT_OP_PREP_WRITE_REQ:
 		val = queue_pop_head(device->pending_requests);
-		if (!val) {
-			error = ATT_ECODE_ATTR_NOT_FOUND;
-			break;
+		if (val->error) {
+			error = val->error;
+			goto done;
 		}
 
 		len = enc_prep_write_resp(val->handle, val->offset, val->value,
-						val->length, rsp, sizeof(rsp));
+							val->length, rsp, mtu);
 		destroy_pending_request(val);
 		break;
 	default:
@@ -3702,8 +4247,7 @@
 
 done:
 	if (!len)
-		len = enc_error_resp(opcode, 0x0000, error, rsp,
-							ATT_DEFAULT_LE_MTU);
+		len = enc_error_resp(opcode, 0x0000, error, rsp, mtu);
 
 	g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL);
 
@@ -3716,13 +4260,6 @@
 	struct gatt_device *device;
 };
 
-static bool match_pending_dev_request(const void *data, const void *user_data)
-{
-	const struct pending_request *pending_request = data;
-
-	return pending_request->length == READ_PENDING;
-}
-
 static bool match_dev_request_by_handle(const void *data, const void *user_data)
 {
 	const struct pending_request *handle_data = data;
@@ -3731,72 +4268,106 @@
 	return handle_data->handle == handle;
 }
 
-static void read_requested_attributes(void *data, void *user_data)
+static uint8_t check_device_permissions(struct gatt_device *device,
+					uint8_t opcode, uint32_t permissions)
 {
-	struct pending_request *resp_data = data;
-	struct request_processing_data *process_data = user_data;
-	uint8_t *value;
-	int value_len;
+	GIOChannel *io;
+	int sec_level;
 
-	if (!gatt_db_read(gatt_db, resp_data->handle,
-						resp_data->offset,
-						process_data->opcode,
-						&process_data->device->bdaddr,
-						&value, &value_len)) {
-		resp_data->length = READ_FAILED;
+	io = g_attrib_get_channel(device->attrib);
+
+	if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
+							BT_IO_OPT_INVALID))
+		return ATT_ECODE_UNLIKELY;
+
+	DBG("opcode %u permissions %u sec_level %u", opcode, permissions,
+								sec_level);
+
+	switch (opcode) {
+	case ATT_OP_SIGNED_WRITE_CMD:
+		if (!(permissions & GATT_PERM_WRITE_SIGNED))
+				return ATT_ECODE_WRITE_NOT_PERM;
+
+		if ((permissions & GATT_PERM_WRITE_SIGNED_MITM) &&
+						sec_level < BT_SECURITY_HIGH)
+			return ATT_ECODE_AUTHENTICATION;
+		break;
+	case ATT_OP_READ_BY_TYPE_REQ:
+	case ATT_OP_READ_REQ:
+	case ATT_OP_READ_BLOB_REQ:
+	case ATT_OP_READ_MULTI_REQ:
+	case ATT_OP_READ_BY_GROUP_REQ:
+	case ATT_OP_FIND_BY_TYPE_REQ:
+	case ATT_OP_FIND_INFO_REQ:
+		if (!(permissions & GATT_PERM_READ))
+			return ATT_ECODE_READ_NOT_PERM;
+
+		if ((permissions & GATT_PERM_READ_MITM) &&
+						sec_level < BT_SECURITY_HIGH)
+			return ATT_ECODE_AUTHENTICATION;
+
+		if ((permissions & GATT_PERM_READ_ENCRYPTED) &&
+						sec_level < BT_SECURITY_MEDIUM)
+			return ATT_ECODE_INSUFF_ENC;
+
+		if (permissions & GATT_PERM_READ_AUTHORIZATION)
+			return ATT_ECODE_AUTHORIZATION;
+		break;
+	case ATT_OP_WRITE_REQ:
+	case ATT_OP_WRITE_CMD:
+	case ATT_OP_PREP_WRITE_REQ:
+	case ATT_OP_EXEC_WRITE_REQ:
+		if (!(permissions & GATT_PERM_WRITE))
+			return ATT_ECODE_WRITE_NOT_PERM;
+
+		if ((permissions & GATT_PERM_WRITE_MITM) &&
+						sec_level < BT_SECURITY_HIGH)
+			return ATT_ECODE_AUTHENTICATION;
+
+		if ((permissions & GATT_PERM_WRITE_ENCRYPTED) &&
+						sec_level < BT_SECURITY_MEDIUM)
+			return ATT_ECODE_INSUFF_ENC;
+
+		if (permissions & GATT_PERM_WRITE_AUTHORIZATION)
+			return ATT_ECODE_AUTHORIZATION;
+		break;
+	default:
+		return ATT_ECODE_UNLIKELY;
+	}
+
+	return 0;
+}
+
+static void fill_gatt_response(struct pending_request *request, uint16_t handle,
+					uint16_t offset, uint8_t status,
+					uint16_t len, const uint8_t *data)
+{
+	request->handle = handle;
+	request->offset = offset;
+	request->length = len;
+	request->state = REQUEST_DONE;
+	request->error = status;
+
+	if (!len)
+		return;
+
+	request->value = malloc0(len);
+	if (!request->value) {
+		request->error = ATT_ECODE_INSUFF_RESOURCES;
+
 		return;
 	}
 
-	/* We have value here already if no callback will be called */
-	if (value_len >= 0) {
-		resp_data->value = malloc0(value_len);
-		if (!resp_data->value) {
-			/* If data cannot be copied, act like when read fails */
-			resp_data->length = READ_FAILED;
-			return;
-		}
-
-		memcpy(resp_data->value, value, value_len);
-		resp_data->length = value_len;
-	} else if (resp_data->length == READ_INIT) {
-		resp_data->length = READ_PENDING;
-	}
+	memcpy(request->value, data, len);
 }
 
-static void process_dev_pending_requests(struct gatt_device *device,
-							uint8_t att_opcode)
+static void fill_gatt_response_by_handle(uint16_t handle, uint16_t offset,
+						uint8_t status, uint16_t len,
+						const uint8_t *data,
+						struct gatt_device *dev)
 {
-	struct request_processing_data process_data;
-
-	process_data.device = device;
-	process_data.opcode = att_opcode;
-
-	/* Process pending requests and prepare response */
-	queue_foreach(device->pending_requests, read_requested_attributes,
-								&process_data);
-
-	if (!queue_find(device->pending_requests,
-					match_pending_dev_request, NULL))
-		send_dev_pending_response(device, att_opcode);
-}
-
-static void send_gatt_response(uint8_t opcode, uint16_t handle,
-					uint16_t offset, uint8_t status,
-					uint16_t len, const uint8_t *data,
-					bdaddr_t *bdaddr)
-{
-	struct gatt_device *dev;
 	struct pending_request *entry;
 
-	dev = find_device_by_addr(bdaddr);
-	if (!dev) {
-		error("gatt: send_gatt_response, could not find dev");
-		goto done;
-	}
-
-	if (status)
-		goto done;
-
 	entry = queue_find(dev->pending_requests, match_dev_request_by_handle,
 							UINT_TO_PTR(handle));
 	if (!entry) {
@@ -3804,49 +4375,104 @@
 		return;
 	}
 
-	entry->handle = handle;
-	entry->offset = offset;
-	entry->length = len;
+	fill_gatt_response(entry, handle, offset, status, len, data);
+}
 
-	if (!len)
+static void read_requested_attributes(void *data, void *user_data)
+{
+	struct pending_request *resp_data = data;
+	struct request_processing_data *process_data = user_data;
+	uint32_t permissions;
+	uint8_t *value, error;
+	int value_len = 0;
+
+	if (!gatt_db_get_attribute_permissions(gatt_db, resp_data->handle,
+								&permissions)) {
+		resp_data->error = ATT_ECODE_ATTR_NOT_FOUND;
+		resp_data->state = REQUEST_DONE;
+		return;
+	}
+
+	/*
+	 * Check if it is attribute we didn't declare permissions, like service
+	 * declaration or included service. Set permissions to read only
+	 */
+	if (permissions == 0)
+		permissions = GATT_PERM_READ;
+
+	error = check_device_permissions(process_data->device,
+							process_data->opcode,
+							permissions);
+	if (error)
 		goto done;
 
-	entry->value = malloc0(len);
-	if (!entry->value) {
-		/* send_dev_pending_request on empty queue sends error resp. */
-		queue_remove(dev->pending_requests, entry);
-		destroy_pending_request(entry);
+	resp_data->state = REQUEST_PENDING;
 
+	if (!gatt_db_read(gatt_db, resp_data->handle,
+						resp_data->offset,
+						process_data->opcode,
+						&process_data->device->bdaddr,
+						&value, &value_len)) {
+		error = ATT_ECODE_UNLIKELY;
 		goto done;
 	}
 
-	memcpy(entry->value, data, len);
-
 done:
-	if (!queue_find(dev->pending_requests, match_pending_dev_request, NULL))
-		send_dev_pending_response(dev, opcode);
+	/* We have value here already if no callback will be called */
+	if (value_len >= 0)
+		fill_gatt_response(resp_data, resp_data->handle,
+					resp_data->offset, error, value_len,
+					value);
 }
 
-static void set_trans_id(struct gatt_app *app, unsigned int id, int8_t opcode)
+static void process_dev_pending_requests(struct gatt_device *device,
+							uint8_t att_opcode)
 {
-	app->trans_id.id = id;
-	app->trans_id.opcode = opcode;
+	struct request_processing_data process_data;
+
+	if (queue_isempty(device->pending_requests))
+		return;
+
+	process_data.device = device;
+	process_data.opcode = att_opcode;
+
+	/* Process pending requests and prepare response */
+	queue_foreach(device->pending_requests, read_requested_attributes,
+								&process_data);
+
+	send_dev_complete_response(device, att_opcode);
 }
 
-static void clear_trans_id(struct gatt_app *app)
+static struct pending_trans_data *conn_add_transact(struct app_connection *conn,
+								uint8_t opcode)
 {
-	app->trans_id.id = 0;
-	app->trans_id.opcode = 0;
+	struct pending_trans_data *transaction;
+	static int32_t trans_id = 1;
+
+	transaction = new0(struct pending_trans_data, 1);
+	if (!transaction)
+		return NULL;
+
+	if (!queue_push_tail(conn->transactions, transaction)) {
+		free(transaction);
+		return NULL;
+	}
+
+	transaction->id = trans_id++;
+	transaction->opcode = opcode;
+
+	return transaction;
 }
 
 static void read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode,
 					bdaddr_t *bdaddr, void *user_data)
 {
+	struct pending_trans_data *transaction;
 	struct hal_ev_gatt_server_request_read ev;
 	struct gatt_app *app;
 	struct app_connection *conn;
 	int32_t id = PTR_TO_INT(user_data);
-	static int32_t trans_id = 1;
+	struct gatt_device *dev;
 
 	app = find_app_by_id(id);
 	if (!app) {
@@ -3863,14 +4489,16 @@
 	memset(&ev, 0, sizeof(ev));
 
 	/* Store the request data, complete callback and transaction id */
-	set_trans_id(app, trans_id++, att_opcode);
+	transaction = conn_add_transact(conn, att_opcode);
+	if (!transaction)
+		goto failed;
 
 	bdaddr2android(bdaddr, ev.bdaddr);
 	ev.conn_id = conn->id;
 	ev.attr_handle = handle;
 	ev.offset = offset;
 	ev.is_long = att_opcode == ATT_OP_READ_BLOB_REQ;
-	ev.trans_id = app->trans_id.id;
+	ev.trans_id = transaction->id;
 
 	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
 					HAL_EV_GATT_SERVER_REQUEST_READ,
@@ -3879,8 +4507,10 @@
 	return;
 
 failed:
-	send_gatt_response(att_opcode, handle, 0, ATT_ECODE_UNLIKELY, 0,
-							NULL, bdaddr);
+	dev = find_device_by_addr(bdaddr);
+	if (dev)
+		fill_gatt_response_by_handle(handle, 0, ATT_ECODE_UNLIKELY, 0,
+							NULL, dev);
 }
 
 static void write_cb(uint16_t handle, uint16_t offset,
@@ -3888,11 +4518,13 @@
 					uint8_t att_opcode, bdaddr_t *bdaddr,
 					void *user_data)
 {
-	struct hal_ev_gatt_server_request_write ev;
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_gatt_server_request_write *ev = (void *) buf;
+	struct pending_trans_data *transaction;
 	struct gatt_app *app;
 	int32_t id = PTR_TO_INT(user_data);
-	static int32_t trans_id = 1;
 	struct app_connection *conn;
+	struct gatt_device *dev;
 
 	app = find_app_by_id(id);
 	if (!app) {
@@ -3906,33 +4538,77 @@
 		goto failed;
 	}
 
-	/* Store the request data, complete callback and transaction id */
-	set_trans_id(app, trans_id++, att_opcode);
+	/*
+	 * Remember that this application has ongoing prep write
+	 * Need it later to find out where to send execute write
+	 */
+	if (att_opcode == ATT_OP_PREP_WRITE_REQ)
+		conn->wait_execute_write = true;
 
-	/* TODO figure it out */
-	if (att_opcode == ATT_OP_EXEC_WRITE_REQ)
+	/* Store the request data, complete callback and transaction id */
+	transaction = conn_add_transact(conn, att_opcode);
+	if (!transaction)
 		goto failed;
 
-	memset(&ev, 0, sizeof(ev));
+	memset(ev, 0, sizeof(*ev));
 
-	bdaddr2android(bdaddr, ev.bdaddr);
-	ev.attr_handle = handle;
-	ev.offset = offset;
+	bdaddr2android(bdaddr, &ev->bdaddr);
+	ev->attr_handle = handle;
+	ev->offset = offset;
 
-	ev.conn_id = conn->id;
-	ev.trans_id = app->trans_id.id;
+	ev->conn_id = conn->id;
+	ev->trans_id = transaction->id;
 
-	ev.is_prep = att_opcode == ATT_OP_PREP_WRITE_REQ;
-	ev.need_rsp = att_opcode == ATT_OP_WRITE_REQ;
+	ev->is_prep = att_opcode == ATT_OP_PREP_WRITE_REQ;
+	ev->need_rsp = att_opcode == ATT_OP_WRITE_REQ;
 
-	ev.length = len;
-	memcpy(&ev.value, value, len);
+	ev->length = len;
+	memcpy(ev->value, value, len);
 
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_REQUEST_WRITE,
+						sizeof(*ev) + ev->length , ev);
 	return;
 
 failed:
-	send_gatt_response(att_opcode, handle, 0, ATT_ECODE_UNLIKELY, 0, NULL,
-								bdaddr);
+	dev = find_device_by_addr(bdaddr);
+	if (dev)
+		fill_gatt_response_by_handle(handle, 0, ATT_ECODE_UNLIKELY, 0,
+								NULL, dev);
+}
+
+static uint32_t android_to_gatt_permissions(int32_t hal_permissions)
+{
+	uint32_t permissions = 0;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_READ)
+		permissions |= GATT_PERM_READ;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_READ_ENCRYPTED)
+		permissions |= GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_READ_ENCRYPTED_MITM)
+		permissions |= GATT_PERM_READ_MITM | GATT_PERM_READ_ENCRYPTED |
+								GATT_PERM_READ;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_WRITE)
+		permissions |= GATT_PERM_WRITE;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_WRITE_ENCRYPTED)
+		permissions |= GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_WRITE_ENCRYPTED_MITM)
+		permissions |= GATT_PERM_WRITE_MITM |
+				GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_WRITE_SIGNED)
+		permissions |= GATT_PERM_WRITE_SIGNED;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_WRITE_SIGNED_MITM)
+		permissions |= GATT_PERM_WRITE_SIGNED_MITM |
+							GATT_PERM_WRITE_SIGNED;
+
+	return permissions;
 }
 
 static void handle_server_add_characteristic(const void *buf, uint16_t len)
@@ -3942,6 +4618,7 @@
 	struct gatt_app *server;
 	bt_uuid_t uuid;
 	uint8_t status;
+	uint32_t permissions;
 	int32_t app_id = cmd->server_if;
 
 	DBG("");
@@ -3955,11 +4632,11 @@
 	}
 
 	android2uuid(cmd->uuid, &uuid);
+	permissions = android_to_gatt_permissions(cmd->permissions);
 
-	/*FIXME: Handle properties. Register callback if needed. */
 	ev.char_handle = gatt_db_add_characteristic(gatt_db,
 							cmd->service_handle,
-							&uuid, cmd->permissions,
+							&uuid, permissions,
 							cmd->properties,
 							read_cb, write_cb,
 							INT_TO_PTR(app_id));
@@ -3989,6 +4666,7 @@
 	struct gatt_app *server;
 	bt_uuid_t uuid;
 	uint8_t status;
+	uint32_t permissions;
 	int32_t app_id = cmd->server_if;
 
 	DBG("");
@@ -4002,11 +4680,11 @@
 	}
 
 	android2uuid(cmd->uuid, &uuid);
+	permissions = android_to_gatt_permissions(cmd->permissions);
 
-	/*FIXME: Handle properties. Register callback if needed. */
 	ev.descr_handle = gatt_db_add_char_descriptor(gatt_db,
 							cmd->service_handle,
-							&uuid, cmd->permissions,
+							&uuid, permissions,
 							read_cb, write_cb,
 							INT_TO_PTR(app_id));
 	if (!ev.descr_handle)
@@ -4027,6 +4705,20 @@
 				HAL_OP_GATT_SERVER_ADD_DESCRIPTOR, status);
 }
 
+static void notify_service_change(void *data, void *user_data)
+{
+	struct att_range range;
+
+	range.start = PTR_TO_UINT(user_data);
+	range.end = gatt_db_get_end_handle(gatt_db, range.start);
+
+	/* In case of db error */
+	if (!range.end)
+		return;
+
+	notify_att_range_change(data, &range);
+}
+
 static void handle_server_start_service(const void *buf, uint16_t len)
 {
 	const struct hal_cmd_gatt_server_start_service *cmd = buf;
@@ -4052,6 +4744,9 @@
 		goto failed;
 	}
 
+	queue_foreach(gatt_devices, notify_service_change,
+					UINT_TO_PTR(cmd->service_handle));
+
 	status = HAL_STATUS_SUCCESS;
 
 failed:
@@ -4088,6 +4783,9 @@
 	else
 		status = HAL_STATUS_SUCCESS;
 
+	queue_foreach(gatt_devices, notify_service_change,
+					UINT_TO_PTR(cmd->service_handle));
+
 failed:
 	ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
 	ev.server_if = cmd->server_if;
@@ -4139,10 +4837,11 @@
 static void handle_server_send_indication(const void *buf, uint16_t len)
 {
 	const struct hal_cmd_gatt_server_send_indication *cmd = buf;
-	uint8_t pdu[ATT_DEFAULT_LE_MTU];
 	struct app_connection *conn;
 	uint8_t status;
 	uint16_t length;
+	uint8_t *pdu;
+	size_t mtu;
 
 	DBG("");
 
@@ -4153,15 +4852,17 @@
 		goto reply;
 	}
 
+	pdu = g_attrib_get_buffer(conn->device->attrib, &mtu);
+
 	if (cmd->confirm)
 		/* TODO: Add data to track confirmation for this request */
 		length = enc_indication(cmd->attribute_handle,
-					(uint8_t *)cmd->value, cmd->len,
-					pdu, sizeof(pdu));
+					(uint8_t *)cmd->value, cmd->len, pdu,
+					mtu);
 	else
 		length = enc_notification(cmd->attribute_handle,
 						(uint8_t *)cmd->value, cmd->len,
-						pdu, sizeof(pdu));
+						pdu, mtu);
 
 	g_attrib_send(conn->device->attrib, 0, pdu, length, NULL, NULL, NULL);
 
@@ -4172,11 +4873,33 @@
 				HAL_OP_GATT_SERVER_SEND_INDICATION, status);
 }
 
+static bool match_trans_id(const void *data, const void *user_data)
+{
+	const struct pending_trans_data *transaction = data;
+
+	return transaction->id == PTR_TO_UINT(user_data);
+}
+
+
+static bool find_conn_waiting_exec_write(const void *data,
+							const void *user_data)
+{
+	const struct app_connection *conn = data;
+
+	return conn->wait_execute_write;
+}
+
+static bool pending_execute_write(void)
+{
+	return queue_find(app_connections, find_conn_waiting_exec_write, NULL);
+}
+
 static void handle_server_send_response(const void *buf, uint16_t len)
 {
 	const struct hal_cmd_gatt_server_send_response *cmd = buf;
+	struct pending_trans_data *transaction;
+	uint16_t handle = cmd->handle;
 	struct app_connection *conn;
-	struct gatt_app *app;
 	uint8_t status;
 
 	DBG("");
@@ -4188,22 +4911,37 @@
 		goto reply;
 	}
 
-	app = conn->app;
-
-	if ((unsigned int)cmd->trans_id != app->trans_id.id) {
-		error("gatt: transaction ID mismatch (%d!=%d)",
-					cmd->trans_id, app->trans_id.id);
-
+	transaction = queue_remove_if(conn->transactions, match_trans_id,
+						UINT_TO_PTR(cmd->trans_id));
+	if (!transaction) {
+		error("gatt: transaction ID = %d not found", cmd->trans_id);
 		status = HAL_STATUS_FAILED;
 		goto reply;
 	}
 
-	send_gatt_response(conn->app->trans_id.opcode, cmd->handle, cmd->offset,
-					cmd->status, cmd->len, cmd->data,
-					&conn->device->bdaddr);
+	if (transaction->opcode == ATT_OP_EXEC_WRITE_REQ) {
+		conn->wait_execute_write = false;
 
+		/* Check for execute response from all server applications */
+		if (pending_execute_write())
+			goto done;
+
+		/* Make sure handle is 0. We need it to find pending request */
+		handle = 0;
+
+		/*
+		 * FIXME: Handle situation when not all server applications
+		 * respond with a success.
+		 */
+	}
+
+	fill_gatt_response_by_handle(handle, cmd->offset, cmd->status, cmd->len,
+						cmd->data, conn->device);
+	send_dev_complete_response(conn->device, transaction->opcode);
+
+done:
 	/* Clean request data */
-	clear_trans_id(app);
+	free(transaction);
 
 	status = HAL_STATUS_SUCCESS;
 
@@ -4321,8 +5059,6 @@
 };
 
 static uint8_t read_by_group_type(const uint8_t *cmd, uint16_t cmd_len,
-						uint8_t *rsp, size_t rsp_size,
-						uint16_t *length,
 						struct gatt_device *device)
 {
 	uint16_t start, end;
@@ -4334,6 +5070,9 @@
 	if (!len)
 		return ATT_ECODE_INVALID_PDU;
 
+	if (start > end || start == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
 	q = queue_new();
 	if (!q)
 		return ATT_ECODE_INSUFF_RESOURCES;
@@ -4347,8 +5086,6 @@
 
 	while (queue_peek_head(q)) {
 		uint16_t handle = PTR_TO_UINT(queue_pop_head(q));
-		uint8_t *value;
-		int value_len;
 		struct pending_request *entry;
 
 		entry = new0(struct pending_request, 1);
@@ -4358,31 +5095,19 @@
 		}
 
 		entry->handle = handle;
-
-		if (!gatt_db_read(gatt_db, handle, 0, ATT_OP_READ_BY_GROUP_REQ,
-					&device->bdaddr, &value, &value_len))
-			break;
-
-		entry->value = malloc0(value_len);
-		if (!entry->value) {
-			queue_destroy(q, destroy_pending_request);
-			return ATT_ECODE_UNLIKELY;
-		}
-
-		memcpy(entry->value, value, value_len);
-		entry->length = value_len;
+		entry->state = REQUEST_INIT;
 
 		if (!queue_push_tail(device->pending_requests, entry)) {
 			queue_remove_all(device->pending_requests, NULL, NULL,
 						destroy_pending_request);
+			free(entry);
 			queue_destroy(q, NULL);
 			return ATT_ECODE_UNLIKELY;
 		}
 	}
 
 	queue_destroy(q, NULL);
-
-	send_dev_pending_response(device, cmd[0]);
+	process_dev_pending_requests(device, cmd[0]);
 
 	return 0;
 }
@@ -4401,6 +5126,9 @@
 	if (!len)
 		return ATT_ECODE_INVALID_PDU;
 
+	if (start > end || start == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
 	q = queue_new();
 	if (!q)
 		return ATT_ECODE_INSUFF_RESOURCES;
@@ -4422,7 +5150,7 @@
 			return ATT_ECODE_INSUFF_RESOURCES;
 		}
 
-		data->length = READ_INIT;
+		data->state = REQUEST_INIT;
 		data->handle = handle;
 		queue_push_tail(device->pending_requests, data);
 	}
@@ -4461,13 +5189,16 @@
 		return ATT_ECODE_REQ_NOT_SUPP;
 	}
 
+	if (handle == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
 	data = new0(struct pending_request, 1);
 	if (!data)
 		return ATT_ECODE_INSUFF_RESOURCES;
 
 	data->offset = offset;
 	data->handle = handle;
-	data->length = READ_INIT;
+	data->state = REQUEST_INIT;
 	if (!queue_push_tail(dev->pending_requests, data)) {
 		free(data);
 		return ATT_ECODE_INSUFF_RESOURCES;
@@ -4479,14 +5210,14 @@
 }
 
 static uint8_t mtu_att_handle(const uint8_t *cmd, uint16_t cmd_len,
-					uint8_t *rsp, size_t rsp_size,
-					struct gatt_device *dev,
-					uint16_t *length)
+							struct gatt_device *dev)
 {
 	uint16_t mtu, imtu, omtu;
+	size_t length;
 	GIOChannel *io;
 	GError *gerr = NULL;
 	uint16_t len;
+	uint8_t *rsp;
 
 	DBG("");
 
@@ -4509,16 +5240,18 @@
 		return ATT_ECODE_UNLIKELY;
 	}
 
-	/* Limit OMTU to received value */
-	mtu = MIN(mtu, omtu);
-	g_attrib_set_mtu(dev->attrib, mtu);
+	rsp = g_attrib_get_buffer(dev->attrib, &length);
 
 	/* Respond with our IMTU */
-	len = enc_mtu_resp(imtu, rsp, rsp_size);
+	len = enc_mtu_resp(imtu, rsp, length);
 	if (!len)
 		return ATT_ECODE_UNLIKELY;
 
-	*length = len;
+	g_attrib_send(dev->attrib, 0, rsp, len, NULL, NULL, NULL);
+
+	/* Limit OMTU to received value */
+	mtu = MIN(mtu, omtu);
+	g_attrib_set_mtu(dev->attrib, mtu);
 
 	return 0;
 }
@@ -4539,6 +5272,9 @@
 	if (!len)
 		return ATT_ECODE_INVALID_PDU;
 
+	if (start > end || start == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
 	q = queue_new();
 	if (!q)
 		return ATT_ECODE_UNLIKELY;
@@ -4593,7 +5329,7 @@
 static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len,
 						struct gatt_device *device)
 {
-	uint8_t search_value[ATT_DEFAULT_LE_MTU];
+	uint8_t search_value[cmd_len];
 	size_t search_vlen;
 	uint16_t start, end;
 	uint16_t handle;
@@ -4608,6 +5344,9 @@
 	if (!len)
 		return ATT_ECODE_INVALID_PDU;
 
+	if (start > end || start == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
 	q = queue_new();
 	if (!q)
 		return ATT_ECODE_UNLIKELY;
@@ -4625,13 +5364,13 @@
 		}
 
 		data->filter_value = malloc0(search_vlen);
-		if (!data) {
+		if (!data->filter_value) {
 			destroy_pending_request(data);
 			queue_destroy(q, NULL);
 			return ATT_ECODE_INSUFF_RESOURCES;
 		}
 
-		data->length = READ_INIT;
+		data->state = REQUEST_INIT;
 		data->handle = handle;
 		data->filter_vlen = search_vlen;
 		memcpy(data->filter_value, search_value, search_vlen);
@@ -4648,40 +5387,139 @@
 	return 0;
 }
 
-static uint8_t write_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
+static void write_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
 						struct gatt_device *dev)
 {
-	uint8_t value[ATT_DEFAULT_LE_MTU];
+	uint8_t value[cmd_len];
+	uint32_t permissions;
 	uint16_t handle;
 	uint16_t len;
 	size_t vlen;
 
 	len = dec_write_cmd(cmd, cmd_len, &handle, value, &vlen);
 	if (!len)
-		return ATT_ECODE_INVALID_PDU;
+		return;
 
-	if (!gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0],
-								&dev->bdaddr))
-		return ATT_ECODE_UNLIKELY;
+	if (handle == 0)
+		return;
 
-	return 0;
+	if (!gatt_db_get_attribute_permissions(gatt_db, handle, &permissions))
+		return;
+
+	if (check_device_permissions(dev, cmd[0], permissions))
+		return;
+
+	gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0], &dev->bdaddr);
+}
+
+static void write_signed_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
+						struct gatt_device *dev)
+{
+	uint8_t value[ATT_DEFAULT_LE_MTU];
+	uint8_t s[ATT_SIGNATURE_LEN];
+	uint32_t permissions;
+	uint16_t handle;
+	uint16_t len;
+	size_t vlen;
+	uint8_t csrk[16];
+	uint32_t sign_cnt;
+
+	if (get_sec_level(dev) != BT_SECURITY_LOW) {
+		error("gatt: Remote tries write signed on encrypted link");
+		connection_cleanup(dev);
+		return;
+	}
+
+	if (!bt_get_csrk(&dev->bdaddr, REMOTE_CSRK, csrk, &sign_cnt)) {
+		error("gatt: No valid csrk from remote device");
+		return;
+	}
+
+	len = dec_signed_write_cmd(cmd, cmd_len, &handle, value, &vlen, s);
+
+	if (handle == 0)
+		return;
+
+	if (!gatt_db_get_attribute_permissions(gatt_db, handle, &permissions))
+		return;
+
+	if (check_device_permissions(dev, cmd[0], permissions))
+		return;
+
+	if (len) {
+		uint8_t t[ATT_SIGNATURE_LEN];
+		uint32_t r_sign_cnt = get_le32(s);
+
+		if (r_sign_cnt != sign_cnt) {
+			error("gatt: sign_cnt does not match (%d!=%d)",
+							sign_cnt, r_sign_cnt);
+			return;
+		}
+
+		/* Generate signature and verify it */
+		if (!bt_crypto_sign_att(crypto, csrk, cmd,
+						cmd_len - ATT_SIGNATURE_LEN,
+						sign_cnt, t)) {
+			error("gatt: Error when generating att signature");
+			return;
+		}
+
+		if (memcmp(t, s, ATT_SIGNATURE_LEN)) {
+			error("gatt: signature does not match");
+			return;
+		}
+		/* Signature OK, proceed with write */
+		bt_update_sign_counter(&dev->bdaddr, REMOTE_CSRK);
+		gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0],
+								&dev->bdaddr);
+	}
 }
 
 static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len,
 						struct gatt_device *dev)
 {
-	uint8_t value[ATT_DEFAULT_LE_MTU];
+	uint8_t value[cmd_len];
+	struct pending_request *data;
+	uint32_t permissions;
 	uint16_t handle;
 	uint16_t len;
+	uint8_t error;
 	size_t vlen;
 
 	len = dec_write_req(cmd, cmd_len, &handle, value, &vlen);
 	if (!len)
 		return ATT_ECODE_INVALID_PDU;
 
+	if (handle == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
+	if (!gatt_db_get_attribute_permissions(gatt_db, handle, &permissions))
+		return ATT_ECODE_ATTR_NOT_FOUND;
+
+	error = check_device_permissions(dev, cmd[0], permissions);
+	if (error)
+		return error;
+
+	data = new0(struct pending_request, 1);
+	if (!data)
+		return ATT_ECODE_INSUFF_RESOURCES;
+
+	data->handle = handle;
+	data->state = REQUEST_PENDING;
+
+	if (!queue_push_tail(dev->pending_requests, data)) {
+		free(data);
+		return ATT_ECODE_INSUFF_RESOURCES;
+	}
+
 	if (!gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0],
-								&dev->bdaddr))
+								&dev->bdaddr)) {
+		queue_remove(dev->pending_requests, data);
+		free(data);
 		return ATT_ECODE_UNLIKELY;
+	}
+
+	send_dev_complete_response(dev, cmd[0]);
 
 	return 0;
 }
@@ -4689,9 +5527,12 @@
 static uint8_t write_prep_request(const uint8_t *cmd, uint16_t cmd_len,
 						struct gatt_device *dev)
 {
-	uint8_t value[ATT_DEFAULT_LE_MTU];
+	uint8_t value[cmd_len];
+	struct pending_request *data;
+	uint32_t permissions;
 	uint16_t handle;
 	uint16_t offset;
+	uint8_t error;
 	uint16_t len;
 	size_t vlen;
 
@@ -4700,6 +5541,29 @@
 	if (!len)
 		return ATT_ECODE_INVALID_PDU;
 
+	if (handle == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
+	if (!gatt_db_get_attribute_permissions(gatt_db, handle, &permissions))
+		return ATT_ECODE_ATTR_NOT_FOUND;
+
+	error = check_device_permissions(dev, cmd[0], permissions);
+	if (error)
+		return error;
+
+	data = new0(struct pending_request, 1);
+	if (!data)
+		return ATT_ECODE_INSUFF_RESOURCES;
+
+	data->handle = handle;
+	data->offset = offset;
+	data->state = REQUEST_PENDING;
+
+	if (!queue_push_tail(dev->pending_requests, data)) {
+		free(data);
+		return ATT_ECODE_INSUFF_RESOURCES;
+	}
+
 	if (!gatt_db_write(gatt_db, handle, offset, value, vlen, cmd[0],
 								&dev->bdaddr))
 		return ATT_ECODE_UNLIKELY;
@@ -4707,27 +5571,85 @@
 	return 0;
 }
 
+static void send_server_write_execute_notify(void *data, void *user_data)
+{
+	struct hal_ev_gatt_server_request_exec_write *ev = user_data;
+	struct pending_trans_data *transaction;
+	struct app_connection *conn = data;
+
+	if (!conn->wait_execute_write)
+		return;
+
+	ev->conn_id = conn->id;
+
+	transaction = conn_add_transact(conn, ATT_OP_EXEC_WRITE_REQ);
+	if (!transaction) {
+		conn->wait_execute_write = false;
+		return;
+	}
+
+	ev->trans_id = transaction->id;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE,
+				sizeof(*ev), ev);
+}
+
+static uint8_t write_execute_request(const uint8_t *cmd, uint16_t cmd_len,
+						struct gatt_device *dev)
+{
+	struct hal_ev_gatt_server_request_exec_write ev;
+	uint8_t value;
+	struct pending_request *data;
+
+	/*
+	 * Check if there was any write prep before.
+	 * TODO: Try to find better error code if possible
+	 */
+	if (!pending_execute_write())
+		return ATT_ECODE_UNLIKELY;
+
+	if (!dec_exec_write_req(cmd, cmd_len, &value))
+		return ATT_ECODE_INVALID_PDU;
+
+	memset(&ev, 0, sizeof(ev));
+	bdaddr2android(&dev->bdaddr, &ev.bdaddr);
+	ev.exec_write = value;
+
+	data = new0(struct pending_request, 1);
+	if (!data)
+		return ATT_ECODE_INSUFF_RESOURCES;
+
+	data->state = REQUEST_PENDING;
+	if (!queue_push_tail(dev->pending_requests, data)) {
+		free(data);
+		return ATT_ECODE_INSUFF_RESOURCES;
+	}
+
+	queue_foreach(app_connections, send_server_write_execute_notify, &ev);
+
+	return 0;
+}
+
 static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
 {
 	struct gatt_device *dev = user_data;
-	uint8_t opdu[ATT_DEFAULT_LE_MTU];
 	uint8_t status;
-	uint16_t length = 0;
-	size_t vlen;
-	uint8_t *value = g_attrib_get_buffer(dev->attrib, &vlen);
+	uint16_t resp_length = 0;
+	size_t length;
+	uint8_t *opdu = g_attrib_get_buffer(dev->attrib, &length);
 
 	DBG("op 0x%02x", ipdu[0]);
 
-	if (len > vlen) {
-		error("gatt: Too much data on ATT socket %p", value);
+	if (len > length) {
+		error("gatt: Too much data on ATT socket %p", opdu);
 		status = ATT_ECODE_INVALID_PDU;
 		goto done;
 	}
 
 	switch (ipdu[0]) {
 	case ATT_OP_READ_BY_GROUP_REQ:
-		status = read_by_group_type(ipdu, len, opdu, sizeof(opdu),
-								&length, dev);
+		status = read_by_group_type(ipdu, len, dev);
 		break;
 	case ATT_OP_READ_BY_TYPE_REQ:
 		status = read_by_type(ipdu, len, dev);
@@ -4737,12 +5659,11 @@
 		status = read_request(ipdu, len, dev);
 		break;
 	case ATT_OP_MTU_REQ:
-		status = mtu_att_handle(ipdu, len, opdu, sizeof(opdu), dev,
-								&length);
+		status = mtu_att_handle(ipdu, len, dev);
 		break;
 	case ATT_OP_FIND_INFO_REQ:
-		status = find_info_handle(ipdu, len, opdu, sizeof(opdu),
-								&length);
+		status = find_info_handle(ipdu, len, opdu, length,
+								&resp_length);
 		break;
 	case ATT_OP_WRITE_REQ:
 		status = write_req_request(ipdu, len, dev);
@@ -4750,10 +5671,13 @@
 			return;
 		break;
 	case ATT_OP_WRITE_CMD:
-		status = write_cmd_request(ipdu, len, dev);
-		if (!status)
-			return;
-		break;
+		write_cmd_request(ipdu, len, dev);
+		/* No response on write cmd */
+		return;
+	case ATT_OP_SIGNED_WRITE_CMD:
+		write_signed_cmd_request(ipdu, len, dev);
+		/* No response on write signed cmd */
+		return;
 	case ATT_OP_PREP_WRITE_REQ:
 		status = write_prep_request(ipdu, len, dev);
 		if (!status)
@@ -4762,11 +5686,24 @@
 	case ATT_OP_FIND_BY_TYPE_REQ:
 		status = find_by_type_request(ipdu, len, dev);
 		break;
-	case ATT_OP_EXEC_WRITE_REQ:
-		/* TODO */
-	case ATT_OP_HANDLE_CNF:
 	case ATT_OP_HANDLE_IND:
+		/*
+		 * We have to send confirmation here. If some client is
+		 * registered for this indication, event will be send in
+		 * handle_notification
+		 */
+		resp_length = enc_confirmation(opdu, sizeof(opdu));
+		status = 0;
+		break;
 	case ATT_OP_HANDLE_NOTIFY:
+		/* Client will handle this */
+		return;
+	case ATT_OP_EXEC_WRITE_REQ:
+		status = write_execute_request(ipdu, len, dev);
+		if (!status)
+			return;
+		break;
+	case ATT_OP_HANDLE_CNF:
 	case ATT_OP_READ_MULTI_REQ:
 	default:
 		DBG("Unsupported request 0x%02x", ipdu[0]);
@@ -4776,11 +5713,12 @@
 
 done:
 	if (status)
-		length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
-							ATT_DEFAULT_LE_MTU);
+		resp_length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
+									length);
 
-	if (length)
-		g_attrib_send(dev->attrib, 0, opdu, length, NULL, NULL, NULL);
+	if (resp_length)
+		g_attrib_send(dev->attrib, 0, opdu, resp_length, NULL, NULL,
+									NULL);
 }
 
 static void create_listen_connections(void *data, void *user_data)
@@ -4794,21 +5732,15 @@
 		create_connection(dev, app);
 }
 
-static void connect_event(GIOChannel *io, GError *gerr, void *user_data)
+static void connect_confirm(GIOChannel *io, void *user_data)
 {
 	struct gatt_device *dev;
 	uint8_t dst_type;
 	bdaddr_t dst;
-	struct connect_data data;
+	GError *gerr = NULL;
 
 	DBG("");
 
-	if (gerr) {
-		error("gatt: %s", gerr->message);
-		g_error_free(gerr);
-		return;
-	}
-
 	bt_io_get(io, &gerr,
 			BT_IO_OPT_DEST_BDADDR, &dst,
 			BT_IO_OPT_DEST_TYPE, &dst_type,
@@ -4825,7 +5757,7 @@
 		dev = create_device(&dst);
 		if (!dev) {
 			error("gatt: Could not create device");
-			return;
+			goto drop;
 		}
 
 		dev->bdaddr_type = dst_type;
@@ -4836,32 +5768,23 @@
 			ba2str(&dst, addr);
 			info("gatt: Rejecting incoming connection from %s",
 									addr);
-			return;
+			goto drop;
 		}
 	}
 
-	dev->attrib = g_attrib_new(io);
-	if (!dev->attrib) {
-		error("gatt: unable to create new GAttrib instance");
-		destroy_device(dev);
-		return;
+	if (!bt_io_accept(io, connect_cb, device_ref(dev), NULL, NULL)) {
+		error("gatt: failed to accept connection");
+		device_unref(dev);
+		goto drop;
 	}
-	dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-							disconnected_cb, dev);
 
 	queue_foreach(listen_apps, create_listen_connections, dev);
+	device_set_state(dev, DEVICE_CONNECT_READY);
 
-	data.dev = dev;
-	data.status = GATT_SUCCESS;
-	device_set_state(dev, DEVICE_CONNECTED);
+	return;
 
-	queue_foreach(app_connections, send_app_connect_notifications, &data);
-
-	dev->server_id = g_attrib_register(dev->attrib, GATTRIB_ALL_REQS,
-						GATTRIB_ALL_HANDLES,
-						att_handler, dev, NULL);
-	if (dev->server_id == 0)
-		error("gatt: Could not attach to server");
+drop:
+	g_io_channel_shutdown(io, TRUE, NULL);
 }
 
 struct gap_srvc_handles {
@@ -4902,9 +5825,8 @@
 
 		entry->value = malloc0(strlen(name));
 		if (!entry->value) {
-			queue_remove(dev->pending_requests, entry);
-			free(entry);
-			return;
+			entry->error = ATT_ECODE_INSUFF_RESOURCES;
+			goto done;
 		}
 
 		entry->length = strlen(name);
@@ -4912,9 +5834,8 @@
 	} else if (handle == gap_srvc_data.appear) {
 		entry->value = malloc0(2);
 		if (!entry->value) {
-			queue_remove(dev->pending_requests, entry);
-			free(entry);
-			return;
+			entry->error = ATT_ECODE_INSUFF_RESOURCES;
+			goto done;
 		}
 
 		put_le16(APPEARANCE_GENERIC_PHONE, entry->value);
@@ -4922,16 +5843,20 @@
 	} else if (handle == gap_srvc_data.priv) {
 		entry->value = malloc0(1);
 		if (!entry->value) {
-			queue_remove(dev->pending_requests, entry);
-			free(entry);
-			return;
+			entry->error = ATT_ECODE_INSUFF_RESOURCES;
+			goto done;
 		}
 
 		*entry->value = PERIPHERAL_PRIVACY_DISABLE;
 		entry->length = sizeof(uint8_t);
+	} else {
+		entry->error = ATT_ECODE_ATTR_NOT_FOUND;
 	}
 
 	entry->offset = offset;
+
+done:
+	entry->state  = REQUEST_DONE;
 }
 
 static void register_gap_service(void)
@@ -4946,7 +5871,7 @@
 	bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
 	gap_srvc_data.dev_name =
 			gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc,
-							&uuid, 0,
+							&uuid, GATT_PERM_READ,
 							GATT_CHR_PROP_READ,
 							gap_read_cb, NULL,
 							NULL);
@@ -4955,7 +5880,7 @@
 	bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
 	gap_srvc_data.appear =
 			gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc,
-							&uuid, 0,
+							&uuid, GATT_PERM_READ,
 							GATT_CHR_PROP_READ,
 							gap_read_cb, NULL,
 							NULL);
@@ -4964,7 +5889,7 @@
 	bt_uuid16_create(&uuid, GATT_CHARAC_PERIPHERAL_PRIV_FLAG);
 	gap_srvc_data.priv =
 			gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc,
-							&uuid, 0,
+							&uuid, GATT_PERM_READ,
 							GATT_CHR_PROP_READ,
 							gap_read_cb, NULL,
 							NULL);
@@ -5012,14 +5937,16 @@
 
 	entry->value = malloc0(strlen(buf));
 	if (!entry->value) {
-		queue_remove(dev->pending_requests, entry);
-		free(entry);
-		return;
+		entry->error = ATT_ECODE_UNLIKELY;
+		goto done;
 	}
 
 	entry->length = strlen(buf);
 	memcpy(entry->value, buf, entry->length);
 	entry->offset = offset;
+
+done:
+	entry->state = REQUEST_DONE;
 }
 
 static void register_device_info_service(void)
@@ -5035,43 +5962,43 @@
 
 	/* User data are not const hence (void *) cast is used */
 	bt_uuid16_create(&uuid, GATT_CHARAC_SYSTEM_ID);
-	gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+	gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ,
 					GATT_CHR_PROP_READ,
 					device_info_read_cb, NULL,
 					(void *) device_info.system_id);
 
 	bt_uuid16_create(&uuid, GATT_CHARAC_MODEL_NUMBER_STRING);
-	gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+	gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ,
 					GATT_CHR_PROP_READ,
 					device_info_read_cb, NULL,
 					(void *) device_info.model_number);
 
 	bt_uuid16_create(&uuid, GATT_CHARAC_SERIAL_NUMBER_STRING);
-	gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+	gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ,
 					GATT_CHR_PROP_READ,
 					device_info_read_cb, NULL,
 					(void *) device_info.serial_number);
 
 	bt_uuid16_create(&uuid, GATT_CHARAC_FIRMWARE_REVISION_STRING);
-	gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+	gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ,
 					GATT_CHR_PROP_READ,
 					device_info_read_cb, NULL,
 					(void *) device_info.firmware_rev);
 
 	bt_uuid16_create(&uuid, GATT_CHARAC_HARDWARE_REVISION_STRING);
-	gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+	gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ,
 					GATT_CHR_PROP_READ,
 					device_info_read_cb, NULL,
 					(void *) device_info.hardware_rev);
 
 	bt_uuid16_create(&uuid, GATT_CHARAC_SOFTWARE_REVISION_STRING);
-	gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+	gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ,
 					GATT_CHR_PROP_READ,
 					device_info_read_cb, NULL,
 					(void *) device_info.software_rev);
 
 	bt_uuid16_create(&uuid, GATT_CHARAC_MANUFACTURER_NAME_STRING);
-	gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+	gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ,
 					GATT_CHR_PROP_READ,
 					device_info_read_cb, NULL,
 					(void *) device_info.manufacturer_name);
@@ -5079,15 +6006,14 @@
 	gatt_db_service_set_active(gatt_db, srvc_handle, true);
 }
 
-static void gatt_srvc_change_register_cb(uint16_t handle, uint16_t offset,
+static void gatt_srvc_change_write_cb(uint16_t handle, uint16_t offset,
 						const uint8_t *val, size_t len,
 						uint8_t att_opcode,
 						bdaddr_t *bdaddr,
 						void *user_data)
 {
-	uint8_t pdu[ATT_DEFAULT_LE_MTU];
+	struct pending_request *entry;
 	struct gatt_device *dev;
-	uint16_t length;
 
 	dev = find_device_by_addr(bdaddr);
 	if (!dev) {
@@ -5095,14 +6021,53 @@
 		return;
 	}
 
-	/* TODO handle CCC */
+	entry = queue_find(dev->pending_requests, match_dev_request_by_handle,
+							UINT_TO_PTR(handle));
+	if (!entry)
+		return;
 
-	/* Set services changed notification flag */
-	dev->notify_services_changed = !!(*val);
+	entry->state = REQUEST_DONE;
 
-	length = enc_write_resp(pdu);
+	if (!bt_device_is_bonded(bdaddr)) {
+		entry->error = ATT_ECODE_AUTHORIZATION;
+		return;
+	}
 
-	g_attrib_send(dev->attrib, 0, pdu, length, NULL, NULL, NULL);
+	/* Set services changed indication value */
+	bt_store_gatt_ccc(bdaddr, *val);
+}
+
+static void gatt_srvc_change_read_cb(uint16_t handle, uint16_t offset,
+					uint8_t att_opcode, bdaddr_t *bdaddr,
+					void *user_data)
+{
+	struct pending_request *entry;
+	struct gatt_device *dev;
+	uint16_t ccc = 0;
+
+	dev = find_device_by_addr(bdaddr);
+	if (!dev) {
+		error("gatt: Could not find device ?!");
+		return;
+	}
+
+	entry = queue_find(dev->pending_requests, match_dev_request_by_handle,
+							UINT_TO_PTR(handle));
+	if (!entry)
+		return;
+
+	ccc = bt_get_gatt_ccc(&dev->bdaddr);
+	entry->state = REQUEST_DONE;
+
+	entry->value = (uint8_t *) new0(uint16_t, 1);
+	if (!entry->value) {
+		entry->error = ATT_ECODE_INSUFF_RESOURCES;
+
+		return;
+	}
+
+	entry->length = sizeof(uint16_t);
+	memcpy(entry->value, &ccc, sizeof(ccc));
 }
 
 static void register_gatt_service(void)
@@ -5116,13 +6081,16 @@
 	srvc_handle = gatt_db_add_service(gatt_db, &uuid, true, 4);
 
 	bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
-	gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+	service_changed_handle =  gatt_db_add_characteristic(gatt_db,
+					srvc_handle, &uuid, 0,
 					GATT_CHR_PROP_INDICATE, NULL, NULL,
 					NULL);
 
 	bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
-	gatt_db_add_char_descriptor(gatt_db, srvc_handle, &uuid, 0, NULL,
-					gatt_srvc_change_register_cb, NULL);
+	gatt_db_add_char_descriptor(gatt_db, srvc_handle, &uuid,
+					GATT_PERM_READ | GATT_PERM_WRITE,
+					gatt_srvc_change_read_cb,
+					gatt_srvc_change_write_cb, NULL);
 
 	gatt_db_service_set_active(gatt_db, srvc_handle, true);
 }
@@ -5132,7 +6100,7 @@
 	GError *gerr = NULL;
 
 	/* For now only listen on BLE */
-	listening_io = bt_io_listen(connect_event, NULL,
+	listening_io = bt_io_listen(NULL, connect_confirm,
 					&listening_io, NULL, &gerr,
 					BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
 					BT_IO_OPT_CID, ATT_CID,
@@ -5154,14 +6122,33 @@
 	if (!start_listening_io())
 		return false;
 
+	if (!bt_le_register(le_device_found_handler)) {
+		error("gatt: bt_le_register failed");
+
+		g_io_channel_unref(listening_io);
+		listening_io = NULL;
+
+		return false;
+	}
+
+	crypto = bt_crypto_new();
+	if (!crypto) {
+		error("gatt: Failed to setup crypto");
+
+		g_io_channel_unref(listening_io);
+		listening_io = NULL;
+
+		return false;
+	}
+
 	gatt_devices = queue_new();
 	gatt_apps = queue_new();
 	app_connections = queue_new();
 	listen_apps = queue_new();
 	gatt_db = gatt_db_new();
 
-	if (!gatt_devices || !gatt_apps || !listen_apps ||
-						!app_connections || !gatt_db) {
+	if (!gatt_devices || !gatt_apps || !listen_apps || !app_connections ||
+								!gatt_db) {
 		error("gatt: Failed to allocate memory for queues");
 
 		queue_destroy(gatt_apps, NULL);
@@ -5182,6 +6169,8 @@
 		g_io_channel_unref(listening_io);
 		listening_io = NULL;
 
+		bt_crypto_unref(crypto);
+
 		return false;
 	}
 
@@ -5223,4 +6212,72 @@
 
 	g_io_channel_unref(listening_io);
 	listening_io = NULL;
+
+	bt_crypto_unref(crypto);
+	crypto = NULL;
+
+	bt_le_unregister();
+}
+
+
+unsigned int bt_gatt_register_app(const char *uuid, gatt_type_t type,
+							gatt_conn_cb_t func)
+{
+	struct gatt_app *app;
+	bt_uuid_t uuid128;
+
+	bt_string_to_uuid(&uuid128, uuid);
+	app = register_app((void *) &uuid128.value.u128, type);
+	if (!app)
+		return 0;
+
+	app->func = func;
+
+	return app->id;
+}
+
+bool bt_gatt_unregister_app(unsigned int id)
+{
+	uint8_t status;
+
+	status = unregister_app(id);
+
+	return status != HAL_STATUS_FAILED;
+}
+
+bool bt_gatt_connect_app(unsigned int id, const bdaddr_t *addr)
+{
+	uint8_t status;
+
+	status = handle_connect(id, addr);
+
+	return status != HAL_STATUS_FAILED;
+}
+
+bool bt_gatt_disconnect_app(unsigned int id, const bdaddr_t *addr)
+{
+	struct app_connection match;
+	struct app_connection *conn;
+	struct gatt_device *device;
+	struct gatt_app *app;
+
+	app = find_app_by_id(id);
+	if (!app)
+		return false;
+
+	device = find_device_by_addr(addr);
+	if (!device)
+		return false;
+
+	match.device = device;
+	match.app = app;
+
+	conn = queue_find(app_connections, match_connection_by_device_and_app,
+									&match);
+	if (!conn)
+		return false;
+
+	trigger_disconnection(conn);
+
+	return true;
 }
diff --git a/android/gatt.h b/android/gatt.h
index d4392d9..5ba9161 100644
--- a/android/gatt.h
+++ b/android/gatt.h
@@ -23,3 +23,18 @@
 
 bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr);
 void bt_gatt_unregister(void);
+
+
+typedef enum {
+	GATT_CLIENT,
+	GATT_SERVER,
+} gatt_type_t;
+
+typedef void (*gatt_conn_cb_t)(const bdaddr_t *addr, int err, void *attrib);
+
+unsigned int bt_gatt_register_app(const char *uuid, gatt_type_t type,
+							gatt_conn_cb_t func);
+bool bt_gatt_unregister_app(unsigned int id);
+
+bool bt_gatt_connect_app(unsigned int id, const bdaddr_t *addr);
+bool bt_gatt_disconnect_app(unsigned int id, const bdaddr_t *addr);
diff --git a/android/hal-a2dp.c b/android/hal-a2dp.c
index 68888b2..87aebde 100644
--- a/android/hal-a2dp.c
+++ b/android/hal-a2dp.c
@@ -31,7 +31,7 @@
 	return cbs != NULL;
 }
 
-static void handle_conn_state(void *buf, uint16_t len)
+static void handle_conn_state(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_a2dp_conn_state *ev = buf;
 
@@ -40,7 +40,7 @@
 						(bt_bdaddr_t *) (ev->bdaddr));
 }
 
-static void handle_audio_state(void *buf, uint16_t len)
+static void handle_audio_state(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_a2dp_audio_state *ev = buf;
 
@@ -53,16 +53,10 @@
  * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
  */
 static const struct hal_ipc_handler ev_handlers[] = {
-	{	/* HAL_EV_A2DP_CONN_STATE */
-		.handler = handle_conn_state,
-		.var_len = false,
-		.data_len = sizeof(struct hal_ev_a2dp_conn_state),
-	},
-	{	/* HAL_EV_A2DP_AUDIO_STATE */
-		.handler = handle_audio_state,
-		.var_len = false,
-		.data_len = sizeof(struct hal_ev_a2dp_audio_state),
-	},
+	/* HAL_EV_A2DP_CONN_STATE */
+	{ handle_conn_state, false, sizeof(struct hal_ev_a2dp_conn_state) },
+	/* HAL_EV_A2DP_AUDIO_STATE */
+	{ handle_audio_state, false, sizeof(struct hal_ev_a2dp_audio_state) },
 };
 
 static bt_status_t a2dp_connect(bt_bdaddr_t *bd_addr)
diff --git a/android/hal-audio-aptx.c b/android/hal-audio-aptx.c
new file mode 100644
index 0000000..09636b8
--- /dev/null
+++ b/android/hal-audio-aptx.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2014 Tieto Poland
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <malloc.h>
+#include <dlfcn.h>
+
+#include "audio-msg.h"
+#include "hal-audio.h"
+#include "hal-log.h"
+#include "src/shared/util.h"
+#include "profiles/audio/a2dp-codecs.h"
+
+#define APTX_SO_NAME	"libbt-aptx.so"
+
+struct aptx_data {
+	a2dp_aptx_t aptx;
+
+	void *enc;
+};
+
+static const a2dp_aptx_t aptx_presets[] = {
+	{
+		.info = {
+			.vendor_id = APTX_VENDOR_ID,
+			.codec_id = APTX_CODEC_ID,
+		},
+		.frequency = APTX_SAMPLING_FREQ_44100 |
+						APTX_SAMPLING_FREQ_48000,
+		.channel_mode = APTX_CHANNEL_MODE_STEREO,
+	},
+	{
+		.info = {
+			.vendor_id = APTX_VENDOR_ID,
+			.codec_id = APTX_CODEC_ID,
+		},
+		.frequency = APTX_SAMPLING_FREQ_48000,
+		.channel_mode = APTX_CHANNEL_MODE_STEREO,
+	},
+	{
+		.info = {
+			.vendor_id = APTX_VENDOR_ID,
+			.codec_id = APTX_CODEC_ID,
+		},
+		.frequency = APTX_SAMPLING_FREQ_44100,
+		.channel_mode = APTX_CHANNEL_MODE_STEREO,
+	},
+};
+
+static void *aptx_handle;
+static int aptx_btencsize;
+static int (*aptx_init)(void *, short);
+static int (*aptx_encode)(void *, void *, void *, void *);
+
+static bool aptx_load(void)
+{
+	const char * (*aptx_version)(void);
+	const char * (*aptx_build)(void);
+	int (*aptx_sizeofenc)(void);
+
+	aptx_handle = dlopen(APTX_SO_NAME, RTLD_LAZY);
+	if (!aptx_handle) {
+		error("APTX: failed to open library %s (%s)", APTX_SO_NAME,
+								dlerror());
+		return false;
+	}
+
+	aptx_version = dlsym(aptx_handle, "aptxbtenc_version");
+	aptx_build = dlsym(aptx_handle, "aptxbtenc_build");
+
+	if (aptx_version && aptx_build)
+		info("APTX: using library version %s build %s", aptx_version(),
+								aptx_build());
+	else
+		warn("APTX: cannot retrieve library version");
+
+	aptx_sizeofenc = dlsym(aptx_handle, "SizeofAptxbtenc");
+	aptx_init = dlsym(aptx_handle, "aptxbtenc_init");
+	aptx_encode = dlsym(aptx_handle, "aptxbtenc_encodestereo");
+	if (!aptx_sizeofenc || !aptx_init || !aptx_encode) {
+		error("APTX: failed initialize library");
+		dlclose(aptx_handle);
+		aptx_handle = NULL;
+		return false;
+	}
+	aptx_btencsize = aptx_sizeofenc();
+
+	info("APTX: codec library initialized (size=%d)", aptx_btencsize);
+
+	return true;
+}
+
+static void aptx_unload(void)
+{
+	if (!aptx_handle)
+		return;
+
+	dlclose(aptx_handle);
+	aptx_handle = NULL;
+}
+
+static int aptx_get_presets(struct audio_preset *preset, size_t *len)
+{
+	int i;
+	int count;
+	size_t new_len = 0;
+	uint8_t *ptr = (uint8_t *) preset;
+	size_t preset_size = sizeof(*preset) + sizeof(a2dp_aptx_t);
+
+	DBG("");
+
+	count = sizeof(aptx_presets) / sizeof(aptx_presets[0]);
+
+	for (i = 0; i < count; i++) {
+		preset = (struct audio_preset *) ptr;
+
+		if (new_len + preset_size > *len)
+			break;
+
+		preset->len = sizeof(a2dp_aptx_t);
+		memcpy(preset->data, &aptx_presets[i], preset->len);
+
+		new_len += preset_size;
+		ptr += preset_size;
+	}
+
+	*len = new_len;
+
+	return i;
+}
+
+static bool aptx_codec_init(struct audio_preset *preset, uint16_t payload_len,
+							void **codec_data)
+{
+	struct aptx_data *aptx_data;
+
+	DBG("");
+
+	if (preset->len != sizeof(a2dp_aptx_t)) {
+		error("APTX: preset size mismatch");
+		return false;
+	}
+
+	aptx_data = new0(struct aptx_data, 1);
+	if (!aptx_data)
+		return false;
+
+	memcpy(&aptx_data->aptx, preset->data, preset->len);
+
+	aptx_data->enc = calloc(1, aptx_btencsize);
+	if (!aptx_data->enc) {
+		error("APTX: failed to create encoder");
+		free(aptx_data);
+		return false;
+	}
+
+	/* 1 = big-endian, this is what devices are using */
+	aptx_init(aptx_data->enc, 1);
+
+	*codec_data = aptx_data;
+
+	return true;
+}
+
+static bool aptx_cleanup(void *codec_data)
+{
+	struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+
+	free(aptx_data->enc);
+	free(codec_data);
+
+	return true;
+}
+
+static bool aptx_get_config(void *codec_data, struct audio_input_config *config)
+{
+	struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+
+	config->rate = aptx_data->aptx.frequency & APTX_SAMPLING_FREQ_48000 ?
+								48000 : 44100;
+	config->channels = AUDIO_CHANNEL_OUT_STEREO;
+	config->format = AUDIO_FORMAT_PCM_16_BIT;
+
+	return true;
+}
+
+static size_t aptx_get_buffer_size(void *codec_data)
+{
+	/* TODO: return actual value */
+	return 0;
+}
+
+static size_t aptx_get_mediapacket_duration(void *codec_data)
+{
+	/* TODO: return actual value */
+	return 0;
+}
+
+static ssize_t aptx_encode_mediapacket(void *codec_data, const uint8_t *buffer,
+					size_t len, struct media_packet *mp,
+					size_t mp_data_len, size_t *written)
+{
+	struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+	const int16_t *ptr = (const void *) buffer;
+	size_t bytes_in = 0;
+	size_t bytes_out = 0;
+
+	while ((len - bytes_in) >= 16 && (mp_data_len - bytes_out) >= 4) {
+		int pcm_l[4], pcm_r[4];
+		int i;
+
+		for (i = 0; i < 4; i++) {
+			pcm_l[i] = ptr[0];
+			pcm_r[i] = ptr[1];
+			ptr += 2;
+		}
+
+		aptx_encode(aptx_data->enc, pcm_l, pcm_r, &mp->data[bytes_out]);
+
+		bytes_in += 16;
+		bytes_out += 4;
+	}
+
+	*written = bytes_out;
+
+	return bytes_in;
+}
+
+static bool aptx_update_qos(void *codec_data, uint8_t op)
+{
+	/*
+	 * aptX has constant bitrate of 352kbps (with constant 4:1 compression
+	 * ratio) thus QoS is not possible here.
+	 */
+
+	return false;
+}
+
+static const struct audio_codec codec = {
+	.type = A2DP_CODEC_VENDOR,
+	.use_rtp = false,
+
+	.load = aptx_load,
+	.unload = aptx_unload,
+
+	.get_presets = aptx_get_presets,
+
+	.init = aptx_codec_init,
+	.cleanup = aptx_cleanup,
+	.get_config = aptx_get_config,
+	.get_buffer_size = aptx_get_buffer_size,
+	.get_mediapacket_duration = aptx_get_mediapacket_duration,
+	.encode_mediapacket = aptx_encode_mediapacket,
+	.update_qos = aptx_update_qos,
+};
+
+const struct audio_codec *codec_aptx(void)
+{
+	return &codec;
+}
diff --git a/android/hal-audio-sbc.c b/android/hal-audio-sbc.c
new file mode 100644
index 0000000..0c31035
--- /dev/null
+++ b/android/hal-audio-sbc.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <malloc.h>
+
+#include <sbc/sbc.h>
+#include "audio-msg.h"
+#include "hal-audio.h"
+#include "hal-log.h"
+#include "../profiles/audio/a2dp-codecs.h"
+
+#define MAX_FRAMES_IN_PAYLOAD 15
+
+#define SBC_QUALITY_MIN_BITPOOL	33
+#define SBC_QUALITY_STEP	5
+
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_payload {
+	unsigned frame_count:4;
+	unsigned rfa0:1;
+	unsigned is_last_fragment:1;
+	unsigned is_first_fragment:1;
+	unsigned is_fragmented:1;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_payload {
+	unsigned is_fragmented:1;
+	unsigned is_first_fragment:1;
+	unsigned is_last_fragment:1;
+	unsigned rfa0:1;
+	unsigned frame_count:4;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct media_packet_sbc {
+	struct media_packet_rtp hdr;
+	struct rtp_payload payload;
+	uint8_t data[0];
+};
+
+struct sbc_data {
+	a2dp_sbc_t sbc;
+
+	sbc_t enc;
+
+	uint16_t payload_len;
+
+	size_t in_frame_len;
+	size_t in_buf_size;
+
+	size_t out_frame_len;
+
+	unsigned frame_duration;
+	unsigned frames_per_packet;
+};
+
+static const a2dp_sbc_t sbc_presets[] = {
+	{
+		.frequency = SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000,
+		.channel_mode = SBC_CHANNEL_MODE_MONO |
+				SBC_CHANNEL_MODE_DUAL_CHANNEL |
+				SBC_CHANNEL_MODE_STEREO |
+				SBC_CHANNEL_MODE_JOINT_STEREO,
+		.subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8,
+		.allocation_method = SBC_ALLOCATION_SNR |
+					SBC_ALLOCATION_LOUDNESS,
+		.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 |
+				SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16,
+		.min_bitpool = MIN_BITPOOL,
+		.max_bitpool = MAX_BITPOOL
+	},
+	{
+		.frequency = SBC_SAMPLING_FREQ_44100,
+		.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
+		.subbands = SBC_SUBBANDS_8,
+		.allocation_method = SBC_ALLOCATION_LOUDNESS,
+		.block_length = SBC_BLOCK_LENGTH_16,
+		.min_bitpool = MIN_BITPOOL,
+		.max_bitpool = MAX_BITPOOL
+	},
+	{
+		.frequency = SBC_SAMPLING_FREQ_48000,
+		.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
+		.subbands = SBC_SUBBANDS_8,
+		.allocation_method = SBC_ALLOCATION_LOUDNESS,
+		.block_length = SBC_BLOCK_LENGTH_16,
+		.min_bitpool = MIN_BITPOOL,
+		.max_bitpool = MAX_BITPOOL
+	},
+};
+
+static int sbc_get_presets(struct audio_preset *preset, size_t *len)
+{
+	int i;
+	int count;
+	size_t new_len = 0;
+	uint8_t *ptr = (uint8_t *) preset;
+	size_t preset_size = sizeof(*preset) + sizeof(a2dp_sbc_t);
+
+	count = sizeof(sbc_presets) / sizeof(sbc_presets[0]);
+
+	for (i = 0; i < count; i++) {
+		preset = (struct audio_preset *) ptr;
+
+		if (new_len + preset_size > *len)
+			break;
+
+		preset->len = sizeof(a2dp_sbc_t);
+		memcpy(preset->data, &sbc_presets[i], preset->len);
+
+		new_len += preset_size;
+		ptr += preset_size;
+	}
+
+	*len = new_len;
+
+	return i;
+}
+
+static int sbc_freq2int(uint8_t freq)
+{
+	switch (freq) {
+	case SBC_SAMPLING_FREQ_16000:
+		return 16000;
+	case SBC_SAMPLING_FREQ_32000:
+		return 32000;
+	case SBC_SAMPLING_FREQ_44100:
+		return 44100;
+	case SBC_SAMPLING_FREQ_48000:
+		return 48000;
+	default:
+		return 0;
+	}
+}
+
+static const char *sbc_mode2str(uint8_t mode)
+{
+	switch (mode) {
+	case SBC_CHANNEL_MODE_MONO:
+		return "Mono";
+	case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+		return "DualChannel";
+	case SBC_CHANNEL_MODE_STEREO:
+		return "Stereo";
+	case SBC_CHANNEL_MODE_JOINT_STEREO:
+		return "JointStereo";
+	default:
+		return "(unknown)";
+	}
+}
+
+static int sbc_blocks2int(uint8_t blocks)
+{
+	switch (blocks) {
+	case SBC_BLOCK_LENGTH_4:
+		return 4;
+	case SBC_BLOCK_LENGTH_8:
+		return 8;
+	case SBC_BLOCK_LENGTH_12:
+		return 12;
+	case SBC_BLOCK_LENGTH_16:
+		return 16;
+	default:
+		return 0;
+	}
+}
+
+static int sbc_subbands2int(uint8_t subbands)
+{
+	switch (subbands) {
+	case SBC_SUBBANDS_4:
+		return 4;
+	case SBC_SUBBANDS_8:
+		return 8;
+	default:
+		return 0;
+	}
+}
+
+static const char *sbc_allocation2str(uint8_t allocation)
+{
+	switch (allocation) {
+	case SBC_ALLOCATION_SNR:
+		return "SNR";
+	case SBC_ALLOCATION_LOUDNESS:
+		return "Loudness";
+	default:
+		return "(unknown)";
+	}
+}
+
+static void sbc_init_encoder(struct sbc_data *sbc_data)
+{
+	a2dp_sbc_t *in = &sbc_data->sbc;
+	sbc_t *out = &sbc_data->enc;
+
+	sbc_init_a2dp(out, 0L, in, sizeof(*in));
+
+	out->endian = SBC_LE;
+	out->bitpool = in->max_bitpool;
+
+	DBG("frequency=%d channel_mode=%s block_length=%d subbands=%d allocation=%s bitpool=%d-%d",
+			sbc_freq2int(in->frequency),
+			sbc_mode2str(in->channel_mode),
+			sbc_blocks2int(in->block_length),
+			sbc_subbands2int(in->subbands),
+			sbc_allocation2str(in->allocation_method),
+			in->min_bitpool, in->max_bitpool);
+}
+
+static void sbc_codec_calculate(struct sbc_data *sbc_data)
+{
+	size_t in_frame_len;
+	size_t out_frame_len;
+	size_t num_frames;
+
+	in_frame_len = sbc_get_codesize(&sbc_data->enc);
+	out_frame_len = sbc_get_frame_length(&sbc_data->enc);
+	num_frames = sbc_data->payload_len / out_frame_len;
+
+	if (num_frames > MAX_FRAMES_IN_PAYLOAD)
+		num_frames = MAX_FRAMES_IN_PAYLOAD;
+
+	sbc_data->in_frame_len = in_frame_len;
+	sbc_data->in_buf_size = num_frames * in_frame_len;
+
+	sbc_data->out_frame_len = out_frame_len;
+
+	sbc_data->frame_duration = sbc_get_frame_duration(&sbc_data->enc);
+	sbc_data->frames_per_packet = num_frames;
+
+	DBG("in_frame_len=%zu out_frame_len=%zu frames_per_packet=%zu",
+				in_frame_len, out_frame_len, num_frames);
+}
+
+static bool sbc_codec_init(struct audio_preset *preset, uint16_t payload_len,
+							void **codec_data)
+{
+	struct sbc_data *sbc_data;
+
+	if (preset->len != sizeof(a2dp_sbc_t)) {
+		error("SBC: preset size mismatch");
+		return false;
+	}
+
+	sbc_data = calloc(sizeof(struct sbc_data), 1);
+	if (!sbc_data)
+		return false;
+
+	memcpy(&sbc_data->sbc, preset->data, preset->len);
+
+	sbc_init_encoder(sbc_data);
+
+	sbc_data->payload_len = payload_len;
+
+	sbc_codec_calculate(sbc_data);
+
+	*codec_data = sbc_data;
+
+	return true;
+}
+
+static bool sbc_cleanup(void *codec_data)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+	sbc_finish(&sbc_data->enc);
+	free(codec_data);
+
+	return true;
+}
+
+static bool sbc_get_config(void *codec_data, struct audio_input_config *config)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+	switch (sbc_data->sbc.frequency) {
+	case SBC_SAMPLING_FREQ_16000:
+		config->rate = 16000;
+		break;
+	case SBC_SAMPLING_FREQ_32000:
+		config->rate = 32000;
+		break;
+	case SBC_SAMPLING_FREQ_44100:
+		config->rate = 44100;
+		break;
+	case SBC_SAMPLING_FREQ_48000:
+		config->rate = 48000;
+		break;
+	default:
+		return false;
+	}
+	config->channels = sbc_data->sbc.channel_mode == SBC_CHANNEL_MODE_MONO ?
+				AUDIO_CHANNEL_OUT_MONO :
+				AUDIO_CHANNEL_OUT_STEREO;
+	config->format = AUDIO_FORMAT_PCM_16_BIT;
+
+	return true;
+}
+
+static size_t sbc_get_buffer_size(void *codec_data)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+	return sbc_data->in_buf_size;
+}
+
+static size_t sbc_get_mediapacket_duration(void *codec_data)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+	return sbc_data->frame_duration * sbc_data->frames_per_packet;
+}
+
+static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
+					size_t len, struct media_packet *mp,
+					size_t mp_data_len, size_t *written)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+	struct media_packet_sbc *mp_sbc = (struct media_packet_sbc *) mp;
+	size_t consumed = 0;
+	size_t encoded = 0;
+	uint8_t frame_count = 0;
+
+	mp_data_len -= sizeof(mp_sbc->payload);
+
+	while (len - consumed >= sbc_data->in_frame_len &&
+			mp_data_len - encoded >= sbc_data->out_frame_len &&
+			frame_count < sbc_data->frames_per_packet) {
+		ssize_t read;
+		ssize_t written = 0;
+
+		read = sbc_encode(&sbc_data->enc, buffer + consumed,
+				sbc_data->in_frame_len, mp_sbc->data + encoded,
+				mp_data_len - encoded, &written);
+
+		if (read < 0) {
+			error("SBC: failed to encode block at frame %d (%zd)",
+							frame_count, read);
+			break;
+		}
+
+		frame_count++;
+		consumed += read;
+		encoded += written;
+	}
+
+	*written = encoded + sizeof(mp_sbc->payload);
+	mp_sbc->payload.frame_count = frame_count;
+
+	return consumed;
+}
+
+static bool sbc_update_qos(void *codec_data, uint8_t op)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+	uint8_t curr_bitpool = sbc_data->enc.bitpool;
+	uint8_t new_bitpool = curr_bitpool;
+
+	switch (op) {
+	case QOS_POLICY_DEFAULT:
+		new_bitpool = sbc_data->sbc.max_bitpool;
+		break;
+
+	case QOS_POLICY_DECREASE:
+		if (curr_bitpool > SBC_QUALITY_MIN_BITPOOL) {
+			new_bitpool = curr_bitpool - SBC_QUALITY_STEP;
+			if (new_bitpool < SBC_QUALITY_MIN_BITPOOL)
+				new_bitpool = SBC_QUALITY_MIN_BITPOOL;
+		}
+		break;
+	}
+
+	if (new_bitpool == curr_bitpool)
+		return false;
+
+	sbc_data->enc.bitpool = new_bitpool;
+
+	sbc_codec_calculate(sbc_data);
+
+	info("SBC: bitpool changed: %d -> %d", curr_bitpool, new_bitpool);
+
+	return true;
+}
+
+static const struct audio_codec codec = {
+	.type = A2DP_CODEC_SBC,
+	.use_rtp = true,
+
+	.get_presets = sbc_get_presets,
+
+	.init = sbc_codec_init,
+	.cleanup = sbc_cleanup,
+	.get_config = sbc_get_config,
+	.get_buffer_size = sbc_get_buffer_size,
+	.get_mediapacket_duration = sbc_get_mediapacket_duration,
+	.encode_mediapacket = sbc_encode_mediapacket,
+	.update_qos = sbc_update_qos,
+};
+
+const struct audio_codec *codec_sbc(void)
+{
+	return &codec;
+}
diff --git a/android/hal-audio.c b/android/hal-audio.c
index 1c889cc..1a3d3ae 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -30,26 +30,20 @@
 #include <hardware/audio.h>
 #include <hardware/hardware.h>
 
-#include <sbc/sbc.h>
-
 #include "audio-msg.h"
 #include "ipc-common.h"
 #include "hal-log.h"
 #include "hal-msg.h"
-#include "../profiles/audio/a2dp-codecs.h"
-#include "../src/shared/util.h"
+#include "hal-audio.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
 
 #define FIXED_A2DP_PLAYBACK_LATENCY_MS 25
 
 #define FIXED_BUFFER_SIZE (20 * 512)
 
-#define MAX_FRAMES_IN_PAYLOAD 15
-
 #define MAX_DELAY	100000 /* 100ms */
 
-#define SBC_QUALITY_MIN_BITPOOL	33
-#define SBC_QUALITY_STEP	5
-
 static const uint8_t a2dp_src_uuid[] = {
 		0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x10, 0x00,
 		0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb };
@@ -60,88 +54,6 @@
 static pthread_t ipc_th = 0;
 static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
 
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-
-struct rtp_header {
-	unsigned cc:4;
-	unsigned x:1;
-	unsigned p:1;
-	unsigned v:2;
-
-	unsigned pt:7;
-	unsigned m:1;
-
-	uint16_t sequence_number;
-	uint32_t timestamp;
-	uint32_t ssrc;
-	uint32_t csrc[0];
-} __attribute__ ((packed));
-
-struct rtp_payload {
-	unsigned frame_count:4;
-	unsigned rfa0:1;
-	unsigned is_last_fragment:1;
-	unsigned is_first_fragment:1;
-	unsigned is_fragmented:1;
-} __attribute__ ((packed));
-
-#elif __BYTE_ORDER == __BIG_ENDIAN
-
-struct rtp_header {
-	unsigned v:2;
-	unsigned p:1;
-	unsigned x:1;
-	unsigned cc:4;
-
-	unsigned m:1;
-	unsigned pt:7;
-
-	uint16_t sequence_number;
-	uint32_t timestamp;
-	uint32_t ssrc;
-	uint32_t csrc[0];
-} __attribute__ ((packed));
-
-struct rtp_payload {
-	unsigned is_fragmented:1;
-	unsigned is_first_fragment:1;
-	unsigned is_last_fragment:1;
-	unsigned rfa0:1;
-	unsigned frame_count:4;
-} __attribute__ ((packed));
-
-#else
-#error "Unknown byte order"
-#endif
-
-struct media_packet {
-	struct rtp_header hdr;
-	struct rtp_payload payload;
-	uint8_t data[0];
-};
-
-struct audio_input_config {
-	uint32_t rate;
-	uint32_t channels;
-	audio_format_t format;
-};
-
-struct sbc_data {
-	a2dp_sbc_t sbc;
-
-	sbc_t enc;
-
-	uint16_t payload_len;
-
-	size_t in_frame_len;
-	size_t in_buf_size;
-
-	size_t out_frame_len;
-
-	unsigned frame_duration;
-	unsigned frames_per_packet;
-};
-
 static void timespec_add(struct timespec *base, uint64_t time_us,
 							struct timespec *res)
 {
@@ -185,59 +97,17 @@
 					struct timespec *remain);
 #endif
 
-static int sbc_get_presets(struct audio_preset *preset, size_t *len);
-static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu,
-							void **codec_data);
-static int sbc_cleanup(void *codec_data);
-static int sbc_get_config(void *codec_data, struct audio_input_config *config);
-static size_t sbc_get_buffer_size(void *codec_data);
-static size_t sbc_get_mediapacket_duration(void *codec_data);
-static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
-					size_t len, struct media_packet *mp,
-					size_t mp_data_len, size_t *written);
-static bool sbc_update_qos(void *codec_data, uint8_t op);
-
-#define QOS_POLICY_DEFAULT	0x00
-#define QOS_POLICY_DECREASE	0x01
-
-struct audio_codec {
-	uint8_t type;
-
-	int (*get_presets) (struct audio_preset *preset, size_t *len);
-
-	int (*init) (struct audio_preset *preset, uint16_t mtu,
-				void **codec_data);
-	int (*cleanup) (void *codec_data);
-	int (*get_config) (void *codec_data,
-					struct audio_input_config *config);
-	size_t (*get_buffer_size) (void *codec_data);
-	size_t (*get_mediapacket_duration) (void *codec_data);
-	ssize_t (*encode_mediapacket) (void *codec_data, const uint8_t *buffer,
-					size_t len, struct media_packet *mp,
-					size_t mp_data_len, size_t *written);
-	bool (*update_qos) (void *codec_data, uint8_t op);
-};
-
-static const struct audio_codec audio_codecs[] = {
-	{
-		.type = A2DP_CODEC_SBC,
-
-		.get_presets = sbc_get_presets,
-
-		.init = sbc_codec_init,
-		.cleanup = sbc_cleanup,
-		.get_config = sbc_get_config,
-		.get_buffer_size = sbc_get_buffer_size,
-		.get_mediapacket_duration = sbc_get_mediapacket_duration,
-		.encode_mediapacket = sbc_encode_mediapacket,
-		.update_qos = sbc_update_qos,
-	}
+static const audio_codec_get_t audio_codecs[] = {
+		codec_aptx,
+		codec_sbc,
 };
 
 #define NUM_CODECS (sizeof(audio_codecs) / sizeof(audio_codecs[0]))
 
 #define MAX_AUDIO_ENDPOINTS NUM_CODECS
 
+static struct queue *loaded_codecs;
+
 struct audio_endpoint {
 	uint8_t id;
 	const struct audio_codec *codec;
@@ -278,330 +148,6 @@
 	struct a2dp_stream_out *out;
 };
 
-static const a2dp_sbc_t sbc_presets[] = {
-	{
-		.frequency = SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000,
-		.channel_mode = SBC_CHANNEL_MODE_MONO |
-				SBC_CHANNEL_MODE_DUAL_CHANNEL |
-				SBC_CHANNEL_MODE_STEREO |
-				SBC_CHANNEL_MODE_JOINT_STEREO,
-		.subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8,
-		.allocation_method = SBC_ALLOCATION_SNR |
-					SBC_ALLOCATION_LOUDNESS,
-		.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 |
-				SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16,
-		.min_bitpool = MIN_BITPOOL,
-		.max_bitpool = MAX_BITPOOL
-	},
-	{
-		.frequency = SBC_SAMPLING_FREQ_44100,
-		.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
-		.subbands = SBC_SUBBANDS_8,
-		.allocation_method = SBC_ALLOCATION_LOUDNESS,
-		.block_length = SBC_BLOCK_LENGTH_16,
-		.min_bitpool = MIN_BITPOOL,
-		.max_bitpool = MAX_BITPOOL
-	},
-	{
-		.frequency = SBC_SAMPLING_FREQ_48000,
-		.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
-		.subbands = SBC_SUBBANDS_8,
-		.allocation_method = SBC_ALLOCATION_LOUDNESS,
-		.block_length = SBC_BLOCK_LENGTH_16,
-		.min_bitpool = MIN_BITPOOL,
-		.max_bitpool = MAX_BITPOOL
-	},
-};
-
-static int sbc_get_presets(struct audio_preset *preset, size_t *len)
-{
-	int i;
-	int count;
-	size_t new_len = 0;
-	uint8_t *ptr = (uint8_t *) preset;
-	size_t preset_size = sizeof(*preset) + sizeof(a2dp_sbc_t);
-
-	count = sizeof(sbc_presets) / sizeof(sbc_presets[0]);
-
-	for (i = 0; i < count; i++) {
-		preset = (struct audio_preset *) ptr;
-
-		if (new_len + preset_size > *len)
-			break;
-
-		preset->len = sizeof(a2dp_sbc_t);
-		memcpy(preset->data, &sbc_presets[i], preset->len);
-
-		new_len += preset_size;
-		ptr += preset_size;
-	}
-
-	*len = new_len;
-
-	return i;
-}
-
-static int sbc_freq2int(uint8_t freq)
-{
-	switch (freq) {
-	case SBC_SAMPLING_FREQ_16000:
-		return 16000;
-	case SBC_SAMPLING_FREQ_32000:
-		return 32000;
-	case SBC_SAMPLING_FREQ_44100:
-		return 44100;
-	case SBC_SAMPLING_FREQ_48000:
-		return 48000;
-	default:
-		return 0;
-	}
-}
-
-static const char *sbc_mode2str(uint8_t mode)
-{
-	switch (mode) {
-	case SBC_CHANNEL_MODE_MONO:
-		return "Mono";
-	case SBC_CHANNEL_MODE_DUAL_CHANNEL:
-		return "DualChannel";
-	case SBC_CHANNEL_MODE_STEREO:
-		return "Stereo";
-	case SBC_CHANNEL_MODE_JOINT_STEREO:
-		return "JointStereo";
-	default:
-		return "(unknown)";
-	}
-}
-
-static int sbc_blocks2int(uint8_t blocks)
-{
-	switch (blocks) {
-	case SBC_BLOCK_LENGTH_4:
-		return 4;
-	case SBC_BLOCK_LENGTH_8:
-		return 8;
-	case SBC_BLOCK_LENGTH_12:
-		return 12;
-	case SBC_BLOCK_LENGTH_16:
-		return 16;
-	default:
-		return 0;
-	}
-}
-
-static int sbc_subbands2int(uint8_t subbands)
-{
-	switch (subbands) {
-	case SBC_SUBBANDS_4:
-		return 4;
-	case SBC_SUBBANDS_8:
-		return 8;
-	default:
-		return 0;
-	}
-}
-
-static const char *sbc_allocation2str(uint8_t allocation)
-{
-	switch (allocation) {
-	case SBC_ALLOCATION_SNR:
-		return "SNR";
-	case SBC_ALLOCATION_LOUDNESS:
-		return "Loudness";
-	default:
-		return "(unknown)";
-	}
-}
-
-static void sbc_init_encoder(struct sbc_data *sbc_data)
-{
-	a2dp_sbc_t *in = &sbc_data->sbc;
-	sbc_t *out = &sbc_data->enc;
-
-	sbc_init_a2dp(out, 0L, in, sizeof(*in));
-
-	out->endian = SBC_LE;
-	out->bitpool = in->max_bitpool;
-
-	DBG("frequency=%d channel_mode=%s block_length=%d subbands=%d "
-			"allocation=%s bitpool=%d-%d",
-			sbc_freq2int(in->frequency),
-			sbc_mode2str(in->channel_mode),
-			sbc_blocks2int(in->block_length),
-			sbc_subbands2int(in->subbands),
-			sbc_allocation2str(in->allocation_method),
-			in->min_bitpool, in->max_bitpool);
-}
-
-static void sbc_codec_calculate(struct sbc_data *sbc_data)
-{
-	size_t in_frame_len;
-	size_t out_frame_len;
-	size_t num_frames;
-
-	in_frame_len = sbc_get_codesize(&sbc_data->enc);
-	out_frame_len = sbc_get_frame_length(&sbc_data->enc);
-	num_frames = sbc_data->payload_len / out_frame_len;
-
-	sbc_data->in_frame_len = in_frame_len;
-	sbc_data->in_buf_size = num_frames * in_frame_len;
-
-	sbc_data->out_frame_len = out_frame_len;
-
-	sbc_data->frame_duration = sbc_get_frame_duration(&sbc_data->enc);
-	sbc_data->frames_per_packet = num_frames;
-
-	DBG("in_frame_len=%zu out_frame_len=%zu frames_per_packet=%zu",
-				in_frame_len, out_frame_len, num_frames);
-}
-
-static int sbc_codec_init(struct audio_preset *preset, uint16_t payload_len,
-							void **codec_data)
-{
-	struct sbc_data *sbc_data;
-
-	if (preset->len != sizeof(a2dp_sbc_t)) {
-		error("SBC: preset size mismatch");
-		return AUDIO_STATUS_FAILED;
-	}
-
-	sbc_data = calloc(sizeof(struct sbc_data), 1);
-	if (!sbc_data)
-		return AUDIO_STATUS_FAILED;
-
-	memcpy(&sbc_data->sbc, preset->data, preset->len);
-
-	sbc_init_encoder(sbc_data);
-
-	sbc_data->payload_len = payload_len;
-
-	sbc_codec_calculate(sbc_data);
-
-	*codec_data = sbc_data;
-
-	return AUDIO_STATUS_SUCCESS;
-}
-
-static int sbc_cleanup(void *codec_data)
-{
-	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
-
-	sbc_finish(&sbc_data->enc);
-	free(codec_data);
-
-	return AUDIO_STATUS_SUCCESS;
-}
-
-static int sbc_get_config(void *codec_data, struct audio_input_config *config)
-{
-	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
-
-	switch (sbc_data->sbc.frequency) {
-	case SBC_SAMPLING_FREQ_16000:
-		config->rate = 16000;
-		break;
-	case SBC_SAMPLING_FREQ_32000:
-		config->rate = 32000;
-		break;
-	case SBC_SAMPLING_FREQ_44100:
-		config->rate = 44100;
-		break;
-	case SBC_SAMPLING_FREQ_48000:
-		config->rate = 48000;
-		break;
-	default:
-		return AUDIO_STATUS_FAILED;
-	}
-	config->channels = sbc_data->sbc.channel_mode == SBC_CHANNEL_MODE_MONO ?
-				AUDIO_CHANNEL_OUT_MONO :
-				AUDIO_CHANNEL_OUT_STEREO;
-	config->format = AUDIO_FORMAT_PCM_16_BIT;
-
-	return AUDIO_STATUS_SUCCESS;
-}
-
-static size_t sbc_get_buffer_size(void *codec_data)
-{
-	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
-
-	return sbc_data->in_buf_size;
-}
-
-static size_t sbc_get_mediapacket_duration(void *codec_data)
-{
-	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
-
-	return sbc_data->frame_duration * sbc_data->frames_per_packet;
-}
-
-static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
-					size_t len, struct media_packet *mp,
-					size_t mp_data_len, size_t *written)
-{
-	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
-	size_t consumed = 0;
-	size_t encoded = 0;
-	uint8_t frame_count = 0;
-
-	while (len - consumed >= sbc_data->in_frame_len &&
-			mp_data_len - encoded >= sbc_data->out_frame_len &&
-			frame_count < MAX_FRAMES_IN_PAYLOAD) {
-		ssize_t read;
-		ssize_t written = 0;
-
-		read = sbc_encode(&sbc_data->enc, buffer + consumed,
-				sbc_data->in_frame_len, mp->data + encoded,
-				mp_data_len - encoded, &written);
-
-		if (read < 0) {
-			error("SBC: failed to encode block at frame %d (%zd)",
-							frame_count, read);
-			break;
-		}
-
-		frame_count++;
-		consumed += read;
-		encoded += written;
-	}
-
-	*written = encoded;
-	mp->payload.frame_count = frame_count;
-
-	return consumed;
-}
-
-static bool sbc_update_qos(void *codec_data, uint8_t op)
-{
-	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
-	uint8_t curr_bitpool = sbc_data->enc.bitpool;
-	uint8_t new_bitpool = curr_bitpool;
-
-	switch (op) {
-	case QOS_POLICY_DEFAULT:
-		new_bitpool = sbc_data->sbc.max_bitpool;
-		break;
-
-	case QOS_POLICY_DECREASE:
-		if (curr_bitpool > SBC_QUALITY_MIN_BITPOOL) {
-			new_bitpool = curr_bitpool - SBC_QUALITY_STEP;
-			if (new_bitpool < SBC_QUALITY_MIN_BITPOOL)
-				new_bitpool = SBC_QUALITY_MIN_BITPOOL;
-		}
-		break;
-	}
-
-	if (new_bitpool == curr_bitpool)
-		return false;
-
-	sbc_data->enc.bitpool = new_bitpool;
-
-	sbc_codec_calculate(sbc_data);
-
-	info("SBC: bitpool changed: %d -> %d", curr_bitpool, new_bitpool);
-
-	return true;
-}
-
 static int audio_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len,
 			void *param, size_t *rsp_len, void *rsp, int *fd)
 {
@@ -795,7 +341,7 @@
 	return result;
 }
 
-static int ipc_open_stream_cmd(uint8_t endpoint_id, uint16_t *mtu, int *fd,
+static int ipc_open_stream_cmd(uint8_t *endpoint_id, uint16_t *mtu, int *fd,
 						struct audio_preset **caps)
 {
 	char buf[BLUEZ_AUDIO_MTU];
@@ -810,13 +356,14 @@
 	if (!caps)
 		return AUDIO_STATUS_FAILED;
 
-	cmd.id = endpoint_id;
+	cmd.id = *endpoint_id;
 
 	result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
 				sizeof(cmd), &cmd, &rsp_len, rsp, fd);
 	if (result == AUDIO_STATUS_SUCCESS) {
 		size_t buf_len = sizeof(struct audio_preset) +
 					rsp->preset[0].len;
+		*endpoint_id = rsp->id;
 		*mtu = rsp->mtu;
 		*caps = malloc(buf_len);
 		memcpy(*caps, &rsp->preset, buf_len);
@@ -872,25 +419,46 @@
 	return result;
 }
 
-static int register_endpoints(void)
+struct register_state {
+	struct audio_endpoint *ep;
+	bool error;
+};
+
+static void register_endpoint(void *data, void *user_data)
 {
-	struct audio_endpoint *ep = &audio_endpoints[0];
-	size_t i;
+	struct audio_codec *codec = data;
+	struct register_state *state = user_data;
+	struct audio_endpoint *ep = state->ep;
 
-	for (i = 0; i < NUM_CODECS; i++, ep++) {
-		const struct audio_codec *codec = &audio_codecs[i];
+	/* don't even try to register more endpoints if one failed */
+	if (state->error)
+		return;
 
-		ep->id = ipc_open_cmd(codec);
+	ep->id = ipc_open_cmd(codec);
 
-		if (!ep->id)
-			return AUDIO_STATUS_FAILED;
-
-		ep->codec = codec;
-		ep->codec_data = NULL;
-		ep->fd = -1;
+	if (!ep->id) {
+		state->error = true;
+		error("Failed to register endpoint");
+		return;
 	}
 
-	return AUDIO_STATUS_SUCCESS;
+	ep->codec = codec;
+	ep->codec_data = NULL;
+	ep->fd = -1;
+
+	state->ep++;
+}
+
+static int register_endpoints(void)
+{
+	struct register_state state;
+
+	state.ep = &audio_endpoints[0];
+	state.error = false;
+
+	queue_foreach(loaded_codecs, register_endpoint, &state);
+
+	return state.error ? AUDIO_STATUS_FAILED : AUDIO_STATUS_SUCCESS;
 }
 
 static void unregister_endpoints(void)
@@ -907,22 +475,43 @@
 	}
 }
 
-static bool open_endpoint(struct audio_endpoint *ep,
+static bool open_endpoint(struct audio_endpoint **epp,
 						struct audio_input_config *cfg)
 {
 	struct audio_preset *preset;
+	struct audio_endpoint *ep = *epp;
 	const struct audio_codec *codec;
 	uint16_t mtu;
 	uint16_t payload_len;
 	int fd;
+	size_t i;
+	uint8_t ep_id = 0;
 
-	if (ipc_open_stream_cmd(ep->id, &mtu, &fd, &preset) !=
+	if (ep)
+		ep_id = ep->id;
+
+	if (ipc_open_stream_cmd(&ep_id, &mtu, &fd, &preset) !=
 							AUDIO_STATUS_SUCCESS)
 		return false;
 
-	DBG("mtu=%u", mtu);
+	DBG("ep_id=%d mtu=%u", ep_id, mtu);
 
-	payload_len = mtu - sizeof(*ep->mp);
+	for (i = 0; i < MAX_AUDIO_ENDPOINTS; i++)
+		if (audio_endpoints[i].id == ep_id) {
+			ep = &audio_endpoints[i];
+			break;
+		}
+
+	if (!ep) {
+		error("Cound not find opened endpoint");
+		goto failed;
+	}
+
+	*epp = ep;
+
+	payload_len = mtu;
+	if (ep->codec->use_rtp)
+		payload_len -= sizeof(struct rtp_header);
 
 	ep->fd = fd;
 
@@ -933,9 +522,14 @@
 	ep->mp = calloc(mtu, 1);
 	if (!ep->mp)
 		goto failed;
-	ep->mp->hdr.v = 2;
-	ep->mp->hdr.pt = 1;
-	ep->mp->hdr.ssrc = htonl(1);
+
+	if (ep->codec->use_rtp) {
+		struct media_packet_rtp *mp_rtp =
+					(struct media_packet_rtp *) ep->mp;
+		mp_rtp->hdr.v = 2;
+		mp_rtp->hdr.pt = 0x60;
+		mp_rtp->hdr.ssrc = htonl(1);
+	}
 
 	ep->mp_data_len = payload_len;
 
@@ -982,9 +576,12 @@
 {
 	const int16_t *input = (const void *) buffer;
 	int16_t *output = (void *) out->downmix_buf;
-	size_t i;
+	size_t i, frames;
 
-	for (i = 0; i < bytes / 2; i++) {
+	/* PCM 16bit stereo */
+	frames = bytes / (2 * sizeof(int16_t));
+
+	for (i = 0; i < frames; i++) {
 		int16_t l = le16_to_cpu(get_unaligned(&input[i * 2]));
 		int16_t r = le16_to_cpu(get_unaligned(&input[i * 2 + 1]));
 
@@ -1026,7 +623,7 @@
 	int ret;
 
 	while (true) {
-		ret = write(ep->fd, mp, sizeof(*mp) + bytes);
+		ret = write(ep->fd, mp, bytes);
 
 		if (ret >= 0)
 			break;
@@ -1056,6 +653,7 @@
 {
 	struct audio_endpoint *ep = out->ep;
 	struct media_packet *mp = (struct media_packet *) ep->mp;
+	struct media_packet_rtp *mp_rtp = (struct media_packet_rtp *) ep->mp;
 	size_t free_space = ep->mp_data_len;
 	size_t consumed = 0;
 
@@ -1072,8 +670,10 @@
 		 * prepare media packet in advance so we don't waste time after
 		 * wakeup
 		 */
-		mp->hdr.sequence_number = htons(ep->seq++);
-		mp->hdr.timestamp = htonl(ep->samples);
+		if (ep->codec->use_rtp) {
+			mp_rtp->hdr.sequence_number = htons(ep->seq++);
+			mp_rtp->hdr.timestamp = htonl(ep->samples);
+		}
 		read = ep->codec->encode_mediapacket(ep->codec_data,
 						buffer + consumed,
 						bytes - consumed, mp,
@@ -1131,17 +731,25 @@
 			}
 		}
 
-		/* in resync mode we'll just drop mediapackets */
-		if (!ep->resync) {
+		/* we send data only in case codec encoded some data, i.e. some
+		 * codecs do internal buffering and output data only if full
+		 * frame can be encoded
+		 * in resync mode we'll just drop mediapackets
+		 */
+		if (written > 0 && !ep->resync) {
 			/* wait some time for socket to be ready for write,
 			 * but we'll just skip writing data if timeout occurs
 			 */
 			if (!wait_for_endpoint(ep, &do_write))
 				return false;
 
-			if (do_write)
+			if (do_write) {
+				if (ep->codec->use_rtp)
+					written += sizeof(struct rtp_header);
+
 				if (!write_to_endpoint(ep, written))
 					return false;
+			}
 		}
 
 		/*
@@ -1528,10 +1136,10 @@
 	out->stream.write = out_write;
 	out->stream.get_render_position = out_get_render_position;
 
-	/* TODO: for now we always use endpoint 0 */
-	out->ep = &audio_endpoints[0];
+	/* We want to autoselect opened endpoint */
+	out->ep = NULL;
 
-	if (!open_endpoint(out->ep, &out->cfg))
+	if (!open_endpoint(&out->ep, &out->cfg))
 		goto fail;
 
 	DBG("rate=%d channels=%d format=%d", out->cfg.rate,
@@ -1686,6 +1294,14 @@
 	return -ENOSYS;
 }
 
+static void unload_codec(void *data)
+{
+	struct audio_codec *codec = data;
+
+	if (codec->unload)
+		codec->unload();
+}
+
 static int audio_close(hw_device_t *device)
 {
 	struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)device;
@@ -1694,6 +1310,9 @@
 
 	unregister_endpoints();
 
+	queue_destroy(loaded_codecs, unload_codec);
+	loaded_codecs = NULL;
+
 	shutdown(listen_sk, SHUT_RDWR);
 	shutdown(audio_sk, SHUT_RDWR);
 
@@ -1833,6 +1452,7 @@
 							hw_device_t **device)
 {
 	struct a2dp_audio_dev *a2dp_dev;
+	size_t i;
 	int err;
 
 	DBG("");
@@ -1871,6 +1491,18 @@
 	a2dp_dev->dev.close_input_stream = audio_close_input_stream;
 	a2dp_dev->dev.dump = audio_dump;
 
+	loaded_codecs = queue_new();
+
+	for (i = 0; i < NUM_CODECS; i++) {
+		audio_codec_get_t get_codec = audio_codecs[i];
+		const struct audio_codec *codec = get_codec();
+
+		if (codec->load && !codec->load())
+			continue;
+
+		queue_push_tail(loaded_codecs, (void *) codec);
+	}
+
 	/*
 	 * Note that &a2dp_dev->dev.common is the same pointer as a2dp_dev.
 	 * This results from the structure of following structs:a2dp_audio_dev,
diff --git a/android/hal-audio.h b/android/hal-audio.h
new file mode 100644
index 0000000..2b47412
--- /dev/null
+++ b/android/hal-audio.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <time.h>
+#include <hardware/audio.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_header {
+	unsigned cc:4;
+	unsigned x:1;
+	unsigned p:1;
+	unsigned v:2;
+
+	unsigned pt:7;
+	unsigned m:1;
+
+	uint16_t sequence_number;
+	uint32_t timestamp;
+	uint32_t ssrc;
+	uint32_t csrc[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_header {
+	unsigned v:2;
+	unsigned p:1;
+	unsigned x:1;
+	unsigned cc:4;
+
+	unsigned m:1;
+	unsigned pt:7;
+
+	uint16_t sequence_number;
+	uint32_t timestamp;
+	uint32_t ssrc;
+	uint32_t csrc[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct media_packet {
+	uint8_t data[0];
+};
+
+struct media_packet_rtp {
+	struct rtp_header hdr;
+	uint8_t data[0];
+};
+
+struct audio_input_config {
+	uint32_t rate;
+	uint32_t channels;
+	audio_format_t format;
+};
+
+struct audio_codec {
+	uint8_t type;
+	bool use_rtp;
+
+	bool (*load) (void);
+	void (*unload) (void);
+
+	int (*get_presets) (struct audio_preset *preset, size_t *len);
+
+	bool (*init) (struct audio_preset *preset, uint16_t mtu,
+				void **codec_data);
+	bool (*cleanup) (void *codec_data);
+	bool (*get_config) (void *codec_data,
+					struct audio_input_config *config);
+	size_t (*get_buffer_size) (void *codec_data);
+	size_t (*get_mediapacket_duration) (void *codec_data);
+	ssize_t (*encode_mediapacket) (void *codec_data, const uint8_t *buffer,
+					size_t len, struct media_packet *mp,
+					size_t mp_data_len, size_t *written);
+	bool (*update_qos) (void *codec_data, uint8_t op);
+};
+
+#define QOS_POLICY_DEFAULT	0x00
+#define QOS_POLICY_DECREASE	0x01
+
+typedef const struct audio_codec * (*audio_codec_get_t) (void);
+
+const struct audio_codec *codec_sbc(void);
+const struct audio_codec *codec_aptx(void);
diff --git a/android/hal-avrcp.c b/android/hal-avrcp.c
index 5e07366..09f5463 100644
--- a/android/hal-avrcp.c
+++ b/android/hal-avrcp.c
@@ -33,7 +33,7 @@
 	return cbs != NULL;
 }
 
-static void handle_remote_features(void *buf, uint16_t len)
+static void handle_remote_features(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_avrcp_remote_features *ev = buf;
 
@@ -42,19 +42,19 @@
 								ev->features);
 }
 
-static void handle_get_play_status(void *buf, uint16_t len)
+static void handle_get_play_status(void *buf, uint16_t len, int fd)
 {
 	if (cbs->get_play_status_cb)
 		cbs->get_play_status_cb();
 }
 
-static void handle_list_player_attrs(void *buf, uint16_t len)
+static void handle_list_player_attrs(void *buf, uint16_t len, int fd)
 {
 	if (cbs->list_player_app_attr_cb)
 		cbs->list_player_app_attr_cb();
 }
 
-static void handle_list_player_values(void *buf, uint16_t len)
+static void handle_list_player_values(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_avrcp_list_player_values *ev = buf;
 
@@ -62,7 +62,7 @@
 		cbs->list_player_app_values_cb(ev->attr);
 }
 
-static void handle_get_player_values(void *buf, uint16_t len)
+static void handle_get_player_values(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_avrcp_get_player_values *ev = buf;
 	btrc_player_attr_t attrs[4];
@@ -78,7 +78,7 @@
 	cbs->get_player_app_value_cb(ev->number, attrs);
 }
 
-static void handle_get_player_attrs_text(void *buf, uint16_t len)
+static void handle_get_player_attrs_text(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_avrcp_get_player_attrs_text *ev = buf;
 	btrc_player_attr_t attrs[4];
@@ -94,7 +94,7 @@
 	cbs->get_player_app_attrs_text_cb(ev->number, attrs);
 }
 
-static void handle_get_player_values_text(void *buf, uint16_t len)
+static void handle_get_player_values_text(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_avrcp_get_player_values_text *ev = buf;
 
@@ -103,7 +103,7 @@
 								ev->values);
 }
 
-static void handle_set_player_value(void *buf, uint16_t len)
+static void handle_set_player_value(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_avrcp_set_player_values *ev = buf;
 	struct hal_avrcp_player_attr_value *attrs;
@@ -125,7 +125,7 @@
 	cbs->set_player_app_value_cb(&values);
 }
 
-static void handle_get_element_attrs(void *buf, uint16_t len)
+static void handle_get_element_attrs(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_avrcp_get_element_attrs *ev = buf;
 	btrc_media_attr_t attrs[BTRC_MAX_APP_SETTINGS];
@@ -141,7 +141,7 @@
 	cbs->get_element_attr_cb(ev->number, attrs);
 }
 
-static void handle_register_notification(void *buf, uint16_t len)
+static void handle_register_notification(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_avrcp_register_notification *ev = buf;
 
@@ -149,7 +149,7 @@
 		cbs->register_notification_cb(ev->event, ev->param);
 }
 
-static void handle_volume_changed(void *buf, uint16_t len)
+static void handle_volume_changed(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_avrcp_volume_changed *ev = buf;
 
@@ -157,7 +157,7 @@
 		cbs->volume_change_cb(ev->volume, ev->type);
 }
 
-static void handle_passthrough_cmd(void *buf, uint16_t len)
+static void handle_passthrough_cmd(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_avrcp_passthrough_cmd *ev = buf;
 
diff --git a/android/hal-bluetooth.c b/android/hal-bluetooth.c
index 2155978..4291430 100644
--- a/android/hal-bluetooth.c
+++ b/android/hal-bluetooth.c
@@ -52,7 +52,7 @@
 	*hal_len = 1; \
 } while (0)
 
-static void handle_adapter_state_changed(void *buf, uint16_t len)
+static void handle_adapter_state_changed(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_adapter_state_changed *ev = buf;
 
@@ -197,7 +197,7 @@
 	exit(EXIT_FAILURE);
 }
 
-static void handle_adapter_props_changed(void *buf, uint16_t len)
+static void handle_adapter_props_changed(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_adapter_props_changed *ev = buf;
 	bt_property_t props[ev->num_props];
@@ -213,7 +213,7 @@
 	bt_hal_cbacks->adapter_properties_cb(ev->status, ev->num_props, props);
 }
 
-static void handle_bond_state_change(void *buf, uint16_t len)
+static void handle_bond_state_change(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_bond_state_changed *ev = buf;
 	bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
@@ -225,7 +225,7 @@
 								ev->state);
 }
 
-static void handle_pin_request(void *buf, uint16_t len)
+static void handle_pin_request(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_pin_request *ev = buf;
 	/* Those are declared as packed, so it's safe to assign pointers */
@@ -238,7 +238,7 @@
 		bt_hal_cbacks->pin_request_cb(addr, name, ev->class_of_dev);
 }
 
-static void handle_ssp_request(void *buf, uint16_t len)
+static void handle_ssp_request(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_ssp_request *ev = buf;
 	/* Those are declared as packed, so it's safe to assign pointers */
@@ -270,7 +270,7 @@
 	return bt_hal_cbacks != NULL;
 }
 
-static void handle_discovery_state_changed(void *buf, uint16_t len)
+static void handle_discovery_state_changed(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_discovery_state_changed *ev = buf;
 
@@ -280,7 +280,7 @@
 		bt_hal_cbacks->discovery_state_changed_cb(ev->state);
 }
 
-static void handle_device_found(void *buf, uint16_t len)
+static void handle_device_found(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_device_found *ev = buf;
 	bt_property_t props[ev->num_props];
@@ -296,7 +296,7 @@
 	bt_hal_cbacks->device_found_cb(ev->num_props, props);
 }
 
-static void handle_device_state_changed(void *buf, uint16_t len)
+static void handle_device_state_changed(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_remote_device_props *ev = buf;
 	bt_property_t props[ev->num_props];
@@ -314,7 +314,7 @@
 						ev->num_props, props);
 }
 
-static void handle_acl_state_changed(void *buf, uint16_t len)
+static void handle_acl_state_changed(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_acl_state_changed *ev = buf;
 	bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
@@ -326,7 +326,7 @@
 								ev->state);
 }
 
-static void handle_dut_mode_receive(void *buf, uint16_t len)
+static void handle_dut_mode_receive(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_dut_mode_receive *ev = buf;
 
@@ -341,7 +341,7 @@
 		bt_hal_cbacks->dut_mode_recv_cb(ev->opcode, ev->data, ev->len);
 }
 
-static void handle_le_test_mode(void *buf, uint16_t len)
+static void handle_le_test_mode(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_le_test_mode *ev = buf;
 
@@ -356,64 +356,38 @@
  * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
  */
 static const struct hal_ipc_handler ev_handlers[] = {
-	{	/* HAL_EV_ADAPTER_STATE_CHANGED */
-		.handler = handle_adapter_state_changed,
-		.var_len = false,
-		.data_len = sizeof(struct hal_ev_adapter_state_changed)
-	},
-	{	/* HAL_EV_ADAPTER_PROPS_CHANGED */
-		.handler = handle_adapter_props_changed,
-		.var_len = true,
-		.data_len = sizeof(struct hal_ev_adapter_props_changed) +
-						sizeof(struct hal_property),
-	},
-	{	/* HAL_EV_REMOTE_DEVICE_PROPS */
-		.handler = handle_device_state_changed,
-		.var_len = true,
-		.data_len = sizeof(struct hal_ev_remote_device_props) +
-						sizeof(struct hal_property),
-	},
-	{	/* HAL_EV_DEVICE_FOUND */
-		.handler = handle_device_found,
-		.var_len = true,
-		.data_len = sizeof(struct hal_ev_device_found) +
-						sizeof(struct hal_property),
-	},
-	{	/* HAL_EV_DISCOVERY_STATE_CHANGED */
-		.handler = handle_discovery_state_changed,
-		.var_len = false,
-		.data_len = sizeof(struct hal_ev_discovery_state_changed),
-	},
-	{	/* HAL_EV_PIN_REQUEST */
-		.handler = handle_pin_request,
-		.var_len = false,
-		.data_len = sizeof(struct hal_ev_pin_request),
-	},
-	{	/* HAL_EV_SSP_REQUEST */
-		.handler = handle_ssp_request,
-		.var_len = false,
-		.data_len = sizeof(struct hal_ev_ssp_request),
-	},
-	{	/* HAL_EV_BOND_STATE_CHANGED */
-		.handler = handle_bond_state_change,
-		.var_len = false,
-		.data_len = sizeof(struct hal_ev_bond_state_changed),
-	},
-	{	/* HAL_EV_ACL_STATE_CHANGED */
-		.handler = handle_acl_state_changed,
-		.var_len = false,
-		.data_len = sizeof(struct hal_ev_acl_state_changed),
-	},
-	{	/* HAL_EV_DUT_MODE_RECEIVE */
-		.handler = handle_dut_mode_receive,
-		.var_len = true,
-		.data_len = sizeof(struct hal_ev_dut_mode_receive),
-	},
-	{	/* HAL_EV_LE_TEST_MODE */
-		.handler = handle_le_test_mode,
-		.var_len = false,
-		.data_len = sizeof(struct hal_ev_le_test_mode),
-	}
+	/* HAL_EV_ADAPTER_STATE_CHANGED */
+	{ handle_adapter_state_changed, false,
+				sizeof(struct hal_ev_adapter_state_changed) },
+	/* HAL_EV_ADAPTER_PROPS_CHANGED */
+	{ handle_adapter_props_changed, true,
+				sizeof(struct hal_ev_adapter_props_changed) +
+				sizeof(struct hal_property) },
+	/* HAL_EV_REMOTE_DEVICE_PROPS */
+	{ handle_device_state_changed, true,
+				sizeof(struct hal_ev_remote_device_props) +
+				sizeof(struct hal_property) },
+	/* HAL_EV_DEVICE_FOUND */
+	{ handle_device_found, true, sizeof(struct hal_ev_device_found) +
+				sizeof(struct hal_property) },
+	/* HAL_EV_DISCOVERY_STATE_CHANGED */
+	{ handle_discovery_state_changed, false,
+				sizeof(struct hal_ev_discovery_state_changed) },
+	/* HAL_EV_PIN_REQUEST */
+	{ handle_pin_request, false, sizeof(struct hal_ev_pin_request) },
+	/* HAL_EV_SSP_REQUEST */
+	{ handle_ssp_request, false, sizeof(struct hal_ev_ssp_request) },
+	/* HAL_EV_BOND_STATE_CHANGED */
+	{ handle_bond_state_change, false,
+				sizeof(struct hal_ev_bond_state_changed) },
+	/* HAL_EV_ACL_STATE_CHANGED */
+	{ handle_acl_state_changed, false,
+				sizeof(struct hal_ev_acl_state_changed) },
+	/* HAL_EV_DUT_MODE_RECEIVE */
+	{ handle_dut_mode_receive, true,
+				sizeof(struct hal_ev_dut_mode_receive) },
+	/* HAL_EV_LE_TEST_MODE */
+	{ handle_le_test_mode, false, sizeof(struct hal_ev_le_test_mode) },
 };
 
 static uint8_t get_mode(void)
@@ -441,12 +415,24 @@
 	if (interface_ready())
 		return BT_STATUS_DONE;
 
-	bt_hal_cbacks = callbacks;
-
 	hal_ipc_register(HAL_SERVICE_ID_BLUETOOTH, ev_handlers,
 				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
 
-	if (!hal_ipc_init()) {
+	if (!hal_ipc_init(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH)))
+		return BT_STATUS_FAIL;
+
+	bt_hal_cbacks = callbacks;
+
+	/* Start Android Bluetooth daemon service */
+	if (property_set("bluetooth.start", "daemon") < 0) {
+		error("Failed to set bluetooth.start=daemon");
+		hal_ipc_cleanup();
+		bt_hal_cbacks = NULL;
+		return BT_STATUS_FAIL;
+	}
+
+	if (!hal_ipc_accept()) {
+		hal_ipc_cleanup();
 		bt_hal_cbacks = NULL;
 		return BT_STATUS_FAIL;
 	}
diff --git a/android/hal-gatt.c b/android/hal-gatt.c
index 93dc066..c22113f 100644
--- a/android/hal-gatt.c
+++ b/android/hal-gatt.c
@@ -63,7 +63,7 @@
 
 /* Client Event Handlers */
 
-static void handle_register_client(void *buf, uint16_t len)
+static void handle_register_client(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_register_client *ev = buf;
 
@@ -72,7 +72,7 @@
 						(bt_uuid_t *) ev->app_uuid);
 }
 
-static void handle_scan_result(void *buf, uint16_t len)
+static void handle_scan_result(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_scan_result *ev = buf;
 	uint8_t ad[62];
@@ -91,7 +91,7 @@
 									ad);
 }
 
-static void handle_connect(void *buf, uint16_t len)
+static void handle_connect(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_connect *ev = buf;
 
@@ -100,7 +100,7 @@
 						(bt_bdaddr_t *) ev->bda);
 }
 
-static void handle_disconnect(void *buf, uint16_t len)
+static void handle_disconnect(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_disconnect *ev = buf;
 
@@ -109,7 +109,7 @@
 						(bt_bdaddr_t *) ev->bda);
 }
 
-static void handle_search_complete(void *buf, uint16_t len)
+static void handle_search_complete(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_search_complete *ev = buf;
 
@@ -117,7 +117,7 @@
 		cbs->client->search_complete_cb(ev->conn_id, ev->status);
 }
 
-static void handle_search_result(void *buf, uint16_t len)
+static void handle_search_result(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_search_result *ev = buf;
 	btgatt_srvc_id_t srvc_id;
@@ -128,7 +128,7 @@
 		cbs->client->search_result_cb(ev->conn_id, &srvc_id);
 }
 
-static void handle_get_characteristic(void *buf, uint16_t len)
+static void handle_get_characteristic(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_get_characteristic *ev = buf;
 	btgatt_gatt_id_t char_id;
@@ -143,7 +143,7 @@
 							ev->char_prop);
 }
 
-static void handle_get_descriptor(void *buf, uint16_t len)
+static void handle_get_descriptor(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_get_descriptor *ev = buf;
 	btgatt_gatt_id_t descr_id;
@@ -159,7 +159,7 @@
 						&srvc_id, &char_id, &descr_id);
 }
 
-static void handle_get_included_service(void *buf, uint16_t len)
+static void handle_get_included_service(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_get_inc_service *ev = buf;
 	btgatt_srvc_id_t srvc_id;
@@ -174,7 +174,7 @@
 								&incl_srvc_id);
 }
 
-static void handle_register_for_notification(void *buf, uint16_t len)
+static void handle_register_for_notification(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_reg_for_notif *ev = buf;
 	btgatt_gatt_id_t char_id;
@@ -191,7 +191,7 @@
 								&char_id);
 }
 
-static void handle_notify(void *buf, uint16_t len)
+static void handle_notify(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_notify *ev = buf;
 	btgatt_notify_params_t params;
@@ -215,7 +215,7 @@
 		cbs->client->notify_cb(ev->conn_id, &params);
 }
 
-static void handle_read_characteristic(void *buf, uint16_t len)
+static void handle_read_characteristic(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_read_characteristic *ev = buf;
 	btgatt_read_params_t params;
@@ -242,7 +242,7 @@
 								&params);
 }
 
-static void handle_write_characteristic(void *buf, uint16_t len)
+static void handle_write_characteristic(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_write_characteristic *ev = buf;
 	btgatt_write_params_t params;
@@ -260,7 +260,7 @@
 								&params);
 }
 
-static void handle_read_descriptor(void *buf, uint16_t len)
+static void handle_read_descriptor(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_read_descriptor *ev = buf;
 	btgatt_read_params_t params;
@@ -287,7 +287,7 @@
 								&params);
 }
 
-static void handle_write_descriptor(void *buf, uint16_t len)
+static void handle_write_descriptor(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_write_descriptor *ev = buf;
 	btgatt_write_params_t params;
@@ -305,7 +305,7 @@
 								&params);
 }
 
-static void handle_execute_write(void *buf, uint16_t len)
+static void handle_execute_write(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_exec_write *ev = buf;
 
@@ -313,7 +313,7 @@
 		cbs->client->execute_write_cb(ev->conn_id, ev->status);
 }
 
-static void handle_read_remote_rssi(void *buf, uint16_t len)
+static void handle_read_remote_rssi(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_read_remote_rssi *ev = buf;
 
@@ -323,7 +323,7 @@
 						ev->rssi, ev->status);
 }
 
-static void handle_listen(void *buf, uint16_t len)
+static void handle_listen(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_client_listen *ev = buf;
 
@@ -333,7 +333,7 @@
 
 /* Server Event Handlers */
 
-static void handle_register_server(void *buf, uint16_t len)
+static void handle_register_server(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_server_register *ev = buf;
 
@@ -342,7 +342,7 @@
 						(bt_uuid_t *) &ev->uuid);
 }
 
-static void handle_connection(void *buf, uint16_t len)
+static void handle_connection(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_server_connection *ev = buf;
 
@@ -352,7 +352,7 @@
 						(bt_bdaddr_t *) &ev->bdaddr);
 }
 
-static void handle_service_added(void *buf, uint16_t len)
+static void handle_service_added(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_server_service_added *ev = buf;
 	btgatt_srvc_id_t srvc_id;
@@ -364,7 +364,7 @@
 						&srvc_id, ev->srvc_handle);
 }
 
-static void handle_included_service_added(void *buf, uint16_t len)
+static void handle_included_service_added(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_server_inc_srvc_added *ev = buf;
 
@@ -375,7 +375,7 @@
 							ev->incl_srvc_handle);
 }
 
-static void handle_characteristic_added(void *buf, uint16_t len)
+static void handle_characteristic_added(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_server_characteristic_added *ev = buf;
 
@@ -386,7 +386,7 @@
 							ev->char_handle);
 }
 
-static void handle_descriptor_added(void *buf, uint16_t len)
+static void handle_descriptor_added(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_server_descriptor_added *ev = buf;
 
@@ -397,7 +397,7 @@
 							ev->descr_handle);
 }
 
-static void handle_service_started(void *buf, uint16_t len)
+static void handle_service_started(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_server_service_started *ev = buf;
 
@@ -406,7 +406,7 @@
 							ev->srvc_handle);
 }
 
-static void handle_service_stopped(void *buf, uint16_t len)
+static void handle_service_stopped(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_server_service_stopped *ev = buf;
 
@@ -415,7 +415,7 @@
 							ev->srvc_handle);
 }
 
-static void handle_service_deleted(void *buf, uint16_t len)
+static void handle_service_deleted(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_server_service_deleted *ev = buf;
 
@@ -424,7 +424,7 @@
 							ev->srvc_handle);
 }
 
-static void handle_request_read(void *buf, uint16_t len)
+static void handle_request_read(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_server_request_read *ev = buf;
 
@@ -435,7 +435,7 @@
 						ev->is_long);
 }
 
-static void handle_request_write(void *buf, uint16_t len)
+static void handle_request_write(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_server_request_write *ev = buf;
 
@@ -452,7 +452,7 @@
 						ev->is_prep, ev->value);
 }
 
-static void handle_request_exec_write(void *buf, uint16_t len)
+static void handle_request_exec_write(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_server_request_exec_write *ev = buf;
 
@@ -462,7 +462,7 @@
 						ev->exec_write);
 }
 
-static void handle_response_confirmation(void *buf, uint16_t len)
+static void handle_response_confirmation(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_gatt_server_rsp_confirmation *ev = buf;
 
@@ -573,6 +573,9 @@
 {
 	struct hal_cmd_gatt_client_register cmd;
 
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
 	memcpy(cmd.uuid, uuid, sizeof(*uuid));
 
 	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER,
@@ -968,19 +971,28 @@
 	return dev_type;
 }
 
-static bt_status_t set_adv_data(int server_if, bool set_scan_rsp,
+static bt_status_t set_adv_data_real(int server_if, bool set_scan_rsp,
 				bool include_name, bool include_txpower,
 				int min_interval, int max_interval,
 				int appearance, uint16_t manufacturer_len,
-				char *manufacturer_data)
+				char *manufacturer_data,
+				uint16_t service_data_len, char *service_data,
+				uint16_t service_uuid_len, char *service_uuid)
 {
 	char buf[IPC_MTU];
 	struct hal_cmd_gatt_client_set_adv_data *cmd = (void *) buf;
-	size_t cmd_len = sizeof(*cmd) + manufacturer_len;
+	size_t cmd_len;
+	uint8_t *data;
 
 	if (!interface_ready())
 		return BT_STATUS_NOT_READY;
 
+	cmd_len = sizeof(*cmd) + manufacturer_len + service_data_len +
+							service_uuid_len;
+
+	if (cmd_len > IPC_MTU)
+		return BT_STATUS_FAIL;
+
 	cmd->server_if = server_if;
 	cmd->set_scan_rsp = set_scan_rsp;
 	cmd->include_name = include_name;
@@ -989,13 +1001,65 @@
 	cmd->max_interval = max_interval;
 	cmd->appearance = appearance;
 	cmd->manufacturer_len = manufacturer_len;
+	cmd->service_data_len = service_data_len;
+	cmd->service_uuid_len = service_uuid_len;
 
-	memcpy(cmd->manufacturer_data, manufacturer_data, manufacturer_len);
+	data = cmd->data;
+
+	if (manufacturer_data && manufacturer_len) {
+		memcpy(data, manufacturer_data, manufacturer_len);
+		data += manufacturer_len;
+	}
+
+	if (service_data && service_data_len) {
+		memcpy(data, service_data, service_data_len);
+		data += service_data_len;
+	}
+
+	if (service_uuid && service_uuid_len) {
+		memcpy(data, service_uuid, service_uuid_len);
+		data += service_uuid_len;
+	}
 
 	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SET_ADV_DATA,
 						cmd_len, cmd, 0, NULL, NULL);
 }
 
+/*
+ * This is temporary solution and support for older Android versions might
+ * be removed at any time.
+ */
+#if ANDROID_VERSION < PLATFORM_VER(4,4,3)
+static bt_status_t set_adv_data(int server_if, bool set_scan_rsp,
+				bool include_name, bool include_txpower,
+				int min_interval, int max_interval,
+				int appearance, uint16_t manufacturer_len,
+				char *manufacturer_data)
+{
+	return set_adv_data_real(server_if, set_scan_rsp, include_name,
+					include_txpower, min_interval,
+					max_interval, appearance,
+					manufacturer_len, manufacturer_data,
+					0, NULL, 0, NULL);
+}
+#else
+static bt_status_t set_adv_data(int server_if, bool set_scan_rsp,
+				bool include_name, bool include_txpower,
+				int min_interval, int max_interval,
+				int appearance, uint16_t manufacturer_len,
+				char *manufacturer_data,
+				uint16_t service_data_len, char *service_data,
+				uint16_t service_uuid_len, char *service_uuid)
+{
+	return set_adv_data_real(server_if, set_scan_rsp, include_name,
+					include_txpower, min_interval,
+					max_interval, appearance,
+					manufacturer_len, manufacturer_data,
+					service_data_len, service_data,
+					service_uuid_len, service_uuid);
+}
+#endif
+
 static bt_status_t test_command(int command, btgatt_test_params_t *params)
 {
 	struct hal_cmd_gatt_client_test_command cmd;
diff --git a/android/hal-handsfree.c b/android/hal-handsfree.c
index d992506..1d9a1a2 100644
--- a/android/hal-handsfree.c
+++ b/android/hal-handsfree.c
@@ -37,7 +37,7 @@
 	return cbs != NULL;
 }
 
-static void handle_conn_state(void *buf, uint16_t len)
+static void handle_conn_state(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_handsfree_conn_state *ev = buf;
 
@@ -46,7 +46,7 @@
 						(bt_bdaddr_t *) (ev->bdaddr));
 }
 
-static void handle_audio_state(void *buf, uint16_t len)
+static void handle_audio_state(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_handsfree_audio_state *ev = buf;
 
@@ -54,7 +54,7 @@
 		cbs->audio_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr));
 }
 
-static void handle_vr_state(void *buf, uint16_t len)
+static void handle_vr_state(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_handsfree_vr_state *ev = buf;
 
@@ -62,19 +62,19 @@
 		cbs->vr_cmd_cb(ev->state);
 }
 
-static void handle_answer(void *buf, uint16_t len)
+static void handle_answer(void *buf, uint16_t len, int fd)
 {
 	if (cbs->answer_call_cmd_cb)
 		cbs->answer_call_cmd_cb();
 }
 
-static void handle_hangup(void *buf, uint16_t len)
+static void handle_hangup(void *buf, uint16_t len, int fd)
 {
 	if (cbs->hangup_call_cmd_cb)
 		cbs->hangup_call_cmd_cb();
 }
 
-static void handle_volume(void *buf, uint16_t len)
+static void handle_volume(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_handsfree_volume *ev = buf;
 
@@ -82,7 +82,7 @@
 		cbs->volume_cmd_cb(ev->type, ev->volume);
 }
 
-static void handle_dial(void *buf, uint16_t len)
+static void handle_dial(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_handsfree_dial *ev = buf;
 	uint16_t num_len = ev->number_len;
@@ -102,7 +102,7 @@
 		cbs->dial_call_cmd_cb(NULL);
 }
 
-static void handle_dtmf(void *buf, uint16_t len)
+static void handle_dtmf(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_handsfree_dtmf *ev = buf;
 
@@ -110,7 +110,7 @@
 		cbs->dtmf_cmd_cb(ev->tone);
 }
 
-static void handle_nrec(void *buf, uint16_t len)
+static void handle_nrec(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_handsfree_nrec *ev = buf;
 
@@ -118,7 +118,7 @@
 		cbs->nrec_cmd_cb(ev->nrec);
 }
 
-static void handle_chld(void *buf, uint16_t len)
+static void handle_chld(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_handsfree_chld *ev = buf;
 
@@ -126,31 +126,31 @@
 		cbs->chld_cmd_cb(ev->chld);
 }
 
-static void handle_cnum(void *buf, uint16_t len)
+static void handle_cnum(void *buf, uint16_t len, int fd)
 {
 	if (cbs->cnum_cmd_cb)
 		cbs->cnum_cmd_cb();
 }
 
-static void handle_cind(void *buf, uint16_t len)
+static void handle_cind(void *buf, uint16_t len, int fd)
 {
 	if (cbs->cind_cmd_cb)
 		cbs->cind_cmd_cb();
 }
 
-static void handle_cops(void *buf, uint16_t len)
+static void handle_cops(void *buf, uint16_t len, int fd)
 {
 	if (cbs->cops_cmd_cb)
 		cbs->cops_cmd_cb();
 }
 
-static void handle_clcc(void *buf, uint16_t len)
+static void handle_clcc(void *buf, uint16_t len, int fd)
 {
 	if (cbs->clcc_cmd_cb)
 		cbs->clcc_cmd_cb();
 }
 
-static void handle_unknown_at(void *buf, uint16_t len)
+static void handle_unknown_at(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_handsfree_unknown_at *ev = buf;
 
@@ -164,7 +164,7 @@
 		cbs->unknown_at_cmd_cb((char *) ev->buf);
 }
 
-static void handle_hsp_key_press(void *buf, uint16_t len)
+static void handle_hsp_key_press(void *buf, uint16_t len, int fd)
 {
 	if (cbs->key_pressed_cmd_cb)
 		cbs->key_pressed_cmd_cb();
diff --git a/android/hal-health.c b/android/hal-health.c
index 427d4c9..ac6e87f 100644
--- a/android/hal-health.c
+++ b/android/hal-health.c
@@ -33,18 +33,41 @@
 	return cbacks != NULL;
 }
 
+static void handle_app_registration_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_health_app_reg_state *ev = buf;
+
+	if (cbacks->app_reg_state_cb)
+		cbacks->app_reg_state_cb(ev->id, ev->state);
+}
+
+static void handle_channel_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_health_channel_state *ev = buf;
+
+	if (cbacks->channel_state_cb)
+		cbacks->channel_state_cb(ev->app_id, (bt_bdaddr_t *) ev->bdaddr,
+						ev->mdep_index, ev->channel_id,
+						ev->channel_state, fd);
+}
+
 /*
  * handlers will be called from notification thread context,
  * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
  */
 static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_HEALTH_APP_REG_STATE */
+	{ handle_app_registration_state, false,
+				sizeof(struct hal_ev_health_app_reg_state) },
+	/* HAL_EV_HEALTH_CHANNEL_STATE */
+	{ handle_channel_state, false,
+				sizeof(struct hal_ev_health_channel_state) },
 };
 
 static bt_status_t register_application(bthl_reg_param_t *reg, int *app_id)
 {
 	uint8_t buf[IPC_MTU];
 	struct hal_cmd_health_reg_app *cmd = (void *) buf;
-	struct hal_cmd_health_mdep *mdep = (void *) buf;
 	struct hal_rsp_health_reg_app rsp;
 	size_t rsp_len = sizeof(rsp);
 	bt_status_t status;
@@ -69,37 +92,40 @@
 	memcpy(cmd->data, reg->application_name, len);
 	off += len;
 
+	cmd->provider_name_off = off;
 	if (reg->provider_name) {
 		len = strlen(reg->provider_name) + 1;
-		cmd->provider_name_off = off;
 		memcpy(cmd->data + off, reg->provider_name, len);
 		off += len;
 	}
 
+	cmd->service_name_off = off;
 	if (reg->srv_name) {
 		len = strlen(reg->srv_name) + 1;
-		cmd->service_name_off = off;
 		memcpy(cmd->data + off, reg->srv_name, len);
 		off += len;
 	}
 
+	cmd->service_descr_off = off;
 	if (reg->srv_desp) {
 		len = strlen(reg->srv_desp) + 1;
-		cmd->service_descr_off = off;
 		memcpy(cmd->data + off, reg->srv_desp, len);
 		off += len;
 	}
 
 	cmd->len = off;
 	status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP,
-						sizeof(*cmd) + cmd->len, &cmd,
+						sizeof(*cmd) + cmd->len, buf,
 							&rsp_len, &rsp, NULL);
 
 	if (status != BT_STATUS_SUCCESS)
 		return status;
 
 	for (i = 0; i < reg->number_of_mdeps; i++) {
+		struct hal_cmd_health_mdep *mdep = (void *) buf;
+
 		memset(buf, 0, IPC_MTU);
+		mdep->app_id = rsp.app_id;
 		mdep->role = reg->mdep_cfg[i].mdep_role;
 		mdep->data_type = reg->mdep_cfg[i].data_type;
 		mdep->channel_type = reg->mdep_cfg[i].channel_type;
@@ -117,7 +143,6 @@
 
 		if (status != BT_STATUS_SUCCESS)
 			return status;
-
 	}
 
 	*app_id = rsp.app_id;
diff --git a/android/hal-hidhost.c b/android/hal-hidhost.c
index 5787b5e..949e2a0 100644
--- a/android/hal-hidhost.c
+++ b/android/hal-hidhost.c
@@ -33,7 +33,7 @@
 	return cbacks != NULL;
 }
 
-static void handle_conn_state(void *buf, uint16_t len)
+static void handle_conn_state(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_hidhost_conn_state *ev = buf;
 
@@ -42,7 +42,7 @@
 								ev->state);
 }
 
-static void handle_info(void *buf, uint16_t len)
+static void handle_info(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_hidhost_info *ev = buf;
 	bthh_hid_info_t info;
@@ -61,7 +61,7 @@
 		cbacks->hid_info_cb((bt_bdaddr_t *) ev->bdaddr, info);
 }
 
-static void handle_proto_mode(void *buf, uint16_t len)
+static void handle_proto_mode(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_hidhost_proto_mode *ev = buf;
 
@@ -70,7 +70,7 @@
 							ev->status, ev->mode);
 }
 
-static void handle_idle_time(void *buf, uint16_t len)
+static void handle_idle_time(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_hidhost_idle_time *ev = buf;
 
@@ -79,7 +79,7 @@
 								ev->idle_rate);
 }
 
-static void handle_get_report(void *buf, uint16_t len)
+static void handle_get_report(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_hidhost_get_report *ev = buf;
 
@@ -93,7 +93,7 @@
 							ev->data, ev->len);
 }
 
-static void handle_virtual_unplug(void *buf, uint16_t len)
+static void handle_virtual_unplug(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_hidhost_virtual_unplug *ev = buf;
 
@@ -107,36 +107,19 @@
  * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
  */
 static const struct hal_ipc_handler ev_handlers[] = {
-	{	/* HAL_EV_HIDHOST_CONN_STATE */
-		.handler = handle_conn_state,
-		.var_len = false,
-		.data_len = sizeof(struct hal_ev_hidhost_conn_state)
-	},
-	{	/* HAL_EV_HIDHOST_INFO */
-		.handler = handle_info,
-		.var_len = false,
-		.data_len = sizeof(struct hal_ev_hidhost_info),
-	},
-	{	/* HAL_EV_HIDHOST_PROTO_MODE */
-		.handler = handle_proto_mode,
-		.var_len = false,
-		.data_len = sizeof(struct hal_ev_hidhost_proto_mode),
-	},
-	{	/* HAL_EV_HIDHOST_IDLE_TIME */
-		.handler = handle_idle_time,
-		.var_len = false,
-		.data_len = sizeof(struct hal_ev_hidhost_idle_time),
-	},
-	{	/* HAL_EV_HIDHOST_GET_REPORT */
-		.handler = handle_get_report,
-		.var_len = true,
-		.data_len = sizeof(struct hal_ev_hidhost_get_report),
-	},
-	{	/* HAL_EV_HIDHOST_VIRTUAL_UNPLUG */
-		.handler = handle_virtual_unplug,
-		.var_len = false,
-		.data_len = sizeof(struct hal_ev_hidhost_virtual_unplug),
-	},
+	/* HAL_EV_HIDHOST_CONN_STATE */
+	{ handle_conn_state, false, sizeof(struct hal_ev_hidhost_conn_state) },
+	/* HAL_EV_HIDHOST_INFO */
+	{ handle_info, false, sizeof(struct hal_ev_hidhost_info) },
+	/* HAL_EV_HIDHOST_PROTO_MODE */
+	{ handle_proto_mode, false, sizeof(struct hal_ev_hidhost_proto_mode) },
+	/* HAL_EV_HIDHOST_IDLE_TIME */
+	{ handle_idle_time, false, sizeof(struct hal_ev_hidhost_idle_time) },
+	/* HAL_EV_HIDHOST_GET_REPORT */
+	{ handle_get_report, true, sizeof(struct hal_ev_hidhost_get_report) },
+	/* HAL_EV_HIDHOST_VIRTUAL_UNPLUG */
+	{ handle_virtual_unplug, false,
+				sizeof(struct hal_ev_hidhost_virtual_unplug) },
 };
 
 static bt_status_t hidhost_connect(bt_bdaddr_t *bd_addr)
diff --git a/android/hal-ipc-api.txt b/android/hal-ipc-api.txt
index 43e2709..ae62c10 100644
--- a/android/hal-ipc-api.txt
+++ b/android/hal-ipc-api.txt
@@ -1143,8 +1143,9 @@
 
 	Opcode 0x02 - Register Application MDEP data command/response
 
-		Command parameters: MDEP Role (1 octet)
-		                    Data type (1 octet)
+		Command parameters: Application ID (2 octets)
+                                    MDEP Role (1 octet)
+		                    Data type (2 octets)
 		                    Channel type (1 octet)
 		                    MDEP description length (2 octets)
 		                    MDEP description (MDEP desciption length)
diff --git a/android/hal-ipc.c b/android/hal-ipc.c
index 8f5babe..494ba85 100644
--- a/android/hal-ipc.c
+++ b/android/hal-ipc.c
@@ -35,6 +35,7 @@
 
 #define CONNECT_TIMEOUT (10 * 1000)
 
+static int listen_sk = -1;
 static int cmd_sk = -1;
 static int notif_sk = -1;
 
@@ -62,41 +63,39 @@
 	services[service].size = 0;
 }
 
-static void handle_msg(void *buf, ssize_t len)
+static bool handle_msg(void *buf, ssize_t len, int fd)
 {
 	struct ipc_hdr *msg = buf;
 	const struct hal_ipc_handler *handler;
 	uint8_t opcode;
 
 	if (len < (ssize_t) sizeof(*msg)) {
-		error("IPC: message too small (%zd bytes), aborting", len);
-		exit(EXIT_FAILURE);
+		error("IPC: message too small (%zd bytes)", len);
+		return false;
 	}
 
 	if (len != (ssize_t) (sizeof(*msg) + msg->len)) {
-		error("IPC: message malformed (%zd bytes), aborting", len);
-		exit(EXIT_FAILURE);
+		error("IPC: message malformed (%zd bytes)", len);
+		return false;
 	}
 
 	/* if service is valid */
 	if (msg->service_id > HAL_SERVICE_ID_MAX) {
-		error("IPC: unknown service (0x%x), aborting",
-							msg->service_id);
-		exit(EXIT_FAILURE);
+		error("IPC: unknown service (0x%x)", msg->service_id);
+		return false;
 	}
 
 	/* if service is registered */
 	if (!services[msg->service_id].handler) {
-		error("IPC: unregistered service (0x%x), aborting",
-							msg->service_id);
-		exit(EXIT_FAILURE);
+		error("IPC: unregistered service (0x%x)", msg->service_id);
+		return false;
 	}
 
 	/* if opcode fit valid range */
 	if (msg->opcode < HAL_MINIMUM_EVENT) {
-		error("IPC: invalid opcode for service 0x%x (0x%x), aborting",
+		error("IPC: invalid opcode for service 0x%x (0x%x)",
 						msg->service_id, msg->opcode);
-		exit(EXIT_FAILURE);
+		return false;
 	}
 
 	/*
@@ -107,9 +106,9 @@
 
 	/* if opcode is valid */
 	if (opcode >= services[msg->service_id].size) {
-		error("IPC: invalid opcode for service 0x%x (0x%x), aborting",
+		error("IPC: invalid opcode for service 0x%x (0x%x)",
 						msg->service_id, msg->opcode);
-		exit(EXIT_FAILURE);
+		return false;
 	}
 
 	handler = &services[msg->service_id].handler[opcode];
@@ -118,12 +117,14 @@
 	if ((handler->var_len && handler->data_len > msg->len) ||
 			(!handler->var_len && handler->data_len != msg->len)) {
 		error("IPC: message size invalid for service 0x%x opcode 0x%x "
-				"(%u bytes), aborting",
+				"(%u bytes)",
 				msg->service_id, msg->opcode, msg->len);
-		exit(EXIT_FAILURE);
+		return false;
 	}
 
-	handler->handler(msg->payload, msg->len);
+	handler->handler(msg->payload, msg->len, fd);
+
+	return true;
 }
 
 static void *notification_handler(void *data)
@@ -154,9 +155,9 @@
 
 		ret = recvmsg(notif_sk, &msg, 0);
 		if (ret < 0) {
-			error("Receiving notifications failed, aborting :%s",
+			error("Receiving notifications failed: %s",
 							strerror(errno));
-			exit(EXIT_FAILURE);
+			goto failed;
 		}
 
 		/* socket was shutdown */
@@ -168,8 +169,8 @@
 			}
 			pthread_mutex_unlock(&cmd_sk_mutex);
 
-			error("Notification socket closed, aborting");
-			exit(EXIT_FAILURE);
+			error("Notification socket closed");
+			goto failed;
 		}
 
 		fd = -1;
@@ -184,7 +185,8 @@
 			}
 		}
 
-		handle_msg(buf, ret);
+		if (!handle_msg(buf, ret, fd))
+			goto failed;
 	}
 
 	close(notif_sk);
@@ -195,6 +197,9 @@
 	DBG("exit");
 
 	return NULL;
+
+failed:
+	exit(EXIT_FAILURE);
 }
 
 static int accept_connection(int sk)
@@ -229,7 +234,39 @@
 	return new_sk;
 }
 
-bool hal_ipc_init(void)
+bool hal_ipc_accept(void)
+{
+	int err;
+
+	cmd_sk = accept_connection(listen_sk);
+	if (cmd_sk < 0)
+		return false;
+
+	notif_sk = accept_connection(listen_sk);
+	if (notif_sk < 0) {
+		close(cmd_sk);
+		cmd_sk = -1;
+		return false;
+	}
+
+	err = pthread_create(&notif_th, NULL, notification_handler, NULL);
+	if (err) {
+		notif_th = 0;
+		error("Failed to start notification thread: %d (%s)", err,
+							strerror(err));
+		close(cmd_sk);
+		cmd_sk = -1;
+		close(notif_sk);
+		notif_sk = -1;
+		return false;
+	}
+
+	info("IPC connected");
+
+	return true;
+}
+
+bool hal_ipc_init(const char *path, size_t size)
 {
 	struct sockaddr_un addr;
 	int sk;
@@ -246,7 +283,7 @@
 	memset(&addr, 0, sizeof(addr));
 	addr.sun_family = AF_UNIX;
 
-	memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH));
+	memcpy(addr.sun_path, path, size);
 
 	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 		err = errno;
@@ -263,53 +300,26 @@
 		return false;
 	}
 
-	/* Start Android Bluetooth daemon service */
-	if (property_set("bluetooth.start", "daemon") < 0) {
-		error("Failed to set bluetooth.start=daemon");
-		close(sk);
-		return false;
-	}
-
-	cmd_sk = accept_connection(sk);
-	if (cmd_sk < 0) {
-		close(sk);
-		return false;
-	}
-
-	notif_sk = accept_connection(sk);
-	if (notif_sk < 0) {
-		close(sk);
-		close(cmd_sk);
-		cmd_sk = -1;
-		return false;
-	}
-
-	info("bluetoothd connected");
-
-	close(sk);
-
-	err = pthread_create(&notif_th, NULL, notification_handler, NULL);
-	if (err) {
-		notif_th = 0;
-		error("Failed to start notification thread: %d (%s)", err,
-							strerror(err));
-		close(cmd_sk);
-		cmd_sk = -1;
-		close(notif_sk);
-		notif_sk = -1;
-		return false;
-	}
+	listen_sk = sk;
 
 	return true;
 }
 
 void hal_ipc_cleanup(void)
 {
+	close(listen_sk);
+	listen_sk = -1;
+
 	pthread_mutex_lock(&cmd_sk_mutex);
-	close(cmd_sk);
-	cmd_sk = -1;
+	if (cmd_sk >= 0) {
+		close(cmd_sk);
+		cmd_sk = -1;
+	}
 	pthread_mutex_unlock(&cmd_sk_mutex);
 
+	if (notif_sk < 0)
+		return;
+
 	shutdown(notif_sk, SHUT_RD);
 
 	pthread_join(notif_th, NULL);
@@ -328,8 +338,8 @@
 	size_t s_len = sizeof(s);
 
 	if (cmd_sk < 0) {
-		error("Invalid cmd socket passed to hal_ipc_cmd, aborting");
-		exit(EXIT_FAILURE);
+		error("Invalid cmd socket passed to hal_ipc_cmd");
+		goto failed;
 	}
 
 	if (!rsp || !rsp_len) {
@@ -358,15 +368,16 @@
 
 	ret = sendmsg(cmd_sk, &msg, 0);
 	if (ret < 0) {
-		error("Sending command failed, aborting :%s", strerror(errno));
+		error("Sending command failed:%s", strerror(errno));
 		pthread_mutex_unlock(&cmd_sk_mutex);
-		exit(EXIT_FAILURE);
+		goto failed;
 	}
 
 	/* socket was shutdown */
 	if (ret == 0) {
-		error("Command socket closed, aborting");
-		exit(EXIT_FAILURE);
+		error("Command socket closed");
+		pthread_mutex_unlock(&cmd_sk_mutex);
+		goto failed;
 	}
 
 	memset(&msg, 0, sizeof(msg));
@@ -388,48 +399,48 @@
 	}
 
 	ret = recvmsg(cmd_sk, &msg, 0);
-	if (ret < 0) {
-		error("Receiving command response failed, aborting :%s",
-							strerror(errno));
-		pthread_mutex_unlock(&cmd_sk_mutex);
-		exit(EXIT_FAILURE);
-	}
 
 	pthread_mutex_unlock(&cmd_sk_mutex);
 
+	if (ret < 0) {
+		error("Receiving command response failed: %s", strerror(errno));
+		goto failed;
+	}
+
+
 	if (ret < (ssize_t) sizeof(cmd)) {
-		error("Too small response received(%zd bytes), aborting", ret);
-		exit(EXIT_FAILURE);
+		error("Too small response received(%zd bytes)", ret);
+		goto failed;
 	}
 
 	if (cmd.service_id != service_id) {
-		error("Invalid service id (0x%x vs 0x%x), aborting",
+		error("Invalid service id (0x%x vs 0x%x)",
 						cmd.service_id, service_id);
-		exit(EXIT_FAILURE);
+		goto failed;
 	}
 
 	if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
-		error("Malformed response received(%zd bytes), aborting", ret);
-		exit(EXIT_FAILURE);
+		error("Malformed response received(%zd bytes)", ret);
+		goto failed;
 	}
 
 	if (cmd.opcode != opcode && cmd.opcode != HAL_OP_STATUS) {
-		error("Invalid opcode received (0x%x vs 0x%x), aborting",
+		error("Invalid opcode received (0x%x vs 0x%x)",
 						cmd.opcode, opcode);
-		exit(EXIT_FAILURE);
+		goto failed;
 	}
 
 	if (cmd.opcode == HAL_OP_STATUS) {
 		struct ipc_status *s = rsp;
 
 		if (sizeof(*s) != cmd.len) {
-			error("Invalid status length, aborting");
-			exit(EXIT_FAILURE);
+			error("Invalid status length");
+			goto failed;
 		}
 
 		if (s->code == HAL_STATUS_SUCCESS) {
-			error("Invalid success status response, aborting");
-			exit(EXIT_FAILURE);
+			error("Invalid success status response");
+			goto failed;
 		}
 
 		return s->code;
@@ -455,4 +466,7 @@
 		*rsp_len = cmd.len;
 
 	return BT_STATUS_SUCCESS;
+
+failed:
+	exit(EXIT_FAILURE);
 }
diff --git a/android/hal-ipc.h b/android/hal-ipc.h
index 2fbf30f..08ed7cc 100644
--- a/android/hal-ipc.h
+++ b/android/hal-ipc.h
@@ -16,12 +16,13 @@
  */
 
 struct hal_ipc_handler {
-	void (*handler) (void *buf, uint16_t len);
+	void (*handler) (void *buf, uint16_t len, int fd);
 	bool var_len;
 	size_t data_len;
 };
 
-bool hal_ipc_init(void);
+bool hal_ipc_init(const char *path, size_t size);
+bool hal_ipc_accept(void);
 void hal_ipc_cleanup(void);
 
 int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
diff --git a/android/hal-msg.h b/android/hal-msg.h
index 09bd9a0..af859c6 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -405,8 +405,9 @@
 
 #define HAL_OP_HEALTH_MDEP		0x02
 struct hal_cmd_health_mdep {
+	uint16_t app_id;
 	uint8_t  role;
-	uint8_t  data_type;
+	uint16_t data_type;
 	uint8_t  channel_type;
 	uint16_t descr_len;
 	uint8_t  descr[0];
@@ -744,9 +745,20 @@
 	int32_t  max_interval;
 	int32_t  appearance;
 	uint16_t manufacturer_len;
-	uint8_t  manufacturer_data[0];
+	uint16_t service_data_len;
+	uint16_t service_uuid_len;
+	uint8_t  data[0];
 } __attribute__((packed));
 
+#define GATT_CLIENT_TEST_CMD_ENABLE		0x01
+#define GATT_CLIENT_TEST_CMD_CONNECT		0x02
+#define GATT_CLIENT_TEST_CMD_DISCONNECT		0x03
+#define GATT_CLIENT_TEST_CMD_DISCOVER		0x04
+#define GATT_CLIENT_TEST_CMD_READ		0xe0
+#define GATT_CLIENT_TEST_CMD_WRITE		0xe1
+#define GATT_CLIENT_TEST_CMD_INCREASE_SECURITY	0xe2
+#define GATT_CLIENT_TEST_CMD_PAIRING_CONFIG	0xf0
+
 #define HAL_OP_GATT_CLIENT_TEST_COMMAND		0x16
 struct hal_cmd_gatt_client_test_command {
 	int32_t command;
@@ -1590,3 +1602,16 @@
 	int32_t status;
 	int32_t handle;
 } __attribute__((packed));
+
+#define HAL_GATT_PERMISSION_READ			0x0001
+#define HAL_GATT_PERMISSION_READ_ENCRYPTED		0x0002
+#define HAL_GATT_PERMISSION_READ_ENCRYPTED_MITM		0x0004
+#define HAL_GATT_PERMISSION_WRITE			0x0010
+#define HAL_GATT_PERMISSION_WRITE_ENCRYPTED		0x0020
+#define HAL_GATT_PERMISSION_WRITE_ENCRYPTED_MITM	0x0040
+#define HAL_GATT_PERMISSION_WRITE_SIGNED		0x0080
+#define HAL_GATT_PERMISSION_WRITE_SIGNED_MITM		0x0100
+
+#define HAL_GATT_AUTHENTICATION_NONE		0
+#define HAL_GATT_AUTHENTICATION_NO_MITM		1
+#define HAL_GATT_AUTHENTICATION_MITM		2
diff --git a/android/hal-pan.c b/android/hal-pan.c
index a9ac4ff..d3f93ff 100644
--- a/android/hal-pan.c
+++ b/android/hal-pan.c
@@ -31,7 +31,7 @@
 	return cbs != NULL;
 }
 
-static void handle_conn_state(void *buf, uint16_t len)
+static void handle_conn_state(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_pan_conn_state *ev = buf;
 
@@ -41,7 +41,7 @@
 					ev->local_role, ev->remote_role);
 }
 
-static void handle_ctrl_state(void *buf, uint16_t len)
+static void handle_ctrl_state(void *buf, uint16_t len, int fd)
 {
 	struct hal_ev_pan_ctrl_state *ev = buf;
 
@@ -65,16 +65,10 @@
  * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
  */
 static const struct hal_ipc_handler ev_handlers[] = {
-	{	/* HAL_EV_PAN_CTRL_STATE */
-		.handler = handle_ctrl_state,
-		.var_len = false,
-		.data_len = sizeof(struct hal_ev_pan_ctrl_state),
-	},
-	{	/* HAL_EV_PAN_CONN_STATE */
-		.handler = handle_conn_state,
-		.var_len = false,
-		.data_len = sizeof(struct hal_ev_pan_conn_state),
-	},
+	/* HAL_EV_PAN_CTRL_STATE */
+	{ handle_ctrl_state, false, sizeof(struct hal_ev_pan_ctrl_state) },
+	/* HAL_EV_PAN_CONN_STATE */
+	{ handle_conn_state, false, sizeof(struct hal_ev_pan_conn_state) },
 };
 
 static bt_status_t pan_enable(int local_role)
diff --git a/android/hal-sco.c b/android/hal-sco.c
index ea9857e..5888275 100644
--- a/android/hal-sco.c
+++ b/android/hal-sco.c
@@ -617,6 +617,9 @@
 
 	return 0;
 failed:
+	if (out->resampler)
+		release_resampler(out->resampler);
+
 	free(out->downmix_buf);
 	free(out);
 	stream_out = NULL;
@@ -638,6 +641,9 @@
 		sco_dev->out->fd = -1;
 	}
 
+	if (out->resampler)
+		release_resampler(out->resampler);
+
 	free(out->downmix_buf);
 	free(out);
 	sco_dev->out = NULL;
diff --git a/android/hal-utils.h b/android/hal-utils.h
index 8a1d015..a0aab57 100644
--- a/android/hal-utils.h
+++ b/android/hal-utils.h
@@ -17,6 +17,8 @@
 
 #include <hardware/bluetooth.h>
 
+#define PLATFORM_VER(a,b,c) ((a << 16) | ( b << 8) | (c))
+
 #define MAX_UUID_STR_LEN	37
 #define HAL_UUID_LEN		16
 #define MAX_ADDR_STR_LEN	18
diff --git a/android/hardware/bt_gatt_client.h b/android/hardware/bt_gatt_client.h
index d6b0cb4..cf4fabe 100644
--- a/android/hardware/bt_gatt_client.h
+++ b/android/hardware/bt_gatt_client.h
@@ -278,7 +278,9 @@
     /** Set the advertising data or scan response data */
     bt_status_t (*set_adv_data)(int server_if, bool set_scan_rsp, bool include_name,
                     bool include_txpower, int min_interval, int max_interval, int appearance,
-                    uint16_t manufacturer_len, char* manufacturer_data);
+                    uint16_t manufacturer_len, char* manufacturer_data,
+                    uint16_t service_data_len, char* service_data,
+                    uint16_t service_uuid_len, char* service_uuid);
 
     /** Test mode interface */
     bt_status_t (*test_command)( int command, btgatt_test_params_t* params);
diff --git a/android/health.c b/android/health.c
index 655d9f9..0462e99 100644
--- a/android/health.c
+++ b/android/health.c
@@ -3,6 +3,7 @@
  *  BlueZ - Bluetooth protocol stack for Linux
  *
  *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
  *
  *
  *  This library is free software; you can redistribute it and/or
@@ -31,10 +32,17 @@
 #include <unistd.h>
 #include <glib.h>
 
+#include "btio/btio.h"
 #include "lib/bluetooth.h"
 #include "lib/sdp.h"
 #include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+#include "lib/l2cap.h"
 #include "src/log.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/uuid-helper.h"
+#include "src/sdp-client.h"
 
 #include "hal-msg.h"
 #include "ipc-common.h"
@@ -42,40 +50,1152 @@
 #include "utils.h"
 #include "bluetooth.h"
 #include "health.h"
+#include "mcap-lib.h"
+
+#define SVC_HINT_HEALTH			0x00
+#define HDP_VERSION			0x0101
+#define DATA_EXCHANGE_SPEC_11073	0x01
+
+#define CHANNEL_TYPE_ANY       0x00
+#define CHANNEL_TYPE_RELIABLE  0x01
+#define CHANNEL_TYPE_STREAM    0x02
 
 static bdaddr_t adapter_addr;
 static struct ipc *hal_ipc = NULL;
+static struct queue *apps = NULL;
+static struct mcap_instance *mcap = NULL;
+static uint32_t record_id = 0;
+static uint32_t record_state = 0;
+
+struct mdep_cfg {
+	uint8_t role;
+	uint16_t data_type;
+	uint8_t channel_type;
+	char *descr;
+
+	uint8_t id; /* mdep id */
+};
+
+struct health_device {
+	bdaddr_t dst;
+	uint16_t app_id;
+
+	struct mcap_mcl *mcl;
+	bool mcl_conn;
+
+	struct queue *channels;     /* data channels */
+
+	uint16_t ccpsm;
+	uint16_t dcpsm;
+};
+
+struct health_channel {
+	uint8_t mdep_id;
+	uint8_t type;
+
+	struct health_device *dev;
+
+	uint16_t id; /* channel id */
+};
+
+struct health_app {
+	char *app_name;
+	char *provider_name;
+	char *service_name;
+	char *service_descr;
+	uint8_t num_of_mdep;
+	struct queue *mdeps;
+
+	uint16_t id; /* app id */
+	struct queue *devices;
+};
+
+static void free_health_channel(void *data)
+{
+	struct health_channel *channel = data;
+
+	if (!channel)
+		return;
+
+	free(channel);
+}
+
+static void destroy_channel(void *data)
+{
+	struct health_channel *channel = data;
+
+	if (!channel)
+		return;
+
+	/* TODO: Notify channel connection status DESTROYED */
+	queue_remove(channel->dev->channels, channel);
+	free_health_channel(channel);
+}
+
+static void unref_mcl(struct health_device *dev)
+{
+	if (!dev || !dev->mcl)
+		return;
+
+	mcap_close_mcl(dev->mcl, FALSE);
+	mcap_mcl_unref(dev->mcl);
+	dev->mcl = NULL;
+	dev->mcl_conn = false;
+}
+
+static void free_health_device(void *data)
+{
+	struct health_device *dev = data;
+
+	if (!dev)
+		return;
+
+	unref_mcl(dev);
+	queue_destroy(dev->channels, free_health_channel);
+	free(dev);
+}
+
+static void free_mdep_cfg(void *data)
+{
+	struct mdep_cfg *cfg = data;
+
+	if (!cfg)
+		return;
+
+	free(cfg->descr);
+	free(cfg);
+}
+
+static void free_health_app(void *data)
+{
+	struct health_app *app = data;
+
+	if (!app)
+		return;
+
+	free(app->app_name);
+	free(app->provider_name);
+	free(app->service_name);
+	free(app->service_descr);
+	queue_destroy(app->mdeps, free_mdep_cfg);
+	queue_destroy(app->devices, free_health_device);
+	free(app);
+}
+
+static void send_app_reg_notify(struct health_app *app, uint8_t state)
+{
+	struct hal_ev_health_app_reg_state ev;
+
+	DBG("");
+
+	ev.id = app->id;
+	ev.state = state;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HEALTH,
+				HAL_EV_HEALTH_APP_REG_STATE, sizeof(ev), &ev);
+}
+
+static void send_channel_state_notify(struct health_channel *channel,
+						uint8_t state, int fd)
+{
+	struct hal_ev_health_channel_state ev;
+
+	DBG("");
+
+	bdaddr2android(&channel->dev->dst, ev.bdaddr);
+	ev.app_id = channel->dev->app_id;
+	ev.mdep_index = channel->mdep_id;
+	ev.channel_id = channel->id;
+	ev.channel_state = state;
+
+	ipc_send_notif_with_fd(hal_ipc, HAL_SERVICE_ID_HEALTH,
+					HAL_EV_HEALTH_CHANNEL_STATE,
+					sizeof(ev), &ev, fd);
+}
+
+static bool mdep_by_mdep_role(const void *data, const void *user_data)
+{
+	const struct mdep_cfg *mdep = data;
+	uint16_t role = PTR_TO_INT(user_data);
+
+	return mdep->role == role;
+}
+
+static bool mdep_by_mdep_id(const void *data, const void *user_data)
+{
+	const struct mdep_cfg *mdep = data;
+	uint16_t mdep_id = PTR_TO_INT(user_data);
+
+	return mdep->id == mdep_id;
+}
+
+static bool app_by_app_id(const void *data, const void *user_data)
+{
+	const struct health_app *app = data;
+	uint16_t app_id = PTR_TO_INT(user_data);
+
+	return app->id == app_id;
+}
+
+static int register_service_protocols(sdp_record_t *rec,
+					struct health_app *app)
+{
+	uuid_t l2cap_uuid, mcap_c_uuid;
+	sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
+	sdp_list_t *access_proto_list = NULL;
+	sdp_data_t *psm = NULL, *mcap_ver = NULL;
+	uint32_t ccpsm;
+	uint16_t version = MCAP_VERSION;
+	GError *err = NULL;
+	int ret = -1;
+
+	DBG("");
+
+	/* set l2cap information */
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+	if (!l2cap_list)
+		goto fail;
+
+	ccpsm = mcap_get_ctrl_psm(mcap, &err);
+	if (err)
+		goto fail;
+
+	psm = sdp_data_alloc(SDP_UINT16, &ccpsm);
+	if (!psm)
+		goto fail;
+
+	if (!sdp_list_append(l2cap_list, psm))
+		goto fail;
+
+	proto_list = sdp_list_append(NULL, l2cap_list);
+	if (!proto_list)
+		goto fail;
+
+	/* set mcap information */
+	sdp_uuid16_create(&mcap_c_uuid, MCAP_CTRL_UUID);
+	mcap_list = sdp_list_append(NULL, &mcap_c_uuid);
+	if (!mcap_list)
+		goto fail;
+
+	mcap_ver = sdp_data_alloc(SDP_UINT16, &version);
+	if (!mcap_ver)
+		goto fail;
+
+	if (!sdp_list_append(mcap_list, mcap_ver))
+		goto fail;
+
+	if (!sdp_list_append(proto_list, mcap_list))
+		goto fail;
+
+	/* attach protocol information to service record */
+	access_proto_list = sdp_list_append(NULL, proto_list);
+	if (!access_proto_list)
+		goto fail;
+
+	sdp_set_access_protos(rec, access_proto_list);
+	ret = 0;
+
+fail:
+	sdp_list_free(l2cap_list, NULL);
+	sdp_list_free(mcap_list, NULL);
+	sdp_list_free(proto_list, NULL);
+	sdp_list_free(access_proto_list, NULL);
+
+	if (psm)
+		sdp_data_free(psm);
+
+	if (mcap_ver)
+		sdp_data_free(mcap_ver);
+
+	if (err)
+		g_error_free(err);
+
+	return ret;
+}
+
+static int register_service_profiles(sdp_record_t *rec)
+{
+	int ret;
+	sdp_list_t *profile_list;
+	sdp_profile_desc_t hdp_profile;
+
+	DBG("");
+
+	/* set hdp information */
+	sdp_uuid16_create(&hdp_profile.uuid, HDP_SVCLASS_ID);
+	hdp_profile.version = HDP_VERSION;
+	profile_list = sdp_list_append(NULL, &hdp_profile);
+	if (!profile_list)
+		return -1;
+
+	/* set profile descriptor list */
+	ret = sdp_set_profile_descs(rec, profile_list);
+	sdp_list_free(profile_list, NULL);
+
+	return ret;
+}
+
+static int register_service_additional_protocols(sdp_record_t *rec,
+						struct health_app *app)
+{
+	int ret = -1;
+	uuid_t l2cap_uuid, mcap_d_uuid;
+	sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
+	sdp_list_t *access_proto_list = NULL;
+	sdp_data_t *psm = NULL;
+	uint32_t dcpsm;
+	GError *err = NULL;
+
+	DBG("");
+
+	/* set l2cap information */
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+	if (!l2cap_list)
+		goto fail;
+
+	dcpsm = mcap_get_ctrl_psm(mcap, &err);
+	if (err)
+		goto fail;
+
+	psm = sdp_data_alloc(SDP_UINT16, &dcpsm);
+	if (!psm)
+		goto fail;
+
+	if (!sdp_list_append(l2cap_list, psm))
+		goto fail;
+
+	proto_list = sdp_list_append(NULL, l2cap_list);
+	if (!proto_list)
+		goto fail;
+
+	/* set mcap information */
+	sdp_uuid16_create(&mcap_d_uuid, MCAP_DATA_UUID);
+	mcap_list = sdp_list_append(NULL, &mcap_d_uuid);
+	if (!mcap_list)
+		goto fail;
+
+	if (!sdp_list_append(proto_list, mcap_list))
+		goto fail;
+
+	/* attach protocol information to service record */
+	access_proto_list = sdp_list_append(NULL, proto_list);
+	if (!access_proto_list)
+		goto fail;
+
+	sdp_set_add_access_protos(rec, access_proto_list);
+	ret = 0;
+
+fail:
+	sdp_list_free(l2cap_list, NULL);
+	sdp_list_free(mcap_list, NULL);
+	sdp_list_free(proto_list, NULL);
+	sdp_list_free(access_proto_list, NULL);
+
+	if (psm)
+		sdp_data_free(psm);
+
+	if (err)
+		g_error_free(err);
+
+	return ret;
+}
+
+static sdp_list_t *mdeps_to_sdp_features(struct mdep_cfg *mdep)
+{
+	sdp_data_t *mdepid, *dtype = NULL, *role = NULL, *descr = NULL;
+	sdp_list_t *f_list = NULL;
+
+	DBG("");
+
+	mdepid = sdp_data_alloc(SDP_UINT8, &mdep->id);
+	if (!mdepid)
+		return NULL;
+
+	dtype = sdp_data_alloc(SDP_UINT16, &mdep->data_type);
+	if (!dtype)
+		goto fail;
+
+	role = sdp_data_alloc(SDP_UINT8, &mdep->role);
+	if (!role)
+		goto fail;
+
+	if (mdep->descr) {
+		descr = sdp_data_alloc(SDP_TEXT_STR8, mdep->descr);
+		if (!descr)
+			goto fail;
+	}
+
+	f_list = sdp_list_append(NULL, mdepid);
+	if (!f_list)
+		goto fail;
+
+	if (!sdp_list_append(f_list, dtype))
+		goto fail;
+
+	if (!sdp_list_append(f_list, role))
+		goto fail;
+
+	if (descr && !sdp_list_append(f_list, descr))
+		goto fail;
+
+	return f_list;
+
+fail:
+	sdp_list_free(f_list, NULL);
+
+	if (mdepid)
+		sdp_data_free(mdepid);
+
+	if (dtype)
+		sdp_data_free(dtype);
+
+	if (role)
+		sdp_data_free(role);
+
+	if (descr)
+		sdp_data_free(descr);
+
+	return NULL;
+}
+
+static void free_hdp_list(void *list)
+{
+	sdp_list_t *hdp_list = list;
+
+	sdp_list_free(hdp_list, (sdp_free_func_t)sdp_data_free);
+}
+
+static void register_features(void *data, void *user_data)
+{
+	struct mdep_cfg *mdep = data;
+	sdp_list_t **sup_features = user_data;
+	sdp_list_t *hdp_feature;
+
+	DBG("");
+
+	hdp_feature = mdeps_to_sdp_features(mdep);
+	if (!hdp_feature)
+		return;
+
+	if (!*sup_features) {
+		*sup_features = sdp_list_append(NULL, hdp_feature);
+		if (!*sup_features)
+			sdp_list_free(hdp_feature,
+					(sdp_free_func_t)sdp_data_free);
+	} else if (!sdp_list_append(*sup_features, hdp_feature)) {
+		sdp_list_free(hdp_feature,
+					(sdp_free_func_t)sdp_data_free);
+	}
+}
+
+static int register_service_sup_features(sdp_record_t *rec,
+						struct health_app *app)
+{
+	sdp_list_t *sup_features = NULL;
+
+	DBG("");
+
+	queue_foreach(app->mdeps, register_features, &sup_features);
+	if (!sup_features)
+		return -1;
+
+	if (sdp_set_supp_feat(rec, sup_features) < 0) {
+		sdp_list_free(sup_features, free_hdp_list);
+		return -1;
+	}
+
+	sdp_list_free(sup_features, free_hdp_list);
+	return 0;
+}
+
+static int register_data_exchange_spec(sdp_record_t *rec)
+{
+	sdp_data_t *spec;
+	uint8_t data_spec = DATA_EXCHANGE_SPEC_11073;
+	/* As of now only 11073 is supported, so we set it as default */
+
+	DBG("");
+
+	spec = sdp_data_alloc(SDP_UINT8, &data_spec);
+	if (!spec)
+		return -1;
+
+	if (sdp_attr_add(rec, SDP_ATTR_DATA_EXCHANGE_SPEC, spec) < 0) {
+		sdp_data_free(spec);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int register_mcap_features(sdp_record_t *rec)
+{
+	sdp_data_t *mcap_proc;
+	uint8_t mcap_sup_proc = MCAP_SUP_PROC;
+
+	DBG("");
+
+	mcap_proc = sdp_data_alloc(SDP_UINT8, &mcap_sup_proc);
+	if (!mcap_proc)
+		return -1;
+
+	if (sdp_attr_add(rec, SDP_ATTR_MCAP_SUPPORTED_PROCEDURES,
+							mcap_proc) < 0) {
+		sdp_data_free(mcap_proc);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int set_sdp_services_uuid(sdp_record_t *rec, uint8_t role)
+{
+	uuid_t source, sink;
+	sdp_list_t *list = NULL;
+
+	sdp_uuid16_create(&sink, HDP_SINK_SVCLASS_ID);
+	sdp_uuid16_create(&source, HDP_SOURCE_SVCLASS_ID);
+	sdp_get_service_classes(rec, &list);
+
+	switch (role) {
+	case HAL_HEALTH_MDEP_ROLE_SOURCE:
+		if (!sdp_list_find(list, &source, sdp_uuid_cmp))
+			list = sdp_list_append(list, &source);
+		break;
+	case HAL_HEALTH_MDEP_ROLE_SINK:
+		if (!sdp_list_find(list, &sink, sdp_uuid_cmp))
+			list = sdp_list_append(list, &sink);
+		break;
+	}
+
+	if (sdp_set_service_classes(rec, list) < 0) {
+		sdp_list_free(list, NULL);
+		return -1;
+	}
+
+	sdp_list_free(list, NULL);
+
+	return 0;
+}
+
+static int update_sdp_record(struct health_app *app)
+{
+	sdp_record_t *rec;
+	uint8_t role;
+
+	DBG("");
+
+	if (record_id > 0) {
+		bt_adapter_remove_record(record_id);
+		record_id = 0;
+	}
+
+	rec = sdp_record_alloc();
+	if (!rec)
+		return -1;
+
+	role = HAL_HEALTH_MDEP_ROLE_SOURCE;
+	if (queue_find(app->mdeps, mdep_by_mdep_role, INT_TO_PTR(role)))
+		set_sdp_services_uuid(rec, role);
+
+	role = HAL_HEALTH_MDEP_ROLE_SINK;
+	if (queue_find(app->mdeps, mdep_by_mdep_role, INT_TO_PTR(role)))
+		set_sdp_services_uuid(rec, role);
+
+	sdp_set_info_attr(rec, app->service_name, app->provider_name,
+							app->service_descr);
+
+	if (register_service_protocols(rec, app) < 0)
+		goto fail;
+
+	if (register_service_profiles(rec) < 0)
+		goto fail;
+
+	if (register_service_additional_protocols(rec, app) < 0)
+		goto fail;
+
+	if (register_service_sup_features(rec, app) < 0)
+		goto fail;
+
+	if (register_data_exchange_spec(rec) < 0)
+		goto fail;
+
+	if (register_mcap_features(rec) < 0)
+		goto fail;
+
+	if (sdp_set_record_state(rec, record_state++) < 0)
+		goto fail;
+
+	if (bt_adapter_add_record(rec, SVC_HINT_HEALTH) < 0) {
+		error("Failed to register HEALTH record");
+		goto fail;
+	}
+
+	record_id = rec->handle;
+
+	return 0;
+
+fail:
+	sdp_record_free(rec);
+
+	return -1;
+}
+
+static struct health_app *create_health_app(const char *app_name,
+				const char *provider, const char *srv_name,
+				const char *srv_descr, uint8_t mdeps)
+{
+	struct health_app *app;
+	static unsigned int app_id = 1;
+
+	DBG("");
+
+	app = new0(struct health_app, 1);
+	if (!app)
+		return NULL;
+
+	app->id = app_id++;
+	app->num_of_mdep = mdeps;
+	app->app_name = strdup(app_name);
+
+	if (provider) {
+		app->provider_name = strdup(provider);
+		if (!app->provider_name)
+			goto fail;
+	}
+
+	if (srv_name) {
+		app->service_name = strdup(srv_name);
+		if (!app->service_name)
+			goto fail;
+	}
+
+	if (srv_descr) {
+		app->service_descr = strdup(srv_descr);
+		if (!app->service_descr)
+			goto fail;
+	}
+
+	return app;
+
+fail:
+	free_health_app(app);
+	return NULL;
+}
 
 static void bt_health_register_app(const void *buf, uint16_t len)
 {
-	DBG("Not implemented");
+	const struct hal_cmd_health_reg_app *cmd = buf;
+	struct hal_rsp_health_reg_app rsp;
+	struct health_app *app;
+	uint16_t off;
+	uint16_t app_name_len, provider_len, srv_name_len, srv_descr_len;
+	char *app_name, *provider = NULL, *srv_name = NULL, *srv_descr = NULL;
 
-	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP,
-							HAL_STATUS_UNSUPPORTED);
+	DBG("");
+
+	if (len != sizeof(*cmd) + cmd->len ||
+			cmd->app_name_off > cmd->provider_name_off ||
+			cmd->provider_name_off > cmd->service_name_off ||
+			cmd->service_name_off > cmd->service_descr_off ||
+			cmd->service_descr_off > cmd->len) {
+		error("health: Invalid register app command, terminating");
+		raise(SIGTERM);
+		return;
+	}
+
+	app_name = (char *) cmd->data;
+	app_name_len = cmd->provider_name_off - cmd->app_name_off;
+
+	off = app_name_len;
+	provider_len = cmd->service_name_off - off;
+	if (provider_len > 0)
+		provider = (char *) cmd->data + off;
+
+	off += provider_len;
+	srv_name_len = cmd->service_descr_off - off;
+	if (srv_name_len > 0)
+		srv_name = (char *) cmd->data + off;
+
+	off += srv_name_len;
+	srv_descr_len = cmd->len - off;
+	if (srv_descr_len > 0)
+		srv_descr = (char *) cmd->data + off;
+
+	app = create_health_app(app_name, provider, srv_name, srv_descr,
+							cmd->num_of_mdep);
+
+	if (!queue_push_tail(apps, app))
+		goto fail;
+
+	rsp.app_id = app->id;
+	ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP,
+							sizeof(rsp), &rsp, -1);
+	return;
+
+fail:
+	free_health_app(app);
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+							HAL_STATUS_FAILED);
+}
+
+static uint8_t android2channel_type(uint8_t type)
+{
+	switch (type) {
+	case HAL_HEALTH_CHANNEL_TYPE_RELIABLE:
+		return CHANNEL_TYPE_RELIABLE;
+	case HAL_HEALTH_CHANNEL_TYPE_STREAMING:
+		return CHANNEL_TYPE_STREAM;
+	default:
+		return CHANNEL_TYPE_ANY;
+	}
 }
 
 static void bt_health_mdep_cfg_data(const void *buf, uint16_t len)
 {
-	DBG("Not implemented");
+	const struct hal_cmd_health_mdep *cmd = buf;
+	struct health_app *app;
+	struct mdep_cfg *mdep = NULL;
+	uint8_t status;
+
+	DBG("");
+
+	app = queue_find(apps, app_by_app_id, INT_TO_PTR(cmd->app_id));
+	if (!app) {
+		status = HAL_STATUS_INVALID;
+		goto fail;
+	}
+
+	mdep = new0(struct mdep_cfg, 1);
+	if (!mdep) {
+		status = HAL_STATUS_INVALID;
+		goto fail;
+	}
+
+	mdep->role = cmd->role;
+	mdep->data_type = cmd->data_type;
+	mdep->channel_type = android2channel_type(cmd->channel_type);
+	mdep->id = queue_length(app->mdeps) + 1;
+
+	if (cmd->descr_len > 0) {
+		mdep->descr = malloc0(cmd->descr_len);
+		memcpy(mdep->descr, cmd->descr, cmd->descr_len);
+	}
+
+	if (app->num_of_mdep > 0 && !app->mdeps) {
+		app->mdeps = queue_new();
+		if (!app->mdeps) {
+			status = HAL_STATUS_FAILED;
+			goto fail;
+		}
+	}
+
+	if (!queue_push_tail(app->mdeps, mdep)) {
+		status = HAL_STATUS_FAILED;
+		goto fail;
+	}
+
+	if (app->num_of_mdep != queue_length(app->mdeps))
+		goto send_rsp;
+
+	/* add sdp record from app configuration data */
+	/*
+	 * TODO: Check what to be done if mupltple applications are trying to
+	 * register with different role and different configurations.
+	 * 1) Does device supports SOURCE and SINK at the same time ?
+	 * 2) Does it require different SDP records or one record with
+	 *    multile MDEP configurations ?
+	 */
+	if (update_sdp_record(app) < 0) {
+		error("Error creating HDP SDP record");
+		status = HAL_STATUS_FAILED;
+		goto fail;
+	}
+
+	send_app_reg_notify(app, HAL_HEALTH_APP_REG_SUCCESS);
+
+send_rsp:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+							HAL_STATUS_SUCCESS);
+	return;
+
+fail:
+	if (status != HAL_STATUS_SUCCESS) {
+		free_mdep_cfg(mdep);
+		queue_remove(apps, app);
+		free_health_app(app);
+	}
 
 	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
-							HAL_STATUS_UNSUPPORTED);
+								status);
 }
 
 static void bt_health_unregister_app(const void *buf, uint16_t len)
 {
-	DBG("Not implemented");
+	const struct hal_cmd_health_unreg_app *cmd = buf;
+	struct health_app *app;
 
-	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_UNREG_APP,
-							HAL_STATUS_UNSUPPORTED);
+	DBG("");
+
+	app = queue_remove_if(apps, app_by_app_id, INT_TO_PTR(cmd->app_id));
+	if (!app) {
+		ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+				HAL_OP_HEALTH_UNREG_APP, HAL_STATUS_INVALID);
+		return;
+	}
+
+	send_app_reg_notify(app, HAL_HEALTH_APP_DEREG_SUCCESS);
+
+	if (record_id > 0) {
+		bt_adapter_remove_record(record_id);
+		record_id = 0;
+	}
+
+	free_health_app(app);
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+				HAL_OP_HEALTH_UNREG_APP, HAL_STATUS_SUCCESS);
+}
+
+static int get_prot_desc_entry(sdp_data_t *entry, int type, guint16 *val)
+{
+	sdp_data_t *iter;
+	int proto;
+
+	if (!entry || !SDP_IS_SEQ(entry->dtd))
+		return -1;
+
+	iter = entry->val.dataseq;
+	if (!(iter->dtd & SDP_UUID_UNSPEC))
+		return -1;
+
+	proto = sdp_uuid_to_proto(&iter->val.uuid);
+	if (proto != type)
+		return -1;
+
+	if (!val)
+		return 0;
+
+	iter = iter->next;
+	if (iter->dtd != SDP_UINT16)
+		return -1;
+
+	*val = iter->val.uint16;
+
+	return 0;
+}
+
+static int get_prot_desc_list(const sdp_record_t *rec, uint16_t *psm,
+							uint16_t *version)
+{
+	sdp_data_t *pdl, *p0, *p1;
+
+	if (!psm && !version)
+		return -1;
+
+	pdl = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST);
+	if (!pdl || !SDP_IS_SEQ(pdl->dtd))
+		return -1;
+
+	p0 = pdl->val.dataseq;
+	if (get_prot_desc_entry(p0, L2CAP_UUID, psm) < 0)
+		return -1;
+
+	p1 = p0->next;
+	if (get_prot_desc_entry(p1, MCAP_CTRL_UUID, version) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int get_ccpsm(sdp_list_t *recs, uint16_t *ccpsm)
+{
+	sdp_list_t *l;
+
+	for (l = recs; l; l = l->next) {
+		sdp_record_t *rec = l->data;
+
+		if (!get_prot_desc_list(rec, ccpsm, NULL))
+			return 0;
+	}
+
+	return -1;
+}
+
+static int get_add_prot_desc_list(const sdp_record_t *rec, uint16_t *psm)
+{
+	sdp_data_t *pdl, *p0, *p1;
+
+	if (!psm)
+		return -1;
+
+	pdl = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST);
+	if (!pdl || pdl->dtd != SDP_SEQ8)
+		return -1;
+
+	pdl = pdl->val.dataseq;
+	if (pdl->dtd != SDP_SEQ8)
+		return -1;
+
+	p0 = pdl->val.dataseq;
+
+	if (get_prot_desc_entry(p0, L2CAP_UUID, psm) < 0)
+		return -1;
+
+	p1 = p0->next;
+	if (get_prot_desc_entry(p1, MCAP_DATA_UUID, NULL) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int get_dcpsm(sdp_list_t *recs, uint16_t *dcpsm)
+{
+	sdp_list_t *l;
+
+	for (l = recs; l; l = l->next) {
+		sdp_record_t *rec = l->data;
+
+		if (!get_add_prot_desc_list(rec, dcpsm))
+			return 0;
+	}
+
+	return -1;
+}
+
+static void mcap_mdl_connected_cb(struct mcap_mdl *mdl, void *data)
+{
+	DBG("Not Implemeneted");
+}
+
+static void mcap_mdl_closed_cb(struct mcap_mdl *mdl, void *data)
+{
+	DBG("Not Implemeneted");
+}
+
+static void mcap_mdl_deleted_cb(struct mcap_mdl *mdl, void *data)
+{
+	DBG("Not Implemeneted");
+}
+
+static void mcap_mdl_aborted_cb(struct mcap_mdl *mdl, void *data)
+{
+	DBG("Not Implemeneted");
+}
+
+static void mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
+				uint16_t mdlid, uint8_t *conf, void *data)
+{
+	DBG("Not Implemeneted");
+}
+
+static void mcap_mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data)
+{
+	DBG("Not Implemeneted");
+}
+
+static void create_mcl_cb(struct mcap_mcl *mcl, GError *err, gpointer data)
+{
+	struct health_channel *channel = data;
+	gboolean ret;
+	GError *gerr = NULL;
+
+	DBG("");
+
+	if (err) {
+		error("error creating MCL : %s", err->message);
+		goto fail;
+	}
+
+	if (!channel->dev->mcl)
+		channel->dev->mcl = mcap_mcl_ref(mcl);
+
+	channel->dev->mcl_conn = true;
+	DBG("MCL connected");
+
+	ret = mcap_mcl_set_cb(channel->dev->mcl, channel, &gerr,
+			MCAP_MDL_CB_CONNECTED, mcap_mdl_connected_cb,
+			MCAP_MDL_CB_CLOSED, mcap_mdl_closed_cb,
+			MCAP_MDL_CB_DELETED, mcap_mdl_deleted_cb,
+			MCAP_MDL_CB_ABORTED, mcap_mdl_aborted_cb,
+			MCAP_MDL_CB_REMOTE_CONN_REQ, mcap_mdl_conn_req_cb,
+			MCAP_MDL_CB_REMOTE_RECONN_REQ, mcap_mdl_reconn_req_cb,
+			MCAP_MDL_CB_INVALID);
+	if (!ret) {
+		error("error setting mdl callbacks on mcl");
+
+		if (gerr)
+			g_error_free(gerr);
+
+		goto fail;
+	}
+
+	/* TODO : create mdl */
+	return;
+
+fail:
+	destroy_channel(channel);
+}
+
+static void search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	struct health_channel *channel = data;
+	GError *gerr = NULL;
+
+	DBG("");
+
+	if (err < 0 || !recs) {
+		error("Error getting remote SDP records");
+		goto fail;
+	}
+
+	if (get_ccpsm(recs, &channel->dev->ccpsm) < 0) {
+		error("Can't get remote PSM for control channel");
+		goto fail;
+	}
+
+	if (get_dcpsm(recs, &channel->dev->dcpsm) < 0) {
+		error("Can't get remote PSM for data channel");
+		goto fail;
+	}
+
+	if (!mcap_create_mcl(mcap, &channel->dev->dst, channel->dev->ccpsm,
+					create_mcl_cb, channel, NULL, &gerr)) {
+		error("error creating mcl %s", gerr->message);
+
+		if (gerr)
+			g_error_free(gerr);
+
+		goto fail;
+	}
+
+	send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_CONNECTING, -1);
+	return;
+
+fail:
+	send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_DESTROYED, -1);
+
+	queue_remove(channel->dev->channels, channel);
+	free_health_channel(channel);
+}
+
+static int connect_mcl(struct health_channel *channel)
+{
+	uuid_t uuid;
+
+	DBG("");
+
+	bt_string2uuid(&uuid, HDP_UUID);
+
+	return bt_search_service(&adapter_addr, &channel->dev->dst, &uuid,
+						search_cb, channel, NULL, 0);
+}
+
+static struct health_device *create_device(uint16_t app_id, const uint8_t *addr)
+{
+	struct health_device *dev;
+
+	dev = new0(struct health_device, 1);
+	if (!dev)
+		return NULL;
+
+	android2bdaddr(addr, &dev->dst);
+	dev->app_id = app_id;
+
+	return dev;
+}
+
+static struct health_channel *create_channel(uint16_t app_id,
+						uint8_t mdep_index)
+{
+	struct health_app *app;
+	struct mdep_cfg *mdep;
+	struct health_channel *channel;
+	uint8_t index;
+	static unsigned int channel_id = 1;
+
+	app = queue_find(apps, app_by_app_id, INT_TO_PTR(app_id));
+	if (!app)
+		return NULL;
+
+	index = mdep_index + 1;
+	mdep = queue_find(app->mdeps, mdep_by_mdep_id, INT_TO_PTR(index));
+	if (!mdep)
+		return NULL;
+
+	channel = new0(struct health_channel, 1);
+	if (!channel)
+		return NULL;
+
+	channel->mdep_id = mdep_index;
+	channel->type = mdep->channel_type;
+	channel->id = channel_id++;
+
+	return channel;
 }
 
 static void bt_health_connect_channel(const void *buf, uint16_t len)
 {
-	DBG("Not implemented");
+	const struct hal_cmd_health_connect_channel *cmd = buf;
+	struct hal_rsp_health_connect_channel rsp;
+	struct health_app *app;
+	struct health_device *dev = NULL;
+	struct health_channel *channel = NULL;
 
+	DBG("");
+
+	app = queue_find(apps, app_by_app_id, INT_TO_PTR(cmd->app_id));
+	if (!app)
+		goto fail;
+
+	dev = create_device(cmd->app_id, cmd->bdaddr);
+	if (!dev)
+		goto fail;
+
+	channel = create_channel(cmd->app_id, cmd->mdep_index);
+	if (!channel)
+		goto fail;
+
+	channel->dev = dev;
+
+	if (!app->devices) {
+		app->devices = queue_new();
+		if (!app->devices)
+			goto fail;
+	}
+
+	if (!queue_push_tail(app->devices, dev))
+		goto fail;
+
+	if (!dev->channels) {
+		dev->channels = queue_new();
+		if (!dev->channels)
+			goto fail;
+	}
+
+	if (!queue_push_tail(dev->channels, channel)) {
+		queue_remove(app->devices, dev);
+		goto fail;
+	}
+
+	if (connect_mcl(channel) < 0) {
+		error("error retrieving HDP SDP record");
+		queue_remove(app->devices, dev);
+		goto fail;
+	}
+
+	rsp.channel_id = channel->id;
+	ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_HEALTH,
+				HAL_OP_HEALTH_CONNECT_CHANNEL,
+				sizeof(rsp), &rsp, -1);
+	return;
+
+fail:
+	free_health_channel(channel);
+	free_health_device(dev);
 	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
-			HAL_OP_HEALTH_CONNECT_CHANNEL, HAL_STATUS_UNSUPPORTED);
+			HAL_OP_HEALTH_CONNECT_CHANNEL, HAL_STATUS_FAILED);
 }
 
 static void bt_health_destroy_channel(const void *buf, uint16_t len)
@@ -104,13 +1224,51 @@
 				sizeof(struct hal_cmd_health_destroy_channel) },
 };
 
+static void mcl_connected(struct mcap_mcl *mcl, gpointer data)
+{
+	DBG("Not implemented");
+}
+
+static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
+{
+	DBG("Not implemented");
+}
+
+static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data)
+{
+	DBG("Not implemented");
+}
+
+static void mcl_uncached(struct mcap_mcl *mcl, gpointer data)
+{
+	DBG("Not implemented");
+}
+
 bool bt_health_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
 {
+	GError *err = NULL;
+
 	DBG("");
 
 	bacpy(&adapter_addr, addr);
 
+	mcap = mcap_create_instance(&adapter_addr, BT_IO_SEC_MEDIUM, 0, 0,
+					mcl_connected, mcl_reconnected,
+					mcl_disconnected, mcl_uncached,
+					NULL, /* CSP is not used right now */
+					NULL, &err);
+
+	if (!mcap) {
+		error("Error creating MCAP instance : %s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
 	hal_ipc = ipc;
+	apps = queue_new();
+	if (!apps)
+		return false;
+
 	ipc_register(hal_ipc, HAL_SERVICE_ID_HEALTH, cmd_handlers,
 						G_N_ELEMENTS(cmd_handlers));
 
@@ -121,6 +1279,8 @@
 {
 	DBG("");
 
+	mcap_instance_unref(mcap);
+	queue_destroy(apps, free_health_app);
 	ipc_unregister(hal_ipc, HAL_SERVICE_ID_HEALTH);
 	hal_ipc = NULL;
 }
diff --git a/android/hidhost.c b/android/hidhost.c
index 1758020..846dd57 100644
--- a/android/hidhost.c
+++ b/android/hidhost.c
@@ -40,20 +40,22 @@
 #include "lib/sdp_lib.h"
 #include "src/shared/mgmt.h"
 #include "src/shared/util.h"
+#include "src/shared/uhid.h"
 #include "src/sdp-client.h"
 #include "src/uuid-helper.h"
-#include "profiles/input/uhid_copy.h"
 #include "src/log.h"
 
 #include "hal-msg.h"
 #include "ipc-common.h"
 #include "ipc.h"
+#include "bluetooth.h"
+#include "gatt.h"
+#include "hog.h"
 #include "hidhost.h"
 #include "utils.h"
 
 #define L2CAP_PSM_HIDP_CTRL	0x11
 #define L2CAP_PSM_HIDP_INTR	0x13
-#define UHID_DEVICE_FILE	"/dev/uhid"
 
 /* HID message types */
 #define HID_MSG_CONTROL		0x10
@@ -78,11 +80,14 @@
 /* HID Virtual Cable Unplug */
 #define HID_VIRTUAL_CABLE_UNPLUG	0x05
 
+#define HOG_UUID		"00001812-0000-1000-8000-00805f9b34fb"
+
 static bdaddr_t adapter_addr;
 
 static GIOChannel *ctrl_io = NULL;
 static GIOChannel *intr_io = NULL;
 static GSList *devices = NULL;
+static unsigned int hog_app = 0;
 
 static struct ipc *hal_ipc = NULL;
 
@@ -101,9 +106,10 @@
 	GIOChannel	*intr_io;
 	guint		ctrl_watch;
 	guint		intr_watch;
-	int		uhid_fd;
-	guint		uhid_watch_id;
+	struct bt_uhid	*uhid;
 	uint8_t		last_hid_msg;
+	struct bt_hog	*hog;
+	guint		reconnect_id;
 };
 
 static int device_cmp(gconstpointer s, gconstpointer user_data)
@@ -114,25 +120,13 @@
 	return bacmp(&dev->dst, dst);
 }
 
-static void uhid_destroy(int fd)
-{
-	struct uhid_event ev;
-
-	/* destroy uHID device */
-	memset(&ev, 0, sizeof(ev));
-	ev.type = UHID_DESTROY;
-
-	if (write(fd, &ev, sizeof(ev)) < 0)
-		error("hidhost: Failed to destroy uHID device: %s (%d)",
-						strerror(errno), errno);
-
-	close(fd);
-}
-
 static void hid_device_free(void *data)
 {
 	struct hid_device *dev = data;
 
+	if (dev->reconnect_id > 0)
+		g_source_remove(dev->reconnect_id);
+
 	if (dev->ctrl_watch > 0)
 		g_source_remove(dev->ctrl_watch);
 
@@ -145,13 +139,11 @@
 	if (dev->ctrl_io)
 		g_io_channel_unref(dev->ctrl_io);
 
-	if (dev->uhid_watch_id) {
-		g_source_remove(dev->uhid_watch_id);
-		dev->uhid_watch_id = 0;
-	}
+	if (dev->uhid)
+		bt_uhid_unref(dev->uhid);
 
-	if (dev->uhid_fd > 0)
-		uhid_destroy(dev->uhid_fd);
+	if (dev->hog)
+		bt_hog_unref(dev->hog);
 
 	g_free(dev->rd_data);
 	g_free(dev);
@@ -196,9 +188,10 @@
 	return true;
 }
 
-static void handle_uhid_output(struct hid_device *dev,
-						struct uhid_output_req *output)
+static void handle_uhid_output(struct uhid_event *event, void *user_data)
 {
+	struct uhid_output_req *output = &event->u.output;
+	struct hid_device *dev = user_data;
 	int fd, req_size;
 	uint8_t *req;
 
@@ -222,87 +215,15 @@
 	free(req);
 }
 
-static gboolean uhid_event_cb(GIOChannel *io, GIOCondition cond,
-							gpointer user_data)
-{
-	struct hid_device *dev = user_data;
-	struct uhid_event ev;
-	ssize_t bread;
-	int fd;
-
-	DBG("");
-
-	if (cond & (G_IO_ERR | G_IO_NVAL))
-		goto failed;
-
-	fd = g_io_channel_unix_get_fd(io);
-	memset(&ev, 0, sizeof(ev));
-
-	bread = read(fd, &ev, sizeof(ev));
-	if (bread < 0) {
-		DBG("read: %s (%d)", strerror(errno), errno);
-		goto failed;
-	}
-
-	DBG("uHID event type %d received", ev.type);
-
-	switch (ev.type) {
-	case UHID_START:
-	case UHID_STOP:
-		/*
-		 * These are called to start and stop the underlying hardware.
-		 * We open the channels before creating the device so the
-		 * hardware is always ready. No need to handle these.
-		 * The kernel never destroys a device itself! Only an explicit
-		 * UHID_DESTROY request can remove a device.
-		 */
-		break;
-	case UHID_OPEN:
-	case UHID_CLOSE:
-		/*
-		 * OPEN/CLOSE are sent whenever user-space opens any interface
-		 * provided by the kernel HID device. Whenever the open-count
-		 * is non-zero we must be ready for I/O. As long as it is zero,
-		 * we can decide to drop all I/O and put the device
-		 * asleep This is optional, though.
-		 */
-		break;
-	case UHID_OUTPUT:
-		handle_uhid_output(dev, &ev.u.output);
-		break;
-	case UHID_FEATURE:
-		/* TODO */
-		break;
-	case UHID_OUTPUT_EV:
-		/*
-		 * This is only sent by kernels prior to linux-3.11. It
-		 * requires us to parse HID-descriptors in user-space to
-		 * properly handle it. This is redundant as the kernel
-		 * does it already. That's why newer kernels assemble
-		 * the output-reports and send it to us via UHID_OUTPUT.
-		 */
-		DBG("UHID_OUTPUT_EV unsupported");
-		break;
-	default:
-		warn("unexpected uHID event");
-	}
-
-	return TRUE;
-
-failed:
-	dev->uhid_watch_id = 0;
-	return FALSE;
-}
-
 static gboolean intr_io_watch_cb(GIOChannel *chan, gpointer data)
 {
 	struct hid_device *dev = data;
 	uint8_t buf[UHID_DATA_MAX];
 	struct uhid_event ev;
-	int fd, bread;
+	int fd, bread, err;
 
 	/* Wait uHID if not ready */
-	if (dev->uhid_fd < 0)
+	if (!dev->uhid)
 		return TRUE;
 
 	fd = g_io_channel_unix_get_fd(chan);
@@ -323,8 +244,9 @@
 	ev.u.input.size = bread - 1;
 	memcpy(ev.u.input.data, &buf[1], ev.u.input.size);
 
-	if (write(dev->uhid_fd, &ev, sizeof(ev)) < 0)
-		DBG("uhid write: %s (%d)", strerror(errno), errno);
+	err = bt_uhid_send(dev->uhid, &ev);
+	if (err < 0)
+		DBG("bt_uhid_send: %s (%d)", strerror(-err), -err);
 
 	return TRUE;
 }
@@ -571,16 +493,13 @@
 
 static int uhid_create(struct hid_device *dev)
 {
-	GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_NVAL;
 	struct uhid_event ev;
-	GIOChannel *io;
 	int err;
 
-	dev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC);
-	if (dev->uhid_fd < 0) {
+	dev->uhid = bt_uhid_new_default();
+	if (!dev->uhid) {
 		err = -errno;
-		error("hidhost: Failed to open uHID device: %s",
-							strerror(errno));
+		error("hidhost: Failed to create bt_uhid instance");
 		return err;
 	}
 
@@ -595,20 +514,16 @@
 	ev.u.create.rd_size = dev->rd_size;
 	ev.u.create.rd_data = dev->rd_data;
 
-	if (write(dev->uhid_fd, &ev, sizeof(ev)) < 0) {
-		err = -errno;
+	err = bt_uhid_send(dev->uhid, &ev);
+	if (err < 0) {
 		error("hidhost: Failed to create uHID device: %s",
-							strerror(errno));
-		close(dev->uhid_fd);
-		dev->uhid_fd = -1;
+							strerror(-err));
+		bt_uhid_unref(dev->uhid);
+		dev->uhid = NULL;
 		return err;
 	}
 
-	io = g_io_channel_unix_new(dev->uhid_fd);
-	g_io_channel_set_encoding(io, NULL, NULL);
-	dev->uhid_watch_id = g_io_add_watch(io, cond, uhid_event_cb, dev);
-	g_io_channel_unref(io);
-
+	bt_uhid_register(dev->uhid, UHID_OUTPUT, handle_uhid_output, dev);
 	bt_hid_set_info(dev);
 
 	return 0;
@@ -821,6 +736,89 @@
 	hid_device_remove(dev);
 }
 
+static gboolean hog_reconnect(void *user_data)
+{
+	struct hid_device *dev = user_data;
+
+	DBG("");
+
+	dev->reconnect_id = 0;
+
+	bt_gatt_connect_app(hog_app, &dev->dst);
+
+	return FALSE;
+}
+
+static void hog_conn_cb(const bdaddr_t *addr, int err, void *attrib)
+{
+	GSList *l;
+	struct hid_device *dev;
+
+	l = g_slist_find_custom(devices, addr, device_cmp);
+	dev = l ? l->data : NULL;
+
+	if (err < 0) {
+		if (!dev)
+			return;
+		if (dev->hog && !dev->reconnect_id) {
+			bt_hid_notify_state(dev,
+						HAL_HIDHOST_STATE_DISCONNECTED);
+			bt_hog_detach(dev->hog);
+			dev->reconnect_id = g_idle_add(hog_reconnect, dev);
+			return;
+		}
+		goto fail;
+	}
+
+	if (!dev) {
+		dev = g_new0(struct hid_device, 1);
+		bacpy(&dev->dst, addr);
+		devices = g_slist_append(devices, dev);
+		bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING);
+	}
+
+	if (!dev->hog) {
+		/* TODO: Get device details and primary */
+		dev->hog = bt_hog_new("bluez-input-device", dev->vendor,
+					dev->product, dev->version, NULL);
+		if (!dev->hog) {
+			error("HoG: unable to create session");
+			goto fail;
+		}
+	}
+
+	if (!bt_hog_attach(dev->hog, attrib)) {
+		error("HoG: unable to attach");
+		goto fail;
+	}
+
+	DBG("");
+
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED);
+
+	return;
+
+fail:
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+	hid_device_remove(dev);
+}
+
+static bool hog_connect(struct hid_device *dev)
+{
+	DBG("");
+
+	if (hog_app)
+		return bt_gatt_connect_app(hog_app, &dev->dst);
+
+	hog_app = bt_gatt_register_app(HOG_UUID, GATT_CLIENT, hog_conn_cb);
+	if (!hog_app) {
+		error("hidhost: bt_gatt_register_app failed");
+		return false;
+	}
+
+	return bt_gatt_connect_app(hog_app, &dev->dst);
+}
+
 static void bt_hid_connect(const void *buf, uint16_t len)
 {
 	const struct hal_cmd_hidhost_connect *cmd = buf;
@@ -843,11 +841,19 @@
 
 	dev = g_new0(struct hid_device, 1);
 	bacpy(&dev->dst, &dst);
-	dev->uhid_fd = -1;
+	dev->state = HAL_HIDHOST_STATE_DISCONNECTED;
 
 	ba2str(&dev->dst, addr);
 	DBG("connecting to %s", addr);
 
+	if (bt_is_device_le(&dst)) {
+		if (!hog_connect(dev)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+		goto done;
+	}
+
 	sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID);
 	if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
 				hid_sdp_did_search_cb, dev, NULL, 0) < 0) {
@@ -857,8 +863,11 @@
 		goto failed;
 	}
 
+done:
 	devices = g_slist_append(devices, dev);
-	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING);
+
+	if (dev->state == HAL_HIDHOST_STATE_DISCONNECTED)
+		bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING);
 
 	status = HAL_STATUS_SUCCESS;
 
@@ -867,6 +876,23 @@
 									status);
 }
 
+static bool hog_disconnect(struct hid_device *dev)
+{
+	DBG("");
+
+	if (dev->state == HAL_HIDHOST_STATE_DISCONNECTED)
+		return false;
+
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
+
+	if (!bt_gatt_disconnect_app(hog_app, &dev->dst)) {
+		bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+		hid_device_remove(dev);
+	}
+
+	return true;
+}
+
 static void bt_hid_disconnect(const void *buf, uint16_t len)
 {
 	const struct hal_cmd_hidhost_disconnect *cmd = buf;
@@ -886,6 +912,13 @@
 	}
 
 	dev = l->data;
+	if (bt_is_device_le(&dst)) {
+		if (!hog_disconnect(dev)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+		goto done;
+	}
 
 	/* Wait either channels to HUP */
 	if (dev->intr_io)
@@ -896,6 +929,8 @@
 
 	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
 
+
+done:
 	status = HAL_STATUS_SUCCESS;
 
 failed:
@@ -1361,7 +1396,6 @@
 		dev = g_new0(struct hid_device, 1);
 		bacpy(&dev->dst, &dst);
 		dev->ctrl_io = g_io_channel_ref(chan);
-		dev->uhid_fd = -1;
 
 		sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID);
 		if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
@@ -1443,6 +1477,9 @@
 {
 	DBG("");
 
+	if (hog_app > 0)
+		bt_gatt_unregister_app(hog_app);
+
 	g_slist_free_full(devices, hid_device_free);
 	devices = NULL;
 
diff --git a/android/hog.c b/android/hog.c
new file mode 100644
index 0000000..d42400d
--- /dev/null
+++ b/android/hog.c
@@ -0,0 +1,862 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation.
+ *  Copyright (C) 2012  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2012  Nordic Semiconductor Inc.
+ *  Copyright (C) 2012  Instituto Nokia de Tecnologia - INdT
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include <glib.h>
+
+#include "src/log.h"
+
+#include "lib/uuid.h"
+#include "src/shared/util.h"
+#include "src/shared/uhid.h"
+
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt.h"
+
+#include "btio/btio.h"
+
+#include "android/hog.h"
+
+#define HOG_UUID		"00001812-0000-1000-8000-00805f9b34fb"
+
+#define HOG_INFO_UUID		0x2A4A
+#define HOG_REPORT_MAP_UUID	0x2A4B
+#define HOG_REPORT_UUID		0x2A4D
+#define HOG_PROTO_MODE_UUID	0x2A4E
+#define HOG_CONTROL_POINT_UUID	0x2A4C
+
+#define HOG_REPORT_TYPE_INPUT	1
+#define HOG_REPORT_TYPE_OUTPUT	2
+#define HOG_REPORT_TYPE_FEATURE	3
+
+#define HOG_PROTO_MODE_BOOT    0
+#define HOG_PROTO_MODE_REPORT  1
+
+#define HOG_REPORT_MAP_MAX_SIZE        512
+#define HID_INFO_SIZE			4
+#define ATT_NOTIFICATION_HEADER_SIZE	3
+
+struct bt_hog {
+	int			ref_count;
+	char			*name;
+	uint16_t		vendor;
+	uint16_t		product;
+	uint16_t		version;
+	struct gatt_primary	*primary;
+	GAttrib			*attrib;
+	GSList			*reports;
+	struct bt_uhid		*uhid;
+	gboolean		has_report_id;
+	uint16_t		bcdhid;
+	uint8_t			bcountrycode;
+	uint16_t		proto_mode_handle;
+	uint16_t		ctrlpt_handle;
+	uint8_t			flags;
+};
+
+struct report {
+	struct bt_hog		*hog;
+	uint8_t			id;
+	uint8_t			type;
+	guint			notifyid;
+	struct gatt_char	*decl;
+};
+
+static void report_value_cb(const guint8 *pdu, guint16 len, gpointer user_data)
+{
+	struct report *report = user_data;
+	struct bt_hog *hog = report->hog;
+	struct uhid_event ev;
+	uint8_t *buf;
+	int err;
+
+	if (len < ATT_NOTIFICATION_HEADER_SIZE) {
+		error("Malformed ATT notification");
+		return;
+	}
+
+	pdu += ATT_NOTIFICATION_HEADER_SIZE;
+	len -= ATT_NOTIFICATION_HEADER_SIZE;
+
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_INPUT;
+	buf = ev.u.input.data;
+
+	if (hog->has_report_id) {
+		buf[0] = report->id;
+		len = MIN(len, sizeof(ev.u.input.data) - 1);
+		memcpy(buf + 1, pdu, len);
+		ev.u.input.size = ++len;
+	} else {
+		len = MIN(len, sizeof(ev.u.input.data));
+		memcpy(buf, pdu, len);
+		ev.u.input.size = len;
+	}
+
+	err = bt_uhid_send(hog->uhid, &ev);
+	if (err < 0) {
+		error("bt_uhid_send: %s (%d)", strerror(-err), -err);
+		return;
+	}
+
+	DBG("HoG report (%u bytes)", ev.u.input.size);
+}
+
+static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
+					guint16 plen, gpointer user_data)
+{
+	struct report *report = user_data;
+	struct bt_hog *hog = report->hog;
+
+	if (status != 0) {
+		error("Write report characteristic descriptor failed: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	report->notifyid = g_attrib_register(hog->attrib,
+					ATT_OP_HANDLE_NOTIFY,
+					report->decl->value_handle,
+					report_value_cb, report, NULL);
+
+	DBG("Report characteristic descriptor written: notifications enabled");
+}
+
+static void write_ccc(uint16_t handle, gpointer user_data)
+{
+	struct report *report = user_data;
+	struct bt_hog *hog = report->hog;
+	uint8_t value[] = { 0x01, 0x00 };
+
+	gatt_write_char(hog->attrib, handle, value, sizeof(value),
+					report_ccc_written_cb, report);
+}
+
+static void report_reference_cb(guint8 status, const guint8 *pdu,
+					guint16 plen, gpointer user_data)
+{
+	struct report *report = user_data;
+
+	if (status != 0) {
+		error("Read Report Reference descriptor failed: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	if (plen != 3) {
+		error("Malformed ATT read response");
+		return;
+	}
+
+	report->id = pdu[1];
+	report->type = pdu[2];
+	DBG("Report ID: 0x%02x Report type: 0x%02x", pdu[1], pdu[2]);
+}
+
+static void external_report_reference_cb(guint8 status, const guint8 *pdu,
+					guint16 plen, gpointer user_data);
+
+
+static void discover_descriptor_cb(uint8_t status, GSList *descs,
+								void *user_data)
+{
+	struct report *report;
+	struct bt_hog *hog;
+	GAttrib *attrib = NULL;
+
+	if (status != 0) {
+		error("Discover all descriptors failed: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	for ( ; descs; descs = descs->next) {
+		struct gatt_desc *desc = descs->data;
+
+		switch (desc->uuid16) {
+		case GATT_CLIENT_CHARAC_CFG_UUID:
+			report = user_data;
+			attrib = report->hog->attrib;
+			write_ccc(desc->handle, report);
+			break;
+		case GATT_REPORT_REFERENCE:
+			report = user_data;
+			attrib = report->hog->attrib;
+			gatt_read_char(attrib, desc->handle,
+						report_reference_cb, report);
+			break;
+		case GATT_EXTERNAL_REPORT_REFERENCE:
+			hog = user_data;
+			attrib = hog->attrib;
+			gatt_read_char(attrib, desc->handle,
+					external_report_reference_cb, hog);
+			break;
+		}
+	}
+}
+
+static void discover_descriptor(GAttrib *attrib, uint16_t start, uint16_t end,
+							gpointer user_data)
+{
+	if (start > end)
+		return;
+
+	gatt_discover_desc(attrib, start, end, NULL,
+					discover_descriptor_cb, user_data);
+}
+
+static void external_service_char_cb(uint8_t status, GSList *chars,
+								void *user_data)
+{
+	struct bt_hog *hog = user_data;
+	struct gatt_primary *primary = hog->primary;
+	struct report *report;
+	GSList *l;
+
+	if (status != 0) {
+		const char *str = att_ecode2str(status);
+		DBG("Discover external service characteristic failed: %s", str);
+		return;
+	}
+
+	for (l = chars; l; l = g_slist_next(l)) {
+		struct gatt_char *chr, *next;
+		uint16_t start, end;
+
+		chr = l->data;
+		next = l->next ? l->next->data : NULL;
+
+		DBG("0x%04x UUID: %s properties: %02x",
+				chr->handle, chr->uuid, chr->properties);
+
+		report = g_new0(struct report, 1);
+		report->hog = hog;
+		report->decl = g_memdup(chr, sizeof(*chr));
+		hog->reports = g_slist_append(hog->reports, report);
+		start = chr->value_handle + 1;
+		end = (next ? next->handle - 1 : primary->range.end);
+		discover_descriptor(hog->attrib, start, end, report);
+	}
+}
+
+static void external_report_reference_cb(guint8 status, const guint8 *pdu,
+					guint16 plen, gpointer user_data)
+{
+	struct bt_hog *hog = user_data;
+	uint16_t uuid16;
+	bt_uuid_t uuid;
+
+	if (status != 0) {
+		error("Read External Report Reference descriptor failed: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	if (plen != 3) {
+		error("Malformed ATT read response");
+		return;
+	}
+
+	uuid16 = get_le16(&pdu[1]);
+	DBG("External report reference read, external report characteristic "
+						"UUID: 0x%04x", uuid16);
+	bt_uuid16_create(&uuid, uuid16);
+	gatt_discover_char(hog->attrib, 0x00, 0xff, &uuid,
+					external_service_char_cb, hog);
+}
+
+static int report_type_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct report *report = a;
+	uint8_t type = GPOINTER_TO_UINT(b);
+
+	return report->type - type;
+}
+
+static void output_written_cb(guint8 status, const guint8 *pdu,
+					guint16 plen, gpointer user_data)
+{
+	if (status != 0) {
+		error("Write output report failed: %s", att_ecode2str(status));
+		return;
+	}
+}
+
+static void forward_report(struct uhid_event *ev, void *user_data)
+{
+	struct bt_hog *hog = user_data;
+	struct report *report;
+	GSList *l;
+	void *data;
+	int size;
+	guint type;
+
+	if (hog->has_report_id) {
+		data = ev->u.output.data + 1;
+		size = ev->u.output.size - 1;
+	} else {
+		data = ev->u.output.data;
+		size = ev->u.output.size;
+	}
+
+	switch (ev->type) {
+	case UHID_OUTPUT:
+		type = HOG_REPORT_TYPE_OUTPUT;
+		break;
+	case UHID_FEATURE:
+		type = HOG_REPORT_TYPE_FEATURE;
+		break;
+	default:
+		return;
+	}
+
+	l = g_slist_find_custom(hog->reports, GUINT_TO_POINTER(type),
+							report_type_cmp);
+	if (!l)
+		return;
+
+	report = l->data;
+
+	DBG("Sending report type %d to handle 0x%X", type,
+						report->decl->value_handle);
+
+	if (hog->attrib == NULL)
+		return;
+
+	if (report->decl->properties & GATT_CHR_PROP_WRITE)
+		gatt_write_char(hog->attrib, report->decl->value_handle,
+				data, size, output_written_cb, hog);
+	else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
+		gatt_write_cmd(hog->attrib, report->decl->value_handle,
+						data, size, NULL, NULL);
+}
+
+static bool get_descriptor_item_info(uint8_t *buf, ssize_t blen, ssize_t *len,
+								bool *is_long)
+{
+	if (!blen)
+		return false;
+
+	*is_long = (buf[0] == 0xfe);
+
+	if (*is_long) {
+		if (blen < 3)
+			return false;
+
+		/*
+		 * long item:
+		 * byte 0 -> 0xFE
+		 * byte 1 -> data size
+		 * byte 2 -> tag
+		 * + data
+		 */
+
+		*len = buf[1] + 3;
+	} else {
+		uint8_t b_size;
+
+		/*
+		 * short item:
+		 * byte 0[1..0] -> data size (=0, 1, 2, 4)
+		 * byte 0[3..2] -> type
+		 * byte 0[7..4] -> tag
+		 * + data
+		 */
+
+		b_size = buf[0] & 0x03;
+		*len = (b_size ? 1 << (b_size - 1) : 0) + 1;
+	}
+
+	/* item length should be no more than input buffer length */
+	return *len <= blen;
+}
+
+static char *item2string(char *str, uint8_t *buf, uint8_t len)
+{
+	char *p = str;
+	int i;
+
+	/*
+	 * Since long item tags are not defined except for vendor ones, we
+	 * just ensure that short items are printed properly (up to 5 bytes).
+	 */
+	for (i = 0; i < 6 && i < len; i++)
+		p += sprintf(p, " %02x", buf[i]);
+
+	/*
+	 * If there are some data left, just add continuation mark to indicate
+	 * this.
+	 */
+	if (i < len)
+		sprintf(p, " ...");
+
+	return str;
+}
+
+static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	struct bt_hog *hog = user_data;
+	uint8_t value[HOG_REPORT_MAP_MAX_SIZE];
+	struct uhid_event ev;
+	ssize_t vlen;
+	char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */
+	int i, err;
+	GError *gerr = NULL;
+
+	if (status != 0) {
+		error("Report Map read failed: %s", att_ecode2str(status));
+		return;
+	}
+
+	vlen = dec_read_resp(pdu, plen, value, sizeof(value));
+	if (vlen < 0) {
+		error("ATT protocol error");
+		return;
+	}
+
+	DBG("Report MAP:");
+	for (i = 0; i < vlen;) {
+		ssize_t ilen = 0;
+		bool long_item = false;
+
+		if (get_descriptor_item_info(&value[i], vlen - i, &ilen,
+								&long_item)) {
+			/* Report ID is short item with prefix 100001xx */
+			if (!long_item && (value[i] & 0xfc) == 0x84)
+				hog->has_report_id = TRUE;
+
+			DBG("\t%s", item2string(itemstr, &value[i], ilen));
+
+			i += ilen;
+		} else {
+			error("Report Map parsing failed at %d", i);
+
+			/* Just print remaining items at once and break */
+			DBG("\t%s", item2string(itemstr, &value[i], vlen - i));
+			break;
+		}
+	}
+
+	/* create uHID device */
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_CREATE;
+
+	bt_io_get(g_attrib_get_channel(hog->attrib), &gerr,
+			BT_IO_OPT_SOURCE, ev.u.create.phys,
+			BT_IO_OPT_DEST, ev.u.create.uniq,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("Failed to connection details: %s", gerr->message);
+		g_error_free(gerr);
+		return;
+	}
+
+	strcpy((char *) ev.u.create.name, hog->name);
+	ev.u.create.vendor = hog->vendor;
+	ev.u.create.product = hog->product;
+	ev.u.create.version = hog->version;
+	ev.u.create.country = hog->bcountrycode;
+	ev.u.create.bus = BUS_BLUETOOTH;
+	ev.u.create.rd_data = value;
+	ev.u.create.rd_size = vlen;
+
+	err = bt_uhid_send(hog->uhid, &ev);
+	if (err < 0) {
+		error("bt_uhid_send: %s", strerror(-err));
+		return;
+	}
+
+	bt_uhid_register(hog->uhid, UHID_OUTPUT, forward_report, hog);
+	bt_uhid_register(hog->uhid, UHID_FEATURE, forward_report, hog);
+}
+
+static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	struct bt_hog *hog = user_data;
+	uint8_t value[HID_INFO_SIZE];
+	ssize_t vlen;
+
+	if (status != 0) {
+		error("HID Information read failed: %s",
+						att_ecode2str(status));
+		return;
+	}
+
+	vlen = dec_read_resp(pdu, plen, value, sizeof(value));
+	if (vlen != 4) {
+		error("ATT protocol error");
+		return;
+	}
+
+	hog->bcdhid = get_le16(&value[0]);
+	hog->bcountrycode = value[2];
+	hog->flags = value[3];
+
+	DBG("bcdHID: 0x%04X bCountryCode: 0x%02X Flags: 0x%02X",
+			hog->bcdhid, hog->bcountrycode, hog->flags);
+}
+
+static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	struct bt_hog *hog = user_data;
+	uint8_t value;
+	ssize_t vlen;
+
+	if (status != 0) {
+		error("Protocol Mode characteristic read failed: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	vlen = dec_read_resp(pdu, plen, &value, sizeof(value));
+	if (vlen < 0) {
+		error("ATT protocol error");
+		return;
+	}
+
+	if (value == HOG_PROTO_MODE_BOOT) {
+		uint8_t nval = HOG_PROTO_MODE_REPORT;
+
+		DBG("HoG is operating in Boot Procotol Mode");
+
+		gatt_write_cmd(hog->attrib, hog->proto_mode_handle, &nval,
+						sizeof(nval), NULL, NULL);
+	} else if (value == HOG_PROTO_MODE_REPORT)
+		DBG("HoG is operating in Report Protocol Mode");
+}
+
+static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data)
+{
+	struct bt_hog *hog = user_data;
+	struct gatt_primary *primary = hog->primary;
+	bt_uuid_t report_uuid, report_map_uuid, info_uuid;
+	bt_uuid_t proto_mode_uuid, ctrlpt_uuid;
+	struct report *report;
+	GSList *l;
+	uint16_t info_handle = 0, proto_mode_handle = 0;
+
+	if (status != 0) {
+		const char *str = att_ecode2str(status);
+		DBG("Discover all characteristics failed: %s", str);
+		return;
+	}
+
+	bt_uuid16_create(&report_uuid, HOG_REPORT_UUID);
+	bt_uuid16_create(&report_map_uuid, HOG_REPORT_MAP_UUID);
+	bt_uuid16_create(&info_uuid, HOG_INFO_UUID);
+	bt_uuid16_create(&proto_mode_uuid, HOG_PROTO_MODE_UUID);
+	bt_uuid16_create(&ctrlpt_uuid, HOG_CONTROL_POINT_UUID);
+
+	for (l = chars; l; l = g_slist_next(l)) {
+		struct gatt_char *chr, *next;
+		bt_uuid_t uuid;
+		uint16_t start, end;
+
+		chr = l->data;
+		next = l->next ? l->next->data : NULL;
+
+		DBG("0x%04x UUID: %s properties: %02x",
+				chr->handle, chr->uuid, chr->properties);
+
+		bt_string_to_uuid(&uuid, chr->uuid);
+
+		start = chr->value_handle + 1;
+		end = (next ? next->handle - 1 : primary->range.end);
+
+		if (bt_uuid_cmp(&uuid, &report_uuid) == 0) {
+			report = g_new0(struct report, 1);
+			report->hog = hog;
+			report->decl = g_memdup(chr, sizeof(*chr));
+			hog->reports = g_slist_append(hog->reports,
+								report);
+			discover_descriptor(hog->attrib, start, end, report);
+		} else if (bt_uuid_cmp(&uuid, &report_map_uuid) == 0) {
+			gatt_read_char(hog->attrib, chr->value_handle,
+						report_map_read_cb, hog);
+			discover_descriptor(hog->attrib, start, end, hog);
+		} else if (bt_uuid_cmp(&uuid, &info_uuid) == 0)
+			info_handle = chr->value_handle;
+		else if (bt_uuid_cmp(&uuid, &proto_mode_uuid) == 0)
+			proto_mode_handle = chr->value_handle;
+		else if (bt_uuid_cmp(&uuid, &ctrlpt_uuid) == 0)
+			hog->ctrlpt_handle = chr->value_handle;
+	}
+
+	if (proto_mode_handle) {
+		hog->proto_mode_handle = proto_mode_handle;
+		gatt_read_char(hog->attrib, proto_mode_handle,
+						proto_mode_read_cb, hog);
+	}
+
+	if (info_handle)
+		gatt_read_char(hog->attrib, info_handle, info_read_cb,
+									hog);
+}
+
+static void report_free(void *data)
+{
+	struct report *report = data;
+	struct bt_hog *hog = report->hog;
+
+	if (hog->attrib)
+		g_attrib_unregister(hog->attrib, report->notifyid);
+
+	g_free(report->decl);
+	g_free(report);
+}
+
+static void hog_free(struct bt_hog *hog)
+{
+	bt_uhid_unref(hog->uhid);
+	g_slist_free_full(hog->reports, report_free);
+	g_attrib_unref(hog->attrib);
+	g_free(hog->name);
+	g_free(hog->primary);
+	g_free(hog);
+}
+
+struct bt_hog *bt_hog_new(const char *name, uint16_t vendor, uint16_t product,
+					uint16_t version, void *primary)
+{
+	struct bt_hog *hog;
+
+	hog = g_try_new0(struct bt_hog, 1);
+	if (!hog)
+		return NULL;
+
+	hog->uhid = bt_uhid_new_default();
+	if (!hog->uhid) {
+		hog_free(hog);
+		return NULL;
+	}
+
+	hog->name = g_strdup(name);
+	hog->vendor = vendor;
+	hog->product = product;
+	hog->version = version;
+
+	if (primary)
+		hog->primary = g_memdup(primary, sizeof(*hog->primary));
+
+	return bt_hog_ref(hog);
+}
+
+struct bt_hog *bt_hog_ref(struct bt_hog *hog)
+{
+	if (!hog)
+		return NULL;
+
+	__sync_fetch_and_add(&hog->ref_count, 1);
+
+	return hog;
+}
+
+void bt_hog_unref(struct bt_hog *hog)
+{
+	if (!hog)
+		return;
+
+	if (__sync_sub_and_fetch(&hog->ref_count, 1))
+		return;
+
+	hog_free(hog);
+}
+
+static void find_included_cb(uint8_t status, GSList *services, void *user_data)
+{
+	struct bt_hog *hog = user_data;
+	struct gatt_included *include;
+	GSList *l;
+
+	DBG("");
+
+	if (hog->primary)
+		return;
+
+	if (status) {
+		const char *str = att_ecode2str(status);
+		DBG("Find included failed: %s", str);
+		return;
+	}
+
+	if (!services) {
+		DBG("No included service found");
+		return;
+	}
+
+	for (l = services; l; l = l->next) {
+		include = l->data;
+
+		if (strcmp(include->uuid, HOG_UUID) == 0)
+			break;
+	}
+
+	if (!l) {
+		for (l = services; l; l = l->next) {
+			include = l->data;
+
+			gatt_find_included(hog->attrib, include->range.start,
+				include->range.end, find_included_cb, hog);
+		}
+		return;
+	}
+
+	hog->primary = g_new0(struct gatt_primary, 1);
+	memcpy(hog->primary->uuid, include->uuid, sizeof(include->uuid));
+	memcpy(&hog->primary->range, &include->range, sizeof(include->range));
+
+	gatt_discover_char(hog->attrib, hog->primary->range.start,
+						hog->primary->range.end, NULL,
+						char_discovered_cb, hog);
+}
+
+static void primary_cb(uint8_t status, GSList *services, void *user_data)
+{
+	struct bt_hog *hog = user_data;
+	struct gatt_primary *primary;
+	GSList *l;
+
+	DBG("");
+
+	if (status) {
+		const char *str = att_ecode2str(status);
+		DBG("Discover primary failed: %s", str);
+		return;
+	}
+
+	if (!services) {
+		DBG("No primary service found");
+		return;
+	}
+
+	for (l = services; l; l = l->next) {
+		primary = l->data;
+
+		if (strcmp(primary->uuid, HOG_UUID) == 0)
+			break;
+	}
+
+	if (!l) {
+		for (l = services; l; l = l->next) {
+			primary = l->data;
+
+			gatt_find_included(hog->attrib, primary->range.start,
+					primary->range.end, find_included_cb,
+					hog);
+		}
+		return;
+	}
+
+	hog->primary = g_memdup(primary, sizeof(*primary));
+
+	gatt_discover_char(hog->attrib, hog->primary->range.start,
+						hog->primary->range.end, NULL,
+						char_discovered_cb, hog);
+}
+
+bool bt_hog_attach(struct bt_hog *hog, void *gatt)
+{
+	struct gatt_primary *primary = hog->primary;
+	GSList *l;
+
+	if (hog->attrib)
+		return false;
+
+	hog->attrib = g_attrib_ref(gatt);
+
+	if (!primary) {
+		gatt_discover_primary(hog->attrib, NULL, primary_cb, hog);
+		return true;
+	}
+
+	if (hog->reports == NULL) {
+		gatt_discover_char(hog->attrib, primary->range.start,
+						primary->range.end, NULL,
+						char_discovered_cb, hog);
+		return true;
+	}
+
+	for (l = hog->reports; l; l = l->next) {
+		struct report *r = l->data;
+
+		r->notifyid = g_attrib_register(hog->attrib,
+					ATT_OP_HANDLE_NOTIFY,
+					r->decl->value_handle,
+					report_value_cb, r, NULL);
+	}
+
+	return true;
+}
+
+void bt_hog_detach(struct bt_hog *hog)
+{
+	GSList *l;
+
+	if (!hog->attrib)
+		return;
+
+	for (l = hog->reports; l; l = l->next) {
+		struct report *r = l->data;
+
+		g_attrib_unregister(hog->attrib, r->notifyid);
+	}
+
+	g_attrib_unref(hog->attrib);
+	hog->attrib = NULL;
+}
+
+int bt_hog_set_control_point(struct bt_hog *hog, bool suspend)
+{
+	uint8_t value = suspend ? 0x00 : 0x01;
+
+	if (hog->attrib == NULL)
+		return -ENOTCONN;
+
+	if (hog->ctrlpt_handle == 0)
+		return -ENOTSUP;
+
+	gatt_write_cmd(hog->attrib, hog->ctrlpt_handle, &value,
+					sizeof(value), NULL, NULL);
+
+	return 0;
+}
diff --git a/android/hog.h b/android/hog.h
new file mode 100644
index 0000000..7cf446b
--- /dev/null
+++ b/android/hog.h
@@ -0,0 +1,35 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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
+ *
+ */
+
+struct bt_hog;
+
+struct bt_hog *bt_hog_new(const char *name, uint16_t vendor, uint16_t product,
+					uint16_t version, void *primary);
+
+struct bt_hog *bt_hog_ref(struct bt_hog *hog);
+void bt_hog_unref(struct bt_hog *hog);
+
+bool bt_hog_attach(struct bt_hog *hog, void *gatt);
+void bt_hog_detach(struct bt_hog *hog);
+
+int bt_hog_set_control_point(struct bt_hog *hog, bool suspend);
diff --git a/android/ipc-tester.c b/android/ipc-tester.c
index 63fd105..f1f93f2 100644
--- a/android/ipc-tester.c
+++ b/android/ipc-tester.c
@@ -279,7 +279,8 @@
 
 failed:
 	close(pipe);
-	close(fd);
+	if (fd >= 0)
+		close(fd);
 }
 
 static int accept_connection(int sk)
diff --git a/android/ipc.c b/android/ipc.c
index 8cd34ea..2e67428 100644
--- a/android/ipc.c
+++ b/android/ipc.c
@@ -40,6 +40,11 @@
 #include "ipc.h"
 #include "src/log.h"
 
+struct service_handler {
+	const struct ipc_handler *handler;
+	uint8_t size;
+};
+
 struct ipc {
 	struct service_handler *services;
 	int service_max;
@@ -399,11 +404,17 @@
 void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
 						uint16_t len, void *param)
 {
+	return ipc_send_notif_with_fd(ipc, service_id, opcode, len, param, -1);
+}
+
+void ipc_send_notif_with_fd(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+					uint16_t len, void *param, int fd)
+{
 	if (!ipc || !ipc->notif_io)
 		return;
 
 	ipc_send(g_io_channel_unix_get_fd(ipc->notif_io), service_id, opcode,
-								len, param, -1);
+								len, param, fd);
 }
 
 void ipc_register(struct ipc *ipc, uint8_t service,
diff --git a/android/ipc.h b/android/ipc.h
index cc4e92d..fd2b985 100644
--- a/android/ipc.h
+++ b/android/ipc.h
@@ -27,11 +27,6 @@
 	size_t data_len;
 };
 
-struct service_handler {
-	const struct ipc_handler *handler;
-	uint8_t size;
-};
-
 struct ipc;
 
 typedef void (*ipc_disconnect_cb) (void *data);
@@ -47,6 +42,9 @@
 					uint16_t len, void *param, int fd);
 void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
 						uint16_t len, void *param);
+void ipc_send_notif_with_fd(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+					uint16_t len, void *param, int fd);
+
 void ipc_register(struct ipc *ipc, uint8_t service,
 			const struct ipc_handler *handlers, uint8_t size);
 void ipc_unregister(struct ipc *ipc, uint8_t service);
diff --git a/android/pics-a2dp.txt b/android/pics-a2dp.txt
index 81debcd..53b3c1c 100644
--- a/android/pics-a2dp.txt
+++ b/android/pics-a2dp.txt
@@ -50,7 +50,7 @@
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
 TSPC_A2DP_3_1	True		SRC: SBC encoder Codec (M)
-TSPC_A2DP_3_2	False		SRC: Additional encoder Codec (O)
+TSPC_A2DP_3_2	True (*)	SRC: Additional encoder Codec (O)
 -------------------------------------------------------------------------------
 
 
diff --git a/android/pics-gap.txt b/android/pics-gap.txt
index cd48332..b3b896c 100644
--- a/android/pics-gap.txt
+++ b/android/pics-gap.txt
@@ -61,7 +61,7 @@
 TSPC_GAP_1_3	True (*)	General-discoverable mode (O)
 TSPC_GAP_1_4	True (*)	Non-connectable mode (O)
 TSPC_GAP_1_5	True		Connectable mode (M)
-TSPC_GAP_1_6	False		Non-bondable mode (O)
+TSPC_GAP_1_6	True (*)	Non-bondable mode (O)
 TSPC_GAP_1_7	True (*)	Bondable mode (C.2)
 TSPC_GAP_1_8	False		Non-Synchronizable Mode (O)
 TSPC_GAP_1_9	False		Synchronizable Mode (O)
@@ -149,7 +149,7 @@
 -------------------------------------------------------------------------------
 TSPC_GAP_5_1	False (*)	Broadcaster (C.1)
 TSPC_GAP_5_2	False		Observer (C.1)
-TSPC_GAP_5_3	False (*)	Peripheral (C.1)
+TSPC_GAP_5_3	True		Peripheral (C.1)
 TSPC_GAP_5_4	True (*)	Central (C.1)
 -------------------------------------------------------------------------------
 C.1: It is mandatory to support at least one of the defined roles.
@@ -306,8 +306,8 @@
 -------------------------------------------------------------------------------
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
-TSPC_GAP_18_1	False		Peripheral: Transmitter
-TSPC_GAP_18_2	False		Peripheral: Receiver
+TSPC_GAP_18_1	True (*)	Peripheral: Transmitter
+TSPC_GAP_18_2	True (*)	Peripheral: Receiver
 -------------------------------------------------------------------------------
 
 
@@ -315,9 +315,9 @@
 -------------------------------------------------------------------------------
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
-TSPC_GAP_19_1	False		Peripheral: Standby
-TSPC_GAP_19_2	False		Peripheral: Advertising
-TSPC_GAP_19_3	False		Peripheral: Connection, Slave Role
+TSPC_GAP_19_1	True (*)	Peripheral: Standby
+TSPC_GAP_19_2	True (*)	Peripheral: Advertising
+TSPC_GAP_19_3	True (*)	Peripheral: Connection, Slave Role
 -------------------------------------------------------------------------------
 
 
@@ -325,11 +325,11 @@
 -------------------------------------------------------------------------------
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
-TSPC_GAP_20_1	False		Peripheral: Connectable Undirected Event (C.1)
-TSPC_GAP_20_2	False		Peripheral: Connectable Directed Event (C.2)
-TSPC_GAP_20_2A	False		Peripheral: Low Duty Directed Advertising (C.3)
-TSPC_GAP_20_3	False		Peripheral: Non-Connectable Undirected Event
-TSPC_GAP_20_4	False		Peripheral: Scannable Undirected Event
+TSPC_GAP_20_1	True (*)	Peripheral: Connectable Undirected Event (C.1)
+TSPC_GAP_20_2	True (*)	Peripheral: Connectable Directed Event (C.2)
+TSPC_GAP_20_2A	True (*)	Peripheral: Low Duty Directed Advertising (C.3)
+TSPC_GAP_20_3	True (*)	Peripheral: Non-Connectable Undirected Event
+TSPC_GAP_20_4	True (*)	Peripheral: Scannable Undirected Event
 -------------------------------------------------------------------------------
 
 
@@ -338,10 +338,10 @@
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
 TSPC_GAP_20A_1	False		AD Type-Service UUID (C.1)
-TSPC_GAP_20A_2	False		AD Type-Local Name (C.1)
-TSPC_GAP_20A_3	False		AD Type-Flags (C.2)
+TSPC_GAP_20A_2	True (*)	AD Type-Local Name (C.1)
+TSPC_GAP_20A_3	True (*)	AD Type-Flags (C.2)
 TSPC_GAP_20A_4	False		AD Type-Manufacturer Specific Data (C.1)
-TSPC_GAP_20A_5	False		AD Type-TX Power Level (C.1)
+TSPC_GAP_20A_5	True (*)	AD Type-TX Power Level (C.1)
 TSPC_GAP_20A_6	False		AD Type-Security Manager Out of Band (OOB) (C.3)
 TSPC_GAP_20A_7	False		AD Type-Security manager TK Value (C.1)
 TSPC_GAP_20A_8	False		AD Type-Slave Connection Interval Range (C.1)
@@ -369,16 +369,16 @@
 -------------------------------------------------------------------------------
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
-TSPC_GAP_21_1	False (*)	Peripheral: Connection Update Procedure (M)
-TSPC_GAP_21_2	False (*)	Peripheral: Channel Map Update Procedure (M)
-TSPC_GAP_21_3	False		Peripheral: Encryption Procedure (O)
-TSPC_GAP_21_4	False (*)	Peripheral: Feature Exchange Procedure (M)
-TSPC_GAP_21_5	False (*)	Peripheral: Version Exchange Procedure (M)
-TSPC_GAP_21_6	False (*)	Peripheral: Termination Procedure (M)
-TSPC_GAP_21_7	False (*)	Peripheral: LE Ping Procedure (C.3)
-TSPC_GAP_21_8	False (*)	Peripheral: Slave Initiated Feature Exchange
+TSPC_GAP_21_1	True		Peripheral: Connection Update Procedure (M)
+TSPC_GAP_21_2	True		Peripheral: Channel Map Update Procedure (M)
+TSPC_GAP_21_3	True (*)	Peripheral: Encryption Procedure (O)
+TSPC_GAP_21_4	True		Peripheral: Feature Exchange Procedure (M)
+TSPC_GAP_21_5	True		Peripheral: Version Exchange Procedure (M)
+TSPC_GAP_21_6	True		Peripheral: Termination Procedure (M)
+TSPC_GAP_21_7	True		Peripheral: LE Ping Procedure (C.3)
+TSPC_GAP_21_8	True		Peripheral: Slave Initiated Feature Exchange
 					Procedure (C.4)
-TSPC_GAP_21_9	False (*)	Peripheral: Connection Parameter Request
+TSPC_GAP_21_9	True		Peripheral: Connection Parameter Request
 					Procedure (C.5)
 -------------------------------------------------------------------------------
 
@@ -387,10 +387,10 @@
 -------------------------------------------------------------------------------
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
-TSPC_GAP_22_1	False		Peripheral: Non-Discoverable Mode (C.2)
-TSPC_GAP_22_2	False		Peripheral: Limited Discoverable Mode (C.1)
-TSPC_GAP_22_3	False		Peripheral: General Discoverable Mode (C.1)
-TSPC_GAP_22_4	False		Peripheral: Name Discovery Procedure (C.3)
+TSPC_GAP_22_1	True (*)	Peripheral: Non-Discoverable Mode (C.2)
+TSPC_GAP_22_2	True (*)	Peripheral: Limited Discoverable Mode (C.1)
+TSPC_GAP_22_3	True (*)	Peripheral: General Discoverable Mode (C.1)
+TSPC_GAP_22_4	True (*)	Peripheral: Name Discovery Procedure (C.3)
 -------------------------------------------------------------------------------
 C.1: Optional if (TSPC_GAP_5_3 OR TSPC_GAP_42_2), otherwise Excluded.
 C.2: Mandatory if (TSPC_GAP_5_3 or TSPC_GAP_42_1) is supported,
@@ -403,12 +403,12 @@
 -------------------------------------------------------------------------------
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
-TSPC_GAP_23_1	False		Peripheral: Non-Connectable Mode (C.1)
-TSPC_GAP_23_2	False		Peripheral: Directed Connectable Mode (O)
-TSPC_GAP_23_3	False		Peripheral: Undirected Connectable Mode (M)
-TSPC_GAP_23_4	False		Peripheral: Connection Parameter Update
+TSPC_GAP_23_1	False (*)	Peripheral: Non-Connectable Mode (C.1)
+TSPC_GAP_23_2	True (*)	Peripheral: Directed Connectable Mode (O)
+TSPC_GAP_23_3	True (*)	Peripheral: Undirected Connectable Mode (M)
+TSPC_GAP_23_4	True (*)	Peripheral: Connection Parameter Update
 					Procedure (O)
-TSPC_GAP_23_5	False		Peripheral: Terminate Connection Procedure (M)
+TSPC_GAP_23_5	True (*)	Peripheral: Terminate Connection Procedure (M)
 -------------------------------------------------------------------------------
 C.1: Mandatory if TSPC_GAP_5_3 (LE Only – Peripheral role) OR TSPC_GAP_42_3
 	(BR/EDR/LE – Non-Connectable Mode) OR TSPC_GAP_42_4
@@ -420,10 +420,10 @@
 -------------------------------------------------------------------------------
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
-TSPC_GAP_24_1	False		Peripheral: Non-Bondable Mode (M)
-TSPC_GAP_24_2	False		Peripheral: Bondable Mode (C.1)
-TSPC_GAP_24_3	False		Peripheral: Bonding Procedure  (C.2)
-TSPC_GAP_24_4	False		Peripheral: Multiple Bonds (C.3)
+TSPC_GAP_24_1	True (*)	Peripheral: Non-Bondable Mode (M)
+TSPC_GAP_24_2	True (*)	Peripheral: Bondable Mode (C.1)
+TSPC_GAP_24_3	True (*)	Peripheral: Bonding Procedure  (C.2)
+TSPC_GAP_24_4	True (*)	Peripheral: Multiple Bonds (C.3)
 -------------------------------------------------------------------------------
 C.1: Optional if TSPC_GAP_5_3 (LE Only – Peripheral role) OR (TSPC_GAP_38_3
 	(BR/EDR/LE – Peripheral role) AND NOT TSPC_GAP_42_6 (BR.EDR/LE -
@@ -437,18 +437,18 @@
 -------------------------------------------------------------------------------
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
-TSPC_GAP_25_1	False		Peripheral: Security Mode (O)
-TSPC_GAP_25_2	False		Peripheral: Security Mode 2 (O)
-TSPC_GAP_25_3	False		Peripheral: Authentication Procedure (C.2)
-TSPC_GAP_25_4	False		Peripheral: Authorization Procedure (O)
-TSPC_GAP_25_5	False		Peripheral: Connection Data Signing Procedure
-					(O)
-TSPC_GAP_25_6	False		Peripheral: Authenticate Signed Data Procedure
-					(O)
-TSPC_GAP_25_7	False		Peripheral: Authenticated Pairing
-					(LE security mode 1 level 3) (C.1)
-TSPC_GAP_25_8	False		Peripheral: Unauthenticated Pairing
-					(LE security mode 1 level 2) (C.1)
+TSPC_GAP_25_1	True (*)	Peripheral: Security Mode (O)
+TSPC_GAP_25_2	True (*)	Peripheral: Security Mode 2 (O)
+TSPC_GAP_25_3	True (*)	Peripheral: Authentication Procedure (C.2)
+TSPC_GAP_25_4	True (*)	Peripheral: Authorization Procedure (O)
+TSPC_GAP_25_5	True (*)	Peripheral: Connection Data Signing Procedure
+				(O)
+TSPC_GAP_25_6	True (*)	Peripheral: Authenticate Signed Data Procedure
+				(O)
+TSPC_GAP_25_7	True (*)	Peripheral: Authenticated Pairing
+				(LE security mode 1 level 3) (C.1)
+TSPC_GAP_25_8	True (*)	Peripheral: Unauthenticated Pairing
+				(LE security mode 1 level 2) (C.1)
 -------------------------------------------------------------------------------
 C.1: Optional if TSPC_GAP_25_1 is supported, otherwise Excluded.
 C.2: Mandatory if TSPC_GAP_0A_1 and TSPC_GAP_27_4 are supported,
@@ -461,12 +461,12 @@
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
 TSPC_GAP_26_1	False		Peripheral: Privacy Feature (O)
-TSPC_GAP_26_1A	False		Peripheral: Privacy Feature v1.1 (O)
-TSPC_GAP_26_2	False		Peripheral: Non-Resolvable Private Address
+TSPC_GAP_26_1A	True (*)	Peripheral: Privacy Feature v1.1 (O)
+TSPC_GAP_26_2	True (*)	Peripheral: Non-Resolvable Private Address
 					Generation Procedure (C.1)
-TSPC_GAP_26_3	False		Peripheral: Resolvable Private Address
+TSPC_GAP_26_3	True (*)	Peripheral: Resolvable Private Address
 					Generation Procedure (C.2)
-TSPC_GAP_26_4	False		Peripheral: Resolvable Private Address
+TSPC_GAP_26_4	True (*)	Peripheral: Resolvable Private Address
 					Generation Procedure (C.4)
 -------------------------------------------------------------------------------
 C.1: Optional if TSPC_GAP_26_1 is supported, otherwise Excluded.
@@ -478,8 +478,8 @@
 -------------------------------------------------------------------------------
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
-TSPC_GAP_27_1	False (*)	Peripheral: Device Name (M)
-TSPC_GAP_27_2	False (*)	Peripheral: Appearance (M)
+TSPC_GAP_27_1	True		Peripheral: Device Name (M)
+TSPC_GAP_27_2	True		Peripheral: Appearance (M)
 TSPC_GAP_27_3	False		Peripheral: Peripheral Privacy Flag (C.1)
 TSPC_GAP_27_4	False		Peripheral: Reconnection Address (C.2)
 TSPC_GAP_27_5	False		Peripheral: Peripheral Preferred Connection
@@ -532,12 +532,12 @@
 -------------------------------------------------------------------------------
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
-TSPC_GAP_31_1	True ()	Central: Connection Update Procedure (M)
-TSPC_GAP_31_2	True ()	Central: Channel Map Update Procedure (M)
+TSPC_GAP_31_1	True (*)	Central: Connection Update Procedure (M)
+TSPC_GAP_31_2	True (*)	Central: Channel Map Update Procedure (M)
 TSPC_GAP_31_3	True (*)	Central: Encryption Procedure (O)
-TSPC_GAP_31_4	True ()	Central: Feature Exchange Procedure (M)
-TSPC_GAP_31_5	True ()	Central: Version Exchange Procedure (M)
-TSPC_GAP_31_6	True ()	Central: Termination Procedure (M)
+TSPC_GAP_31_4	True (*)	Central: Feature Exchange Procedure (M)
+TSPC_GAP_31_5	True (*)	Central: Version Exchange Procedure (M)
+TSPC_GAP_31_6	True (*)	Central: Termination Procedure (M)
 TSPC_GAP_31_7	True (*)	Central: LE Ping Procedure (C.1)
 TSPC_GAP_31_8	True (*)	Central: Slave Initiated Feature Exchange
 					Procedure (C.2)
@@ -572,7 +572,7 @@
 					Procedure (C.1)
 TSPC_GAP_33_3	True (*)	Central: Selective Connection Establishment
 					Procedure (C.3)
-TSPC_GAP_33_4	True (*)	Central: Direct Connectin Establishment
+TSPC_GAP_33_4	True (*)	Central: Direct Connection Establishment
 					Procedure (C.2)
 TSPC_GAP_33_5	True (*)	Central: Connection Parameter Update Procedure
 					(C.2)
@@ -656,7 +656,7 @@
 -------------------------------------------------------------------------------
 TSPC_GAP_38_1	False		BR/EDR/LE: Broadcaster (C.1)
 TSPC_GAP_38_2	False		BR/EDR/LE: Observer (C.1)
-TSPC_GAP_38_3	False		BR/EDR/LE: Peripheral (C.1)
+TSPC_GAP_38_3	True (*)	BR/EDR/LE: Peripheral (C.1)
 TSPC_GAP_38_4	True (*)	BR/EDR/LE: Central (C.1)
 -------------------------------------------------------------------------------
 C.1: It is mandatory to support at least one of the defined roles.
@@ -672,8 +672,8 @@
 TSPC_GAP_39_1	True (*)	Central BR/EDR/LE: Non-Discoverable Mode (C.1)
 TSPC_GAP_39_2	True (*)	Central BR/EDR/LE: Discoverable Mode (C.2)
 TSPC_GAP_39_3	True (*)	Central BR/EDR/LE: Non-Connectable Mode (C.3)
-TSPC_GAP_39_4	True ()	Central BR/EDR/LE: Connectable Mode (M)
-TSPC_GAP_39_5	False		Central BR/EDR/LE: Non-Bondable Mode (C.4)
+TSPC_GAP_39_4	True (*)	Central BR/EDR/LE: Connectable Mode (M)
+TSPC_GAP_39_5	True (*)	Central BR/EDR/LE: Non-Bondable Mode (C.4)
 TSPC_GAP_39_6	True (*)	Central BR/EDR/LE: Bondable Mode (C.5)
 -------------------------------------------------------------------------------
 C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded.
@@ -717,12 +717,16 @@
 -------------------------------------------------------------------------------
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
-TSPC_GAP_42_1	False		Peripheral BR/EDR/LE: Non-Discoverable Mode (See Spec)
-TSPC_GAP_42_2	False		Peripheral BR/EDR/LE: Discoverable Mode (See Spec)
-TSPC_GAP_42_3	False		Peripheral BR/EDR/LE: Non-Connectable Mode (See Spec)
-TSPC_GAP_42_4	False (*)	Peripheral BR/EDR/LE: Connectable Mode (M)
-TSPC_GAP_42_5	False		Peripheral BR/EDR/LE: Non-Bondable Mode (See Spec)
-TSPC_GAP_42_6	False		Peripheral BR/EDR/LE: Bondable Mode (See Spec)
+TSPC_GAP_42_1	True (*)	Peripheral BR/EDR/LE: Non-Discoverable Mode
+				(See Spec)
+TSPC_GAP_42_2	True (*)	Peripheral BR/EDR/LE: Discoverable Mode
+				(See Spec)
+TSPC_GAP_42_3	False		Peripheral BR/EDR/LE: Non-Connectable Mode
+				(See Spec)
+TSPC_GAP_42_4	True		Peripheral BR/EDR/LE: Connectable Mode (M)
+TSPC_GAP_42_5	True (*)	Peripheral BR/EDR/LE: Non-Bondable Mode
+				(See Spec)
+TSPC_GAP_42_6	True (*)	Peripheral BR/EDR/LE: Bondable Mode (See Spec)
 -------------------------------------------------------------------------------
 C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded.
 C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR,
@@ -737,7 +741,7 @@
 -------------------------------------------------------------------------------
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
-TSPC_GAP_43_1	False (*)	Peripheral BR/EDR/LE: Non-Discoverable Mode
+TSPC_GAP_43_1	True		Peripheral BR/EDR/LE: Non-Discoverable Mode
 -------------------------------------------------------------------------------
 
 
@@ -758,9 +762,9 @@
 -------------------------------------------------------------------------------
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
-TSPC_GAP_45_1	False		Simultaneous BR/EDR and LE Transports – BR/EDR
+TSPC_GAP_45_1	True (*)	Simultaneous BR/EDR and LE Transports – BR/EDR
 					Slave to the same device (C.1)
-TSPC_GAP_45_2	False		Simultaneous BR/EDR and LE Transports – BR/EDR
+TSPC_GAP_45_2	True (*)	Simultaneous BR/EDR and LE Transports – BR/EDR
 					Master to the same device (C.1)
 -------------------------------------------------------------------------------
 C.1: Optional if ((SUM ICS 31/14 (Core Spec Version 4.1) or SUM ICS 31/15
diff --git a/android/pics-gatt.txt b/android/pics-gatt.txt
index f3e8199..9cdbe85 100644
--- a/android/pics-gatt.txt
+++ b/android/pics-gatt.txt
@@ -13,8 +13,8 @@
 -------------------------------------------------------------------------------
 TSPC_GATT_1_1	True		Generic Attribute Profile Client (C.1)
 TSPC_GATT_1_2	True		Generic Attribute Profile Server (C.2)
-TSPC_GATT_1A_1	False (*)	Complete GATT client (C.3)
-TSPC_GATT_1A_2	False (*)	Complete GATT server (C.4)
+TSPC_GATT_1A_1	True		Complete GATT client (C.3)
+TSPC_GATT_1A_2	True		Complete GATT server (C.4)
 -------------------------------------------------------------------------------
 C.1: Optional to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is mandatory
 	to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2
@@ -29,7 +29,7 @@
 -------------------------------------------------------------------------------
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
-TSPC_GATT_2_1	True		Attribute Protocol Supported over BR/EDR
+TSPC_GATT_2_1	False (*)	Attribute Protocol Supported over BR/EDR
 					(L2CAP fixed channel support) (C.1)
 TSPC_GATT_2_2	True		Attribute Protocol Supported over LE (C.2)
 -------------------------------------------------------------------------------
@@ -58,7 +58,7 @@
 TSPC_GATT_3_8	True		Client: Read Characteristic Value (C.1)
 TSPC_GATT_3_9	True		Client: Read using Characteristic UUID (C.1)
 TSPC_GATT_3_10	True		Client: Read Long Characteristic Values (C.1)
-TSPC_GATT_3_11	True		Client: Read Multiple Characteristic
+TSPC_GATT_3_11	False (*)	Client: Read Multiple Characteristic
 					Values (C.1)
 TSPC_GATT_3_12	True		Client: Write without Response (C.1)
 TSPC_GATT_3_13	True		Client: Signed Write Without Response (C.1)
@@ -140,10 +140,10 @@
 TSPC_GATT_4_8	True		Server: Read Characteristic Value (M)
 TSPC_GATT_4_9	True		Server: Read using Characteristic UUID (M)
 TSPC_GATT_4_10	True		Server: Read Long Characteristic Values (C.4)
-TSPC_GATT_4_11	True		Server: Read Multiple Characteristic
+TSPC_GATT_4_11	False (*)	Server: Read Multiple Characteristic
 					Values (C.4)
 TSPC_GATT_4_12	True		Server: Write without Response (C.2)
-TSPC_GATT_4_13	False (*)	Server: Signed Write Without Response (C.4)
+TSPC_GATT_4_13	True		Server: Signed Write Without Response (C.4)
 TSPC_GATT_4_14	True		Server: Write Characteristic Value (C.3)
 TSPC_GATT_4_15	True		Server: Write Long Characteristic Values (C.4)
 TSPC_GATT_4_16	True		Server: Characteristic Value Reliable
@@ -221,7 +221,7 @@
 -------------------------------------------------------------------------------
 TSPC_GATT_6_2	True		Discover GATT Services using Service Discovery
 					Profile (C.1)
-TSPC_GATT_6_3	True		Publish SDP record for GATT services support
+TSPC_GATT_6_3	False (*)	Publish SDP record for GATT services support
 					via BR/EDR (C.2)
 -------------------------------------------------------------------------------
 C.1: Mandatory IF TSPC_GATT_1_1 is supported, otherwise Excluded
@@ -239,7 +239,7 @@
 TSPC_GATT_7_4	True		LE Authentication Procedure (C.2)
 TSPC_GATT_7_5	True		LE connection data signing procedure (C.2)
 TSPC_GATT_7_6	True		LE Authenticate signed data procedure (C.2)
-TSPC_GATT_7_7	False (*)	LE Authorization Procedure (C.2)
+TSPC_GATT_7_7	True		LE Authorization Procedure (C.2)
 -------------------------------------------------------------------------------
 C.1: Mandatory IF TSPC_GATT_2_1 is supported, otherwise Excluded
 C.2: Optional IF TSPC_GATT_2_2 is supported, otherwise Excluded
@@ -251,20 +251,20 @@
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
 TSPC_ATT_3_1	True		Attribute Error Response (M)
-TSPC_ATT_3_2	False (*)	Exchange MTU Request (O)
-TSPC_ATT_3_4	False (*)	Find Information Request (O)
-TSPC_ATT_3_6	False (*)	Find by Type Value Request (O)
-TSPC_ATT_3_8	False (*)	Read by Type Request (O)
-TSPC_ATT_3_10	False (*)	Read Request (O)
-TSPC_ATT_3_12	False (*)	Read Blob Request (O)
+TSPC_ATT_3_2	True		Exchange MTU Request (O)
+TSPC_ATT_3_4	True		Find Information Request (O)
+TSPC_ATT_3_6	True		Find by Type Value Request (O)
+TSPC_ATT_3_8	True		Read by Type Request (O)
+TSPC_ATT_3_10	True		Read Request (O)
+TSPC_ATT_3_12	True		Read Blob Request (O)
 TSPC_ATT_3_14	False (*)	Read Multiple Request (O)
-TSPC_ATT_3_16	False (*)	Read by Group Type Request (O)
-TSPC_ATT_3_17	False (*)	Read by Group Type Response (C.6)
-TSPC_ATT_3_18	False (*)	Write Request (O)
-TSPC_ATT_3_20	False (*)	Write Command (O)
-TSPC_ATT_3_21	False (*)	Signed Write Command (O)
-TSPC_ATT_3_22	False (*)	Prepare Write Request (O)
-TSPC_ATT_3_24	False (*)	Execute Write Request (C.8)
+TSPC_ATT_3_16	True		Read by Group Type Request (O)
+TSPC_ATT_3_17	True		Read by Group Type Response (C.6)
+TSPC_ATT_3_18	True		Write Request (O)
+TSPC_ATT_3_20	True		Write Command (O)
+TSPC_ATT_3_21	True		Signed Write Command (O)
+TSPC_ATT_3_22	True		Prepare Write Request (O)
+TSPC_ATT_3_24	True		Execute Write Request (C.8)
 TSPC_ATT_3_26	True		Handle Value Notification (M)
 -------------------------------------------------------------------------------
 C.6: Mandatory IF TSPC_ATT_3_16 is supported, otherwise Excluded
@@ -284,12 +284,12 @@
 TSPC_ATT_4_11	True		Read Response (M)
 TSPC_ATT_4_15	False (*)	Read Multiple Response (C.2)
 TSPC_ATT_4_17	True		Read by Group Type Response (M)
-TSPC_ATT_4_19	False (*)	Write Response (C.3)
-TSPC_ATT_4_20	False (*)	Write Command (O)
-TSPC_ATT_4_21	False (*)	Signed Write Command (O)
-TSPC_ATT_4_23	False (*)	Prepare Write Response (C.4)
-TSPC_ATT_4_25	False (*)	Execute Write Response (C.5)
-TSPC_ATT_4_26	False (*)	Handle Value Notification (O)
+TSPC_ATT_4_19	True		Write Response (C.3)
+TSPC_ATT_4_20	True		Write Command (O)
+TSPC_ATT_4_21	True		Signed Write Command (O)
+TSPC_ATT_4_23	True		Prepare Write Response (C.4)
+TSPC_ATT_4_25	True		Execute Write Response (C.5)
+TSPC_ATT_4_26	True		Handle Value Notification (O)
 -------------------------------------------------------------------------------
 C.2: Mandatory IF TSPC_ATT_4_14 is supported, otherwise Excluded
 C.3: Mandatory IF TSPC_ATT_4_18 is supported, otherwise Excluded
@@ -302,9 +302,9 @@
 -------------------------------------------------------------------------------
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
-TSPC_ATT_5_2	False (*)	LE Security Mode 1 (C.2)
-TSPC_ATT_5_4	False (*)	LE Authentication Procedure (C.2)
-TSPC_ATT_5_7	False (*)	LE Authorization Procedure (C.2)
+TSPC_ATT_5_2	True		LE Security Mode 1 (C.2)
+TSPC_ATT_5_4	True		LE Authentication Procedure (C.2)
+TSPC_ATT_5_7	True		LE Authorization Procedure (C.2)
 -------------------------------------------------------------------------------
 C.2: Optional to support if 2/2 (Attribute Protocol Supported over LE),
 	otherwise Excluded
@@ -315,7 +315,7 @@
 -------------------------------------------------------------------------------
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
-TSPC_GAP_0_2	False (*)	LE (C.2)
+TSPC_GAP_0_2	True		LE (C.2)
 -------------------------------------------------------------------------------
 C.2: Mandatory IF (SUM ICS 34/2 (LE GAP) AND NOT SUM ICS 32/3 (BR/EDR GAP))
 	is supported, otherwise Excluded
diff --git a/android/pics-hdp.txt b/android/pics-hdp.txt
index 3d8020d..95d9917 100644
--- a/android/pics-hdp.txt
+++ b/android/pics-hdp.txt
@@ -47,7 +47,7 @@
 TSPC_HDP_2_8	True		Supports Acceptance of Bonding requests (M)
 TSPC_HDP_2_9	True (*)	Supports Initiation of Bonding (O)
 TSPC_HDP_2_10	True (*)	Supports Extended Inquiry Response (C.7)
-TSPC_HDP_2_11	True (*)	Supports use of Health Class of Device (O)
+TSPC_HDP_2_11	False		Supports use of Health Class of Device (O)
 -------------------------------------------------------------------------------
 C.1: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional.
 C.2: Mandatory if Security Mode 2, 3, or 4 is supported, otherwise Optional.
@@ -189,9 +189,9 @@
 TSPC_HDP_8_6	True		Supports Initiation of Encryption (M) (C.3)
 TSPC_HDP_8_7	True		Supports General Inquiry (M) C.4)
 TSPC_HDP_8_8	True		Supports Acceptance of Bonding requests (M)
-TSPC_HDP_8_9	Ture (*)	Supports Initiation of Bonding (O)
+TSPC_HDP_8_9	True (*)	Supports Initiation of Bonding (O)
 TSPC_HDP_8_10	True (*)	Supports Extended Inquiry Response (C.5) (C.6)
-TSPC_HDP_8_11	True (*)	Supports use of Health Class of Device (O)
+TSPC_HDP_8_11	False		Supports use of Health Class of Device (O)
 -------------------------------------------------------------------------------
 C.1: Mandatory if TSPC_HDP_8_9 is supported, otherwise Optional.
 C.2: Mandatory if Security Mode 2, 3, or 4 is supported, otherwise Optional.
diff --git a/android/pics-hogp.txt b/android/pics-hogp.txt
new file mode 100644
index 0000000..81e182c
--- /dev/null
+++ b/android/pics-hogp.txt
@@ -0,0 +1,397 @@
+HOGP PICS for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+		Profile Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_1_1	False (*)	HID Device (C.1)
+TSPC_HOGP_1_2	True		Report Host (C.1)
+TSPC_HOGP_1_3	False (*)	Boot Host (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of TSPC_HOGP_1_1 or TSPC_HOGP_1_2
+	or TSPC_HOGP_1_3.
+-------------------------------------------------------------------------------
+
+
+		Transport
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_2_1	False (*)	Profile supported over BR/EDR (C.1)
+TSPC_HOGP_2_2	True		Profile supported over LE (M)
+-------------------------------------------------------------------------------
+C.1: Excluded for this profile.
+-------------------------------------------------------------------------------
+
+
+		Services - HID Device
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_3_1	False (*)	Implements HID Service (M.1)
+TSPC_HOGP_3_2	False (*)	Multiple Service instances - HID Service (O)
+TSPC_HOGP_3_3	False (*)	Implements Battery Service (M.1)
+TSPC_HOGP_3_4	False (*)	Implements Device Information Service (M.1)
+TSPC_HOGP_3_5	False (*)	Implements Scan Parameters Service (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_1 selected
+-------------------------------------------------------------------------------
+
+
+		Features - HID Device
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_4_1	False (*)	Include HID Service UUID in AD in GAP
+					Discoverable Mode (O)
+TSPC_HOGP_4_2	False (*)	Include Local Name in AD or Scan Response Data
+					(O)
+TSPC_HOGP_4_3	False (*)	Include Appearance in AD or Scan Response Data
+					(O)
+TSPC_HOGP_4_4	False (*)	Support Device Information Service
+					characteristic: PnP ID (M)
+TSPC_HOGP_4_5	False (*)	Report characteristic (C.1)
+TSPC_HOGP_4_6	False (*)	Non-HID Service characteristic described within
+					Report Map characteristic (C.1)
+TSPC_HOGP_4_7	False (*)	External Report Reference characteristic
+					descriptor for Report Map characteristic
+					(C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these features.
+C.2: Mandatory if TSPC_HOGP_4_6 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+		GAP Requirements - HID Device
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_5_1	False (*)	Peripheral (M.1)
+TSPC_HOGP_5_2	False (*)	Directed Connectable Mode (O)
+TSPC_HOGP_5_3	False (*)	Undirected Connectable Mode (M.1)
+TSPC_HOGP_5_4	False (*)	Bondable mode (peripheral) (M.1)
+TSPC_HOGP_5_5	False (*)	Bonding procedure (peripheral) (M.1)
+TSPC_HOGP_5_6	False (*)	LE Security Mode 1 (peripheral) (M.1)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_1 selected
+-------------------------------------------------------------------------------
+
+
+		SM Requirements - HID Device
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_6_1	False (*)	SM 2.3.1 (M.1)
+TSPC_HOGP_6_2	False (*)	Unauthenticated no MITM protection
+					(LE Security Level 2, Just Works) (M.1)
+TSPC_HOGP_6_3	False (*)	Authenticated MITM protection
+					(LE Security Level 3, Passkey) (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_1 selected
+-------------------------------------------------------------------------------
+
+
+		Client Services Support - Report Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_7_1	True		HID Service (M)
+TSPC_HOGP_7_2	True		Battery Service (M)
+TSPC_HOGP_7_3	True		Device Information Service (M)
+TSPC_HOGP_7_4	True		Scan Parameters Service (M)
+-------------------------------------------------------------------------------
+
+
+		GATT based Profile Support - Report Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_7a_1	True		Scan Parameters Profile (M)
+-------------------------------------------------------------------------------
+
+
+		Client Service Support - Boot Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_8_1	False (*)	HID Service (M.1)
+TSPC_HOGP_8_2	False (*)	Battery Service (O)
+TSPC_HOGP_8_3	False (*)	Device Information Service (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_3 selected
+-------------------------------------------------------------------------------
+
+
+		Discover Services & Characteristics - Report Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_9_1	True		Discover HID Service (M)
+TSPC_HOGP_9_2	True		Discover Battery Service (M)
+TSPC_HOGP_9_3	True		Discover Device Information Service (M)
+TSPC_HOGP_9_4	True		Discover Scan Parameters Service (M)
+TSPC_HOGP_9_5	True		Discover HID Service characteristic: Report Map
+					(M)
+TSPC_HOGP_9_6	True		Discover HID Service characteristic: Report Map
+					- External Report Reference
+					characteristic descriptor (M)
+TSPC_HOGP_9_7	True		Discover HID Service characteristic: Report (M)
+TSPC_HOGP_9_8	True		Discover HID Service characteristic: Report (M)
+					- Client Characteristic Configuration
+					characteristic descriptor (M)
+TSPC_HOGP_9_9	True		Discover HID Service characteristic: Report
+					- Report Reference characteristic
+					descriptor (M)
+TSPC_HOGP_9_10	True		Discover HID Service characteristic: HID
+					Information (M)
+TSPC_HOGP_9_11	True		Discover HID Service characteristic: HID
+					Control Point (M)
+TSPC_HOGP_9_12	True		Discover HID Service characteristic: Protocol
+					Mode (O)
+TSPC_HOGP_9_13	True		Discover Battery Service characteristic: Battery
+					Level (M)
+TSPC_HOGP_9_14	True		Discover Battery Service characteristic: Battery
+					Level - Client Characteristic
+					Configuration characteristic descriptor
+					(M)
+TSPC_HOGP_9_15	True		Discover Device Information Service
+					characteristic: PnP ID (M)
+TSPC_HOGP_9_16	True		Discover non-HID Service characteristic: Report
+					Reference characteristic descriptor (M)
+-------------------------------------------------------------------------------
+
+
+		Discover Services & Characteristics - Boot Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_10_1	False (*)	Discover HID Service (M.1)
+TSPC_HOGP_10_2	False (*)	Discover Battery Service (O)
+TSPC_HOGP_10_3	False (*)	Discover Device Information Service (O)
+TSPC_HOGP_10_4	False (*)	Discover HID Service characteristic: Protocol
+					Mode (M.1)
+TSPC_HOGP_10_5	False (*)	Discover HID Service characteristic: Boot
+					Keyboard Input Report (C.1, C.2)
+TSPC_HOGP_10_6	False (*)	Discover HID Service characteristic: Boot
+					Keyboard Input Report - Client
+					Characteristic Configuration
+					characteristic descriptor (C.3)
+TSPC_HOGP_10_7	False (*)	Discover HID Service characteristic: Boot
+					Keyboard Output Report (C.1, C.2)
+TSPC_HOGP_10_8	False (*)	Discover HID Service characteristic: Boot
+					Mouse Input Report (C.1)
+TSPC_HOGP_10_9	False (*)	Discover HID Service characteristic: Boot
+					Mouse Input Report - Client
+					Characteristic Configuration
+					characteristic descriptor (C.4)
+TSPC_HOGP_10_10	False (*)	Discover Battery Service characteristic:
+					Battery Level (O)
+TSPC_HOGP_10_11	False (*)	Discover Battery Service characteristic:
+					Battery Level - Client Characteristic
+					Configuration characteristic descriptor
+					(O)
+TSPC_HOGP_10_12	False (*)	Discover Device Information Service
+					characteristic: PnP ID (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_3 selected
+C.1: Mandatory to support at least one of TSPC_HOGP_10_5, TSPC_HOGP_10_7, or
+	TSPC_HOGP_10_8.
+C.2: If one of TSPC_HOGP_10_5 or TSPC_HOGP_10_7 is supported, both shall be
+	supported.
+C.3: Mandatory to support if TSPC_HOGP_10_5 is supported, else excluded.
+C.4: Mandatory to support if TSPC_HOGP_10_8 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+		Features - Report Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_11_1	True		Read Report Map characteristic (M)
+TSPC_HOGP_11_2	True		Read Report Map characteristic: External
+					Report Reference characteristic
+					descriptor (M)
+TSPC_HOGP_11_3	True		Read Report characteristic: Report Type:
+					Input Report (M)
+TSPC_HOGP_11_4	True		Write Report characteristic: Report Type:
+					Input Report (M)
+TSPC_HOGP_11_5	True		Read Report characteristic: Report Type:
+					Output Report (M)
+TSPC_HOGP_11_6	True		Write HID Report characteristic: Report Type:
+					Output Report (M)
+TSPC_HOGP_11_7	True		Read HID Report characteristic: Report Type:
+					Feature Report (M)
+TSPC_HOGP_11_8	True		Write HID Report characteristic: Report Type:
+					Feature Report (M)
+TSPC_HOGP_11_9	True		Read Report characteristic: Report
+					Reference characteristic descriptor (M)
+TSPC_HOGP_11_10	True		Read Report characteristic: Input Report:
+					Client Characteristic Configuration
+					characteristic descriptor (M)
+TSPC_HOGP_11_11	True		Report characteristic configuration with 0x0001
+					(M)
+
+TSPC_HOGP_11_11a True		Report characteristic configuration with 0x0000
+					(?)
+
+TSPC_HOGP_11_12	True		Read HID Information characteristic
+					(M)
+TSPC_HOGP_11_13	True		Suspend State (O)
+TSPC_HOGP_11_14	True		Exit Suspend State (C.1)
+TSPC_HOGP_11_15	True		Write HID Control Point characteristic: Suspend
+					command (C.1)
+TSPC_HOGP_11_16	True		Write HID Control Point characteristic: Exit
+					Suspend command (C.1)
+TSPC_HOGP_11_17	True		Read Protocol Mode characteristic: Get Protocol
+					command (O)
+TSPC_HOGP_11_18	True		Write Protocol Mode characteristic: Set Report
+					Protocol Mode command (O)
+TSPC_HOGP_11_19	True		Read Battery Level characteristic (M)
+TSPC_HOGP_11_20	True		Read Battery Level characteristic: Client
+					Characteristic Configuration
+					characteristic descriptor (M)
+TSPC_HOGP_11_21	True		Battery Level characteristic configuration with
+					0x0000 0r 0x0001 (M)
+TSPC_HOGP_11_22	True		Read non-HID Service characteristic: Report
+					Reference characteristic descriptor (M)
+TSPC_HOGP_11_23	True		Read PnP ID characteristic (M)
+TSPC_HOGP_11_24	True		Notify Report characteristic (M)
+TSPC_HOGP_11_25	True		Notify Battery Level characteristic (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support if TSPC_HOGP_11_13 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+		Features - Boot Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_12_1	False (*)	Read Protocol Mode characteristic: Get Protocol
+					Mode command (M.1)
+TSPC_HOGP_12_2	False (*)	Write Protocol Mode characteristic: Set Boot
+					Protocol Mode command (M.1)
+TSPC_HOGP_12_3	False (*)	Read HID Service characteristic: Boot Keyboard
+					Input Report (C.1)
+TSPC_HOGP_12_4	False (*)	Write HID Service characteristic: Boot Keyboard
+					Input Report (C.1)
+TSPC_HOGP_12_5	False (*)	Read Client Characteristic Configuration
+					characteristic descriptor for Boot
+					Keyboard Input Report (C.1)
+TSPC_HOGP_12_6	False (*)	Boot Keyboard Input Report characteristic:
+					configuration with 0x0000 or 0x0001
+					(C.1)
+TSPC_HOGP_12_7	False (*)	Read HID Service characteristic: Boot Keyboard
+					Output Report (C.1)
+TSPC_HOGP_12_8	False (*)	Write HID Service characteristic: Boot Keyboard
+					Output Report (C.1)
+TSPC_HOGP_12_9	False (*)	Read HID Service characteristic: Boot Mouse
+					Input Report (C.2)
+TSPC_HOGP_12_10	False (*)	Write HID Service characteristic: Boot Mouse
+					Input Report (C.2)
+TSPC_HOGP_12_11	False (*)	Read Client Characteristic Configuration
+					characteristic descriptor for Boot
+					Mouse Input Report (C.2)
+TSPC_HOGP_12_12	False (*)	Boot Mouse Input Report characteristic:
+					configuration with 0x0000 or 0x0001
+					(C.2)
+TSPC_HOGP_12_13	False (*)	Notify Boot Keyboard Input Report characteristic
+					(C.1)
+TSPC_HOGP_12_14	False (*)	Notify Boot Mouse Input Report characteristic
+					(C.2)
+TSPC_HOGP_12_15	False (*)	Read Battery Level characteristic (O)
+TSPC_HOGP_12_16	False (*)	Read Battery Level characteristic: Client
+					Characteristic Configuration
+					characteristic descriptor (O)
+TSPC_HOGP_12_17	False (*)	Battery Level characteristic: configuration with
+					0x0000 or 0x0001 (O)
+TSPC_HOGP_12_18	False (*)	Notify Battery Level characteristic (O)
+TSPC_HOGP_12_19	False (*)	Read PnP ID characteristic (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_3 selected
+C.1: Mandatory to support if TSPC_HOGP_12_5 or TSPC_HOGP_12_7 is supported,
+	else excluded.
+C.2: Mandatory to support if TSPC_HOGP_12_8 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+		GATT Requirements - Report Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_13_1	True		Attribute Protocol supported over LE Transport
+				(M)
+TSPC_HOGP_13_2	True		Generic Attribute Profile Client (M)
+TSPC_HOGP_13_3	True		Discover All Primary Services (C.1)
+TSPC_HOGP_13_4	True		Discover Primary Services by Service UUID (C.1)
+TSPC_HOGP_13_5	True		Find Included Services (M)
+TSPC_HOGP_13_6	True		Discover All Characteristics of a Service (C.2)
+TSPC_HOGP_13_7	True		Discover Characteristics by UUID (C.2)
+TSPC_HOGP_13_8	True		Discover All Characteristic Descriptors (M)
+TSPC_HOGP_13_9	True		Read Characteristic Value (M)
+TSPC_HOGP_13_10	True		Read using Characteristic UUID (O)
+TSPC_HOGP_13_11	True		Read Long Characteristic Value (M)
+TSPC_HOGP_13_12	True		Read Characteristic Descriptors (M)
+TSPC_HOGP_13_13	True		Write without Response (M)
+TSPC_HOGP_13_14	True		Write Characteristic Value (M)
+TSPC_HOGP_13_15	True		Write Characteristic Descriptors (M)
+TSPC_HOGP_13_16	True		Notifications (M)
+TSPC_HOGP_13_17	True		Exchange MTU (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these features.
+C.2: Mandatory to support at least one of these features.
+-------------------------------------------------------------------------------
+
+
+		GATT Requirements - Boot Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_14_1	False (*)	Attribute Protocol supported over LE Transport
+				(M.1)
+TSPC_HOGP_14_2	False (*)	Generic Attribute Profile Client (M.1)
+TSPC_HOGP_14_3	False (*)	Discover All Primary Services (C.1)
+TSPC_HOGP_14_4	False (*)	Discover Primary Services by Service UUID (C.1)
+TSPC_HOGP_14_5	False (*)	Discover All Characteristics of a Service (O)
+TSPC_HOGP_14_6	False (*)	Discover Characteristics by UUID (O)
+TSPC_HOGP_14_7	False (*)	Discover All Characteristic Descriptors (M.1)
+TSPC_HOGP_14_8	False (*)	Read Characteristic Value (M.1)
+TSPC_HOGP_14_9	False (*)	Read using Characteristic UUID (M.1)
+TSPC_HOGP_14_10	False (*)	Read Characteristic Descriptors (M.1)
+TSPC_HOGP_14_11	False (*)	Write without Response (M.1)
+TSPC_HOGP_14_12	False (*)	Write Characteristic Value (M.1)
+TSPC_HOGP_14_13	False (*)	Write Characteristic Descriptors (M.1)
+TSPC_HOGP_14_14	False (*)	Notifications (M.1)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_3 selected
+C.1: Mandatory to support at least one of these features.
+-------------------------------------------------------------------------------
+
+
+		GAP Requirements - HID Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_15_1	True		Central (M)
+TSPC_HOGP_15_2	True		LE Security Mode 1 (central) (M)
+-------------------------------------------------------------------------------
+
+
+		SM Requirements - HID Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_16_1	True		No Security Requirements (LE Security Level 1,
+					No Security) (M)
+TSPC_HOGP_16_2	True		Unauthenticated no MITM protection (LE Security
+					Level 2, Just Works) (M)
+TSPC_HOGP_16_3	True		Authenticated MITM protection (LE Security
+					Level 3, Passkey) (O)
+-------------------------------------------------------------------------------
diff --git a/android/pics-l2cap.txt b/android/pics-l2cap.txt
index 096fbf4..5cc9d06 100644
--- a/android/pics-l2cap.txt
+++ b/android/pics-l2cap.txt
@@ -70,21 +70,21 @@
 TSPC_L2CAP_2_32	False		ERTM over AMP (C.12)
 TSPC_L2CAP_2_33	False		Streaming Mode Source over AMP Support (C.15)
 TSPC_L2CAP_2_34	False		Streaming Mode Sink over AMP Support (C.15)
-TSPC_L2CAP_2_35	False		Unicast Connectionless Data, Reception (C.1, C.16)
-TSPC_L2CAP_2_36	False		Ability to transmit an unencrypted packet over
+TSPC_L2CAP_2_35	True (*)	Unicast Connectionless Data, Reception (C.1, C.16)
+TSPC_L2CAP_2_36	True (*)	Ability to transmit an unencrypted packet over
 					a Unicast connectionless L2CAP
 					channel (C.16)
-TSPC_L2CAP_2_37	False		Ability to transmit an encrypted packet over
+TSPC_L2CAP_2_37	True (*)	Ability to transmit an encrypted packet over
 					a Unicast connectionless L2CAP
 					channel (C.16)
-TSPC_L2CAP_2_38	False		Extended Flow Specification for BR/EDR (C.8)
-TSPC_L2CAP_2_39	False		Extended Window Size (C.8)
+TSPC_L2CAP_2_38	True (*)	Extended Flow Specification for BR/EDR (C.8)
+TSPC_L2CAP_2_39	True (*)	Extended Window Size (C.8)
 TSPC_L2CAP_2_40	True (*)	Support of Low Energy signaling channel (C.17)
 TSPC_L2CAP_2_41	True (*)	Support of command reject (C.17)
 TSPC_L2CAP_2_42	True (*)	Send Connection Parameter Update Request (C.18)
 TSPC_L2CAP_2_43	True (*)	Send Connection Parameter Update Response (C.19)
 TSPC_L2CAP_2_44	False		Extended Flow Specification for AMP (C.22)
-TSPC_L2CAP_2_45	False		Send disconnect request command (O)
+TSPC_L2CAP_2_45	True (*)	Send disconnect request command (O)
 -------------------------------------------------------------------------------
 C.1: Mandatory to support at least one of TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 OR
 	TSPC_L2CAP_2_35 IF BR/EDR BR/EDR/LE AND SUM_ICS 31/7 (CSA1) OR
diff --git a/android/pics-mps.txt b/android/pics-mps.txt
index e12cad0..5cbe1d8 100644
--- a/android/pics-mps.txt
+++ b/android/pics-mps.txt
@@ -24,8 +24,8 @@
 TSPC_MPS_1_2	True (*)	AVRCP 1.3 or later (O)
 TSPC_MPS_1_3	False		DUN 1.1 or later (O)
 TSPC_MPS_1_4	True (*)	HFP 1.5 or later (O)
-TSPC_MPS_1_5	False		PAN 1.0 or later (O)
-TSPC_MPS_1_6	False		PBAP 1.1 or later (O)
+TSPC_MPS_1_5	True (*)	PAN 1.0 or later (O)
+TSPC_MPS_1_6	True (*)	PBAP 1.1 or later (O)
 -------------------------------------------------------------------------------
 
 
@@ -41,11 +41,11 @@
 TSPC_MPS_2_6	False		DUN Data Terminal (DT) (C.1)
 TSPC_MPS_2_7	True (*)	HFP Audio Gateway (AG) (C.1)
 TSPC_MPS_2_8	False		HFP Hands-Free (HF) (C.1)
-TSPC_MPS_2_9	False		PAN Network Access Point (NAP) (C.1)
+TSPC_MPS_2_9	True (*)	PAN Network Access Point (NAP) (C.1)
 TSPC_MPS_2_10	False		PAN Group Ad-hoc Network (GN) (C.1)
-TSPC_MPS_2_11	False		PAN User (PANU) (C.1)
+TSPC_MPS_2_11	True (*)	PAN User (PANU) (C.1)
 TSPC_MPS_2_12	False		PBAP PCE (C.1)
-TSPC_MPS_2_13	False		PBAP PSE (C.1)
+TSPC_MPS_2_13	True (*)	PBAP PSE (C.1)
 -------------------------------------------------------------------------------
 C.1: Mandatory to declare each role as supported within the represented Profile
 	otherwise Excluded. The roles declared shall match that of the roles
@@ -77,10 +77,10 @@
 				the HF (C.6)
 TSPC_MPS_3_10	True (*)	Register Notification: PLAYBACK_STATUS_CHANGED
 				(C.7)
-TSPC_MPS_3_11	False		Ability to support parallel data and call
+TSPC_MPS_3_11	True (*)	Ability to support parallel data and call
 				operation (O)
-TSPC_MPS_3_12	False		PBAP Phone Book Download (C.8)
-TSPC_MPS_3_13	False		Ability to support multiple concurrent device
+TSPC_MPS_3_12	True (*)	PBAP Phone Book Download (C.8)
+TSPC_MPS_3_13	True (*)	Ability to support multiple concurrent device
 				connections (O)
 -------------------------------------------------------------------------------
 C.1: Mandatory if TSPC_MPS_2_1 (A2DP Source role) and TSPC_MPS_2_4 (AVRCP
@@ -109,7 +109,7 @@
 Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
 TSPC_MPS_4_1	True		Multiple Profiles Single Device (MPSD) (M)
-TSPC_MPS_4_2	False		Multiple Profiles Multiple Devices (MPMD) (C.1)
+TSPC_MPS_4_2	True (*)	Multiple Profiles Multiple Devices (MPMD) (C.1)
 -------------------------------------------------------------------------------
 C.1: Mandatory if TSPC_MPS_3_13 (Ability to support multiple concurrent device
 	connections), otherwise Excluded.
@@ -184,41 +184,41 @@
 TSPC_MPS_6_26	False		HFP-HF and DUN-DT Implementation Terminate
 				Voice Call/Data Call during Data Communication
 				and Voice Call (C.6)
-TSPC_MPS_6_27	False		HFP-AG and PAN-NAP Implementation Data
+TSPC_MPS_6_27	True (*)	HFP-AG and PAN-NAP Implementation Data
 				Communication in Personal Area Network during
 				Active Voice Call (C.7)
 TSPC_MPS_6_28	False		HFP-HF and PAN-PANU Implementation Data
 				Communication in Personal Area Network during
 				Active Voice Call (C.8)
-TSPC_MPS_6_29	False		HFP-AG and PAN-NAP Implementation Outgoing
+TSPC_MPS_6_29	True (*)	HFP-AG and PAN-NAP Implementation Outgoing
 				Voice Call during Data Communication in Personal
 				Area Network (C.7)
 TSPC_MPS_6_30	False		HFP-HF and PAN-PANU Implementation Outgoing
 				Voice Call during Data Communication in Personal
 				Area Network (C.8)
-TSPC_MPS_6_31	False		HFP-AG and PAN-NAP Implementation Incoming Voice
+TSPC_MPS_6_31	True (*)	HFP-AG and PAN-NAP Implementation Incoming Voice
 				Call during Data Communication in Personal Area
 				Network (C.7)
 TSPC_MPS_6_32	False		HFP-HF and PAN-PANU Implementation Incoming
 				Voice Call during Data Communication in Personal
 				Area Network (C.8)
-TSPC_MPS_6_33	False		A2DP-SRC and PAN-NAP Implementation Start Audio
+TSPC_MPS_6_33	True (*)	A2DP-SRC and PAN-NAP Implementation Start Audio
 				Streaming during Data Communication in Personal
 				Area Network (C.9)
 TSPC_MPS_6_34	False		A2DP-SNK and PAN-PANU Implementation Start Audio
 				Streaming during Data Communication in Personal
 				Area Network (C.10)
-TSPC_MPS_6_35	False		A2DP-SRC and PAN-NAP Implementation Data
+TSPC_MPS_6_35	True (*)	A2DP-SRC and PAN-NAP Implementation Data
 				Communication Establishment in Personal Area
 				Network during Audio Streaming (C.9)
 TSPC_MPS_6_36	False		A2DP-SNK and PAN_PANU Implementation Data
 				Communication Establishment in Personal Area
 				Network during Audio Streaming (C.10)
-TSPC_MPS_6_37	False		A2DP-SRC_PBAP-Server Implementation Phonebook
+TSPC_MPS_6_37	True (*)	A2DP-SRC_PBAP-Server Implementation Phonebook
 				Download during Audio Streaming (C.11)
 TSPC_MPS_6_38	False		A2DP-SNK and PBAP-Client Implementation
 				Phonebook Download during Audio Streaming (C.12)
-TSPC_MPS_6_39	False		HFP-AG and PBAP-Server Implementation PBAP and
+TSPC_MPS_6_39	True (*)	HFP-AG and PBAP-Server Implementation PBAP and
 				HFP Connection Behavior (C.13)
 -------------------------------------------------------------------------------
 C.1: Mandatory if 2/1 (A2DP Source role), 2/4 (AVRCP Target role) and 2/7
diff --git a/android/pics-sm.txt b/android/pics-sm.txt
index 3060d40..239f568 100644
--- a/android/pics-sm.txt
+++ b/android/pics-sm.txt
@@ -25,7 +25,7 @@
 TSPC_SM_2_1	True		Authenticated MITM protection (O)
 TSPC_SM_2_2	True		Unauthenticated no MITM protection (C.1)
 TSPC_SM_2_3	True		No security requirements (M)
-TSPC_SM_2_4	True		OOB supported (O)
+TSPC_SM_2_4	False		OOB supported (O)
 -------------------------------------------------------------------------------
 C.1: If TSPC_SM_2_1 is supported then Mandatory, else Optional
 -------------------------------------------------------------------------------
@@ -45,7 +45,7 @@
 -------------------------------------------------------------------------------
 TSPC_SM_4_1	True		Just Works (O)
 TSPC_SM_4_2	True		Passkey Entry (C.1)
-TSPC_SM_4_3	True		Out of Band (C.1)
+TSPC_SM_4_3	False		Out of Band (C.1)
 -------------------------------------------------------------------------------
 C.1: If TSPC_SM_2_1 is supported, at least one of these features shall be
 	supported.
diff --git a/android/pixit-hogp.txt b/android/pixit-hogp.txt
new file mode 100644
index 0000000..4fe1e0f
--- /dev/null
+++ b/android/pixit-hogp.txt
@@ -0,0 +1,28 @@
+HOGP PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_time_guard						180000
+TSPX_use_implicit_send					TRUE
+TSPX_tester_database_file				[Path to HOGP Test
+								Database]
+TSPX_mtu_size						23
+TSPX_secure_simple_pairing_pass_key_confirmation	FALSE
+TSPX_delete_link_key					FALSE
+TSPX_pin_code						0000
+TSPX_use_dynamic_pin					FALSE
+TSPX_delete_ltk						FALSE
+TSPX_security_enabled					TRUE
+TSPX_input_report_data					CDA6F8B3AA
+TSPX_output_report_data					001234567890EF
+TSPX_feature_report_data				872D3F45EA
+TSPX_tester_appearance					03C0
+-------------------------------------------------------------------------------
diff --git a/android/pixit-mps.txt b/android/pixit-mps.txt
index c59c913..e3c8096 100644
--- a/android/pixit-mps.txt
+++ b/android/pixit-mps.txt
@@ -10,11 +10,11 @@
 -------------------------------------------------------------------------------
 Parameter Name						Value
 -------------------------------------------------------------------------------
-TSPX_avrcp_revision
+TSPX_avrcp_revision					1.5 (^)
 TSPX_class_of_device					20050C
 TSPX_establish_avdtp_stream				TRUE
 TSPX_iut_establishes_initial_condition			FALSE
-TSPX_tester_device
+TSPX_tester_device					A (*)
 TSPX_media_directory
 TSPX_bd_addr_iut					112233445566 (*&)
 TSPX_delete_link_key					FALSE
diff --git a/android/pts-a2dp.txt b/android/pts-a2dp.txt
index 3670dd0..5780db1 100644
--- a/android/pts-a2dp.txt
+++ b/android/pts-a2dp.txt
@@ -1,7 +1,7 @@
 PTS test results for A2DP
 
 PTS version: 5.1
-Tested: 07-April-2014
+Tested: 06-June-2014
 Android version: 4.4.2
 
 Results:
diff --git a/android/pts-gap.txt b/android/pts-gap.txt
index d1f0402..02430f4 100644
--- a/android/pts-gap.txt
+++ b/android/pts-gap.txt
@@ -1,8 +1,9 @@
 PTS test results for GAP
 
 PTS version: 5.1
-Tested: 16-May-2014
+Tested: 28-May-2014
 Android version: 4.4.2
+Kernel version: 3.17
 
 Results:
 PASS	test passed
@@ -29,16 +30,41 @@
 TC_BROB_OBSV_BV_03_C	N/A
 TC_BROB_OBSV_BV_04_C	N/A
 TC_BROB_OBSV_BV_05_C	N/A
-TC_DISC_NONM_BV_01_C	N/A
-TC_DISC_NONM_BV_02_C	N/A
-TC_DISC_LIMM_BV_01_C	N/A
-TC_DISC_LIMM_BV_02_C	N/A
-TC_DISC_LIMM_BV_03_C	N/A
-TC_DISC_LIMM_BV_04_C	N/A
-TC_DISC_GENM_BV_01_C	N/A
-TC_DISC_GENM_BV_02_C	N/A
-TC_DISC_GENM_BV_03_C	N/A
-TC_DISC_GENM_BV_04_C	N/A
+TC_DISC_NONM_BV_01_C	PASS	btmgmt connectable off
+				btmgmt advertising on
+TC_DISC_NONM_BV_02_C	PASS	btmgmt connectable on
+				btmgmt discov off
+				btmgmt advertising on
+TC_DISC_LIMM_BV_01_C	PASS	btmgmt connectable on
+				btmgmt discov off
+				<answer NO to non-connectable adv question>
+				btmgmt discov limited 30
+TC_DISC_LIMM_BV_02_C	PASS	btmgmt connectable on
+				btmgmt advertising on
+				btmgmt discov limited 30
+TC_DISC_LIMM_BV_03_C	PASS	btmgmt connectable on
+				btmgmt discov off
+				<answer NO to non-connectable adv question>
+				btmgmt discov limited 30
+TC_DISC_LIMM_BV_04_C	PASS	btmgmt connectable on
+				btmgmt discov off
+				btmgmt power off
+				btmgmt bredr off
+				btmgmt power on
+				btmgmt discov limited 30
+TC_DISC_GENM_BV_01_C	PASS	btmgmt connectable on
+				btmgmt discov on
+				<answer NO to non-connectable adv question>
+TC_DISC_GENM_BV_02_C	PASS	btmgmt connectable on
+				btmgmt advertising on
+				btmgmt discov on
+TC_DISC_GENM_BV_03_C	PASS	btmgmt connectable on
+				btmgmt discov on
+TC_DISC_GENM_BV_04_C	PASS	btmgmt connectable on
+				btmgmt power off
+				btmgmt bredr off
+				btmgmt discov on
+				btmgmt advertising on
 TC_DISC_LIMP_BV_01_C	PASS	btmgmt find -l
 				PTS AD flags must have bit 1 unset and bit 0 set
 TC_DISC_LIMP_BV_02_C	PASS	btmgmt find -l
@@ -66,88 +92,258 @@
 				all services, get characteristic and then read
 				characteristic (name)
 TC_IDLE_NAMP_BV_02_C	PASS	haltest: gatts connect
-TC_CONN_NCON_BV_01_C	N/A
-TC_CONN_NCON_BV_02_C	N/A
-TC_CONN_NCON_BV_03_C	N/A
-TC_CONN_DCON_BV_01_C	N/A
+TC_CONN_NCON_BV_01_C	PASS	btmgmt connectable off
+				btmgmt advertising on
+TC_CONN_NCON_BV_02_C	PASS	<answer NO to non-connectable adv question>
+				Note: non-connectable and discoverable ?
+TC_CONN_NCON_BV_03_C	PASS	<answer NO to non-connectable adv question>
+				Note: non-connectable and discoverable ?
+TC_CONN_DCON_BV_01_C	PASS	btmgmt connectable on
+				btmgmt advertising on
 TC_CONN_DCON_BV_02_C	N/A
 TC_CONN_DCON_BV_03_C	N/A
-TC_CONN_UCON_BV_01_C	N/A
-TC_CONN_UCON_BV_02_C	N/A
-TC_CONN_UCON_BV_03_C	N/A
+TC_CONN_UCON_BV_01_C	PASS	btmgmt connectable on
+				btmgmt advertising on
+TC_CONN_UCON_BV_02_C	PASS	btmgmt connectable on
+				btmgmt discov on
+				btmgmt advertising on
+TC_CONN_UCON_BV_03_C	PASS	btmgmt connectable on
+				btmgmt advertising on
+				btmgmt discov limited 30
 TC_CONN_UCON_BV_04_C	N/A
 TC_CONN_UCON_BV_05_C	N/A
-TC_CONN_ACEP_BV_01_C	PASS	'gattc connect' prior to pressing OK on PTS
-TC_CONN_ACEP_BV_02_C	INC	Privacy feature - not implemented
+TC_CONN_ACEP_BV_01_C	PASS	debugfs:
+				echo -n "add <PTS addr> 0 1" > /sys/kernel/
+					debug/bluetooth/hciX/le_auto_conn
+				btmgmt le on
+				btmgmt power on
+				btmgmt disconnect -t 1 <PTS addr>
+				debugfs:
+				echo -n "clr" > /sys/kernel/debug/bluetooth/
+					hciX/le_auto_conn
+TC_CONN_ACEP_BV_02_C	INC	Privacy feature - PTS issue #12308
 TC_CONN_GCEP_BV_01_C	PASS	'gattc connect' prior to pressing OK on PTS
 TC_CONN_GCEP_BV_02_C	PASS	'gattc connect' prior to pressing OK on PTS
-TC_CONN_GCEP_BV_03_C	INC	Privacy feature - not implemented
-TC_CONN_GCEP_BV_04_C	INC	Privacy feature - not implemented
+TC_CONN_GCEP_BV_03_C	PASS	gattc connect
+				bluetooth create_bond
+				gattc connect
+				gattc disconnect
+TC_CONN_GCEP_BV_04_C	INC	Privacy feature - PTS issue #12308
 TC_CONN_SCEP_BV_01_C	PASS	'gattc connect' prior to pressing OK on PTS
-TC_CONN_SCEP_BV_02_C	INC	Privacy feature - not implemented
+TC_CONN_SCEP_BV_02_C	INC	Privacy feature - PTS issue #12308
 TC_CONN_DCEP_BV_01_C	PASS	'gattc connect' prior to pressing OK on PTS
-TC_CONN_DCEP_BV_02_C	INC	Privacy feature - not implemented
+TC_CONN_DCEP_BV_02_C	INC	Privacy feature - PTS issue #12308
 TC_CONN_DCEP_BV_03_C	PASS
-TC_CONN_DCEP_BV_04_C	INC	Privacy feature - not implemented
+TC_CONN_DCEP_BV_04_C	PASS	gattc connect
+				gattc create_bond
+				gattc connect
+				gattc disconnect
 TC_CONN_CPUP_BV_01_C	N/A
 TC_CONN_CPUP_BV_02_C	N/A
 TC_CONN_CPUP_BV_03_C	N/A
-TC_CONN_CPUP_BV_04_C	PASS
-TC_CONN_CPUP_BV_05_C	PASS
-TC_CONN_CPUP_BV_06_C	PASS	PTS issue - ID: 12187
-				ETS + new pixit file should be added
+TC_CONN_CPUP_BV_04_C	PASS	gattc register_client
+				gattc connect 1 <pts_bdaddr>
+				gattc disconnect 1 <pts_bdaddr> 1
+TC_CONN_CPUP_BV_05_C	PASS	gattc register_client
+				gattc connect 1 <pts_bdaddr>
+				gattc disconnect 1 <pts_bdaddr> 1
+TC_CONN_CPUP_BV_06_C	PASS	gattc register_client
+				gattc connect 1 <pts_bdaddr>
+				hcitool lecup <handle> 0x0008 0x0C7B 0x0010
+					0x0014
+				gattc disconnect <client_if> <pts_bdaddr>
+					<conn_id>
 TC_CONN_TERM_BV_01_C	PASS
-TC_CONN_PRDA_BV_01_C	N/A
-TC_CONN_PRDA_BV_02_C	INC	Privacy feature - not implemented
+TC_CONN_PRDA_BV_01_C	INC	PTS issue #12207
+TC_CONN_PRDA_BV_02_C	INC	PTS issue #12310
 TC_BOND_NBON_BV_01_C	PASS
 TC_BOND_NBON_BV_02_C	PASS	haltest: gattc_register_client
 				gattc connect <client_id> <address>
 				bluetooth create_bond <address>
 				bluetooth remove_bond <address>
-TC_BOND_NBON_BV_03_C	N/A
-TC_BOND_BON_BV_01_C	N/A
+TC_BOND_NBON_BV_03_C	PASS
+TC_BOND_BON_BV_01_C	PASS	gattc register_client
+				gattc listen 1
+				bluetooth create_bond <pts_bdaddr>
 TC_BOND_BON_BV_02_C	PASS
-TC_BOND_BON_BV_03_C	N/A
+TC_BOND_BON_BV_03_C	PASS	haltest: bluetooth remove_bond <pts_bdaddr>
+				gattc_register_client
+				gattc listen 1
 TC_BOND_BON_BV_04_C	PASS	haltest: gattc_register_client
 				gattc connect <client_id> <address>
 				bluetooth remove_bond <address>
 				gattc connect <client_id> <address>
-TC_SEC_AUT_BV_11_C	N/A
-TC_SEC_AUT_BV_12_C	INC	JIRA: BZ-29
-TC_SEC_AUT_BV_13_C	INC	JIRA: BZ-30
-TC_SEC_AUT_BV_14_C	N/A
+TC_SEC_AUT_BV_11_C	PASS	haltest:gattc register_client
+				gatts register_server
+				gatts add_service 2 <uuid> 3
+				gatts add_characteristic 2 1b <uuid> 10 68
+				gatts start_service 2 1b 1
+				gattc listen
+				PTS asks for handle with Insufficient auth
+				bluetooth ssp_reply <addr> <passkey>
+				gatts send_response
+TC_SEC_AUT_BV_12_C	PASS	haltest: gatts register_server
+				gatts add_service 2 <uuid> 3
+				gatts add_characteristic 2 <service_handle>
+								<uuid> 10 68
+				gatts start_service 2 <service_handle> 1
+				gatts connect <server_if> <addr>
+				PTS asks for handle with Insufficient auth
+				bluetooth ssp_reply <addr> <passkey>
+				gatts send_response
+TC_SEC_AUT_BV_13_C	PASS	haltest: gatts register_server
+				gatts add_service 2 <uuid> 3
+				gatts add_characteristic 2 <service_handle>
+								<uuid> 10 68
+				gatts start_service 2 <service_handle> 1
+				gatts connect <server_if> <addr>
+				PTS asks for handle with Insufficient auth
+				bluetooth ssp_reply <addr> <passkey>
+				gatts send_response
+TC_SEC_AUT_BV_14_C	PASS	haltest:gattc register_client
+				gatts register_server
+				gatts add_service 2 <uuid> 3
+				gatts add_characteristic 2 1b <uuid> 10 68
+				gatts start_service 2 1b 1
+				gattc listen
+				PTS asks for handle with Insufficient auth
+				bluetooth ssp_reply <addr> <passkey>
+				gatts send_response
 TC_SEC_AUT_BV_15_C	N/A
-TC_SEC_AUT_BV_16_C	INC	Not implemented
+TC_SEC_AUT_BV_16_C	PASS	haltest: gatts register_server
+				gatts add_service 2 <uuid> 3
+				gatts add_characteristic 2 <service_handle>
+								<uuid> 10 34
+				gatts start_service 2 <service_handle> 1
+				gatts connect <server_if> <addr>
+				gatts disconnect <server_if> <addr>
+				gatts connect <server_if> <addr>
+				PTS asks for handle with Insufficient encrypt
+				bluetooth ssp_reply <addr> <passkey>
+				gatts send_response
 TC_SEC_AUT_BV_17_C	PASS
-TC_SEC_AUT_BV_18_C	N/A
+TC_SEC_AUT_BV_18_C	PASS	haltest: gattc register_client
+				gattc listen
+				gattc search_service
+				gattc get_characteristic
+				gattc read_characteristic
+				bluetooth create_bond
+				gattc read_characteristic
 TC_SEC_AUT_BV_19_C	PASS
-TC_SEC_AUT_BV_20_C	N/A
-TC_SEC_AUT_BV_21_C	INC	Not implemented
-TC_SEC_AUT_BV_22_C	N/A
-TC_SEC_AUT_BV_23_C	N/A
-TC_SEC_AUT_BV_24_C	INC	Not implemented
-TC_SEC_CSIGN_BV_01_C	INC	Not yet implemented - signed write
-TC_SEC_CSIGN_BV_02_C	INC	Not implemented
-TC_SEC_CSIGN_BI_01_C	INC	Not implemented
-TC_SEC_CSIGN_BI_02_C	INC	Not implemented
-TC_SEC_CSIGN_BI_03_C	INC	Not implemented
-TC_SEC_CSIGN_BI_04_C	INC	Not implemented
-TC_PRIV_CONN_BV_01_C	INC	Privacy feature - not implemented
-TC_PRIV_CONN_BV_02_C	INC	Privacy feature - not implemented
-TC_PRIV_CONN_BV_03_C	INC	Privacy feature - not implemented
-TC_PRIV_CONN_BV_04_C	INC	Privacy feature - not implemented
+TC_SEC_AUT_BV_20_C	INC	PTS issue #12284
+TC_SEC_AUT_BV_21_C	PASS	haltest: gattc register_client
+				gattc connect
+				bluetooth create_bond
+				gattc connect
+TC_SEC_AUT_BV_22_C	PASS	btmgmt io-cap 3
+				haltest: gattc register_client
+				gattc listen
+				gattc test_command 226 <addr> <u1> 1
+TC_SEC_AUT_BV_23_C	PASS	haltest: gatts register_server
+				gatts add_service 2 <uuid> 3
+				gatts add_characteristic 2 <service_handle>
+								<uuid> 10 34
+				gatts start_service 2 <service_handle> 1
+				gattc register_client
+				gattc listen
+				bluetooth ssp_reply
+				gatts send_response
+TC_SEC_AUT_BV_24_C	PASS	haltest: gatts register_server
+				gatts add_service 2 <uuid> 3
+				gatts add_characteristic 2 <service_handle>
+								<uuid> 10 34
+				gatts start_service 2 <service_handle> 1
+				gatts connect <PTS addr>
+				bluetooth ssp_reply
+				gatts disconnect
+				gatts connect
+				PTS asks for handle with insufficient encryption
+				gatts send_response
+TC_SEC_CSIGN_BV_01_C	PASS	haltest:
+				gattc connect
+				bluetooth create_bond
+				gattc connect
+				gattc write_characteristic: <write_type> 4
+				gattc disconnect
+TC_SEC_CSIGN_BV_02_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 66
+						<permissions> 129
+				gatts start_service
+				gatts disconnect
+				gattc disconnect
+TC_SEC_CSIGN_BI_01_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 66
+						<permissions> 129
+				gatts start_service
+				gatts disconnect
+				gattc disconnect
+TC_SEC_CSIGN_BI_02_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 66
+						<permissions> 129
+				gatts start_service
+				gatts disconnect
+				gattc disconnect
+TC_SEC_CSIGN_BI_03_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 64
+						<permissions> 128
+				gatts start_service
+				gattc listen
+				bluetooth ssp_reply
+				gatts disconnect
+				bluetooth remove_bond
+TC_SEC_CSIGN_BI_04_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 64
+						<permissions> 256
+				gatts start_service
+				gattc listen
+				bluetooth ssp_reply
+				gatts disconnect
+TC_PRIV_CONN_BV_01_C	PASS	gattc connect
+				gattc create_bond
+				gattc search service
+				gattc get_characteristic
+				gattc write_characteristic (privacy flag)
+				gattc write_characteristic (reconnection addr.)
+				gattc disconnect
+TC_PRIV_CONN_BV_02_C	PASS	gattc connect
+				gattc search service
+				gattc get_characteristic
+				gattc write_characteristic (reconnection addr.)
+				gattc disconnect
+TC_PRIV_CONN_BV_03_C	INC	Privacy feature - PTS issue #12308
+TC_PRIV_CONN_BV_04_C	PASS	gattc connect
+				gattc create_bond
+				gattc search service
+				gattc get_characteristic
+				gattc write_characteristic (privacy flag)
+				gattc write_characteristic (reconnection addr.)
+				gattc disconnect
+				gattc connect
+				gattc disconnect
 TC_PRIV_CONN_BV_05_C	N/A
 TC_PRIV_CONN_BV_06_C	N/A
 TC_PRIV_CONN_BV_07_C	N/A
 TC_PRIV_CONN_BV_08_C	N/A
 TC_PRIV_CONN_BV_09_C	N/A
-TC_PRIV_CONN_BV_10_C	N/A
-TC_PRIV_CONN_BV_11_C	INC	Privacy feature - not implemented
+TC_PRIV_CONN_BV_10_C	INC	PTS issue #12312
+TC_PRIV_CONN_BV_11_C	INC	PTS issue #12310
 TC_ADV_BV_01_C		N/A
-TC_ADV_BV_02_C		N/A
-TC_ADV_BV_03_C		N/A
+TC_ADV_BV_02_C		FAIL	PTS issue #12254
+TC_ADV_BV_03_C		PASS	gattc register_client
+				gattc listen 1 1
 TC_ADV_BV_04_C		N/A
-TC_ADV_BV_05_C		N/A
+TC_ADV_BV_05_C		PASS	gattc register_client
+				gattc listen 1 1
 TC_ADV_BV_06_C		N/A
 TC_ADV_BV_07_C		N/A
 TC_ADV_BV_08_C		N/A
@@ -169,25 +365,34 @@
 TC_GAT_BV_06_C		N/A
 TC_GAT_BV_07_C		N/A
 TC_GAT_BV_08_C		N/A
-TC_DM_NCON_BV_01_C	N/A
-TC_DM_CON_BV_01_C	N/A
-TC_DM_NBON_BV_01_C	N/A
+TC_DM_NCON_BV_01_C	PASS
+TC_DM_CON_BV_01_C	PASS
+TC_DM_NBON_BV_01_C	PASS
 TC_DM_BON_BV_01_C	PASS	haltest:
 				create_bond and remove_bond when requested
 TC_DM_GIN_BV_01_C	PASS
 TC_DM_LIN_BV_01_C	PASS
 TC_DM_NAD_BV_01_C	PASS	Start discovery from IUT
 TC_DM_NAD_BV_02_C	PASS
-TC_DM_LEP_BV_01_C	N/A
-TC_DM_LEP_BV_02_C	N/A
-TC_DM_LEP_BV_03_C	N/A
+TC_DM_LEP_BV_01_C	PASS	gattc register_client
+				gattc listen 1 1
+TC_DM_LEP_BV_02_C	PASS	Use basic rate PTS dongle
+				haltest:
+				bluetooth set_adapter_property
 TC_DM_LEP_BV_04_C	PASS	l2test -n <PTS bdaddr>
 TC_DM_LEP_BV_05_C	PASS	btmgmt find -b
 				l2test -n 00:1B:DC:06:06:22
 TC_DM_LEP_BV_06_C	PASS
-TC_DM_LEP_BV_07_C	N/A
-TC_DM_LEP_BV_08_C	N/A
-TC_DM_LEP_BV_09_C	N/A
-TC_DM_LEP_BV_10_C	N/A
-TC_DM_LEP_BV_11_C	N/A
+TC_DM_LEP_BV_07_C	PASS
+TC_DM_LEP_BV_08_C	PASS
+TC_DM_LEP_BV_09_C	PASS
+TC_DM_LEP_BV_10_C	PASS	haltest:
+				gattc listen 1
+				gattc listen 1 0
+				bluetooth start_discovery
+				bluetooth cancel_discovery
+				gattc listen 1
+				gattc listen 1 0
+				bluetooth create_bond
+TC_DM_LEP_BV_11_C	PASS
 -------------------------------------------------------------------------------
diff --git a/android/pts-gatt.txt b/android/pts-gatt.txt
index 342db47..9cf0758 100644
--- a/android/pts-gatt.txt
+++ b/android/pts-gatt.txt
@@ -1,7 +1,7 @@
-PTS test results for GAP
+PTS test results for GATT
 
 PTS version: 5.1
-Tested: 7-May-2014
+Tested: 19-May-2014
 Android version: 4.4.2
 
 Results:
@@ -13,107 +13,1171 @@
 -------------------------------------------------------------------------------
 Test Name		Result	Notes
 -------------------------------------------------------------------------------
-TC_GAC_CL_BV_01_C	PASS	Send exchanged MTU
-TC_GAC_SR_BV_01_C	PASS	IUT should accept incoming GATT con request
-TC_GAD_CL_BV_01_C	PASS	Send discover all primary services command to
-				PTS
-TC_GAD_CL_BV_02_C	PASS	Send discover primary services (UUID=1800)
-				command to PTS
-TC_GAD_CL_BV_03_C	PASS	Send discover all include service to PTS
-TC_GAD_CL_BV_04_C	PASS	Discover all characteristics of service (UUID=
-				180A)
-TC_GAD_CL_BV_05_C	PASS	Send discover characteristics by UUID
-TC_GAD_CL_BV_06_C	PASS	Send discover characteristics descriptor
-TC_GAD_CL_BV_07_C	PASS
-TC_GAD_CL_BV_08_C	PASS
-TC_GAD_SR_BV_01_C	INC
-TC_GAD_SR_BV_02_C	INC
-TC_GAD_SR_BV_03_C	INC
-TC_GAD_SR_BV_04_C	INC
-TC_GAD_SR_BV_05_C	INC
-TC_GAD_SR_BV_06_C	INC
-TC_GAD_SR_BV_07_C	INC
-TC_GAD_SR_BV_08_C	INC
-TC_GAR_CL_BV_01_C	PASS
-TC_GAR_CL_BI_01_C	PASS
-TC_GAR_CL_BI_02_C	PASS
-TC_GAR_CL_BI_03_C	PASS
-TC_GAR_CL_BI_04_C	PASS
-TC_GAR_CL_BI_05_C	PASS
-TC_GAR_CL_BV_03_C	PASS
-TC_GAR_CL_BI_06_C	PASS
-TC_GAR_CL_BI_07_C	PASS
-TC_GAR_CL_BI_09_C	PASS
-TC_GAR_CL_BI_10_C	PASS
-TC_GAR_CL_BI_11_C	PASS
-TC_GAR_CL_BV_04_C	PASS
-TC_GAR_CL_BI_12_C	PASS
-TC_GAR_CL_BI_13_C	PASS
-TC_GAR_CL_BI_14_C	PASS
-TC_GAR_CL_BI_15_C	INC
-TC_GAR_CL_BI_16_C	INC
-TC_GAR_CL_BI_17_C	INC
-TC_GAR_CL_BV_05_C	INC
-TC_GAR_CL_BI_18_C	INC
-TC_GAR_CL_BI_19_C	INC
-TC_GAR_CL_BI_20_C	INC
-TC_GAR_CL_BI_21_C	INC
-TC_GAR_CL_BI_22_C	INC
-TC_GAR_CL_BV_06_C	INC
-TC_GAR_CL_BI_23_C	INC
-TC_GAR_CL_BI_24_C	INC
-TC_GAR_CL_BI_25_C	INC
-TC_GAR_CL_BI_26_C	INC
-TC_GAR_CL_BI_27_C	INC
-TC_GAR_CL_BV_07_C	INC
-TC_GAR_CL_BI_28_C	INC
-TC_GAR_CL_BI_29_C	INC
-TC_GAR_CL_BI_30_C	INC
-TC_GAR_CL_BI_31_C	INC
-TC_GAR_CL_BI_32_C	INC
-TC_GAR_CL_BI_33_C	PASS
-TC_GAR_CL_BI_34_C	INC
-TC_GAR_CL_BI_35_C	PASS
-TC_GAR_SR_BV_01_C	INC
-TC_GAR_SR_BI_01_C	INC
-TC_GAR_SR_BI_02_C	INC
-TC_GAR_SR_BI_03_C	INC
-TC_GAR_SR_BI_04_C	INC
-TC_GAR_SR_BI_05_C	INC
-TC_GAR_SR_BV_03_C	INC
-TC_GAR_SR_BI_06_C	INC
-TC_GAR_SR_BI_07_C	INC
-TC_GAR_SR_BI_08_C	INC
-TC_GAR_SR_BI_09_C	INC
-TC_GAR_SR_BI_10_C	INC
-TC_GAR_SR_BI_11_C	INC
-TC_GAR_SR_BV_04_C	INC
-TC_GAR_SR_BI_12_C	INC
-TC_GAR_SR_BI_13_C	INC
-TC_GAR_SR_BI_14_C	INC
-TC_GAR_SR_BI_15_C	INC
-TC_GAR_SR_BI_16_C	INC
-TC_GAR_SR_BI_17_C	INC
-TC_GAR_SR_BV_05_C	INC
-TC_GAR_SR_BI_18_C	INC
-TC_GAR_SR_BI_19_C	INC
-TC_GAR_SR_BI_20_C	INC
-TC_GAR_SR_BI_21_C	INC
-TC_GAR_SR_BI_22_C	INC
-TC_GAR_SR_BV_06_C	INC
-TC_GAR_SR_BI_23_C	INC
-TC_GAR_SR_BI_24_C	INC
-TC_GAR_SR_BI_25_C	INC
-TC_GAR_SR_BI_26_C	INC
-TC_GAR_SR_BI_27_C	INC
-TC_GAR_SR_BV_07_C	INC
-TC_GAR_SR_BV_08_C	INC
-TC_GAR_SR_BI_28_C	INC
-TC_GAR_SR_BI_29_C	INC
-TC_GAR_SR_BI_30_C	INC
-TC_GAR_SR_BI_31_C	INC
-TC_GAR_SR_BI_32_C	INC
-TC_GAR_SR_BI_33_C	INC
-TC_GAR_SR_BI_34_C	INC
-TC_GAR_SR_BI_35_C	INC
+TC_GAC_CL_BV_01_C	PASS	haltest:
+				gattc scan
+				gattc search_service
+				gattc get_characteristic
+				gattc write_characteristic: type 3
+TC_GAC_SR_BV_01_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+					<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+					<data> value greater than MTU
+					repeat with correct offset
+				gatts send_response:
+					<data> value greater than MTU
+					repeat with correct offset
+TC_GAD_CL_BV_01_C	PASS	haltest:
+				gattc register_client
+				gattc scan
+				gattc connect
+				gattc search_service
+				gattc disconnect
+				gattc connect
+				gattc refresh - NOTE: refresh should be called
+				otherwise services are being read from the cache
+TC_GAD_CL_BV_02_C	PASS	haltest:
+				gattc register_client
+				gattc scan
+				gattc connect
+				gattc search_service with given uuid
+				gattc disconnect
+				gattc connect
+				gattc refresh
+TC_GAD_CL_BV_03_C	PASS	haltest:
+				when requested: gattc get_characteristic
+TC_GAD_CL_BV_04_C	PASS	haltest:
+				when requested: gattc get_characteristic
+TC_GAD_CL_BV_05_C	PASS	haltest:
+				when requested: gattc get_characteristic
+				handle: check from btmon logs
+TC_GAD_CL_BV_06_C	PASS	haltest:
+				when requested: gattc get_descriptor
+TC_GAD_CL_BV_07_C	N/A
+TC_GAD_CL_BV_08_C	N/A
+TC_GAD_SR_BV_01_C	PASS	haltest:
+				gattc register_client
+				gattc listen
+				gatts register_server
+				gatts add_service
+				gatts add_characteristic
+				gatts start_service
+				gatts add_service
+				gatts add_included_service
+				gatts start_service
+TC_GAD_SR_BV_02_C	PASS	haltest:
+				gattc register_client
+				gattc listen
+				gatts register_server
+				gatts add_service
+				gatts add_characteristic
+				gatts start_service
+				gatts add_service
+				gatts add_included_service
+				gatts start_service
+TC_GAD_SR_BV_03_C	PASS	haltest:
+				gattc register_client
+				gattc listen
+				gatts register_server
+				gatts add_service
+				gatts add_characteristic
+				gatts start_service
+				gatts add_service
+				gatts add_included_service
+				gatts start_service
+TC_GAD_SR_BV_04_C	PASS	haltest:
+				gattc register_client
+				gattc listen
+				gatts register_server
+				gatts add_service
+				gatts add_characteristic
+				gatts start_service
+				gatts add_service
+				gatts add_included_service
+				gatts start_service
+TC_GAD_SR_BV_05_C	PASS	haltest:
+				gattc register_client
+				gattc listen
+				gatts register_server
+				gatts add_service
+				gatts add_characteristic
+				gatts start_service
+				gatts add_service
+				gatts add_included_service
+				gatts start_service
+TC_GAD_SR_BV_06_C	PASS	haltest:
+				gattc register_client
+				gattc listen
+				gatts register_server
+				gatts add_service
+				gatts add_characteristic
+				gatts start_service
+				gatts add_service
+				gatts add_included_service
+				gatts start_service
+TC_GAD_SR_BV_07_C	N/A
+TC_GAD_SR_BV_08_C	N/A
+TC_GAR_CL_BV_01_C	PASS	haltest:
+				gattc read_characteristic
+TC_GAR_CL_BI_01_C	PASS	haltest:
+				gattc read_characteristic
+TC_GAR_CL_BI_02_C	PASS	haltest:
+				gattc read_characteristic
+TC_GAR_CL_BI_03_C	PASS	haltest:
+				gattc read_characteristic
+TC_GAR_CL_BI_04_C	PASS	haltest:
+				gattc read_characteristic
+TC_GAR_CL_BI_05_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc read_characteristic
+				gattc disconnect
+TC_GAR_CL_BV_03_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 224 [u1] 8
+				test_command: <cmd> 224 [u1] 8
+				gattc disconnect
+TC_GAR_CL_BI_06_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 224 [u1] 8
+				gattc disconnect
+TC_GAR_CL_BI_07_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 224 [u1] 8
+				gattc disconnect
+TC_GAR_CL_BI_09_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 224 [u1] 8
+				gattc disconnect
+TC_GAR_CL_BI_10_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 224 [u1] 8
+				gattc disconnect
+TC_GAR_CL_BI_11_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 224 [u1] 8
+				gattc disconnect
+TC_GAR_CL_BV_04_C	PASS	haltest:
+				gattc read_characteristic
+TC_GAR_CL_BI_12_C	PASS	haltest:
+				gattc read_characteristic
+TC_GAR_CL_BI_13_C	PASS	haltest:
+				gattc read_characteristic
+TC_GAR_CL_BI_14_C	PASS	haltest:
+				gattc read_characteristic
+TC_GAR_CL_BI_15_C	PASS	haltest:
+				gattc read_characteristic
+TC_GAR_CL_BI_16_C	PASS	haltest:
+				gattc read_characteristic
+TC_GAR_CL_BI_17_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc read_characteristic
+				gattc disconnect
+TC_GAR_CL_BV_05_C	N/A
+TC_GAR_CL_BI_18_C	N/A
+TC_GAR_CL_BI_19_C	N/A
+TC_GAR_CL_BI_20_C	N/A
+TC_GAR_CL_BI_21_C	N/A
+TC_GAR_CL_BI_22_C	N/A
+TC_GAR_CL_BV_06_C	PASS	haltest:
+				gattc read_descriptor
+TC_GAR_CL_BI_23_C	PASS	haltest:
+				gattc read_descriptor
+TC_GAR_CL_BI_24_C	PASS	haltest:
+				gattc read_descriptor
+TC_GAR_CL_BI_25_C	PASS	haltest:
+				gattc read_descriptor
+TC_GAR_CL_BI_26_C	PASS	haltest:
+				gattc read_descriptor
+TC_GAR_CL_BI_27_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc get_descriptor: srvc_id based on
+								handle from logs
+				gattc read_descriptor
+				gattc disconnect
+TC_GAR_CL_BV_07_C	PASS	haltest:
+				gattc read_descriptor
+TC_GAR_CL_BI_28_C	PASS	haltest:
+				gattc read_descriptor
+TC_GAR_CL_BI_29_C	PASS	haltest:
+				gattc read_descriptor
+TC_GAR_CL_BI_30_C	PASS	haltest:
+				gattc read_descriptor
+TC_GAR_CL_BI_31_C	PASS	haltest:
+				gattc read_descriptor
+TC_GAR_CL_BI_32_C	PASS	haltest:
+				gattc read_descriptor
+TC_GAR_CL_BI_33_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc get_descriptor: srvc_id based on
+								handle from logs
+				gattc read_descriptor
+				gattc disconnect
+TC_GAR_CL_BI_34_C	N/A
+TC_GAR_CL_BI_35_C	PASS	haltest:
+				gattc read_characteristic
+TC_GAR_SR_BV_01_C	PASS
+TC_GAR_SR_BI_01_C	PASS
+TC_GAR_SR_BI_02_C	PASS
+TC_GAR_SR_BI_03_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						 <properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 8
+TC_GAR_SR_BI_04_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 3
+				gatts start_service
+				gatts send_response
+TC_GAR_SR_BI_05_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						 <properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAR_SR_BV_03_C	PASS
+TC_GAR_SR_BI_06_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 16
+				gatts start_service
+TC_GAR_SR_BI_07_C	PASS
+TC_GAR_SR_BI_08_C	PASS
+TC_GAR_SR_BI_09_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 8
+TC_GAR_SR_BI_10_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 5
+TC_GAR_SR_BI_11_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAR_SR_BV_04_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+TC_GAR_SR_BI_12_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 8 <permissions> 16
+				gatts start_service
+				gatts send_response
+TC_GAR_SR_BI_13_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 7
+TC_GAR_SR_BI_14_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						 <properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 1
+TC_GAR_SR_BI_15_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						 <properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 8
+TC_GAR_SR_BI_16_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						 <properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 5
+TC_GAR_SR_BI_17_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						 <properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAR_SR_BV_05_C	N/A
+TC_GAR_SR_BI_18_C	N/A
+TC_GAR_SR_BI_19_C	N/A
+TC_GAR_SR_BI_20_C	N/A
+TC_GAR_SR_BI_21_C	N/A
+TC_GAR_SR_BI_22_C	N/A
+TC_GAR_SR_BV_06_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor
+				gatts start_service
+				gatts send_response
+TC_GAR_SR_BI_23_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 16
+				gatts start_service
+TC_GAR_SR_BI_24_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor
+				gatts start_service
+				gatts send_response: <status> 1
+TC_GAR_SR_BI_25_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 8
+TC_GAR_SR_BI_26_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 5
+TC_GAR_SR_BI_27_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAR_SR_BV_07_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+TC_GAR_SR_BV_08_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+TC_GAR_SR_BI_28_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 16
+				gatts start_service
+TC_GAR_SR_BI_29_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 7
+TC_GAR_SR_BI_30_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 1
+TC_GAR_SR_BI_31_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 8
+TC_GAR_SR_BI_32_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 5
+TC_GAR_SR_BI_33_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAR_SR_BI_34_C	N/A
+TC_GAR_SR_BI_35_C	N/A
+TC_GAW_CL_BV_01_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic
+				gattc disconnect
+TC_GAW_CL_BV_02_C	PASS	haltest:
+				bluetooth create_bond
+				gattc disconnect
+				gattc connect
+				gattc search_service
+				gattc get_characteristics
+				gattc write_characteristics: <type> 4
+				gattc disconnect
+
+TC_GAW_CL_BV_03_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic
+				gattc disconnect
+TC_GAW_CL_BI_02_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 225 [u1] 18
+				gattc disconnect
+TC_GAW_CL_BI_03_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_04_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long value>
+				gattc disconnect
+TC_GAW_CL_BI_05_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_06_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BV_05_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_07_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 225 [u1] 22
+				gattc disconnect
+TC_GAW_CL_BI_08_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_09_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_11_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_12_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_13_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BV_06_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_14_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 225 [u1] 22
+				gattc disconnect
+TC_GAW_CL_BI_15_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_17_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_18_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_19_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BV_08_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc get_descriptor
+				gattc write_descriptor 2 <short_value>
+				gattc disconnect
+TC_GAW_CL_BI_20_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 225 [u1] 18
+				gattc disconnect
+TC_GAW_CL_BI_21_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc get_descriptor
+				gattc write_descriptor 2 <short_value>
+				gattc disconnect
+TC_GAW_CL_BI_22_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc get_descriptor
+				gattc write_descriptor 2 <short_value>
+				gattc disconnect
+TC_GAW_CL_BI_23_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc get_descriptor
+				gattc write_descriptor 2 <short_value>
+				gattc disconnect
+TC_GAW_CL_BI_24_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc get_descriptor
+				gattc write_descriptor 2 <short_value>
+				gattc disconnect
+TC_GAW_CL_BV_09_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc get_descriptor
+				gattc write_descriptor 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_25_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 225 [u1] 22
+				gattc disconnect
+TC_GAW_CL_BI_26_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_27_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_29_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_30_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_31_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_32_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc execute_write
+				gattc disconnect
+TC_GAW_CL_BI_33_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2
+				gattc disconnect
+TC_GAW_CL_BI_34_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characteristic 2 <long_value>
+				gattc disconnect
+TC_GAW_CL_BI_35_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc get_descriptor
+				gattc write_descriptor 2
+				gattc disconnect
+TC_GAW_CL_BI_36_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc get_descriptor
+				gattc write_descriptor 2 <long_value>
+				gattc disconnect
+TC_GAW_SR_BV_01_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 4 <permissions> 16
+				gatts start_service
+TC_GAW_SR_BV_02_C	PASS	haltest:
+				gatts add service
+				gatts add_characteristics:
+					<properties> 66 <permisions> 147
+				gatts start_service
+				gattc listen
+				gatts send_response: (twice)
+				NOTE: gatts_request_write_cb shall be called
+								 (verify it)
+TC_GAW_SR_BI_01_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 68
+						<permissions> 129
+				gatts start_service
+				gatts send_response: repeat with <data> 1
+TC_GAW_SR_BV_03_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+TC_GAW_SR_BI_02_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 1
+TC_GAW_SR_BI_03_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 1
+				gatts start_service
+TC_GAW_SR_BI_04_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 8
+TC_GAW_SR_BI_05_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 5
+TC_GAW_SR_BI_06_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAW_SR_BV_05_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response:
+						repeat with correct value
+TC_GAW_SR_BI_07_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response
+TC_GAW_SR_BI_08_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 2 <permissions> 1
+				gatts start_service
+TC_GAW_SR_BI_09_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 7
+TC_GAW_SR_BI_11_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 8
+TC_GAW_SR_BI_12_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 5
+TC_GAW_SR_BI_13_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 12
+TC_GAW_SR_BV_06_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						repeat with correct value
+TC_GAW_SR_BV_10_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response:
+						repeat with correct value
+TC_GAW_SR_BI_14_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 1
+TC_GAW_SR_BI_15_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 3
+TC_GAW_SR_BI_17_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 8
+TC_GAW_SR_BI_18_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 5
+TC_GAW_SR_BI_19_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAW_SR_BV_07_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						repeat with correct value
+TC_GAW_CL_BV_08_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response
+TC_GAW_SR_BI_20_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response: <status> 1
+TC_GAW_SR_BI_21_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permmisions> 1
+				gatts start_service
+TC_GAW_SR_BI_22_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response: <status> 8
+
+TC_GAW_SR_BI_23_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response: <status> 5
+TC_GAW_SR_BI_24_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAW_SR_BV_09_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response:
+						repeat with correct value
+TC_GAW_SR_BI_25_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response: <status> 1
+TC_GAW_SR_BI_26_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 1
+				gatts start_service
+TC_GAW_SR_BI_27_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 1
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 7
+TC_GAW_SR_BI_29_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response: <status> 8
+TC_GAW_SR_BI_30_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response: <status> 5
+TC_GAW_SR_BI_31_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAW_SR_BI_32_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response
+				gatts send_response: <status> 13
+TC_GAW_SR_BI_33_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 13
+TC_GAW_SR_BI_34_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response
+				gatts send_response: <status> 13
+TC_GAW_SR_BI_35_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 13
+TC_GAN_CL_BV_01_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc get_descriptor
+				gattc write_descriptor 2 <hex_value> 0001
+				gattc disconnect
+TC_GAN_SR_BV_01_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 26 <permissions> 17
+				gatts add_descriptor: <uuid> 2902
+				gatts start_service
+				gatts send_response
+				gatts send_response
+				gatts send_indication:
+						<attr_handle> char value handle
+						<confirm> 0
+TC_GAI_CL_BV_01_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc get_descriptor
+				gattc write_descriptor 2 <hex_value> 0002
+				gattc disconnect
+TC_GAI_SR_BV_01_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 42 <permissions> 17
+				gatts add_descriptor: <permissions> 17
+				gatts start_service
+				gatts add_service
+				gatts start_service
+TC_GAS_CL_BV_01_C	PASS	haltest:
+				gattc connect
+				gattc disconnect
+TC_GAS_SR_BV_01_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 42 <permissions> 17
+				gatts add_descriptor: <permissions> 17
+				gatts start_service
+				gatts add_service
+				gatts start_service
+TC_GAT_CL_BV_01_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc read_characcteristic
+				gattc disconnect
+TC_GAT_CL_BV_02_C	PASS	haltest:
+				gattc connect
+				gattc search_service
+				gattc get_characteristic: srvc_id based on
+								handle from logs
+				gattc write_characcteristic 2 <short_value>
+				gattc disconnect
+TC_GAT_SR_BV_01_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 42 <permissions> 17
+				gatts add_descriptor: <permissions> 17
+				gatts start_service
+				gatts add_service
+				gatts start_service
+TC_GPA_CL_BV_01_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 224 [u1] 8
+				gattc disconnect
+TC_GPA_CL_BV_02_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 224 [u1] 8
+				gattc disconnect
+TC_GPA_CL_BV_03_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 224 [u1] 8
+				gattc disconnect
+TC_GPA_CL_BV_04_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 224 [u1] 8
+				gattc disconnect
+TC_GPA_CL_BV_05_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 224 [u1] 8
+				gattc disconnect
+TC_GPA_CL_BV_06_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 224 [u1] 8
+				gattc disconnect
+TC_GPA_CL_BV_07_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 224 [u1] 8
+				gattc disconnect
+TC_GPA_CL_BV_08_C	PASS	haltest:
+				gattc connect
+				test_command: <cmd> 224 [u1] 8
+				gattc disconnect
+TC_GPA_CL_BV_11_C	PASS	haltest:
+				gattc connect
+				Repeat following steps 5 times:
+				1.Find Characteristic Aggregate Format
+				gattc test_command <cmd> 224 [u1] 8
+				2.Read aggregate descriptor
+				gattc test_command <cmd> 224 [u1] 10
+				3.Read 3 handles from aggregate descriptor
+									value
+				gattc test_command <cmd> 224 [u1] 10
+				4.Compare descriptors values
+				gattc disconnect
+
+TC_GPA_CL_BV_12_C	PASS	haltest:
+				gattc connect
+				Repeat following steps 5 times:
+				1.Find Characteristic Presentation Format
+				gattc test_command <cmd> 224 [u1] 8
+				2.Find characteristic in this range
+				gattc test_command <cmd> 224 <uuid> 2803 [u1] 8
+				3.Read characteristic declaration
+				gattc test_command <cmd> 224 [u1] 10
+				4.Read characteristic value
+				gattc test_command <cmd> 224 [u1] 10
+				5.Compare characteristic value and
+							presentation format
+				gattc disconnect
+TC_GPA_SR_BV_01_C	PASS
+TC_GPA_SR_BV_02_C	PASS	haltest:
+				gatts add_service
+				gatts start_service
+TC_GPA_SR_BV_03_C	PASS	haltest:
+				gatts add_service
+				gatts add_service
+				add_included_service
+				gatts start_service
+				gatts start_service
+TC_GPA_SR_BV_04_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+TC_GPA_SR_BV_05_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 138 <permissions> 17
+				gatts add_descriptor <UUID> 2900
+				gatts start_service
+				gatts send_response
+TC_GPA_SR_BV_06_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 138 <permissions> 17
+				gatts add_descriptor <UUID> 2901
+				gatts start_service
+				gatts send_response
+TC_GPA_SR_BV_07_C	PASS
+TC_GPA_SR_BV_08_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 138 <permissions> 17
+				gatts add_descriptor <UUID> 2903
+				gatts start_service
+				gatts send_response
+TC_GPA_SR_BV_11_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 138 <permissions> 17
+				gatts add_descriptor <UUID> 2905
+				gatts start_service
+				gatts send_response: repeat with correct offset
+								and data
+TC_GPA_SR_BV_12_C	PASS	PTS issue #12262
+				haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor <UUID> 2904
+				gatts start_service
+				gatts send_response: repeat with correct data
 -------------------------------------------------------------------------------
diff --git a/android/pts-hdp.txt b/android/pts-hdp.txt
index 1dec18c..bdf8eca 100644
--- a/android/pts-hdp.txt
+++ b/android/pts-hdp.txt
@@ -1,7 +1,7 @@
 PTS test results for HDP
 
 PTS version: 5.1
-Tested: not tested
+Tested: 17-June-2014
 Android version: 4.4.2
 
 Results:
@@ -13,18 +13,51 @@
 -------------------------------------------------------------------------------
 Test Name		Result	Notes
 -------------------------------------------------------------------------------
-TC_SRC_CON_BV_01_I	INC
-TC_SRC_CON_BV_02_I	INC
-TC_SRC_CON_BV_03_I	INC
-TC_SRC_CON_BV_04_I	INC
-TC_SRC_CON_BV_05_I	INC
-TC_SRC_CON_BV_06_I	INC
-TC_SRC_CON_BV_07_I	INC
-TC_SRC_CON_BV_08_I	INC
-TC_SRC_CON_BV_09_I	INC
-TC_SRC_CON_BV_10_I	INC
-TC_SRC_CC_BV_01_C	INC
-TC_SRC_CC_BV_02_C	INC
+TC_SRC_CON_BV_01_I	PASS	haltest:
+				bluetooth enable
+
+				bluetooth set_adapter_property
+				BT_PROPERTY_ADAPTER_SCAN_MODE
+				BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
+
+				hl register_application <args>
+				for instance:
+				hl register_application health intel heartrate
+				heartrate-monitor 1 BTHL_MDEP_ROLE_SOURCE 4100
+				BTHL_CHANNEL_TYPE_RELIABLE testing
+
+				when prompted:
+				bluetooth ssp_reply <args>
+				for instance:
+				bluetooth ssp_reply <bdaddr>
+				BT_SSP_VARIANT_CONSENT 1
+TC_SRC_CON_BV_02_I	PASS
+TC_SRC_CON_BV_03_I	PASS	when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_04_I	PASS	haltest:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_05_I	PASS	when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_06_I	PASS	haltest:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_07_I	PASS	when prompted: bluetooth start_discovery
+TC_SRC_CON_BV_08_I	PASS	when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_09_I	PASS	haltest:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_10_I	N/A
+TC_SRC_CC_BV_01_C	PASS	haltest:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SRC_CC_BV_02_C	PASS	when prompted: bluetooth ssp_reply <args>
 TC_SRC_CC_BV_03_C	INC
 TC_SRC_CC_BV_05_C	INC
 TC_SRC_CC_BV_07_C	INC
@@ -41,18 +74,45 @@
 TC_SRC_DE_BV_02_I	INC
 TC_SRC_DEP_BV_01_I	INC
 TC_SRC_DEP_BV_02_I	N/A
-TC_SNK_CON_BV_01_I	INC
-TC_SNK_CON_BV_02_I	INC
-TC_SNK_CON_BV_03_I	INC
-TC_SNK_CON_BV_04_I	INC
-TC_SNK_CON_BV_05_I	INC
-TC_SNK_CON_BV_06_I	INC
-TC_SNK_CON_BV_07_I	INC
-TC_SNK_CON_BV_08_I	INC
-TC_SNK_CON_BV_09_I	INC
-TC_SNK_CON_BV_10_I	INC
-TC_SNK_CC_BV_01_C	INC
-TC_SNK_CC_BV_02_C	INC
+TC_SNK_CON_BV_01_I	PASS	haltest:
+				hl register_application <args>
+				for instance:
+				hl register_application health intel heartrate
+				heartrate-monitor 1 BTHL_MDEP_ROLE_SINK 4100
+				BTHL_CHANNEL_TYPE_RELIABLE testing
+
+				when prompted:
+				bluetooth ssp_reply <args>
+				for instance:
+				bluetooth ssp_reply <bdaddr>
+				BT_SSP_VARIANT_CONSENT 1
+TC_SNK_CON_BV_02_I	PASS
+TC_SNK_CON_BV_03_I	PASS	when prompted: bluetooth ssp_reply <args>
+TC_SNK_CON_BV_04_I	PASS	haltest:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SNK_CON_BV_05_I	PASS	when prompted: bluetooth ssp_reply <args>
+TC_SNK_CON_BV_06_I	PASS	haltest:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SNK_CON_BV_07_I	PASS	when prompted: bluetooth start_discovery
+TC_SNK_CON_BV_08_I	PASS	when prompted: bluetooth ssp_reply <args>
+TC_SNK_CON_BV_09_I	PASS	haltest:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SNK_CON_BV_10_I	N/A
+TC_SNK_CC_BV_01_C	PASS	haltest:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SNK_CC_BV_02_C	PASS	when prompted: bluetooth ssp_reply <args>
 TC_SNK_CC_BV_04_C	INC
 TC_SNK_CC_BV_06_C	INC
 TC_SNK_CC_BV_08_C	INC
diff --git a/android/pts-hogp.txt b/android/pts-hogp.txt
new file mode 100644
index 0000000..9b962b4
--- /dev/null
+++ b/android/pts-hogp.txt
@@ -0,0 +1,80 @@
+PTS test results for HoG
+
+PTS version: 5.1
+Tested: 17-June-2014
+Android version: 4.4.2
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_HGDS_HH_BV_01_I	PASS
+TC_HGDS_HH_BV_02_I	PASS
+TC_HGDS_HH_BV_03_I	PASS
+TC_HGDS_HD_BV_01_I	N/A
+TC_HGDS_HD_BV_02_I	N/A
+TC_HGDR_RH_BV_01_I	PASS
+TC_HGDC_RH_BV_01_I	PASS
+TC_HGDC_RH_BV_02_I	PASS
+TC_HGDC_RH_BV_03_I	PASS
+TC_HGDC_RH_BV_04_I	PASS
+TC_HGDC_RH_BV_05_I	PASS
+TC_HGDC_RH_BV_06_I	PASS
+TC_HGDC_RH_BV_07_I	PASS
+TC_HGDC_HH_BV_08_I	PASS
+TC_HGDC_HH_BV_14_I	PASS
+TC_HGDC_HH_BV_15_I	PASS
+TC_HGDC_HH_BV_16_I	PASS
+TC_HGDC_BH_BV_09_I	N/A
+TC_HGDC_BH_BV_10_I	N/A
+TC_HGDC_BH_BV_11_I	N/A
+TC_HGDC_BH_BV_12_I	N/A
+TC_HGDC_BH_BV_13_I	N/A
+TC_HGRF_RH_BV_01_I	INC
+TC_HGRF_RH_BV_02_I	INC
+TC_HGRF_RH_BV_03_I	INC
+TC_HGRF_RH_BV_04_I	INC
+TC_HGRF_RH_BV_05_I	INC
+TC_HGRF_RH_BV_19_I	INC
+TC_HGRF_RH_BV_06_I	INC
+TC_HGRF_RH_BV_07_I	INC
+TC_HGRF_RH_BV_08_I	INC
+TC_HGRF_RH_BV_09_I	INC
+TC_HGRF_HH_BV_10_I	INC
+TC_HGRF_HH_BV_11_I	INC
+TC_HGRF_HH_BV_12_I	INC
+TC_HGRF_BH_BV_13_I	N/A
+TC_HGRF_BH_BV_14_I	N/A
+TC_HGRF_BH_BV_15_I	N/A
+TC_HGRF_BH_BV_16_I	N/A
+TC_HGRF_BH_BV_17_I	N/A
+TC_HGRF_HH_BV_18_I	INC
+TC_HGWF_RH_BV_01_I	INC
+TC_HGWF_RH_BV_02_I	INC
+TC_HGWF_RH_BV_03_I	INC
+TC_HGWF_RH_BV_04_I	INC
+TC_HGWF_RH_BV_05_I	INC
+TC_HGWF_RH_BV_06_I	INC
+TC_HGWF_RH_BV_07_I	INC
+TC_HGWF_BH_BV_08_I	N/A
+TC_HGWF_BH_BV_09_I	N/A
+TC_HGWF_BH_BV_10_I	N/A
+TC_HGWF_BH_BV_11_I	N/A
+TC_HGCF_RH_BV_01_I	INC
+TC_HGCF_RH_BV_02_I	INC
+TC_HGCF_BH_BV_03_I	N/A
+TC_HGCF_BH_BV_04_I	N/A
+TC_HGCF_BH_BV_05_I	N/A
+TC_HGCF_BH_BV_06_I	N/A
+TC_HGNF_RH_BV_01_I	INC
+TC_HGNF_RH_BI_01_I	INC
+TC_HGNF_RH_BI_01_I	INC
+TC_HGNF_BH_BV_02_I	N/A
+TC_HGNF_BH_BV_03_I	N/A
+TC_HGNF_BH_BI_01_I	N/A
+-------------------------------------------------------------------------------
diff --git a/android/pts-l2cap.txt b/android/pts-l2cap.txt
index b14614a..6e012c1 100644
--- a/android/pts-l2cap.txt
+++ b/android/pts-l2cap.txt
@@ -41,15 +41,15 @@
 TC_COS_ECH_BV_01_C	PASS
 TC_COS_ECH_BV_02_C	PASS	l2ping <bdaddr>
 TC_CLS_CLR_BV_01_C	N/A
-TC_CLS_UCD_BV_01_C	N/A
-TC_CLS_UCD_BV_02_C	N/A
-TC_CLS_UCD_BV_03_C	N/A
+TC_CLS_UCD_BV_01_C	INC	not tested yet
+TC_CLS_UCD_BV_02_C	INC	not tested yet
+TC_CLS_UCD_BV_03_C	INC	not tested yet
 TC_EXF_BV_01_C		PASS
 TC_EXF_BV_02_C		PASS
 TC_EXF_BV_03_C		PASS
-TC_EXF_BV_04_C		N/A
+TC_EXF_BV_04_C		INC	not tested yet
 TC_EXF_BV_05_C		PASS
-TC_EXF_BV_06_C		N/A
+TC_EXF_BV_06_C		INC	not tested yet
 
 TC_CMC_BV_01_C		PASS    l2test -X ertm -P <PSM - not reserved>
 TC_CMC_BV_02_C		PASS    l2test -X ertm -P <PSM - not reserved>
@@ -109,7 +109,7 @@
 TC_ERM_BV_14_C		PASS
 TC_ERM_BV_15_C		PASS	l2test -X ertm -P <PSM - not reserved> -N 4
 				-Y 4
-TC_ERM_BV_16_C		N/A
+TC_ERM_BV_16_C		INC	not tested yet
 TC_ERM_BV_17_C		PASS	l2test -X ertm -P <PSM - not reserved>
 TC_ERM_BV_18_C		PASS
 TC_ERM_BV_19_C		PASS	l2test -x -X ertm -P <PSM - not reserved> -N 1
@@ -117,7 +117,7 @@
 TC_ERM_BV_21_C		PASS	l2test -w -X ertm -P 4097 -N 3
 TC_ERM_BV_22_C		PASS
 TC_ERM_BV_23_C		PASS
-TC_ERM_BI_01_C		N/A
+TC_ERM_BI_01_C		INC	not tested yet
 TC_ERM_BI_02_C		PASS	l2test -X ertm -P <PSM - not reserved>
 TC_ERM_BI_03_C		PASS
 TC_ERM_BI_04_C		PASS
@@ -129,11 +129,11 @@
 TC_STM_BV_11_C		N/A
 TC_STM_BV_12_C		N/A
 TC_STM_BV_13_C		N/A
-TC_FIX_BV_01_C		N/A
+TC_FIX_BV_01_C		INC	not tested yet
 TC_FIX_BV_02_C		N/A
-TC_EWC_BV_01_C		N/A
-TC_EWC_BV_02_C		N/A
-TC_EWC_BV_03_C		N/A
+TC_EWC_BV_01_C		INC	not tested yet
+TC_EWC_BV_02_C		INC	not tested yet
+TC_EWC_BV_03_C		INC	not tested yet
 TC_LSC_BV_01_C		N/A
 TC_LSC_BV_02_C		N/A
 TC_LSC_BV_03_C		N/A
@@ -150,16 +150,16 @@
 TC_CCH_BV_02_C		N/A
 TC_CCH_BV_03_C		N/A
 TC_CCH_BV_04_C		N/A
-TC_ECF_BV_01_C		N/A
-TC_ECF_BV_02_C		N/A
-TC_ECF_BV_03_C		N/A
-TC_ECF_BV_04_C		N/A
-TC_ECF_BV_05_C		N/A
-TC_ECF_BV_06_C		N/A
-TC_ECF_BV_07_C		N/A
-TC_ECF_BV_08_C		N/A
-TC_LE_CPU_BV_01_C	N/A
-TC_LE_CPU_BV_02_C	N/A
-TC_LE_CPU_BI_01_C	N/A
-TC_LE_CPU_BI_02_C	N/A
-TC_LE_REJ_BV_01_C	N/A
+TC_ECF_BV_01_C		INC	not tested yet
+TC_ECF_BV_02_C		INC	not tested yet
+TC_ECF_BV_03_C		INC	not tested yet
+TC_ECF_BV_04_C		INC	not tested yet
+TC_ECF_BV_05_C		INC	not tested yet
+TC_ECF_BV_06_C		INC	not tested yet
+TC_ECF_BV_07_C		INC	not tested yet
+TC_ECF_BV_08_C		INC	not tested yet
+TC_LE_CPU_BV_01_C	INC	not tested yet
+TC_LE_CPU_BV_02_C	INC	not tested yet
+TC_LE_CPU_BI_01_C	INC	not tested yet
+TC_LE_CPU_BI_02_C	INC	not tested yet
+TC_LE_REJ_BV_01_C	INC	not tested yet
diff --git a/android/pts-mps.txt b/android/pts-mps.txt
index 005318d..93bce31 100644
--- a/android/pts-mps.txt
+++ b/android/pts-mps.txt
@@ -1,7 +1,7 @@
 PTS test results for MPS
 
 PTS version: 5.1
-Tested: not tested
+Tested: 23.05.2014
 Android version: 4.4.2
 
 Results:
@@ -14,17 +14,17 @@
 -------------------------------------------------------------------------------
 Test Name				Result	Notes
 -------------------------------------------------------------------------------
-TC_AG_SRC_HFAV_ACT_SD_BV_01_I		INC
-TC_AG_SRC_HFAV_ACT_SD_BV_02_I		INC
-TC_AG_SRC_HFAV_ACT_SD_BV_03_I		INC
-TC_AG_SRC_HFAV_CLH_SD_BV_01_I		INC
+TC_AG_SRC_HFAV_ACT_SD_BV_01_I		PASS
+TC_AG_SRC_HFAV_ACT_SD_BV_02_I		PASS
+TC_AG_SRC_HFAV_ACT_SD_BV_03_I		PASS
+TC_AG_SRC_HFAV_CLH_SD_BV_01_I		PASS
 TC_AG_SRC_HFAV_CLH_SD_BV_02_I		N/A
-TC_AG_SRC_HFAV_CLH_SD_BV_03_I		INC
-TC_AG_SRC_HFAV_CLH_SD_BV_04_I		INC
-TC_AG_SRC_HFAV_CLH_SD_BV_05_I		INC
-TC_AG_SRC_HFAV_CLH_SD_BV_06_I		INC
-TC_AVP_CTH_SD_BI_01_I			INC
-TC_AVP_CTH_SD_BI_02_I			INC
+TC_AG_SRC_HFAV_CLH_SD_BV_03_I		PASS
+TC_AG_SRC_HFAV_CLH_SD_BV_04_I		PASS
+TC_AG_SRC_HFAV_CLH_SD_BV_05_I		PASS
+TC_AG_SRC_HFAV_CLH_SD_BV_06_I		PASS
+TC_AVP_CTH_SD_BI_01_I			PASS
+TC_AVP_CTH_SD_BI_02_I			PASS
 TC_HF_SNK_HFAV_ACT_SD_BV_01_I		N/A
 TC_HF_SNK_HFAV_ACT_SD_BV_02_I		N/A
 TC_HF_SNK_HFAV_ACT_SD_BV_03_I		N/A
@@ -41,13 +41,13 @@
 TC_HF_SNK_CT_HFAV_CLH_MD_BV_04_I	N/A
 TC_HF_SNK_CT_HFAV_CLH_MD_BV_05_I	N/A
 TC_HF_SNK_CT_HFAV_CLH_MD_BV_06_I	N/A
-TC_SDP_CTH_SD_BV_01_I			INC
-TC_SRC_TG_HFAV_ACT_MD_BV_01_I		INC
-TC_SRC_TG_HFAV_CLH_MD_BV_01_I		INC
-TC_SRC_TG_HFAV_CLH_MD_BV_02_I		INC
-TC_SRC_TG_HFAV_CLH_MD_BV_03_I		INC
-TC_SRC_TG_HFAV_CLH_MD_BV_04_I		INC
-TC_SRC_TG_HFAV_CLH_MD_BV_05_I		INC
-TC_SRC_TG_HFAV_CLH_MD_BV_06_I		INC
-TC_PAIRING_HF_SNK_CT			INC
+TC_SDP_CTH_SD_BV_01_I			FAIL	PTS issue #12245
+TC_SRC_TG_HFAV_ACT_MD_BV_01_I		PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_01_I		PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_02_I		PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_03_I		PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_04_I		PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_05_I		PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_06_I		PASS
+TC_PAIRING_HF_SNK_CT			PASS
 -------------------------------------------------------------------------------
diff --git a/android/pts-opp.txt b/android/pts-opp.txt
index 0a070ca..e1b0def 100644
--- a/android/pts-opp.txt
+++ b/android/pts-opp.txt
@@ -28,9 +28,9 @@
 TC_CLIENT_BCP_BV_04_I   N/A
 TC_CLIENT_BCP_BV_05_I   N/A
 TC_CLIENT_CON_BV_01_C	N/A
-TC_CLIENT_OPH_BI_01_C	PASS	PTS issue: ID: 12081 (bluetooth.org)
+TC_CLIENT_OPH_BI_01_C	PASS	PTS issue #12081
 				ETS-12081 required
-TC_CLIENT_OPH_BV_01_I	PASS	PTS issue: ID: 12081.
+TC_CLIENT_OPH_BV_01_I	PASS	PTS issue #12081
 				ETS-12160 required
 TC_CLIENT_OPH_BV_02_I	N/A
 TC_CLIENT_OPH_BV_03_I	PASS	ETS-12081 required (bluetooth.org)
@@ -48,10 +48,13 @@
 TC_CLIENT_OPH_BV_16_I	N/A
 TC_CLIENT_OPH_BV_17_I	N/A
 TC_CLIENT_OPH_BV_18_I	N/A
-TC_CLIENT_OPH_BV_19_I	PASS	ETS-12081 required
+TC_CLIENT_OPH_BV_19_I	PASS	PTS issue #12081
+				ETS-12081 required
 TC_CLIENT_OPH_BV_20_I	PASS
-TC_CLIENT_OPH_BV_22_I	PASS	ETS-12081 required
-TC_CLIENT_OPH_BV_23_I	PASS	ETS-12081 required
+TC_CLIENT_OPH_BV_22_I	PASS	PTS issue #12081
+				ETS-12081 required
+TC_CLIENT_OPH_BV_23_I	PASS	PTS issue #12081
+				ETS-12081 required
 TC_CLIENT_OPH_BV_24_I	N/A
 TC_CLIENT_OPH_BV_25_I	N/A
 TC_CLIENT_OPH_BV_26_I	N/A
@@ -73,43 +76,53 @@
 TC_SERVER_BCE_BV_06_I	N/A
 TC_SERVER_BCE_BV_07_I	N/A
 TC_SERVER_BCP_BV_01_I	N/A
-TC_SERVER_BCP_BV_02_I	PASS	ETS-12081 required (bluetooth.org)
+TC_SERVER_BCP_BV_02_I	PASS	PTS issue #12081
+				ETS-12081 required
 TC_SERVER_BCP_BV_03_I	N/A
 TC_SERVER_BCP_BV_04_I	N/A
 TC_SERVER_BCP_BV_05_I	N/A
 TC_SERVER_CON_BV_02_C	N/A
 TC_SERVER_OPH_BV_01_I	PASS
 TC_SERVER_OPH_BV_02_I	PASS
-TC_SERVER_OPH_BV_03_I	PASS	ETS-12081 required
+TC_SERVER_OPH_BV_03_I	PASS	PTS issue #12081
+				ETS-12081 required
 				IUT must be in the connectable mode. Tester must
 					accept incoming file
-TC_SERVER_OPH_BV_04_I	PASS	ETS-12081 required
+TC_SERVER_OPH_BV_04_I	PASS	PTS issue #12081
+				ETS-12081 required
 				IUT must be in the connectable mode. Tester must
 					accept incoming file
-TC_SERVER_OPH_BV_05_I	PASS	ETS-12081 required
+TC_SERVER_OPH_BV_05_I	PASS	PTS issue #12081
+				ETS-12081 required
 				IUT must be in the connectable mode. Tester must
 					reject incoming file
 TC_SERVER_OPH_BV_07_I	N/A
 TC_SERVER_OPH_BV_08_I	N/A
 TC_SERVER_OPH_BV_09_I	N/A
-TC_SERVER_OPH_BV_10_I	PASS	ETS-12081 required
+TC_SERVER_OPH_BV_10_I	PASS	PTS issue #12081
+				ETS-12081 required
 				IUT must be in the connectable mode
 TC_SERVER_OPH_BV_11_I	N/A
 TC_SERVER_OPH_BV_12_I	N/A
 TC_SERVER_OPH_BV_13_I	N/A
-TC_SERVER_OPH_BV_14_I	PASS	ETS-12081 required
+TC_SERVER_OPH_BV_14_I	PASS	PTS issue #12081
+				ETS-12081 required
 				IUT must be in the connectable mode
 TC_SERVER_OPH_BV_15_I	N/A
 TC_SERVER_OPH_BV_16_I	N/A
 TC_SERVER_OPH_BV_17_I	N/A
-TC_SERVER_OPH_BV_18_I	PASS	ETS-12081 required
+TC_SERVER_OPH_BV_18_I	PASS	PTS issue #12081
+				ETS-12081 required
 				IUT must be in the connectable mode
-TC_SERVER_OPH_BV_19_I	PASS	ETS-12081 required
+TC_SERVER_OPH_BV_19_I	PASS	PTS issue #12081
+				ETS-12081 required
 				IUT must be in the connectable mode
 TC_SERVER_OPH_BV_21_I	N/A
-TC_SERVER_OPH_BV_22_I	PASS	ETS-12081 required
+TC_SERVER_OPH_BV_22_I	PASS	PTS issue #12081
+				ETS-12081 required
 				IUT must be in the connectable mode
-TC_SERVER_OPH_BV_23_I	PASS	ETS-12081 required
+TC_SERVER_OPH_BV_23_I	PASS	PTS issue #12081
+				ETS-12081 required
 				IUT must be in the connectable mode
 TC_SERVER_OPH_BV_24_I	N/A
 TC_SERVER_OPH_BV_25_I	N/A
@@ -129,5 +142,6 @@
 TC_SERVER_OPH_BV_31_I	N/A
 TC_SERVER_OPH_BV_32_I	N/A
 TC_SERVER_OPH_BV_33_I	N/A
-TC_SERVER_OPH_BV_34_I	PASS	ETS-12081 required
+TC_SERVER_OPH_BV_34_I	PASS	PTS issue #12081
+				ETS-12081 required
 -------------------------------------------------------------------------------
diff --git a/android/pts-sm.txt b/android/pts-sm.txt
index 0792de8..ff713cc 100644
--- a/android/pts-sm.txt
+++ b/android/pts-sm.txt
@@ -3,6 +3,7 @@
 PTS version: 5.1
 Tested: 14-May-2014
 Android version: 4.4.2
+kernel version: 3.17
 
 Results:
 PASS	test passed
@@ -15,49 +16,85 @@
 Test Name		Result	Notes
 -------------------------------------------------------------------------------
 TC_PROT_BV_01_C		PASS
-TC_PROT_BV_02_C		FAIL	JIRA: BZ-24
+TC_PROT_BV_02_C		PASS
 				btmgmt advertising on
-TC_JW_BV_01_C		FAIL	JIRA: BZ-25
-TC_JW_BV_02_C		INC	JIRA: BZ-26
-TC_JW_BV_05_C		INC
+TC_JW_BV_01_C		PASS	btmgmt pairable off
+				btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_JW_BV_02_C		PASS
+TC_JW_BV_05_C		PASS	btmgmt pair -c 0x03 -t 0x01 <addr>
 TC_JW_BI_01_C		PASS
-TC_JW_BI_02_C		INC
-TC_JW_BI_03_C		INC
-TC_JW_BI_04_C		FAIL
-TC_PKE_BV_01_C		FAIL
-TC_PKE_BV_02_C		INC
-TC_PKE_BV_04_C		FAIL
-TC_PKE_BV_05_C		INC
-TC_PKE_BI_01_C		INC
+TC_JW_BI_02_C		PASS
+TC_JW_BI_03_C		PASS	bluetoothd is NOT running
+				btmgmt power on
+				btmgmt le on
+				btmgmt connectable on
+				btmgmt pairable on
+				btmgmt discov on
+				btmgmt advertising on
+TC_JW_BI_04_C		PASS	btmgmt pairable off
+				btmgmg pair -c 0x03 -t 0x01 <addr>
+TC_PKE_BV_01_C		PASS	btmgmt pairable off
+				btmgmt pair -c 0x04 -t 0x01 <addr>
+				Note: provide passkey to PTS
+TC_PKE_BV_02_C		PASS	btmgmt pairable off
+				btmgmt io-cap 0x04
+				btmgmt advertising on
+				btmgmt monitor
+				Note: provide passkey
+TC_PKE_BV_04_C		PASS	btmgmt pair -c 0x04 -t 0x01 <addr>
+TC_PKE_BV_05_C		PASS	btmgmt io-cap 0x04
+				l2test -r -J4 -AES -V le_public
+TC_PKE_BI_01_C		PASS	btmgmt pair -c 0x04 -t 0x01 <addr>
+				Note: Enter invalid passkey in PTS
 TC_PKE_BI_02_C		PASS
-TC_PKE_BI_03_C		INC
-TC_OOB_BV_01_C		FAIL
-TC_OOB_BV_02_C		INC
-TC_OOB_BV_03_C		FAIL
-TC_OOB_BV_04_C		INC
-TC_OOB_BV_05_C		INC
-TC_OOB_BV_06_C		INC
-TC_OOB_BV_07_C		INC
-TC_OOB_BV_08_C		INC
-TC_OOB_BV_09_C		INC
-TC_OOB_BV_10_C		INC
-TC_OOB_BI_01_C		INC
-TC_OOB_BI_02_C		INC
-TC_EKS_BV_01_C		INC
-TC_EKS_BV_02_C		INC
-TC_EKS_BI_01_C		INC
-TC_EKS_BI_02_C		INC
-TC_SIGN_BV_01_C		INC
-TC_SIGN_BV_03_C		INC
-TC_SIGN_BI_01_C		INC
-TC_KDU_BV_01_C		INC
-TC_KDU_BV_02_C		INC
-TC_KDU_BV_03_C		INC
-TC_KDU_BV_04_C		INC
-TC_KDU_BV_05_C		INC
-TC_KDU_BV_06_C		INC
-TC_KDU_BV_07_C		INC
-TC_SIP_BV_01_C		INC
-TC_SIP_BV_02_C		FAIL
-TC_SIE_BV_01_C		INC
+TC_PKE_BI_03_C		PASS	btmgmt io-cap 0x04
+				btmgmt advertising on
+				btmgmt monitor
+				Note: Enter invalid passkey in PTS
+TC_OOB_BV_01_C		NA
+TC_OOB_BV_02_C		NA
+TC_OOB_BV_03_C		NA
+TC_OOB_BV_04_C		NA
+TC_OOB_BV_05_C		PASS
+TC_OOB_BV_06_C		PASS
+TC_OOB_BV_07_C		PASS
+TC_OOB_BV_08_C		PASS
+TC_OOB_BV_09_C		NA
+TC_OOB_BV_10_C		NA
+TC_OOB_BI_01_C		NA
+TC_OOB_BI_02_C		NA
+TC_EKS_BV_01_C		PASS
+TC_EKS_BV_02_C		PASS
+TC_EKS_BI_01_C		PASS	btmgmt io-cap 0x03
+TC_EKS_BI_02_C		PASS
+TC_SIGN_BV_01_C		INC	PTS issue #12305
+TC_SIGN_BV_03_C		PASS	haltest
+				gattc listen
+
+TC_SIGN_BI_01_C		PASS	haltest
+				gattc listen
+TC_KDU_BV_01_C		PASS	btmgmt pairable on
+TC_KDU_BV_02_C		PASS	PTS issue #12302
+				Note: Can pass it with following instructions:
+				btmgmt privacy on
+				btmgmt advetising on
+				Check our random address (valid for 15 min)
+				Set PIXIT TSPX_bd_addr_iut to random address
+				Set PIXIT TSPX_peer_type to 01
+TC_KDU_BV_03_C		PASS	btmgmt pairable on
+				btmgmt advertising on
+
+TC_KDU_BV_04_C		INC	PTS issue #12301
+TC_KDU_BV_05_C		PASS	PTS issue #12302
+				Note: Can pass it with following instructions:
+				btmgmt privacy on
+				Check our random address (valid for 15 min)
+				Set PIXIT TSPX_bd_addr_iut to random address
+				Set PIXIT TSPX_peer_type to 01
+TC_KDU_BV_06_C		FAIL	PTS issue #12301
+				PTS issue #12302
+TC_KDU_BV_07_C		PASS	btmgmt pairable on
+TC_SIP_BV_01_C		PASS	btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_SIP_BV_02_C		PASS	l2test -n -J4 -V le_public <addr>
+TC_SIE_BV_01_C		INC	PTS issue #12306
 -------------------------------------------------------------------------------
diff --git a/android/socket.c b/android/socket.c
index 99d6bec..2b836e9 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -360,6 +360,7 @@
 	uuid_t uuid;
 
 	sdp_uuid128_create(&uuid, app_uuid);
+	sdp_uuid128_to_uuid(&uuid);
 
 	record = create_rfcomm_record(chan, &uuid, svc_name, false);
 	if (!record)
diff --git a/android/system-emulator.c b/android/system-emulator.c
index c1b1b25..4d41fce 100644
--- a/android/system-emulator.c
+++ b/android/system-emulator.c
@@ -47,14 +47,10 @@
 static pid_t daemon_pid = -1;
 static pid_t snoop_pid = -1;
 
-static void ctl_start(void)
+static void run_valgrind(char *prg_name)
 {
-	char prg_name[PATH_MAX + 1];
 	char *prg_argv[6];
 	char *prg_envp[3];
-	pid_t pid;
-
-	snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd");
 
 	prg_argv[0] = "/usr/bin/valgrind";
 	prg_argv[1] = "--leak-check=full";
@@ -67,6 +63,30 @@
 	prg_envp[1] = "G_DEBUG=gc-friendly";
 	prg_envp[2] = NULL;
 
+	execve(prg_argv[0], prg_argv, prg_envp);
+}
+
+static void run_bluetoothd(char *prg_name)
+{
+	char *prg_argv[3];
+	char *prg_envp[1];
+
+	prg_argv[0] = prg_name;
+	prg_argv[1] = "-d";
+	prg_argv[2] = NULL;
+
+	prg_envp[0] = NULL;
+
+	execve(prg_argv[0], prg_argv, prg_envp);
+}
+
+static void ctl_start(void)
+{
+	char prg_name[PATH_MAX + 1];
+	pid_t pid;
+
+	snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd");
+
 	printf("Starting %s\n", prg_name);
 
 	pid = fork();
@@ -76,7 +96,10 @@
 	}
 
 	if (pid == 0) {
-		execve(prg_argv[0], prg_argv, prg_envp);
+		run_valgrind(prg_name);
+
+		/* Fallback to no valgrind if running with valgind failed */
+		run_bluetoothd(prg_name);
 		exit(0);
 	}
 
diff --git a/attrib/att.c b/attrib/att.c
index 8e9c06d..2680458 100644
--- a/attrib/att.c
+++ b/attrib/att.c
@@ -561,6 +561,64 @@
 	return len;
 }
 
+uint16_t enc_signed_write_cmd(uint16_t handle, const uint8_t *value,
+					size_t vlen, struct bt_crypto *crypto,
+					const uint8_t csrk[16],
+					uint32_t sign_cnt,
+					uint8_t *pdu, size_t len)
+{
+	const uint16_t hdr_len = sizeof(pdu[0]) + sizeof(handle);
+	const uint16_t min_len =  hdr_len + ATT_SIGNATURE_LEN;
+
+	if (pdu == NULL)
+		return 0;
+
+	if (vlen > len - min_len)
+		vlen = len - min_len;
+
+	pdu[0] = ATT_OP_SIGNED_WRITE_CMD;
+	put_le16(handle, &pdu[1]);
+
+	if (vlen > 0)
+		memcpy(&pdu[hdr_len], value, vlen);
+
+	if (!bt_crypto_sign_att(crypto, csrk, pdu, hdr_len + vlen, sign_cnt,
+							&pdu[hdr_len + vlen]))
+		return 0;
+
+	return min_len + vlen;
+}
+
+uint16_t dec_signed_write_cmd(const uint8_t *pdu, size_t len,
+						uint16_t *handle,
+						uint8_t *value, size_t *vlen,
+						uint8_t signature[12])
+{
+	const uint16_t hdr_len = sizeof(pdu[0]) + sizeof(*handle);
+	const uint16_t min_len =  hdr_len + ATT_SIGNATURE_LEN;
+
+
+	if (pdu == NULL)
+		return 0;
+
+	if (value == NULL || vlen == NULL || handle == NULL)
+		return 0;
+
+	if (len < min_len)
+		return 0;
+
+	if (pdu[0] != ATT_OP_SIGNED_WRITE_CMD)
+		return 0;
+
+	*vlen = len - min_len;
+	*handle = get_le16(&pdu[1]);
+	memcpy(value, pdu + hdr_len, *vlen);
+
+	memcpy(signature, pdu + hdr_len + *vlen, ATT_SIGNATURE_LEN);
+
+	return len;
+}
+
 uint16_t enc_write_req(uint16_t handle, const uint8_t *value, size_t vlen,
 						uint8_t *pdu, size_t len)
 {
diff --git a/attrib/att.h b/attrib/att.h
index c612d80..2311aaf 100644
--- a/attrib/att.h
+++ b/attrib/att.h
@@ -22,6 +22,11 @@
  *
  */
 
+#include "src/shared/crypto.h"
+
+/* Len of signature in write signed packet */
+#define ATT_SIGNATURE_LEN		12
+
 /* Attribute Protocol Opcodes */
 #define ATT_OP_ERROR			0x01
 #define ATT_OP_MTU_REQ			0x02
@@ -83,8 +88,8 @@
 #define ATT_PSM					31
 
 /* Flags for Execute Write Request Operation */
-#define ATT_CANCEL_ALL_PREP_WRITES              0x00
-#define ATT_WRITE_ALL_PREP_WRITES               0x01
+#define ATT_CANCEL_ALL_PREP_WRITES		0x00
+#define ATT_WRITE_ALL_PREP_WRITES		0x01
 
 /* Find Information Response Formats */
 #define ATT_FIND_INFO_RESP_FMT_16BIT		0x01
@@ -129,6 +134,16 @@
 						uint8_t *pdu, size_t len);
 uint16_t dec_write_cmd(const uint8_t *pdu, size_t len, uint16_t *handle,
 						uint8_t *value, size_t *vlen);
+uint16_t enc_signed_write_cmd(uint16_t handle,
+					const uint8_t *value, size_t vlen,
+					struct bt_crypto *crypto,
+					const uint8_t csrk[16],
+					uint32_t sign_cnt,
+					uint8_t *pdu, size_t len);
+uint16_t dec_signed_write_cmd(const uint8_t *pdu, size_t len,
+						uint16_t *handle,
+						uint8_t *value, size_t *vlen,
+						uint8_t signature[12]);
 struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, size_t len);
 uint16_t enc_write_req(uint16_t handle, const uint8_t *value, size_t vlen,
 						uint8_t *pdu, size_t len);
diff --git a/attrib/gatt.c b/attrib/gatt.c
index 73eaf7a..27fb0b3 100644
--- a/attrib/gatt.c
+++ b/attrib/gatt.c
@@ -1065,6 +1065,27 @@
 	return g_attrib_send(attrib, 0, buf, plen, NULL, user_data, notify);
 }
 
+guint gatt_signed_write_cmd(GAttrib *attrib, uint16_t handle,
+						const uint8_t *value, int vlen,
+						struct bt_crypto *crypto,
+						const uint8_t csrk[16],
+						uint32_t sign_cnt,
+						GDestroyNotify notify,
+						gpointer user_data)
+{
+	uint8_t *buf;
+	size_t buflen;
+	guint16 plen;
+
+	buf = g_attrib_get_buffer(attrib, &buflen);
+	plen = enc_signed_write_cmd(handle, value, vlen, crypto, csrk, sign_cnt,
+								buf, buflen);
+	if (plen == 0)
+		return 0;
+
+	return g_attrib_send(attrib, 0, buf, plen, NULL, user_data, notify);
+}
+
 static sdp_data_t *proto_seq_find(sdp_list_t *proto_list)
 {
 	sdp_list_t *list;
diff --git a/attrib/gatt.h b/attrib/gatt.h
index 7d055f0..f6db10f 100644
--- a/attrib/gatt.h
+++ b/attrib/gatt.h
@@ -105,6 +105,13 @@
 guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, const uint8_t *value,
 			int vlen, GDestroyNotify notify, gpointer user_data);
 
+guint gatt_signed_write_cmd(GAttrib *attrib, uint16_t handle,
+						const uint8_t *value, int vlen,
+						struct bt_crypto *crypto,
+						const uint8_t csrk[16],
+						uint32_t sign_cnt,
+						GDestroyNotify notify,
+						gpointer user_data);
 guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end,
 				bt_uuid_t *uuid, GAttribResultFunc func,
 				gpointer user_data);
diff --git a/btio/btio.c b/btio/btio.c
index 5f91171..50760f0 100644
--- a/btio/btio.c
+++ b/btio/btio.c
@@ -1085,9 +1085,10 @@
 				break;
 			}
 
+			len = sizeof(l2o.omtu);
 			if (getsockopt(sock, SOL_BLUETOOTH, BT_SNDMTU,
 							&l2o.omtu, &len) < 0) {
-				ERROR_FAILED(err, "getsockopt(BT_RCVMTU)",
+				ERROR_FAILED(err, "getsockopt(BT_SNDMTU)",
 									errno);
 				return FALSE;
 			}
diff --git a/configure.ac b/configure.ac
index d858ff6..fb6b502 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(bluez, 5.19)
+AC_INIT(bluez, 5.20)
 
 AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
 					tar-pax no-dist-gzip dist-xz])
@@ -125,8 +125,8 @@
 AC_ARG_ENABLE(udev, AC_HELP_STRING([--disable-udev],
 		[disable udev device support]), [enable_udev=${enableval}])
 if (test "${enable_tools}" != "no" && test "${enable_udev}" != "no"); then
-	PKG_CHECK_MODULES(UDEV, libudev >= 143, dummy=yes,
-				AC_MSG_ERROR(libudev >= 143 is required))
+	PKG_CHECK_MODULES(UDEV, libudev >= 172, dummy=yes,
+				AC_MSG_ERROR(libudev >= 172 is required))
 	AC_SUBST(UDEV_CFLAGS)
 	AC_SUBST(UDEV_LIBS)
 	AC_CHECK_LIB(udev, udev_hwdb_new,
diff --git a/doc/device-api.txt b/doc/device-api.txt
index 2d9fa3c..577ee60 100644
--- a/doc/device-api.txt
+++ b/doc/device-api.txt
@@ -192,4 +192,4 @@
 		int16 RSSI [readonly, optional]
 
 			Received Signal Strength Indicator of the remote
-			device.
+			device (inquiry or advertising).
diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt
index b691ae0..e15a78f 100644
--- a/doc/mgmt-api.txt
+++ b/doc/mgmt-api.txt
@@ -19,7 +19,8 @@
 Linux kernel v3.7	Version 1.2
 Linux kernel v3.9	Version 1.3
 Linux kernel v3.13	Version 1.4
-Linux kernel v3.15	Version 1.5	(not yet released)
+Linux kernel v3.15	Version 1.5
+Linux kernel v3.16	Version 1.6	(not yet released)
 
 Version 1.1 introduces Set Device ID command.
 
@@ -34,6 +35,10 @@
 and Load Identity Resolving Keys commands. It also introduces New Identity
 Resolving Key and New Signature Resolving Key events.
 
+Version 1.6 introduces Get Connection Information command. It also updates
+the Device Found event to combine advertising data and scan response data
+into a single event.
+
 
 Example
 =======
@@ -214,20 +219,20 @@
 	Current_Settings & Supported_Settings is a bitmask with
 	currently the following available bits:
 
-		1	Powered
-		2	Connectable
-		3	Fast Connectable
-		4	Discoverable
-		5	Pairable
-		6	Link Level Security (Sec. mode 3)
-		7	Secure Simple Pairing
-		8	Basic Rate/Enhanced Data Rate
-		9	High Speed
-		10	Low Energy
-		11	Advertising
-		12	Secure Connections
-		13	Debug Keys
-		14	Privacy
+		0	Powered
+		1	Connectable
+		2	Fast Connectable
+		3	Discoverable
+		4	Pairable
+		5	Link Level Security (Sec. mode 3)
+		6	Secure Simple Pairing
+		7	Basic Rate/Enhanced Data Rate
+		8	High Speed
+		9	Low Energy
+		10	Advertising
+		11	Secure Connections
+		12	Debug Keys
+		13	Privacy
 
 	This command generates a Command Complete event on success or
 	a Command Status event on failure.
@@ -910,9 +915,18 @@
 	Return Parameters:
 
 	This command is used to set the IO Capability used for pairing.
-	The command accepts both SSP and SMP values. The KeyboardDisplay
-	SMP value (which doesn't exist for SSP will) automatically be
-	downgraded to DisplayYesNo by the kernel for SSP pairing events.
+	The command accepts both SSP and SMP values.
+
+	Possible values for the IO_Capability parameter:
+		0	DisplayOnly
+		1	DisplayYesNo
+		2	KeyboardOnly
+		3	NoInputNoOutput
+		4	KeyboardDisplay
+
+	Passing a value 4 (KeyboardDisplay) will cause the kernel to
+	convert it to 1 (DisplayYesNo) in the case of a BR/EDR
+	connection (as KeyboardDisplay is specific to SMP).
 
 	This command can be used when the controller is not powered.
 
@@ -944,6 +958,17 @@
 		1	LE Public
 		2	LE Random
 
+	Possible values for the IO_Capability parameter:
+		0	DisplayOnly
+		1	DisplayYesNo
+		2	KeyboardOnly
+		3	NoInputNoOutput
+		4	KeyboardDisplay
+
+	Passing a value 4 (KeyboardDisplay) will cause the kernel to
+	convert it to 1 (DisplayYesNo) in the case of a BR/EDR
+	connection (as KeyboardDisplay is specific to SMP).
+
 	The Address and Address_Type of the return parameters will
 	return the identity address if know. In case of resolvable
 	random address given as command parameters and the remote
@@ -1270,9 +1295,9 @@
 	Possible values for the Address_Type parameter are a bit-wise or
 	of the following bits:
 
-		1	BR/EDR
-		2	LE Public
-		3	LE Random
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
 
 	By combining these e.g. the following values are possible:
 
diff --git a/doc/test-coverage.txt b/doc/test-coverage.txt
index 7b05ff6..f70a8d5 100644
--- a/doc/test-coverage.txt
+++ b/doc/test-coverage.txt
@@ -13,13 +13,15 @@
 test-sdp		 133	SDP qualification test cases
 test-uuid		  30	UUID conversion handling
 test-mgmt		   2	Management interface handling
+test-crypto		   4	Cryptographic toolbox helpers
 test-textfile		   4	Old textfile storage format
 test-ringbuf		   3	Ring buffer functionality
 test-queue		   1	Queue handling functionality
-test-hfp		  13	HFP Audio Gateway functionality
+test-uhid		   6	Userspace HID functionality
+test-hfp		  14	HFP Audio Gateway functionality
 test-avdtp		  60	AVDTP qualification test cases
 test-avctp		   9	AVCTP qualification test cases
-test-avrcp		  90	AVRCP qualification test cases
+test-avrcp		  92	AVRCP qualification test cases
 test-gobex		  31	Generic OBEX functionality
 test-gobex-packet	   9	OBEX packet handling
 test-gobex-header	  28	OBEX header handling
@@ -27,7 +29,7 @@
 test-gobex-transfer	  36	OBEX transfer handling
 test-gdbus-client	  12	D-Bus client handling
 			-----
-			 506
+			 519
 
 
 Automated end-to-end testing
@@ -35,7 +37,7 @@
 
 Application		Count	Description
 -------------------------------------------
-mgmt-tester		 184	Kernel management interface testing
+mgmt-tester		 189	Kernel management interface testing
 l2cap-tester		  26	Kernel L2CAP implementation testing
 rfcomm-tester		   9	Kernel RFCOMM implementation testing
 smp-tester		   5	Kernel SMP implementation testing
@@ -43,7 +45,7 @@
 gap-tester		   1	Daemon D-Bus API testing
 hci-tester		  14	Controller hardware testing
 			-----
-			 247
+			 252
 
 
 Android end-to-end testing
diff --git a/emulator/btdev.c b/emulator/btdev.c
index e590efa..641edea 100644
--- a/emulator/btdev.c
+++ b/emulator/btdev.c
@@ -223,6 +223,29 @@
 	return NULL;
 }
 
+static inline struct btdev *find_btdev_by_bdaddr_type(const uint8_t *bdaddr,
+							uint8_t bdaddr_type)
+{
+	int i;
+
+	for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+		int cmp;
+
+		if (!btdev_list[i])
+			continue;
+
+		if (bdaddr_type == 0x01)
+			cmp = memcmp(btdev_list[i]->random_addr, bdaddr, 6);
+		else
+			cmp = memcmp(btdev_list[i]->bdaddr, bdaddr, 6);
+
+		if (!cmp)
+			return btdev_list[i];
+	}
+
+	return NULL;
+}
+
 static void hexdump(const unsigned char *buf, uint16_t len)
 {
 	static const char hexdigits[] = "0123456789abcdef";
@@ -981,7 +1004,8 @@
 }
 
 static void le_conn_complete(struct btdev *btdev,
-					const uint8_t *bdaddr, uint8_t status)
+					const uint8_t *bdaddr, uint8_t bdaddr_type,
+					uint8_t status)
 {
 	char buf[1 + sizeof(struct bt_hci_evt_le_conn_complete)];
 	struct bt_hci_evt_le_conn_complete *cc = (void *) &buf[1];
@@ -991,13 +1015,18 @@
 	buf[0] = BT_HCI_EVT_LE_CONN_COMPLETE;
 
 	if (!status) {
-		struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+		struct btdev *remote = find_btdev_by_bdaddr_type(bdaddr,
+								bdaddr_type);
 
 		btdev->conn = remote;
 		remote->conn = btdev;
 
 		cc->status = status;
-		memcpy(cc->peer_addr, btdev->bdaddr, 6);
+		cc->peer_addr_type = btdev->le_scan_own_addr_type;
+		if (cc->peer_addr_type == 0x01)
+			memcpy(cc->peer_addr, btdev->random_addr, 6);
+		else
+			memcpy(cc->peer_addr, btdev->bdaddr, 6);
 
 		cc->role = 0x01;
 		cc->handle = cpu_to_le16(42);
@@ -1008,6 +1037,7 @@
 	}
 
 	cc->status = status;
+	cc->peer_addr_type = bdaddr_type;
 	memcpy(cc->peer_addr, bdaddr, 6);
 	cc->role = 0x00;
 
@@ -1050,14 +1080,15 @@
 	return btdev->le_adv_type != 0x03;
 }
 
-static void le_conn_request(struct btdev *btdev, const uint8_t *bdaddr)
+static void le_conn_request(struct btdev *btdev, const uint8_t *bdaddr,
+							uint8_t bdaddr_type)
 {
-	struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+	struct btdev *remote = find_btdev_by_bdaddr_type(bdaddr, bdaddr_type);
 
 	if (remote && adv_connectable(remote) && adv_match(btdev, remote))
-		le_conn_complete(btdev, bdaddr, 0);
+		le_conn_complete(btdev, bdaddr, bdaddr_type, 0);
 	else
-		le_conn_complete(btdev, bdaddr,
+		le_conn_complete(btdev, bdaddr, bdaddr_type,
 					BT_HCI_ERR_CONN_FAILED_TO_ESTABLISH);
 }
 
@@ -1763,6 +1794,8 @@
 	const struct bt_hci_cmd_le_start_encrypt *lse;
 	const struct bt_hci_cmd_le_ltk_req_reply *llrr;
 	const struct bt_hci_cmd_read_local_amp_assoc *rlaa_cmd;
+	const struct bt_hci_cmd_read_rssi *rrssi;
+	const struct bt_hci_cmd_read_tx_power *rtxp;
 	struct bt_hci_rsp_read_default_link_policy rdlp;
 	struct bt_hci_rsp_read_stored_link_key rslk;
 	struct bt_hci_rsp_write_stored_link_key wslk;
@@ -1813,6 +1846,8 @@
 	struct bt_hci_rsp_pin_code_request_neg_reply pcrnr_rsp;
 	struct bt_hci_rsp_user_confirm_request_reply ucrr_rsp;
 	struct bt_hci_rsp_user_confirm_request_neg_reply ucrnr_rsp;
+	struct bt_hci_rsp_read_rssi rrssi_rsp;
+	struct bt_hci_rsp_read_tx_power rtxp_rsp;
 	uint8_t status, page;
 
 	switch (opcode) {
@@ -2465,6 +2500,39 @@
 		cmd_complete(btdev, opcode, &rdbs, sizeof(rdbs));
 		break;
 
+	case BT_HCI_CMD_READ_RSSI:
+		rrssi = data;
+
+		rrssi_rsp.status = BT_HCI_ERR_SUCCESS;
+		rrssi_rsp.handle = rrssi->handle;
+		rrssi_rsp.rssi = -1; /* non-zero so we can see it in tester */
+		cmd_complete(btdev, opcode, &rrssi_rsp, sizeof(rrssi_rsp));
+		break;
+
+	case BT_HCI_CMD_READ_TX_POWER:
+		rtxp = data;
+
+		switch (rtxp->type) {
+		case 0x00:
+			rtxp_rsp.status = BT_HCI_ERR_SUCCESS;
+			rtxp_rsp.level =  -1; /* non-zero */
+			break;
+
+		case 0x01:
+			rtxp_rsp.status = BT_HCI_ERR_SUCCESS;
+			rtxp_rsp.level = 4; /* max for class 2 radio */
+			break;
+
+		default:
+			rtxp_rsp.level = 0;
+			rtxp_rsp.status = BT_HCI_ERR_INVALID_PARAMETERS;
+			break;
+		}
+
+		rtxp_rsp.handle = rtxp->handle;
+		cmd_complete(btdev, opcode, &rtxp_rsp, sizeof(rtxp_rsp));
+		break;
+
 	case BT_HCI_CMD_READ_LOCAL_AMP_INFO:
 		if (btdev->type != BTDEV_TYPE_AMP)
 			goto unsupported;
@@ -2907,7 +2975,7 @@
 			return;
 		lecc = data;
 		btdev->le_scan_own_addr_type = lecc->own_addr_type;
-		le_conn_request(btdev, lecc->peer_addr);
+		le_conn_request(btdev, lecc->peer_addr, lecc->peer_addr_type);
 		break;
 	}
 }
diff --git a/lib/bluetooth.c b/lib/bluetooth.c
index 406b508..8ee6fb4 100644
--- a/lib/bluetooth.c
+++ b/lib/bluetooth.c
@@ -336,7 +336,7 @@
 	case 36:
 		return "Alcatel";
 	case 37:
-		return "Philips Semiconductors";
+		return "NXP Semiconductors (formerly Philips Semiconductors)";
 	case 38:
 		return "C Technologies";
 	case 39:
@@ -542,7 +542,7 @@
 	case 139:
 		return "Topcorn Positioning Systems, LLC";
 	case 140:
-		return "Qualcomm Retail Solutions, Inc. (formerly Qualcomm Labs, Inc.)";
+		return "Gimbal Inc. (formerly Qualcomm Labs, Inc. and Qualcomm Retail Solutions, Inc.)";
 	case 141:
 		return "Zscan Software";
 	case 142:
@@ -928,11 +928,35 @@
 	case 332:
 		return "Mesh-Net Ltd";
 	case 333:
-		return "HUIZHOU DESAY SV AUTOMOTIVE CO., LTD.";
+		return "Huizhou Desay SV Automotive CO., LTD.";
 	case 334:
 		return "Tangerine, Inc.";
 	case 335:
 		return "B&W Group Ltd.";
+	case 336:
+		return "Pioneer Corporation";
+	case 337:
+		return "OnBeep";
+	case 338:
+		return "Vernier Software & Technology";
+	case 339:
+		return "ROL Ergo";
+	case 340:
+		return "Pebble Technology";
+	case 341:
+		return "NETATMO";
+	case 342:
+		return "Accumulate AB";
+	case 343:
+		return "Anhui Huami Information Technology Co., Ltd.";
+	case 344:
+		return "Inmite s.r.o.";
+	case 345:
+		return "ChefSteps, Inc.";
+	case 346:
+		return "micus AG";
+	case 347:
+		return "Biomedical Research Ltd.";
 	case 65535:
 		return "internal use";
 	default:
diff --git a/lib/sdp.h b/lib/sdp.h
index 34adf9a..cc10e9f 100644
--- a/lib/sdp.h
+++ b/lib/sdp.h
@@ -134,6 +134,8 @@
 #define MAP_SVCLASS_ID			0x1134
 #define GNSS_SVCLASS_ID			0x1135
 #define GNSS_SERVER_SVCLASS_ID		0x1136
+#define MPS_SC_SVCLASS_ID		0x113A
+#define MPS_SVCLASS_ID			0x113B
 #define PNP_INFO_SVCLASS_ID		0x1200
 #define GENERIC_NETWORKING_SVCLASS_ID	0x1201
 #define GENERIC_FILETRANS_SVCLASS_ID	0x1202
@@ -228,6 +230,7 @@
 #define GENERIC_ACCESS_PROFILE_ID	GENERIC_ACCESS_SVCLASS_ID
 #define GENERIC_ATTRIB_PROFILE_ID	GENERIC_ATTRIB_SVCLASS_ID
 #define APPLE_AGENT_PROFILE_ID		APPLE_AGENT_SVCLASS_ID
+#define MPS_PROFILE_ID			MPS_SC_SVCLASS_ID
 
 /*
  * Compatibility macros for the old MDP acronym
@@ -270,6 +273,10 @@
 #define SDP_ATTR_GOEP_L2CAP_PSM			0x0200
 #define SDP_ATTR_SVCDB_STATE			0x0201
 
+#define SDP_ATTR_MPSD_SCENARIOS			0x0200
+#define SDP_ATTR_MPMD_SCENARIOS			0x0201
+#define SDP_ATTR_MPS_DEPENDENCIES		0x0202
+
 #define SDP_ATTR_SERVICE_VERSION		0x0300
 #define SDP_ATTR_EXTERNAL_NETWORK		0x0301
 #define SDP_ATTR_SUPPORTED_DATA_STORES_LIST	0x0301
diff --git a/monitor/control.c b/monitor/control.c
index 8197813..c808a84 100644
--- a/monitor/control.c
+++ b/monitor/control.c
@@ -345,7 +345,7 @@
 
 	ba2str(&ev->addr.bdaddr, str);
 
-	printf("@ PIN User Passkey Request: %s (%d)\n", str, ev->addr.type);
+	printf("@ User Passkey Request: %s (%d)\n", str, ev->addr.type);
 
 	buf += sizeof(*ev);
 	len -= sizeof(*ev);
diff --git a/monitor/sdp.c b/monitor/sdp.c
index a0ab314..8acc8bd 100644
--- a/monitor/sdp.c
+++ b/monitor/sdp.c
@@ -737,13 +737,13 @@
 	print_indent(6, pdu_color, "SDP: ", pdu_str, COLOR_OFF,
 				" (0x%2.2x) tid %d len %d", pdu, tid, plen);
 
-	if (!sdp_data || !sdp_data->func) {
+	tid_info = get_tid(tid, channel);
+
+	if (!sdp_data || !sdp_data->func || !tid_info) {
 		packet_hexdump(frame->data + 5, frame->size - 5);
 		return;
 	}
 
-	tid_info = get_tid(tid, channel);
-
 	l2cap_frame_pull(&sdp_frame, frame, 5);
 	sdp_data->func(&sdp_frame, tid_info);
 }
diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
index 1b7bb30..b09404a 100644
--- a/plugins/sixaxis.c
+++ b/plugins/sixaxis.c
@@ -44,6 +44,7 @@
 #include "src/device.h"
 #include "src/plugin.h"
 #include "src/log.h"
+#include "src/shared/util.h"
 
 static const struct {
 	const char *name;
@@ -61,6 +62,17 @@
 	},
 };
 
+struct leds_data {
+	char *syspath_prefix;
+	uint8_t bitmap;
+};
+
+static void leds_data_destroy(struct leds_data *data)
+{
+	free(data->syspath_prefix);
+	free(data);
+}
+
 static struct udev *ctx = NULL;
 static struct udev_monitor *monitor = NULL;
 static guint watch_id = 0;
@@ -178,23 +190,58 @@
 		error("sixaxis: failed to set LEDS (%d bytes written)", ret);
 }
 
+static bool set_leds_sysfs(struct leds_data *data)
+{
+	int i;
+
+	if (!data->syspath_prefix)
+		return false;
+
+	/* start from 1, LED0 is never used */
+	for (i = 1; i <= 4; i++) {
+		char path[PATH_MAX] = { 0 };
+		char buf[2] = { 0 };
+		int fd;
+		int ret;
+
+		snprintf(path, PATH_MAX, "%s%d/brightness",
+						data->syspath_prefix, i);
+
+		fd = open(path, O_WRONLY);
+		if (fd < 0) {
+			error("sixaxis: cannot open %s (%s)", path,
+							strerror(errno));
+			return false;
+		}
+
+		buf[0] = '0' + !!(data->bitmap & (1 << i));
+		ret = write(fd, buf, sizeof(buf));
+		close(fd);
+		if (ret != sizeof(buf))
+			return false;
+	}
+
+	return true;
+}
+
 static gboolean setup_leds(GIOChannel *channel, GIOCondition cond,
 							gpointer user_data)
 {
-	int number = GPOINTER_TO_INT(user_data);
-	uint8_t bitmap;
-	int fd;
+	struct leds_data *data = user_data;
 
-	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+	if (!data)
 		return FALSE;
 
-	DBG("number %d", number);
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+		goto out;
 
-	fd = g_io_channel_unix_get_fd(channel);
+	if(!set_leds_sysfs(data)) {
+		int fd = g_io_channel_unix_get_fd(channel);
+		set_leds_hidraw(fd, data->bitmap);
+	}
 
-	bitmap = calc_leds_bitmap(number);
-	if (bitmap != 0)
-		set_leds_hidraw(fd, bitmap);
+out:
+	leds_data_destroy(data);
 
 	return FALSE;
 }
@@ -257,15 +304,24 @@
 	struct udev_enumerate *enumerate;
 	struct udev_device *hid_parent;
 	const char *hidraw_node;
-	const char *hid_phys;
+	const char *hid_id;
 	int number = 0;
 
 	hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
 								"hid", NULL);
 
-	hid_phys = udev_device_get_property_value(hid_parent, "HID_PHYS");
+	/*
+	 * Look for HID_UNIQ first for the correct behavior via BT, if
+	 * HID_UNIQ is not available it means the USB bus is being used and we
+	 * can rely on HID_PHYS.
+	 */
+	hid_id = udev_device_get_property_value(hid_parent, "HID_UNIQ");
+	if (!hid_id)
+		hid_id = udev_device_get_property_value(hid_parent,
+							"HID_PHYS");
+
 	hidraw_node = udev_device_get_devnode(udevice);
-	if (!hid_phys || !hidraw_node)
+	if (!hid_id || !hidraw_node)
 		return 0;
 
 	enumerate = udev_enumerate_new(udev_device_get_udev(udevice));
@@ -276,7 +332,7 @@
 	udev_list_entry_foreach(dev_list_entry, devices) {
 		struct udev_device *input_parent;
 		struct udev_device *js_dev;
-		const char *input_phys;
+		const char *input_id;
 		const char *devname;
 
 		devname = udev_list_entry_get_name(dev_list_entry);
@@ -291,12 +347,20 @@
 
 		/* check if this is the joystick relative to the hidraw device
 		 * above */
-		input_phys = udev_device_get_sysattr_value(input_parent,
-									"phys");
-		if (!input_phys)
+		input_id = udev_device_get_sysattr_value(input_parent, "uniq");
+
+		/*
+		 * A strlen() check is needed because input device over USB
+		 * have the UNIQ attribute defined but with an empty value.
+		 */
+		if (!input_id || strlen(input_id) == 0)
+			input_id = udev_device_get_sysattr_value(input_parent,
+								 "phys");
+
+		if (!input_id)
 			goto next;
 
-		if (!strcmp(input_phys, hid_phys)) {
+		if (!strcmp(input_id, hid_id)) {
 			number = atoi(udev_device_get_sysnum(js_dev));
 
 			/* joystick numbers start from 0, leds from 1 */
@@ -314,6 +378,72 @@
 	return number;
 }
 
+static char *get_leds_syspath_prefix(struct udev_device *udevice)
+{
+	struct udev_list_entry *dev_list_entry;
+	struct udev_enumerate *enumerate;
+	struct udev_device *hid_parent;
+	const char *syspath;
+	char *syspath_prefix;
+
+	hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
+								"hid", NULL);
+
+	enumerate = udev_enumerate_new(udev_device_get_udev(udevice));
+	udev_enumerate_add_match_parent(enumerate, hid_parent);
+	udev_enumerate_add_match_subsystem(enumerate, "leds");
+	udev_enumerate_scan_devices(enumerate);
+
+	dev_list_entry = udev_enumerate_get_list_entry(enumerate);
+	if (!dev_list_entry) {
+		syspath_prefix = NULL;
+		goto out;
+	}
+
+	syspath = udev_list_entry_get_name(dev_list_entry);
+
+	/*
+	 * All the sysfs paths of the LEDs have the same structure, just the
+	 * number changes, so strip it and store only the common prefix.
+	 *
+	 * Subtracting 1 here means assuming that the LED number is a single
+	 * digit, this is safe as the kernel driver only exposes 4 LEDs.
+	 */
+	syspath_prefix = strndup(syspath, strlen(syspath) - 1);
+
+out:
+	udev_enumerate_unref(enumerate);
+
+	return syspath_prefix;
+}
+
+static struct leds_data *get_leds_data(struct udev_device *udevice)
+{
+	struct leds_data *data;
+	int number;
+
+	number = get_js_number(udevice);
+	DBG("number %d", number);
+
+	data = malloc0(sizeof(*data));
+	if (!data)
+		return NULL;
+
+	data->bitmap = calc_leds_bitmap(number);
+	if (data->bitmap == 0) {
+		leds_data_destroy(data);
+		return NULL;
+	}
+
+	/*
+	 * It's OK if this fails, set_leds_hidraw() will be used in
+	 * case data->syspath_prefix is NULL.
+	 */
+	data->syspath_prefix = get_leds_syspath_prefix(udevice);
+
+	return data;
+}
+
 static int get_supported_device(struct udev_device *udevice, uint16_t *bus)
 {
 	struct udev_device *hid_parent;
@@ -374,8 +504,7 @@
 	case BUS_BLUETOOTH:
 		/* wait for events before setting leds */
 		g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-				setup_leds,
-				GINT_TO_POINTER(get_js_number(udevice)));
+				setup_leds, get_leds_data(udevice));
 
 		break;
 	default:
diff --git a/profiles/audio/a2dp-codecs.h b/profiles/audio/a2dp-codecs.h
index 3dc31cb..4d2584d 100644
--- a/profiles/audio/a2dp-codecs.h
+++ b/profiles/audio/a2dp-codecs.h
@@ -85,6 +85,67 @@
 #define MPEG_BIT_RATE_32000		0x0002
 #define MPEG_BIT_RATE_FREE		0x0001
 
+#define AAC_OBJECT_TYPE_MPEG2_AAC_LC	0x80
+#define AAC_OBJECT_TYPE_MPEG4_AAC_LC	0x40
+#define AAC_OBJECT_TYPE_MPEG4_AAC_LTP	0x20
+#define AAC_OBJECT_TYPE_MPEG4_AAC_SCA	0x10
+
+#define AAC_SAMPLING_FREQ_8000		0x0800
+#define AAC_SAMPLING_FREQ_11025		0x0400
+#define AAC_SAMPLING_FREQ_12000		0x0200
+#define AAC_SAMPLING_FREQ_16000		0x0100
+#define AAC_SAMPLING_FREQ_22050		0x0080
+#define AAC_SAMPLING_FREQ_24000		0x0040
+#define AAC_SAMPLING_FREQ_32000		0x0020
+#define AAC_SAMPLING_FREQ_44100		0x0010
+#define AAC_SAMPLING_FREQ_48000		0x0008
+#define AAC_SAMPLING_FREQ_64000		0x0004
+#define AAC_SAMPLING_FREQ_88200		0x0002
+#define AAC_SAMPLING_FREQ_96000		0x0001
+
+#define AAC_CHANNELS_1			0x02
+#define AAC_CHANNELS_2			0x01
+
+#define AAC_GET_BITRATE(a) ((a).bitrate1 << 16 | \
+					(a).bitrate2 << 8 | (a).bitrate3)
+#define AAC_GET_FREQUENCY(a) ((a).frequency1 << 4 | (a).frequency2)
+
+#define AAC_SET_BITRATE(a, b) \
+	do { \
+		(a).bitrate1 = (b >> 16) & 0x7f; \
+		(a).bitrate2 = (b >> 8) & 0xff; \
+		(a).bitrate3 = b & 0xff; \
+	} while (0)
+#define AAC_SET_FREQUENCY(a, f) \
+	do { \
+		(a).frequency1 = (f >> 4) & 0xff; \
+		(a).frequency2 = f & 0x0f; \
+	} while (0)
+
+#define AAC_INIT_BITRATE(b) \
+	.bitrate1 = (b >> 16) & 0x7f, \
+	.bitrate2 = (b >> 8) & 0xff, \
+	.bitrate3 = b & 0xff,
+#define AAC_INIT_FREQUENCY(f) \
+	.frequency1 = (f >> 4) & 0xff, \
+	.frequency2 = f & 0x0f,
+
+#define APTX_VENDOR_ID			0x0000004f
+#define APTX_CODEC_ID			0x0001
+
+#define APTX_CHANNEL_MODE_MONO		0x01
+#define APTX_CHANNEL_MODE_STEREO	0x02
+
+#define APTX_SAMPLING_FREQ_16000	0x08
+#define APTX_SAMPLING_FREQ_32000	0x04
+#define APTX_SAMPLING_FREQ_44100	0x02
+#define APTX_SAMPLING_FREQ_48000	0x01
+
+typedef struct {
+	uint32_t vendor_id;
+	uint16_t codec_id;
+} __attribute__ ((packed)) a2dp_vendor_codec_t;
+
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 
 typedef struct {
@@ -107,6 +168,24 @@
 	uint16_t bitrate;
 } __attribute__ ((packed)) a2dp_mpeg_t;
 
+typedef struct {
+	uint8_t object_type;
+	uint8_t frequency1;
+	uint8_t rfa:2;
+	uint8_t channels:2;
+	uint8_t frequency2:4;
+	uint8_t bitrate1:7;
+	uint8_t vbr:1;
+	uint8_t bitrate2;
+	uint8_t bitrate3;
+} __attribute__ ((packed)) a2dp_aac_t;
+
+typedef struct {
+	a2dp_vendor_codec_t info;
+	uint8_t channel_mode:4;
+	uint8_t frequency:4;
+} __attribute__ ((packed)) a2dp_aptx_t;
+
 #elif __BYTE_ORDER == __BIG_ENDIAN
 
 typedef struct {
@@ -129,11 +208,24 @@
 	uint16_t bitrate;
 } __attribute__ ((packed)) a2dp_mpeg_t;
 
+typedef struct {
+	uint8_t object_type;
+	uint8_t frequency1;
+	uint8_t frequency2:4;
+	uint8_t channels:2;
+	uint8_t rfa:2;
+	uint8_t vbr:1;
+	uint8_t bitrate1:7;
+	uint8_t bitrate2;
+	uint8_t bitrate3;
+} __attribute__ ((packed)) a2dp_aac_t;
+
+typedef struct {
+	a2dp_vendor_codec_t info;
+	uint8_t frequency:4;
+	uint8_t channel_mode:4;
+} __attribute__ ((packed)) a2dp_aptx_t;
+
 #else
 #error "Unknown byte order"
 #endif
-
-typedef struct {
-	uint8_t vendor_id[4];
-	uint8_t codec_id[2];
-} __attribute__ ((packed)) a2dp_vendor_codec_t;
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index cabdd66..c9dac9a 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -1390,18 +1390,14 @@
 
 	local_codec = (a2dp_vendor_codec_t *) capabilities;
 
-	if (memcmp(remote_codec->vendor_id, local_codec->vendor_id,
-					sizeof(local_codec->vendor_id)))
+	if (btohl(remote_codec->vendor_id) != btohl(local_codec->vendor_id))
 		return FALSE;
 
-	if (memcmp(remote_codec->codec_id, local_codec->codec_id,
-					sizeof(local_codec->codec_id)))
+	if (btohs(remote_codec->codec_id) != btohs(local_codec->codec_id))
 		return FALSE;
 
-	DBG("vendor 0x%02x%02x%02x%02x codec 0x%02x%02x",
-			remote_codec->vendor_id[0], remote_codec->vendor_id[1],
-			remote_codec->vendor_id[2], remote_codec->vendor_id[3],
-			remote_codec->codec_id[0], remote_codec->codec_id[1]);
+	DBG("vendor 0x%08x codec 0x%04x", btohl(remote_codec->vendor_id),
+						btohs(remote_codec->codec_id));
 
 	return TRUE;
 }
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index b71196a..fa5c013 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -1032,6 +1032,8 @@
 		return "Track";
 	else if (strcasecmp(value, "alltracks") == 0)
 		return "Playlist";
+	else if (strcasecmp(value, "group") == 0)
+		return "Playlist";
 
 	return NULL;
 }
@@ -1057,12 +1059,17 @@
 	const char *iface = MEDIA_PLAYER_INTERFACE;
 	DBusMessage *msg;
 	DBusMessageIter iter;
+	const char *curval;
 
 	DBG("%s = %s", key, value);
 
-	if (!g_hash_table_lookup(mp->settings, key))
+	curval = g_hash_table_lookup(mp->settings, key);
+	if (!curval)
 		return -EINVAL;
 
+	if (strcasecmp(curval, value) == 0)
+		return 0;
+
 	msg = dbus_message_new_method_call(mp->sender, mp->path,
 					DBUS_INTERFACE_PROPERTIES, "Set");
 	if (msg == NULL) {
diff --git a/profiles/audio/sink.c b/profiles/audio/sink.c
index d16af23..da8992e 100644
--- a/profiles/audio/sink.c
+++ b/profiles/audio/sink.c
@@ -229,11 +229,14 @@
 	if (err) {
 		avdtp_unref(sink->session);
 		sink->session = NULL;
-		if (avdtp_error_category(err) == AVDTP_ERRNO
-				&& avdtp_error_posix_errno(err) != EHOSTDOWN) {
-			perr = -EAGAIN;
-		} else
-			perr = -EIO;
+
+		perr = -avdtp_error_posix_errno(err);
+		if (perr != -EHOSTDOWN) {
+			if (avdtp_error_category(err) == AVDTP_ERRNO)
+				perr = -EAGAIN;
+			else
+				perr = -EIO;
+		}
 		goto failed;
 	}
 
diff --git a/profiles/audio/source.c b/profiles/audio/source.c
index 843b3e8..b0abaa3 100644
--- a/profiles/audio/source.c
+++ b/profiles/audio/source.c
@@ -229,11 +229,14 @@
 	if (err) {
 		avdtp_unref(source->session);
 		source->session = NULL;
-		if (avdtp_error_category(err) == AVDTP_ERRNO
-				&& avdtp_error_posix_errno(err) != EHOSTDOWN) {
-			perr = -EAGAIN;
-		} else
-			perr = -EIO;
+
+		perr = -avdtp_error_posix_errno(err);
+		if (perr != -EHOSTDOWN) {
+			if (avdtp_error_category(err) == AVDTP_ERRNO)
+				perr = -EAGAIN;
+			else
+				perr = -EIO;
+		}
 		goto failed;
 	}
 
diff --git a/profiles/gatt/gas.c b/profiles/gatt/gas.c
index d240450..b51b4a8 100644
--- a/profiles/gatt/gas.c
+++ b/profiles/gatt/gas.c
@@ -329,6 +329,11 @@
 		DBG("MTU Exchange: Requesting %d", imtu);
 	}
 
+	if (gerr) {
+		error("Could not acquire att imtu and cid: %s", gerr->message);
+		g_error_free(gerr);
+	}
+
 	if (device_get_appearance(gas->device, &app) < 0) {
 		bt_uuid_t uuid;
 
diff --git a/profiles/health/hdp.c b/profiles/health/hdp.c
index 48dad52..4f90380 100644
--- a/profiles/health/hdp.c
+++ b/profiles/health/hdp.c
@@ -747,8 +747,11 @@
 {
 	struct hdp_channel *hdp_chann;
 
-	if (dev == NULL)
+	if (dev == NULL) {
+		g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+					"HDP device uninitialized");
 		return NULL;
+	}
 
 	hdp_chann = g_new0(struct hdp_channel, 1);
 	hdp_chann->config = config;
@@ -860,7 +863,10 @@
 	chan->edata->echo_done = TRUE;
 
 	fd = g_io_channel_unix_get_fd(io_chan);
+
 	len = read(fd, buf, sizeof(buf));
+	if (len < 0)
+		goto fail;
 
 	if (send_echo_data(fd, buf, len)  >= 0)
 		return TRUE;
@@ -930,7 +936,8 @@
 	struct hdp_device *dev = data;
 	struct hdp_channel *chan;
 
-	DBG("hdp_mcap_mdl_connected_cb");
+	DBG("");
+
 	if (dev->ndc == NULL)
 		return;
 
@@ -1501,8 +1508,8 @@
 	}
 
 	fd = g_io_channel_unix_get_fd(io_chan);
-	len = read(fd, buf, sizeof(buf));
 
+	len = read(fd, buf, sizeof(buf));
 	if (len != HDP_ECHO_LEN) {
 		value = FALSE;
 		goto end;
diff --git a/profiles/health/hdp_util.c b/profiles/health/hdp_util.c
index 58770b5..47c464e 100644
--- a/profiles/health/hdp_util.c
+++ b/profiles/health/hdp_util.c
@@ -652,6 +652,8 @@
 		return FALSE;
 	}
 
+	sdp_list_free(sup_features, free_hdp_list);
+
 	return TRUE;
 }
 
diff --git a/profiles/input/device.c b/profiles/input/device.c
index ccde452..e37e7b9 100644
--- a/profiles/input/device.c
+++ b/profiles/input/device.c
@@ -52,15 +52,13 @@
 #include "src/dbus-common.h"
 #include "src/error.h"
 #include "src/sdp-client.h"
+#include "src/shared/uhid.h"
 
 #include "device.h"
 #include "hidp_defs.h"
-#include "uhid_copy.h"
 
 #define INPUT_INTERFACE "org.bluez.Input1"
 
-#define UHID_DEVICE_FILE "/dev/uhid"
-
 enum reconnect_mode_t {
 	RECONNECT_NONE = 0,
 	RECONNECT_DEVICE,
@@ -81,14 +79,11 @@
 	guint			intr_watch;
 	guint			sec_watch;
 	struct hidp_connadd_req *req;
-	guint			dc_id;
 	bool			disable_sdp;
 	enum reconnect_mode_t	reconnect_mode;
 	guint			reconnect_timer;
 	uint32_t		reconnect_attempt;
-	bool			uhid_enabled;
-	int			uhid_fd;
-	guint			uhid_watch;
+	struct bt_uhid		*uhid;
 	bool			uhid_created;
 	uint8_t			report_req_pending;
 	guint			report_req_timer;
@@ -119,9 +114,7 @@
 
 static void input_device_free(struct input_device *idev)
 {
-	if (idev->dc_id)
-		device_remove_disconnect_watch(idev->device, idev->dc_id);
-
+	bt_uhid_unref(idev->uhid);
 	btd_service_unref(idev->service);
 	btd_device_unref(idev->device);
 	g_free(idev->path);
@@ -204,7 +197,7 @@
 					uint32_t id, uint16_t err)
 {
 	struct uhid_event ev;
-	ssize_t len;
+	int ret;
 
 	if (data == NULL)
 		size = 0;
@@ -226,20 +219,13 @@
 	if (size > 0)
 		memcpy(ev.u.feature_answer.data, data, size);
 
-	len = write(idev->uhid_fd, &ev, sizeof(ev));
-	if (len < 0) {
-		error("uHID dev write error: %s (%d)", strerror(errno), errno);
+	ret = bt_uhid_send(idev->uhid, &ev);
+	if (ret < 0) {
+		error("bt_uhid_send: %s (%d)", strerror(-ret), -ret);
 		return false;
 	}
 
-	/* uHID kernel driver does not handle partial writes */
-	if ((size_t) len < sizeof(ev)) {
-		error("uHID dev write error: partial write (%zd of %zu bytes)",
-							len, sizeof(ev));
-		return false;
-	}
-
-	DBG("HID report (%zu bytes) -> uHID fd %d", size, idev->uhid_fd);
+	DBG("HID report (%zu bytes)", size);
 
 	return true;
 }
@@ -248,7 +234,7 @@
 					const uint8_t *data, size_t size)
 {
 	struct uhid_event ev;
-	ssize_t len;
+	int err;
 
 	if (data == NULL)
 		size = 0;
@@ -268,20 +254,13 @@
 	if (size > 0)
 		memcpy(ev.u.input.data, data, size);
 
-	len = write(idev->uhid_fd, &ev, sizeof(ev));
-	if (len < 0) {
-		error("uHID dev write error: %s (%d)", strerror(errno), errno);
+	err = bt_uhid_send(idev->uhid, &ev);
+	if (err < 0) {
+		error("bt_uhid_send: %s (%d)", strerror(-err), -err);
 		return false;
 	}
 
-	/* uHID kernel driver does not handle partial writes */
-	if ((size_t) len < sizeof(ev)) {
-		error("uHID dev write error: partial write (%zd of %zu bytes)",
-							len, sizeof(ev));
-		return false;
-	}
-
-	DBG("HID report (%zu bytes) -> uHID fd %d", size, idev->uhid_fd);
+	DBG("HID report (%zu bytes)", size);
 
 	return true;
 }
@@ -342,9 +321,6 @@
 	if ((cond & (G_IO_HUP | G_IO_ERR)) && idev->ctrl_watch)
 		g_io_channel_shutdown(chan, TRUE, NULL);
 
-	device_remove_disconnect_watch(idev->device, idev->dc_id);
-	idev->dc_id = 0;
-
 	idev->intr_watch = 0;
 
 	if (idev->intr_io) {
@@ -586,9 +562,9 @@
 	return FALSE;
 }
 
-static void hidp_send_set_report(struct input_device *idev,
-							struct uhid_event *ev)
+static void hidp_send_set_report(struct uhid_event *ev, void *user_data)
 {
+	struct input_device *idev = user_data;
 	uint8_t hdr;
 	bool sent;
 
@@ -624,9 +600,9 @@
 	}
 }
 
-static void hidp_send_get_report(struct input_device *idev,
-							struct uhid_event *ev)
+static void hidp_send_get_report(struct uhid_event *ev, void *user_data)
 {
+	struct input_device *idev = user_data;
 	uint8_t hdr;
 	bool sent;
 
@@ -666,91 +642,6 @@
 	}
 }
 
-static gboolean uhid_watch_cb(GIOChannel *chan, GIOCondition cond,
-							gpointer user_data)
-{
-	struct input_device *idev = user_data;
-	int fd;
-	ssize_t len;
-	struct uhid_event ev;
-
-	if (cond & (G_IO_ERR | G_IO_NVAL))
-		goto failed;
-
-	fd = g_io_channel_unix_get_fd(chan);
-	memset(&ev, 0, sizeof(ev));
-
-	len = read(fd, &ev, sizeof(ev));
-	if (len < 0) {
-		error("uHID dev read error: %s (%d)", strerror(errno), errno);
-		goto failed;
-	}
-
-	if ((size_t) len < sizeof(ev.type)) {
-		error("uHID dev read returned too few bytes");
-		goto failed;
-	}
-
-	DBG("uHID event type %u received (%zd bytes)", ev.type, len);
-
-	switch (ev.type) {
-	case UHID_START:
-	case UHID_STOP:
-		/* These are called to start and stop the underlying hardware.
-		 * For HID we open the channels before creating the device so
-		 * the hardware is always ready. No need to handle these.
-		 * Note that these are also called when the kernel switches
-		 * between device-drivers loaded on the HID device. But we can
-		 * simply keep the hardware alive during transitions and it
-		 * works just fine.
-		 * The kernel never destroys a device itself! Only an explicit
-		 * UHID_DESTROY request can remove a device.
-		 */
-		break;
-	case UHID_OPEN:
-	case UHID_CLOSE:
-		/* OPEN/CLOSE are sent whenever user-space opens any interface
-		 * provided by the kernel HID device. Whenever the open-count
-		 * is non-zero we must be ready for I/O. As long as it is zero,
-		 * we can decide to drop all I/O and put the device
-		 * asleep This is optional, though. Moreover, some
-		 * special device drivers are buggy in that regard, so
-		 * maybe we just keep I/O always awake like HIDP in the
-		 * kernel does.
-		 */
-		break;
-	case UHID_OUTPUT:
-		hidp_send_set_report(idev, &ev);
-		break;
-	case UHID_FEATURE:
-		hidp_send_get_report(idev, &ev);
-		break;
-	case UHID_OUTPUT_EV:
-		/* This is only sent by kernels prior to linux-3.11. It
-		 * requires us to parse HID-descriptors in user-space to
-		 * properly handle it. This is redundant as the kernel
-		 * does it already. That's why newer kernels assemble
-		 * the output-reports and send it to us via UHID_OUTPUT.
-		 * We never implemented this, so we rely on users to use
-		 * recent-enough kernels if they want this feature. No reason
-		 * to implement this for older kernels.
-		 */
-		DBG("Unsupported uHID output event: type %u code %u value %d",
-				ev.u.output_ev.type, ev.u.output_ev.code,
-				ev.u.output_ev.value);
-		break;
-	default:
-		warn("unexpected uHID event");
-		break;
-	}
-
-	return TRUE;
-
-failed:
-	idev->uhid_watch = 0;
-	return FALSE;
-}
-
 static void epox_endian_quirk(unsigned char *data, int size)
 {
 	/* USAGE_PAGE (Keyboard)	05 07
@@ -955,33 +846,38 @@
 
 static int uhid_connadd(struct input_device *idev, struct hidp_connadd_req *req)
 {
-	int err = 0;
+	int err;
 	struct uhid_event ev;
 
-	if (!idev->uhid_created) {
-		/* create uHID device */
-		memset(&ev, 0, sizeof(ev));
-		ev.type = UHID_CREATE;
-		strncpy((char *) ev.u.create.name, req->name,
-						sizeof(ev.u.create.name) - 1);
-		ba2str(&idev->src, (char *) ev.u.create.phys);
-		ba2str(&idev->dst, (char *) ev.u.create.uniq);
-		ev.u.create.vendor = req->vendor;
-		ev.u.create.product = req->product;
-		ev.u.create.version = req->version;
-		ev.u.create.country = req->country;
-		ev.u.create.bus = BUS_BLUETOOTH;
-		ev.u.create.rd_data = req->rd_data;
-		ev.u.create.rd_size = req->rd_size;
+	if (idev->uhid_created)
+		return 0;
 
-		if (write(idev->uhid_fd, &ev, sizeof(ev)) < 0) {
-			err = -errno;
-			error("Failed to create uHID device: %s (%d)",
-							strerror(-err), -err);
-		} else
-			idev->uhid_created = true;
+	/* create uHID device */
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_CREATE;
+	strncpy((char *) ev.u.create.name, req->name,
+						sizeof(ev.u.create.name) - 1);
+	ba2str(&idev->src, (char *) ev.u.create.phys);
+	ba2str(&idev->dst, (char *) ev.u.create.uniq);
+	ev.u.create.vendor = req->vendor;
+	ev.u.create.product = req->product;
+	ev.u.create.version = req->version;
+	ev.u.create.country = req->country;
+	ev.u.create.bus = BUS_BLUETOOTH;
+	ev.u.create.rd_data = req->rd_data;
+	ev.u.create.rd_size = req->rd_size;
+
+	err = bt_uhid_send(idev->uhid, &ev);
+	if (err < 0) {
+		error("bt_uhid_send: %s", strerror(-err));
+		return err;
 	}
 
+	bt_uhid_register(idev->uhid, UHID_OUTPUT, hidp_send_set_report, idev);
+	bt_uhid_register(idev->uhid, UHID_FEATURE, hidp_send_get_report, idev);
+
+	idev->uhid_created = true;
+
 	return err;
 }
 
@@ -993,7 +889,7 @@
 
 	DBG("");
 
-	if (idev->uhid_enabled)
+	if (idev->uhid)
 		err = uhid_connadd(idev, idev->req);
 	else
 		err = ioctl_connadd(idev->req);
@@ -1095,7 +991,7 @@
 		return 0;
 	}
 
-	if (idev->uhid_enabled)
+	if (idev->uhid)
 		err = uhid_connadd(idev, req);
 	else
 		err = ioctl_connadd(req);
@@ -1109,7 +1005,7 @@
 
 static bool is_connected(struct input_device *idev)
 {
-	if (idev->uhid_enabled)
+	if (idev->uhid)
 		return (idev->intr_io != NULL && idev->ctrl_io != NULL);
 	else
 		return ioctl_is_connected(idev);
@@ -1126,25 +1022,12 @@
 	if (idev->ctrl_io)
 		g_io_channel_shutdown(idev->ctrl_io, TRUE, NULL);
 
-	if (idev->uhid_enabled)
+	if (idev->uhid)
 		return 0;
 	else
 		return ioctl_disconnect(idev, flags);
 }
 
-static void disconnect_cb(struct btd_device *device, gboolean removal,
-				void *user_data)
-{
-	struct input_device *idev = user_data;
-	int flags;
-
-	info("Input: disconnect %s", idev->path);
-
-	flags = removal ? (1 << HIDP_VIRTUAL_CABLE_UNPLUG) : 0;
-
-	connection_disconnect(idev, flags);
-}
-
 static int input_device_connected(struct input_device *idev)
 {
 	int err;
@@ -1156,9 +1039,6 @@
 	if (err < 0)
 		return err;
 
-	idev->dc_id = device_add_disconnect_watch(idev->device, disconnect_cb,
-							idev, NULL);
-
 	btd_service_connecting_complete(idev->service, 0);
 
 	return 0;
@@ -1180,7 +1060,7 @@
 	if (err < 0)
 		goto failed;
 
-	if (idev->uhid_enabled)
+	if (idev->uhid)
 		cond |= G_IO_IN;
 
 	idev->intr_watch = g_io_add_watch(idev->intr_io, cond, intr_watch_cb,
@@ -1235,7 +1115,7 @@
 
 	idev->intr_io = io;
 
-	if (idev->uhid_enabled)
+	if (idev->uhid)
 		cond |= G_IO_IN;
 
 	idev->ctrl_watch = g_io_add_watch(idev->ctrl_io, cond, ctrl_watch_cb,
@@ -1366,13 +1246,16 @@
 int input_device_disconnect(struct btd_service *service)
 {
 	struct input_device *idev;
-	int err;
+	int err, flags;
 
 	DBG("");
 
 	idev = btd_service_get_user_data(service);
 
-	err = connection_disconnect(idev, 0);
+	flags = device_is_temporary(idev->device) ?
+					(1 << HIDP_VIRTUAL_CABLE_UNPLUG) : 0;
+
+	err = connection_disconnect(idev, flags);
 	if (err < 0)
 		return err;
 
@@ -1441,7 +1324,6 @@
 	idev->path = g_strdup(path);
 	idev->handle = rec->handle;
 	idev->disable_sdp = is_device_sdp_disable(rec);
-	idev->uhid_enabled = uhid_enabled;
 
 	/* Initialize device properties */
 	extract_hid_props(idev, rec);
@@ -1471,8 +1353,6 @@
 	struct btd_device *device = btd_service_get_device(service);
 	const char *path = device_get_path(device);
 	struct input_device *idev;
-	int err;
-	GIOChannel *io;
 
 	DBG("%s", path);
 
@@ -1480,22 +1360,13 @@
 	if (!idev)
 		return -EINVAL;
 
-	if (idev->uhid_enabled) {
-		idev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC);
-		if (idev->uhid_fd < 0) {
-			err = errno;
-			error("Failed to open uHID device: %s (%d)",
-							strerror(err), err);
+	if (uhid_enabled) {
+		idev->uhid = bt_uhid_new_default();
+		if (!idev->uhid) {
+			error("bt_uhid_new_default: failed");
 			input_device_free(idev);
-			return -err;
+			return -EIO;
 		}
-
-		io = g_io_channel_unix_new(idev->uhid_fd);
-		g_io_channel_set_encoding(io, NULL, NULL);
-		idev->uhid_watch = g_io_add_watch(io,
-						G_IO_IN | G_IO_ERR | G_IO_NVAL,
-						uhid_watch_cb, idev);
-		g_io_channel_unref(io);
 	}
 
 	if (g_dbus_register_interface(btd_get_dbus_connection(),
@@ -1535,31 +1406,12 @@
 	struct btd_device *device = btd_service_get_device(service);
 	const char *path = device_get_path(device);
 	struct input_device *idev = btd_service_get_user_data(service);
-	struct uhid_event ev;
 
 	DBG("%s", path);
 
 	g_dbus_unregister_interface(btd_get_dbus_connection(),
 						idev->path, INPUT_INTERFACE);
 
-	if (idev->uhid_enabled) {
-		if (idev->uhid_watch) {
-			g_source_remove(idev->uhid_watch);
-			idev->uhid_watch = 0;
-		}
-
-		if (idev->uhid_created) {
-			memset(&ev, 0, sizeof(ev));
-			ev.type = UHID_DESTROY;
-			if (write(idev->uhid_fd, &ev, sizeof(ev)) < 0)
-				error("Failed to destroy uHID device: %s (%d)",
-							strerror(errno), errno);
-		}
-
-		close(idev->uhid_fd);
-		idev->uhid_fd = -1;
-	}
-
 	input_device_free(idev);
 }
 
@@ -1605,7 +1457,7 @@
 	if (!idev)
 		return -ENOENT;
 
-	if (idev->uhid_enabled)
+	if (uhid_enabled)
 		cond |= G_IO_IN;
 
 	switch (psm) {
diff --git a/profiles/input/hog.c b/profiles/input/hog.c
index e82e827..3753343 100644
--- a/profiles/input/hog.c
+++ b/profiles/input/hog.c
@@ -34,7 +34,6 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
-#include "uhid_copy.h"
 
 #include <bluetooth/bluetooth.h>
 
@@ -48,6 +47,7 @@
 #include "src/profile.h"
 #include "src/service.h"
 #include "src/shared/util.h"
+#include "src/shared/uhid.h"
 
 #include "src/plugin.h"
 
@@ -72,8 +72,6 @@
 #define HOG_PROTO_MODE_BOOT    0
 #define HOG_PROTO_MODE_REPORT  1
 
-#define UHID_DEVICE_FILE	"/dev/uhid"
-
 #define HOG_REPORT_MAP_MAX_SIZE        512
 #define HID_INFO_SIZE			4
 #define ATT_NOTIFICATION_HEADER_SIZE	3
@@ -85,9 +83,8 @@
 	guint			attioid;
 	struct gatt_primary	*hog_primary;
 	GSList			*reports;
-	int			uhid_fd;
+	struct bt_uhid		*uhid;
 	gboolean		has_report_id;
-	guint			uhid_watch_id;
 	uint16_t		bcdhid;
 	uint8_t			bcountrycode;
 	uint16_t		proto_mode_handle;
@@ -112,7 +109,7 @@
 	struct hog_device *hogdev = report->hogdev;
 	struct uhid_event ev;
 	uint8_t *buf;
-	ssize_t status;
+	int err;
 
 	if (len < ATT_NOTIFICATION_HEADER_SIZE) {
 		error("Malformed ATT notification");
@@ -137,21 +134,13 @@
 		ev.u.input.size = len;
 	}
 
-	status = write(hogdev->uhid_fd, &ev, sizeof(ev));
-	if (status < 0) {
-		error("uHID dev write error: %s (%d)", strerror(errno), errno);
+	err = bt_uhid_send(hogdev->uhid, &ev);
+	if (err < 0) {
+		error("bt_uhid_send: %s (%d)", strerror(-err), -err);
 		return;
 	}
 
-	/* uHID kernel driver does not handle partial writes */
-	if ((size_t) status < sizeof(ev)) {
-		error("uHID dev write error: partial write (%zd of %zu bytes)",
-							status, sizeof(ev));
-		return;
-	}
-
-	DBG("HoG report (%u bytes) -> uHID fd %d", ev.u.input.size,
-							hogdev->uhid_fd);
+	DBG("HoG report (%u bytes)", ev.u.input.size);
 }
 
 static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
@@ -317,6 +306,72 @@
 					external_service_char_cb, hogdev);
 }
 
+static int report_type_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct report *report = a;
+	uint8_t type = GPOINTER_TO_UINT(b);
+
+	return report->type - type;
+}
+
+static void output_written_cb(guint8 status, const guint8 *pdu,
+					guint16 plen, gpointer user_data)
+{
+	if (status != 0) {
+		error("Write output report failed: %s", att_ecode2str(status));
+		return;
+	}
+}
+
+static void forward_report(struct uhid_event *ev, void *user_data)
+{
+	struct hog_device *hogdev = user_data;
+	struct report *report;
+	GSList *l;
+	void *data;
+	int size;
+	guint type;
+
+	if (hogdev->has_report_id) {
+		data = ev->u.output.data + 1;
+		size = ev->u.output.size - 1;
+	} else {
+		data = ev->u.output.data;
+		size = ev->u.output.size;
+	}
+
+	switch (ev->type) {
+	case UHID_OUTPUT:
+		type = HOG_REPORT_TYPE_OUTPUT;
+		break;
+	case UHID_FEATURE:
+		type = HOG_REPORT_TYPE_FEATURE;
+		break;
+	default:
+		return;
+	}
+
+	l = g_slist_find_custom(hogdev->reports, GUINT_TO_POINTER(type),
+							report_type_cmp);
+	if (!l)
+		return;
+
+	report = l->data;
+
+	DBG("Sending report type %d to device 0x%04X handle 0x%X", type,
+				hogdev->id, report->decl->value_handle);
+
+	if (hogdev->attrib == NULL)
+		return;
+
+	if (report->decl->properties & GATT_CHR_PROP_WRITE)
+		gatt_write_char(hogdev->attrib, report->decl->value_handle,
+				data, size, output_written_cb, hogdev);
+	else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
+		gatt_write_cmd(hogdev->attrib, report->decl->value_handle,
+						data, size, NULL, NULL);
+}
+
 static bool get_descriptor_item_info(uint8_t *buf, ssize_t blen, ssize_t *len,
 								bool *is_long)
 {
@@ -389,7 +444,7 @@
 	uint16_t vendor_src, vendor, product, version;
 	ssize_t vlen;
 	char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */
-	int i;
+	int i, err;
 
 	if (status != 0) {
 		error("Report Map read failed: %s", att_ecode2str(status));
@@ -450,8 +505,14 @@
 	ev.u.create.rd_data = value;
 	ev.u.create.rd_size = vlen;
 
-	if (write(hogdev->uhid_fd, &ev, sizeof(ev)) < 0)
-		error("Failed to create uHID device: %s", strerror(errno));
+	err = bt_uhid_send(hogdev->uhid, &ev);
+	if (err < 0) {
+		error("bt_uhid_send: %s", strerror(-err));
+		return;
+	}
+
+	bt_uhid_register(hogdev->uhid, UHID_OUTPUT, forward_report, hogdev);
+	bt_uhid_register(hogdev->uhid, UHID_FEATURE, forward_report, hogdev);
 }
 
 static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
@@ -581,148 +642,6 @@
 									hogdev);
 }
 
-static void output_written_cb(guint8 status, const guint8 *pdu,
-					guint16 plen, gpointer user_data)
-{
-	if (status != 0) {
-		error("Write output report failed: %s", att_ecode2str(status));
-		return;
-	}
-}
-
-static int report_type_cmp(gconstpointer a, gconstpointer b)
-{
-	const struct report *report = a;
-	uint8_t type = GPOINTER_TO_UINT(b);
-
-	return report->type - type;
-}
-
-static void forward_report(struct hog_device *hogdev,
-						struct uhid_event *ev)
-{
-	struct report *report;
-	GSList *l;
-	void *data;
-	int size;
-	guint type;
-
-	if (hogdev->has_report_id) {
-		data = ev->u.output.data + 1;
-		size = ev->u.output.size - 1;
-	} else {
-		data = ev->u.output.data;
-		size = ev->u.output.size;
-	}
-
-	switch (ev->type) {
-	case UHID_OUTPUT:
-		type = HOG_REPORT_TYPE_OUTPUT;
-		break;
-	case UHID_FEATURE:
-		type = HOG_REPORT_TYPE_FEATURE;
-		break;
-	default:
-		return;
-	}
-
-	l = g_slist_find_custom(hogdev->reports, GUINT_TO_POINTER(type),
-							report_type_cmp);
-	if (!l)
-		return;
-
-	report = l->data;
-
-	DBG("Sending report type %d to device 0x%04X handle 0x%X", type,
-				hogdev->id, report->decl->value_handle);
-
-	if (hogdev->attrib == NULL)
-		return;
-
-	if (report->decl->properties & GATT_CHR_PROP_WRITE)
-		gatt_write_char(hogdev->attrib, report->decl->value_handle,
-				data, size, output_written_cb, hogdev);
-	else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
-		gatt_write_cmd(hogdev->attrib, report->decl->value_handle,
-						data, size, NULL, NULL);
-}
-
-static gboolean uhid_event_cb(GIOChannel *io, GIOCondition cond,
-							gpointer user_data)
-{
-	struct hog_device *hogdev = user_data;
-	struct uhid_event ev;
-	ssize_t bread;
-	int fd;
-
-	if (cond & (G_IO_ERR | G_IO_NVAL))
-		goto failed;
-
-	fd = g_io_channel_unix_get_fd(io);
-	memset(&ev, 0, sizeof(ev));
-
-	bread = read(fd, &ev, sizeof(ev));
-	if (bread < 0) {
-		int err = -errno;
-		DBG("uhid-dev read: %s(%d)", strerror(-err), -err);
-		goto failed;
-	}
-
-	DBG("uHID event type %d received", ev.type);
-
-	switch (ev.type) {
-	case UHID_START:
-	case UHID_STOP:
-		/* These are called to start and stop the underlying hardware.
-		 * For HoG we open the channels before creating the device so
-		 * the hardware is always ready. No need to handle these.
-		 * Note that these are also called when the kernel switches
-		 * between device-drivers loaded on the HID device. But we can
-		 * simply keep the hardware alive during transitions and it
-		 * works just fine.
-		 * The kernel never destroys a device itself! Only an explicit
-		 * UHID_DESTROY request can remove a device. */
-		break;
-	case UHID_OPEN:
-	case UHID_CLOSE:
-		/* OPEN/CLOSE are sent whenever user-space opens any interface
-		 * provided by the kernel HID device. Whenever the open-count
-		 * is non-zero we must be ready for I/O. As long as it is zero,
-		 * we can decide to drop all I/O and put the device
-		 * asleep This is optional, though. Moreover, some
-		 * special device drivers are buggy in that regard, so
-		 * maybe we just keep I/O always awake like HIDP in the
-		 * kernel does. */
-		break;
-	case UHID_OUTPUT:
-	case UHID_FEATURE:
-		forward_report(hogdev, &ev);
-		break;
-	case UHID_OUTPUT_EV:
-		/* This is only sent by kernels prior to linux-3.11. It
-		 * requires us to parse HID-descriptors in user-space to
-		 * properly handle it. This is redundant as the kernel
-		 * does it already. That's why newer kernels assemble
-		 * the output-reports and send it to us via UHID_OUTPUT.
-		 * We never implemented this, so we rely on users to use
-		 * recent-enough kernels if they want this feature. No reason
-		 * to implement this for older kernels. */
-		DBG("Unsupported uHID output event: type %d code %d value %d",
-			ev.u.output_ev.type, ev.u.output_ev.code,
-			ev.u.output_ev.value);
-		break;
-	default:
-		warn("unexpected uHID event");
-		break;
-	}
-
-	return TRUE;
-
-failed:
-	hogdev->uhid_watch_id = 0;
-	return FALSE;
-}
-
 static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
 {
 	struct hog_device *hogdev = user_data;
@@ -807,27 +726,18 @@
 						struct gatt_primary *prim)
 {
 	struct hog_device *hogdev;
-	GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_NVAL;
-	GIOChannel *io;
 
 	hogdev = hog_new_device(device, prim->range.start);
 	if (!hogdev)
 		return NULL;
 
-	hogdev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC);
-	if (hogdev->uhid_fd < 0) {
-		error("Failed to open uHID device: %s(%d)", strerror(errno),
-									errno);
+	hogdev->uhid = bt_uhid_new_default();
+	if (!hogdev->uhid) {
+		error("bt_uhid_new_default: failed");
 		hog_free_device(hogdev);
 		return NULL;
 	}
 
-	io = g_io_channel_unix_new(hogdev->uhid_fd);
-	g_io_channel_set_encoding(io, NULL, NULL);
-	hogdev->uhid_watch_id = g_io_add_watch(io, cond, uhid_event_cb,
-								hogdev);
-	g_io_channel_unref(io);
-
 	hogdev->hog_primary = g_memdup(prim, sizeof(*prim));
 
 	hogdev->attioid = btd_device_add_attio_callback(device,
@@ -840,23 +750,8 @@
 
 static int hog_unregister_device(struct hog_device *hogdev)
 {
-	struct uhid_event ev;
-
 	btd_device_remove_attio_callback(hogdev->device, hogdev->attioid);
-
-	if (hogdev->uhid_watch_id) {
-		g_source_remove(hogdev->uhid_watch_id);
-		hogdev->uhid_watch_id = 0;
-	}
-
-	memset(&ev, 0, sizeof(ev));
-	ev.type = UHID_DESTROY;
-	if (write(hogdev->uhid_fd, &ev, sizeof(ev)) < 0)
-		error("Failed to destroy uHID device: %s", strerror(errno));
-
-	close(hogdev->uhid_fd);
-	hogdev->uhid_fd = -1;
-
+	bt_uhid_unref(hogdev->uhid);
 	hog_free_device(hogdev);
 
 	return 0;
diff --git a/profiles/input/server.c b/profiles/input/server.c
index 772a605..307db96 100644
--- a/profiles/input/server.c
+++ b/profiles/input/server.c
@@ -47,12 +47,17 @@
 #include "device.h"
 #include "server.h"
 
+struct confirm_data {
+	bdaddr_t dst;
+	GIOChannel *io;
+};
+
 static GSList *servers = NULL;
 struct input_server {
 	bdaddr_t src;
 	GIOChannel *ctrl;
 	GIOChannel *intr;
-	GIOChannel *confirm;
+	struct confirm_data *confirm;
 };
 
 static int server_cmp(gconstpointer s, gconstpointer user_data)
@@ -191,44 +196,36 @@
 static void auth_callback(DBusError *derr, void *user_data)
 {
 	struct input_server *server = user_data;
-	bdaddr_t src, dst;
+	struct confirm_data *confirm = server->confirm;
 	GError *err = NULL;
 
-	bt_io_get(server->confirm, &err,
-			BT_IO_OPT_SOURCE_BDADDR, &src,
-			BT_IO_OPT_DEST_BDADDR, &dst,
-			BT_IO_OPT_INVALID);
-	if (err) {
-		error("%s", err->message);
-		g_error_free(err);
-		goto reject;
-	}
-
 	if (derr) {
 		error("Access denied: %s", derr->message);
 		goto reject;
 	}
 
-	if (!input_device_exists(&src, &dst) && !dev_is_sixaxis(&src, &dst))
+	if (!input_device_exists(&server->src, &confirm->dst) &&
+				!dev_is_sixaxis(&server->src, &confirm->dst))
 		return;
 
-	if (!bt_io_accept(server->confirm, connect_event_cb, server,
-				NULL, &err)) {
+	if (!bt_io_accept(confirm->io, connect_event_cb, server, NULL, &err)) {
 		error("bt_io_accept: %s", err->message);
 		g_error_free(err);
 		goto reject;
 	}
 
-	g_io_channel_unref(server->confirm);
+	g_io_channel_unref(confirm->io);
+	g_free(server->confirm);
 	server->confirm = NULL;
 
 	return;
 
 reject:
-	g_io_channel_shutdown(server->confirm, TRUE, NULL);
-	g_io_channel_unref(server->confirm);
+	g_io_channel_shutdown(confirm->io, TRUE, NULL);
+	g_io_channel_unref(confirm->io);
 	server->confirm = NULL;
-	input_device_close_channels(&src, &dst);
+	input_device_close_channels(&server->src, &confirm->dst);
+	g_free(confirm);
 }
 
 static void confirm_event_cb(GIOChannel *chan, gpointer user_data)
@@ -248,7 +245,8 @@
 	if (err) {
 		error("%s", err->message);
 		g_error_free(err);
-		goto drop;
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
 	}
 
 	ba2str(&dst, addr);
@@ -263,7 +261,9 @@
 		goto drop;
 	}
 
-	server->confirm = g_io_channel_ref(chan);
+	server->confirm = g_new0(struct confirm_data, 1);
+	server->confirm->io = g_io_channel_ref(chan);
+	bacpy(&server->confirm->dst, &dst);
 
 	ret = btd_request_authorization(&src, &dst, HID_UUID,
 					auth_callback, server);
@@ -272,7 +272,8 @@
 
 	error("input: authorization for device %s failed", addr);
 
-	g_io_channel_unref(server->confirm);
+	g_io_channel_unref(server->confirm->io);
+	g_free(server->confirm);
 	server->confirm = NULL;
 
 drop:
diff --git a/profiles/input/uhid_copy.h b/profiles/input/uhid_copy.h
index aff1b3a..9eb11fd 100644
--- a/profiles/input/uhid_copy.h
+++ b/profiles/input/uhid_copy.h
@@ -31,7 +31,7 @@
 	UHID_OPEN,
 	UHID_CLOSE,
 	UHID_OUTPUT,
-	UHID_OUTPUT_EV,
+	UHID_OUTPUT_EV,			/* obsolete! */
 	UHID_INPUT,
 	UHID_FEATURE,
 	UHID_FEATURE_ANSWER,
@@ -90,6 +90,8 @@
 	__u8 rtype;
 } __attribute__((__packed__));
 
+/* Obsolete! Newer kernels will no longer send these events but instead convert
+ * it into raw output reports via UHID_OUTPUT. */
 struct uhid_output_ev_req {
 	__u16 type;
 	__u16 code;
diff --git a/src/device.c b/src/device.c
index 8222610..a9b644b 100644
--- a/src/device.c
+++ b/src/device.c
@@ -1220,6 +1220,9 @@
 		return;
 
 done:
+	g_slist_free(dev->pending);
+	dev->pending = NULL;
+
 	if (!dev->connect)
 		return;
 
@@ -1241,9 +1244,6 @@
 		g_dbus_send_reply(dbus_conn, dev->connect, DBUS_TYPE_INVALID);
 	}
 
-	g_slist_free(dev->pending);
-	dev->pending = NULL;
-
 	dbus_message_unref(dev->connect);
 	dev->connect = NULL;
 }
diff --git a/src/sdp-xml.c b/src/sdp-xml.c
index 6492781..0535d09 100644
--- a/src/sdp-xml.c
+++ b/src/sdp-xml.c
@@ -91,6 +91,10 @@
 	/* Null terminate the text */
 	elem->size = DEFAULT_XML_DATA_SIZE;
 	elem->text = malloc(DEFAULT_XML_DATA_SIZE);
+	if (!elem->text) {
+		free(elem);
+		return NULL;
+	}
 	elem->text[0] = '\0';
 
 	return elem;
@@ -333,6 +337,8 @@
 		int i;
 
 		decoded = malloc((len >> 1) + 1);
+		if (!decoded)
+			return NULL;
 
 		/* Ensure the string is a power of 2 */
 		len = (len >> 1) << 1;
@@ -363,6 +369,8 @@
 
 	url = sdp_xml_parse_string_decode(data,
 				SDP_XML_ENCODING_NORMAL, &length);
+	if (!url)
+		return NULL;
 
 	if (length > UCHAR_MAX)
 		dtd = SDP_URL_STR16;
@@ -382,6 +390,8 @@
 	sdp_data_t *ret;
 
 	text = sdp_xml_parse_string_decode(data, encoding, &length);
+	if (!text)
+		return NULL;
 
 	if (length > UCHAR_MAX)
 		dtd = SDP_TEXT_STR16;
@@ -823,7 +833,7 @@
 	{
 		int num_chars_to_escape = 0;
 		int length = value->unitSize - 1;
-		char *strBuf = 0;
+		char *strBuf;
 
 		hex = 0;
 
@@ -850,6 +860,10 @@
 			appender(data, "encoding=\"hex\" ");
 			strBuf = malloc(sizeof(char)
 						 * ((value->unitSize-1) * 2 + 1));
+			if (!strBuf) {
+				DBG("No memory to convert raw data to xml");
+				return;
+			}
 
 			/* Unit Size seems to include the size for dtd
 			   It is thus off by 1
@@ -866,6 +880,10 @@
 			/* escape the XML disallowed chars */
 			strBuf = malloc(sizeof(char) *
 					(value->unitSize + 1 + num_chars_to_escape * 4));
+			if (!strBuf) {
+				DBG("No memory to convert raw data to xml");
+				return;
+			}
 			for (i = 0, j = 0; i < length; i++) {
 				if (value->val.str[i] == '&') {
 					strBuf[j++] = '&';
diff --git a/src/sdpd-database.c b/src/sdpd-database.c
index f65a526..6f5577b 100644
--- a/src/sdpd-database.c
+++ b/src/sdpd-database.c
@@ -157,6 +157,12 @@
 void sdp_svcdb_set_collectable(sdp_record_t *record, int sock)
 {
 	sdp_indexed_t *item = malloc(sizeof(sdp_indexed_t));
+
+	if (!item) {
+		SDPDBG("No memory");
+		return;
+	}
+
 	item->sock = sock;
 	item->record = record;
 	socket_index = sdp_list_insert_sorted(socket_index, item, compare_indices);
diff --git a/src/shared/crypto.c b/src/shared/crypto.c
index 9e94122..e2b0e7a 100644
--- a/src/shared/crypto.c
+++ b/src/shared/crypto.c
@@ -74,6 +74,7 @@
 	int ref_count;
 	int ecb_aes;
 	int urandom;
+	int cmac_aes;
 };
 
 static int urandom_setup(void)
@@ -109,6 +110,28 @@
 	return fd;
 }
 
+static int cmac_aes_setup(void)
+{
+	struct sockaddr_alg salg;
+	int fd;
+
+	fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
+	if (fd < 0)
+		return -1;
+
+	memset(&salg, 0, sizeof(salg));
+	salg.salg_family = AF_ALG;
+	strcpy((char *) salg.salg_type, "hash");
+	strcpy((char *) salg.salg_name, "cmac(aes)");
+
+	if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
 struct bt_crypto *bt_crypto_new(void)
 {
 	struct bt_crypto *crypto;
@@ -130,6 +153,14 @@
 		return NULL;
 	}
 
+	crypto->cmac_aes = cmac_aes_setup();
+	if (crypto->cmac_aes < 0) {
+		close(crypto->urandom);
+		close(crypto->ecb_aes);
+		free(crypto);
+		return NULL;
+	}
+
 	return bt_crypto_ref(crypto);
 }
 
@@ -153,6 +184,7 @@
 
 	close(crypto->urandom);
 	close(crypto->ecb_aes);
+	close(crypto->cmac_aes);
 
 	free(crypto);
 }
@@ -220,14 +252,74 @@
 	return true;
 }
 
-static inline void swap128(const uint8_t src[16], uint8_t dst[16])
+static inline void swap_buf(const uint8_t *src, uint8_t *dst, uint16_t len)
 {
 	int i;
 
-	for (i = 0; i < 16; i++)
-		dst[15 - i] = src[i];
+	for (i = 0; i < len; i++)
+		dst[len - 1 - i] = src[i];
 }
 
+bool bt_crypto_sign_att(struct bt_crypto *crypto, const uint8_t key[16],
+				const uint8_t *m, uint16_t m_len,
+				uint32_t sign_cnt, uint8_t signature[12])
+{
+	int fd;
+	int len;
+	uint8_t tmp[16], out[16];
+	uint16_t msg_len = m_len + sizeof(uint32_t);
+	uint8_t msg[msg_len];
+	uint8_t msg_s[msg_len];
+
+	if (!crypto)
+		return false;
+
+	memset(msg, 0, msg_len);
+	memcpy(msg, m, m_len);
+
+	/* Add sign_counter to the message */
+	put_le32(sign_cnt, msg + m_len);
+
+	/* The most significant octet of key corresponds to key[0] */
+	swap_buf(key, tmp, 16);
+
+	fd = alg_new(crypto->cmac_aes, tmp, 16);
+	if (fd < 0)
+		return false;
+
+	/* Swap msg before signing */
+	swap_buf(msg, msg_s, msg_len);
+
+	len = send(fd, msg_s, msg_len, 0);
+	if (len < 0) {
+		close(fd);
+		return false;
+	}
+
+	len = read(fd, out, 16);
+	if (len < 0) {
+		close(fd);
+		return false;
+	}
+
+	close(fd);
+
+	/*
+	 * As to BT spec. 4.1 Vol[3], Part C, chapter 10.4.1 sign counter should
+	 * be placed in the signature
+	 */
+	put_be32(sign_cnt, out + 8);
+
+	/*
+	 * The most significant octet of hash corresponds to out[0]  - swap it.
+	 * Then truncate in most significant bit first order to a length of
+	 * 12 octets
+	 */
+	swap_buf(out, tmp, 16);
+	memcpy(signature, tmp + 4, 12);
+
+	return true;
+}
 /*
  * Security function e
  *
@@ -251,7 +343,7 @@
 		return false;
 
 	/* The most significant octet of key corresponds to key[0] */
-	swap128(key, tmp);
+	swap_buf(key, tmp, 16);
 
 	fd = alg_new(crypto->ecb_aes, tmp, 16);
 	if (fd < 0)
@@ -259,7 +351,7 @@
 
 
 	/* Most significant octet of plaintextData corresponds to in[0] */
-	swap128(plaintext, in);
+	swap_buf(plaintext, in, 16);
 
 	if (!alg_encrypt(fd, in, 16, out, 16)) {
 		close(fd);
@@ -267,7 +359,7 @@
 	}
 
 	/* Most significant octet of encryptedData corresponds to out[0] */
-	swap128(out, encrypted);
+	swap_buf(out, encrypted, 16);
 
 	close(fd);
 
diff --git a/src/shared/crypto.h b/src/shared/crypto.h
index cae8daa..b10f3ff 100644
--- a/src/shared/crypto.h
+++ b/src/shared/crypto.h
@@ -46,3 +46,6 @@
 bool bt_crypto_s1(struct bt_crypto *crypto, const uint8_t k[16],
 			const uint8_t r1[16], const uint8_t r2[16],
 			uint8_t res[16]);
+bool bt_crypto_sign_att(struct bt_crypto *crypto, const uint8_t key[16],
+				const uint8_t *m, uint16_t m_len,
+				uint32_t sign_cnt, uint8_t signature[12]);
diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index 36316af..9cbf409 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -48,7 +48,7 @@
 struct gatt_db_attribute {
 	uint16_t handle;
 	bt_uuid_t uuid;
-	uint8_t permissions;
+	uint32_t permissions;
 	uint16_t value_len;
 	uint8_t *value;
 
@@ -95,10 +95,8 @@
 	return attribute;
 }
 
-static void attribute_destroy(void *data)
+static void attribute_destroy(struct gatt_db_attribute *attribute)
 {
-	struct gatt_db_attribute *attribute = data;
-
 	/* Attribute was not initialized by user */
 	if (!attribute)
 		return;
@@ -258,7 +256,7 @@
 static void set_attribute_data(struct gatt_db_attribute *attribute,
 						gatt_db_read_t read_func,
 						gatt_db_write_t write_func,
-						uint8_t permissions,
+						uint32_t permissions,
 						void *user_data)
 {
 	attribute->permissions = permissions;
@@ -269,7 +267,7 @@
 
 uint16_t gatt_db_add_characteristic(struct gatt_db *db, uint16_t handle,
 						const bt_uuid_t *uuid,
-						uint8_t permissions,
+						uint32_t permissions,
 						uint8_t properties,
 						gatt_db_read_t read_func,
 						gatt_db_write_t write_func,
@@ -317,7 +315,7 @@
 
 uint16_t gatt_db_add_char_descriptor(struct gatt_db *db, uint16_t handle,
 						const bt_uuid_t *uuid,
-						uint8_t permissions,
+						uint32_t permissions,
 						gatt_db_read_t read_func,
 						gatt_db_write_t write_func,
 						void *user_data)
@@ -733,3 +731,31 @@
 
 	return service->attributes[0]->handle + service->num_handles - 1;
 }
+
+bool gatt_db_get_attribute_permissions(struct gatt_db *db, uint16_t handle,
+							uint32_t *permissions)
+{
+	struct gatt_db_attribute *attribute;
+	struct gatt_db_service *service;
+	uint16_t service_handle;
+
+	service = queue_find(db->services, find_service_for_handle,
+							INT_TO_PTR(handle));
+	if (!service)
+		return false;
+
+	service_handle = service->attributes[0]->handle;
+
+	/*
+	 * We can safely get attribute from attributes array with offset,
+	 * because find_service_for_handle() check if given handle is
+	 * in service range.
+	 */
+	attribute = service->attributes[handle - service_handle];
+	if (!attribute)
+		return false;
+
+	*permissions = attribute->permissions;
+	return true;
+
+}
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 3d46730..a88f637 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -41,7 +41,7 @@
 
 uint16_t gatt_db_add_characteristic(struct gatt_db *db, uint16_t handle,
 						const bt_uuid_t *uuid,
-						uint8_t permissions,
+						uint32_t permissions,
 						uint8_t properties,
 						gatt_db_read_t read_func,
 						gatt_db_write_t write_func,
@@ -49,7 +49,7 @@
 
 uint16_t gatt_db_add_char_descriptor(struct gatt_db *db, uint16_t handle,
 						const bt_uuid_t *uuid,
-						uint8_t permissions,
+						uint32_t permissions,
 						gatt_db_read_t read_func,
 						gatt_db_write_t write_func,
 						void *user_data);
@@ -91,3 +91,6 @@
 							uint16_t handle);
 
 uint16_t gatt_db_get_end_handle(struct gatt_db *db, uint16_t handle);
+
+bool gatt_db_get_attribute_permissions(struct gatt_db *db, uint16_t handle,
+							uint32_t *permissions);
diff --git a/src/shared/hfp.c b/src/shared/hfp.c
index 36c8c3e..196e777 100644
--- a/src/shared/hfp.c
+++ b/src/shared/hfp.c
@@ -381,6 +381,7 @@
 {
 	char *str, *ptr;
 	size_t len, count;
+	bool free_ptr = false;
 
 	str = ringbuf_peek(hfp->read_buf, 0, &len);
 	if (!str)
@@ -407,6 +408,7 @@
 
 		*ptr = '\0';
 		count = asprintf(&ptr, "%s%s", str, str2);
+		free_ptr = true;
 		str = ptr;
 	} else {
 		count = ptr - str;
@@ -424,7 +426,7 @@
 
 	len = ringbuf_drain(hfp->read_buf, count + 1);
 
-	if (str == ptr)
+	if (free_ptr)
 		free(ptr);
 }
 
diff --git a/src/shared/queue.c b/src/shared/queue.c
index 8a69729..8bbb7df 100644
--- a/src/shared/queue.c
+++ b/src/shared/queue.c
@@ -34,11 +34,30 @@
 };
 
 struct queue {
+	int ref_count;
 	struct queue_entry *head;
 	struct queue_entry *tail;
 	unsigned int entries;
 };
 
+static struct queue *queue_ref(struct queue *queue)
+{
+	if (!queue)
+		return NULL;
+
+	__sync_fetch_and_add(&queue->ref_count, 1);
+
+	return queue;
+}
+
+static void queue_unref(struct queue *queue)
+{
+	if (__sync_sub_and_fetch(&queue->ref_count, 1))
+		return;
+
+	free(queue);
+}
+
 struct queue *queue_new(void)
 {
 	struct queue *queue;
@@ -51,7 +70,7 @@
 	queue->tail = NULL;
 	queue->entries = 0;
 
-	return queue;
+	return queue_ref(queue);
 }
 
 void queue_destroy(struct queue *queue, queue_destroy_func_t destroy)
@@ -74,7 +93,7 @@
 		free(tmp);
 	}
 
-	free(queue);
+	queue_unref(queue);
 }
 
 bool queue_push_tail(struct queue *queue, void *data)
@@ -168,6 +187,17 @@
 	return queue->tail->data;
 }
 
+static bool queue_find_entry(struct queue *queue, const void *data)
+{
+	struct queue_entry *entry;
+
+	for (entry = queue->head; entry; entry = entry->next)
+		if (entry == data)
+			return true;
+
+	return false;
+}
+
 void queue_foreach(struct queue *queue, queue_foreach_func_t function,
 							void *user_data)
 {
@@ -177,26 +207,41 @@
 		return;
 
 	entry = queue->head;
+	if (!entry)
+		return;
 
-	while (entry) {
+	queue_ref(queue);
+	while (entry && queue->ref_count > 1) {
 		struct queue_entry *tmp = entry;
 
 		entry = tmp->next;
 
 		function(tmp->data, user_data);
+
+		if (!queue_find_entry(queue, entry))
+			break;
 	}
+	queue_unref(queue);
+}
+
+static bool direct_match(const void *a, const void *b)
+{
+	return a == b;
 }
 
 void *queue_find(struct queue *queue, queue_match_func_t function,
-							void *user_data)
+							const void *match_data)
 {
 	struct queue_entry *entry;
 
 	if (!queue || !function)
 		return NULL;
 
+	if (!function)
+		function = direct_match;
+
 	for (entry = queue->head; entry; entry = entry->next)
-		if (function(entry->data, user_data))
+		if (function(entry->data, match_data))
 			return entry->data;
 
 	return NULL;
diff --git a/src/shared/queue.h b/src/shared/queue.h
index 8201ff8..709590b 100644
--- a/src/shared/queue.h
+++ b/src/shared/queue.h
@@ -44,7 +44,7 @@
 typedef bool (*queue_match_func_t)(const void *a, const void *b);
 
 void *queue_find(struct queue *queue, queue_match_func_t function,
-							void *user_data);
+							const void *match_data);
 
 bool queue_remove(struct queue *queue, void *data);
 void *queue_remove_if(struct queue *queue, queue_match_func_t function,
diff --git a/src/shared/uhid.c b/src/shared/uhid.c
new file mode 100644
index 0000000..28c1792
--- /dev/null
+++ b/src/shared/uhid.c
@@ -0,0 +1,245 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "src/shared/io.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/uhid.h"
+
+#define UHID_DEVICE_FILE "/dev/uhid"
+
+struct bt_uhid {
+	int ref_count;
+	struct io *io;
+	unsigned int notify_id;
+	struct queue *notify_list;
+};
+
+struct uhid_notify {
+	unsigned int id;
+	uint32_t event;
+	bt_uhid_callback_t func;
+	void *user_data;
+};
+
+static void uhid_free(struct bt_uhid *uhid)
+{
+	if (uhid->io)
+		io_destroy(uhid->io);
+
+	if (uhid->notify_list)
+		queue_destroy(uhid->notify_list, free);
+
+	free(uhid);
+}
+
+static void notify_handler(void *data, void *user_data)
+{
+	struct uhid_notify *notify = data;
+	struct uhid_event *ev = user_data;
+
+	if (notify->event != ev->type)
+		return;
+
+	if (notify->func)
+		notify->func(ev, notify->user_data);
+}
+
+static bool uhid_read_handler(struct io *io, void *user_data)
+{
+	struct bt_uhid *uhid = user_data;
+	int fd;
+	ssize_t len;
+	struct uhid_event ev;
+
+	fd = io_get_fd(io);
+	if (fd < 0)
+		return false;
+
+	memset(&ev, 0, sizeof(ev));
+
+	len = read(fd, &ev, sizeof(ev));
+	if (len < 0)
+		return false;
+
+	if ((size_t) len < sizeof(ev.type))
+		return false;
+
+	queue_foreach(uhid->notify_list, notify_handler, &ev);
+
+	return true;
+}
+
+struct bt_uhid *bt_uhid_new_default(void)
+{
+	struct bt_uhid *uhid;
+	int fd;
+
+	fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC);
+	if (fd < 0)
+		return NULL;
+
+	uhid = bt_uhid_new(fd);
+	if (!uhid) {
+		close(fd);
+		return NULL;
+	}
+
+	io_set_close_on_destroy(uhid->io, true);
+
+	return uhid;
+}
+
+struct bt_uhid *bt_uhid_new(int fd)
+{
+	struct bt_uhid *uhid;
+
+	uhid = new0(struct bt_uhid, 1);
+	if (!uhid)
+		return NULL;
+
+	uhid->io = io_new(fd);
+	if (!uhid->io)
+		goto failed;
+
+	uhid->notify_list = queue_new();
+	if (!uhid->notify_list)
+		goto failed;
+
+	if (!io_set_read_handler(uhid->io, uhid_read_handler, uhid, NULL))
+		goto failed;
+
+	return bt_uhid_ref(uhid);
+
+failed:
+	uhid_free(uhid);
+	return NULL;
+}
+
+struct bt_uhid *bt_uhid_ref(struct bt_uhid *uhid)
+{
+	if (!uhid)
+		return NULL;
+
+	__sync_fetch_and_add(&uhid->ref_count, 1);
+
+	return uhid;
+}
+
+void bt_uhid_unref(struct bt_uhid *uhid)
+{
+	if (!uhid)
+		return;
+
+	if (__sync_sub_and_fetch(&uhid->ref_count, 1))
+		return;
+
+	uhid_free(uhid);
+}
+
+bool bt_uhid_set_close_on_unref(struct bt_uhid *uhid, bool do_close)
+{
+	if (!uhid || !uhid->io)
+		return false;
+
+	io_set_close_on_destroy(uhid->io, do_close);
+
+	return true;
+}
+
+unsigned int bt_uhid_register(struct bt_uhid *uhid, uint32_t event,
+				bt_uhid_callback_t func, void *user_data)
+{
+	struct uhid_notify *notify;
+
+	if (!uhid)
+		return 0;
+
+	notify = new0(struct uhid_notify, 1);
+	if (!notify)
+		return 0;
+
+	notify->id = uhid->notify_id++;
+	notify->event = event;
+	notify->func = func;
+	notify->user_data = user_data;
+
+	if (!queue_push_tail(uhid->notify_list, notify)) {
+		free(notify);
+		return 0;
+	}
+
+	return notify->id;
+}
+
+static bool match_notify_id(const void *a, const void *b)
+{
+	const struct uhid_notify *notify = a;
+	unsigned int id = PTR_TO_UINT(b);
+
+	return notify->id == id;
+}
+
+bool bt_uhid_unregister(struct bt_uhid *uhid, unsigned int id)
+{
+	struct uhid_notify *notify;
+
+	if (!uhid || !id)
+		return false;
+
+	notify = queue_remove_if(uhid->notify_list, match_notify_id,
+							UINT_TO_PTR(id));
+	if (!notify)
+		return false;
+
+	free(notify);
+	return true;
+}
+
+int bt_uhid_send(struct bt_uhid *uhid, const struct uhid_event *ev)
+{
+	int fd;
+	ssize_t len;
+
+	if (!uhid->io)
+		return -ENOTCONN;
+
+	fd = io_get_fd(uhid->io);
+
+	len = write(fd, ev, sizeof(*ev));
+	if (len < 0)
+		return -errno;
+
+	/* uHID kernel driver does not handle partial writes */
+	return len != sizeof(*ev) ? -EIO : 0;
+}
diff --git a/src/shared/uhid.h b/src/shared/uhid.h
new file mode 100644
index 0000000..459a249
--- /dev/null
+++ b/src/shared/uhid.h
@@ -0,0 +1,44 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 <stdbool.h>
+#include <stdint.h>
+
+#include "profiles/input/uhid_copy.h"
+
+struct bt_uhid;
+
+struct bt_uhid *bt_uhid_new_default(void);
+struct bt_uhid *bt_uhid_new(int fd);
+
+struct bt_uhid *bt_uhid_ref(struct bt_uhid *uhid);
+void bt_uhid_unref(struct bt_uhid *uhid);
+
+bool bt_uhid_set_close_on_unref(struct bt_uhid *uhid, bool do_close);
+
+typedef void (*bt_uhid_callback_t)(struct uhid_event *ev, void *user_data);
+unsigned int bt_uhid_register(struct bt_uhid *uhid, uint32_t event,
+				bt_uhid_callback_t func, void *user_data);
+bool bt_uhid_unregister(struct bt_uhid *uhid, unsigned int id);
+
+int bt_uhid_send(struct bt_uhid *uhid, const struct uhid_event *ev);
diff --git a/tools/avinfo.c b/tools/avinfo.c
index a4deaac..7d58e25 100644
--- a/tools/avinfo.c
+++ b/tools/avinfo.c
@@ -158,21 +158,95 @@
 	uint8_t caps[0];
 } __attribute__ ((packed));
 
-static void print_vendor(a2dp_vendor_codec_t *vendor)
+static void print_aptx(a2dp_aptx_t *aptx)
 {
-	printf("\tMedia Codec: Vendor Specific A2DP Codec");
+	printf("\t\tVendor Specific Value (aptX)");
 
-	printf("\n\t\tVendor ID 0x%02x%02x%02x%02x", vendor->vendor_id[0],
-		vendor->vendor_id[1], vendor->vendor_id[2],
-		vendor->vendor_id[3]);
+	printf("\n\t\t\tFrequencies: ");
+	if (aptx->frequency & APTX_SAMPLING_FREQ_16000)
+		printf("16kHz ");
+	if (aptx->frequency & APTX_SAMPLING_FREQ_32000)
+		printf("32kHz ");
+	if (aptx->frequency & APTX_SAMPLING_FREQ_44100)
+		printf("44.1kHz ");
+	if (aptx->frequency & APTX_SAMPLING_FREQ_48000)
+		printf("48kHz ");
 
-	printf("\n\t\tVendor Specific Codec ID 0x%02x%02x\n",
-			vendor->codec_id[0], vendor->codec_id[1]);
+	printf("\n\t\t\tChannel modes: ");
+	if (aptx->channel_mode & APTX_CHANNEL_MODE_MONO)
+		printf("Mono ");
+	if (aptx->channel_mode & APTX_CHANNEL_MODE_STEREO)
+		printf("Stereo ");
+
+	printf("\n");
 }
 
-static void print_mpeg24(a2dp_mpeg_t *mpeg)
+static void print_vendor(a2dp_vendor_codec_t *vendor)
 {
-	printf("\tMedia Codec: MPEG24\n");
+	uint32_t vendor_id = btohl(vendor->vendor_id);
+	uint16_t codec_id = btohs(vendor->codec_id);
+
+	printf("\tMedia Codec: Vendor Specific A2DP Codec");
+
+	printf("\n\t\tVendor ID 0x%08x", vendor_id);
+
+	printf("\n\t\tVendor Specific Codec ID 0x%04x\n", codec_id);
+
+	if (vendor_id == APTX_VENDOR_ID && codec_id == APTX_CODEC_ID)
+		print_aptx((void *) vendor);
+}
+
+static void print_mpeg24(a2dp_aac_t *aac)
+{
+	unsigned freq = AAC_GET_FREQUENCY(*aac);
+	unsigned bitrate = AAC_GET_BITRATE(*aac);
+
+	printf("\tMedia Codec: MPEG24\n\t\tObject Types: ");
+
+	if (aac->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC)
+		printf("MPEG-2 AAC LC ");
+	if (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC)
+		printf("MPEG-4 AAC LC ");
+	if (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LTP)
+		printf("MPEG-4 AAC LTP ");
+	if (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_SCA)
+		printf("MPEG-4 AAC scalable ");
+
+	printf("\n\t\tFrequencies: ");
+	if (freq & AAC_SAMPLING_FREQ_8000)
+		printf("8kHz ");
+	if (freq & AAC_SAMPLING_FREQ_11025)
+		printf("11.025kHz ");
+	if (freq & AAC_SAMPLING_FREQ_12000)
+		printf("12kHz ");
+	if (freq & AAC_SAMPLING_FREQ_16000)
+		printf("16kHz ");
+	if (freq & AAC_SAMPLING_FREQ_22050)
+		printf("22.05kHz ");
+	if (freq & AAC_SAMPLING_FREQ_24000)
+		printf("24kHz ");
+	if (freq & AAC_SAMPLING_FREQ_32000)
+		printf("32kHz ");
+	if (freq & AAC_SAMPLING_FREQ_44100)
+		printf("44.1kHz ");
+	if (freq & AAC_SAMPLING_FREQ_48000)
+		printf("48kHz ");
+	if (freq & AAC_SAMPLING_FREQ_64000)
+		printf("64kHz ");
+	if (freq & AAC_SAMPLING_FREQ_88200)
+		printf("88.2kHz ");
+	if (freq & AAC_SAMPLING_FREQ_96000)
+		printf("96kHz ");
+
+	printf("\n\t\tChannels: ");
+	if (aac->channels & AAC_CHANNELS_1)
+		printf("1 ");
+	if (aac->channels & AAC_CHANNELS_2)
+		printf("2 ");
+
+	printf("\n\t\tBitrate: %u", bitrate);
+
+	printf("\n\t\tVBR: %s", aac->vbr ? "Yes\n" : "No\n");
 }
 
 static void print_mpeg12(a2dp_mpeg_t *mpeg)
diff --git a/tools/btmgmt.c b/tools/btmgmt.c
index 0618de1..8f5a981 100644
--- a/tools/btmgmt.c
+++ b/tools/btmgmt.c
@@ -538,12 +538,12 @@
 }
 
 static int mgmt_confirm_reply(struct mgmt *mgmt, uint16_t index,
-							const bdaddr_t *bdaddr)
+					const struct mgmt_addr_info *addr)
 {
 	struct mgmt_cp_user_confirm_reply cp;
 
 	memset(&cp, 0, sizeof(cp));
-	bacpy(&cp.addr.bdaddr, bdaddr);
+	memcpy(&cp.addr, addr, sizeof(*addr));
 
 	return mgmt_reply(mgmt, MGMT_OP_USER_CONFIRM_REPLY, index,
 				sizeof(cp), &cp, confirm_rsp, NULL, NULL);
@@ -564,12 +564,12 @@
 }
 
 static int mgmt_confirm_neg_reply(struct mgmt *mgmt, uint16_t index,
-							const bdaddr_t *bdaddr)
+					const struct mgmt_addr_info *addr)
 {
 	struct mgmt_cp_user_confirm_reply cp;
 
 	memset(&cp, 0, sizeof(cp));
-	bacpy(&cp.addr.bdaddr, bdaddr);
+	memcpy(&cp.addr, addr, sizeof(*addr));
 
 	return mgmt_reply(mgmt, MGMT_OP_USER_CONFIRM_NEG_REPLY, index,
 				sizeof(cp), &cp, confirm_neg_rsp, NULL, NULL);
@@ -609,7 +609,7 @@
 	memset(rsp, 0, sizeof(rsp));
 
 	if (fgets(rsp, sizeof(rsp), stdin) == NULL || rsp[0] == '\n') {
-		mgmt_confirm_neg_reply(mgmt, index, &ev->addr.bdaddr);
+		mgmt_confirm_neg_reply(mgmt, index, &ev->addr);
 		return;
 	}
 
@@ -618,9 +618,124 @@
 		rsp[rsp_len - 1] = '\0';
 
 	if (rsp[0] == 'y' || rsp[0] == 'Y')
-		mgmt_confirm_reply(mgmt, index, &ev->addr.bdaddr);
+		mgmt_confirm_reply(mgmt, index, &ev->addr);
 	else
-		mgmt_confirm_neg_reply(mgmt, index, &ev->addr.bdaddr);
+		mgmt_confirm_neg_reply(mgmt, index, &ev->addr);
+}
+
+static void passkey_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0) {
+		fprintf(stderr,
+			"User Passkey reply failed. status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+
+	printf("User Passkey Reply successful\n");
+}
+
+static int mgmt_passkey_reply(struct mgmt *mgmt, uint16_t index,
+					const struct mgmt_addr_info *addr,
+					uint32_t passkey)
+{
+	struct mgmt_cp_user_passkey_reply cp;
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(&cp.addr, addr, sizeof(*addr));
+	put_le32(passkey, &cp.passkey);
+
+	return mgmt_reply(mgmt, MGMT_OP_USER_PASSKEY_REPLY, index,
+				sizeof(cp), &cp, passkey_rsp, NULL, NULL);
+}
+
+static void passkey_neg_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0) {
+		fprintf(stderr,
+			"Passkey Neg reply failed. status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+
+	printf("User Passkey Negative Reply successful\n");
+}
+
+static int mgmt_passkey_neg_reply(struct mgmt *mgmt, uint16_t index,
+					const struct mgmt_addr_info *addr)
+{
+	struct mgmt_cp_user_passkey_reply cp;
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(&cp.addr, addr, sizeof(*addr));
+
+	return mgmt_reply(mgmt, MGMT_OP_USER_PASSKEY_NEG_REPLY, index,
+				sizeof(cp), &cp, passkey_neg_rsp, NULL, NULL);
+}
+
+
+static void request_passkey(uint16_t index, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_user_passkey_request *ev = param;
+	struct mgmt *mgmt = user_data;
+	char passkey[7];
+
+	if (len != sizeof(*ev)) {
+		fprintf(stderr,
+			"Invalid passkey request length (%u bytes)\n", len);
+		return;
+	}
+
+	if (monitor) {
+		char addr[18];
+		ba2str(&ev->addr.bdaddr, addr);
+		printf("hci%u %s request passkey\n", index, addr);
+	}
+
+	printf("Passkey Request (press enter to reject) >> ");
+	fflush(stdout);
+
+	memset(passkey, 0, sizeof(passkey));
+
+	if (fgets(passkey, sizeof(passkey), stdin) == NULL ||
+							passkey[0] == '\n') {
+		mgmt_passkey_neg_reply(mgmt, index, &ev->addr);
+		return;
+	}
+
+	len = strlen(passkey);
+	if (passkey[len - 1] == '\n') {
+		passkey[len - 1] = '\0';
+		len--;
+	}
+
+	mgmt_passkey_reply(mgmt, index, &ev->addr, atoi(passkey));
+}
+
+static void passkey_notify(uint16_t index, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_passkey_notify *ev = param;
+
+	if (len != sizeof(*ev)) {
+		fprintf(stderr,
+			"Invalid passkey request length (%u bytes)\n", len);
+		return;
+	}
+
+	if (monitor) {
+		char addr[18];
+		ba2str(&ev->addr.bdaddr, addr);
+		printf("hci%u %s request passkey\n", index, addr);
+	}
+
+	printf("Passkey Notify: %06u (entered %u)\n", get_le32(&ev->passkey),
+								ev->entered);
 }
 
 static void cmd_monitor(struct mgmt *mgmt, uint16_t index, int argc,
@@ -2225,6 +2340,89 @@
 	}
 }
 
+static void io_cap_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		fprintf(stderr, "Could not set IO Capability with "
+						"status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+	else
+		printf("IO Capabilities successfully set\n");
+
+	mainloop_quit();
+}
+
+static void io_cap_usage(void)
+{
+	printf("Usage: btmgmt io-cap <cap>\n");
+}
+
+static void cmd_io_cap(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	struct mgmt_cp_set_io_capability cp;
+	uint8_t cap;
+
+	if (argc < 2) {
+		io_cap_usage();
+		exit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	cap = strtol(argv[1], NULL, 0);
+	memset(&cp, 0, sizeof(cp));
+	cp.io_capability = cap;
+
+	if (mgmt_send(mgmt, MGMT_OP_SET_IO_CAPABILITY, index, sizeof(cp), &cp,
+					io_cap_rsp, NULL, NULL) == 0) {
+		fprintf(stderr, "Unable to send set-io-cap cmd\n");
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void scan_params_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		fprintf(stderr, "Set scan parameters failed with status 0x%02x (%s)\n",
+						status, mgmt_errstr(status));
+	else
+		printf("Scan parameters successfully set\n");
+
+	mainloop_quit();
+}
+
+static void scan_params_usage(void)
+{
+	printf("Usage: btmgmt scan-params <interval> <window>\n");
+}
+
+static void cmd_scan_params(struct mgmt *mgmt, uint16_t index,
+							int argc, char **argv)
+{
+	struct mgmt_cp_set_scan_params cp;
+
+	if (argc < 3) {
+		scan_params_usage();
+		exit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	cp.interval = strtol(argv[1], NULL, 0);
+	cp.window = strtol(argv[2], NULL, 0);
+
+	if (mgmt_send(mgmt, MGMT_OP_SET_SCAN_PARAMS, index, sizeof(cp), &cp,
+					scan_params_rsp, NULL, NULL) == 0) {
+		fprintf(stderr, "Unable to send set_scan_params cmd\n");
+		exit(EXIT_FAILURE);
+	}
+}
+
 static struct {
 	char *cmd;
 	void (*func)(struct mgmt *mgmt, uint16_t index, int argc, char **argv);
@@ -2245,7 +2443,7 @@
 	{ "hs",		cmd_hs,		"Toggle HS support"		},
 	{ "le",		cmd_le,		"Toggle LE support"		},
 	{ "advertising",cmd_advertising,"Toggle LE advertising",	},
-	{ "bredr",      cmd_bredr,      "Toggle BR/EDR support",	},
+	{ "bredr",	cmd_bredr,	"Toggle BR/EDR support",	},
 	{ "privacy",	cmd_privacy,	"Toggle privacy support"	},
 	{ "class",	cmd_class,	"Set device major/minor class"	},
 	{ "disconnect", cmd_disconnect, "Disconnect device"		},
@@ -2268,6 +2466,8 @@
 	{ "static-addr",cmd_static_addr,"Set static address"		},
 	{ "debug-keys",	cmd_debug_keys,	"Toogle debug keys"		},
 	{ "conn-info",	cmd_conn_info,	"Get connection information"	},
+	{ "io-cap",	cmd_io_cap,	"Set IO Capability"		},
+	{ "scan-params",cmd_scan_params,"Set Scan Parameters"		},
 	{ }
 };
 
@@ -2386,6 +2586,10 @@
 								mgmt, NULL);
 	mgmt_register(mgmt, MGMT_EV_USER_CONFIRM_REQUEST, index, user_confirm,
 								mgmt, NULL);
+	mgmt_register(mgmt, MGMT_EV_USER_PASSKEY_REQUEST, index,
+						request_passkey, mgmt, NULL);
+	mgmt_register(mgmt, MGMT_EV_PASSKEY_NOTIFY, index,
+						passkey_notify, mgmt, NULL);
 
 	exit_status = mainloop_run();
 
diff --git a/tools/l2test.c b/tools/l2test.c
index c70bac0..ffad7c4 100644
--- a/tools/l2test.c
+++ b/tools/l2test.c
@@ -271,7 +271,7 @@
 
 	memset(opts, 0, sizeof(*opts));
 
-	if (bdaddr_type == BDADDR_BREDR || cid) {
+	if (bdaddr_type == BDADDR_BREDR) {
 		optlen = sizeof(*opts);
 		return getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, opts, &optlen);
 	}
@@ -287,7 +287,7 @@
 
 static int setopts(int sk, struct l2cap_options *opts)
 {
-	if (bdaddr_type == BDADDR_BREDR || cid)
+	if (bdaddr_type == BDADDR_BREDR)
 		return setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, opts,
 								sizeof(*opts));
 
diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c
index 4cc7ba4..c99fe18 100644
--- a/tools/mgmt-tester.c
+++ b/tools/mgmt-tester.c
@@ -395,6 +395,7 @@
 	uint8_t client_auth_req;
 	bool reject_ssp;
 	bool client_reject_ssp;
+	bool just_works;
 };
 
 static const char dummy_data[] = { 0x00 };
@@ -2212,6 +2213,15 @@
 	.expect_status = MGMT_STATUS_INVALID_PARAMS,
 };
 
+static const char set_io_cap_invalid_param_1[] = { 0xff };
+
+static const struct generic_data set_io_cap_invalid_param_test_1 = {
+	.send_opcode = MGMT_OP_SET_IO_CAPABILITY,
+	.send_param = set_io_cap_invalid_param_1,
+	.send_len = sizeof(set_io_cap_invalid_param_1),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
 static const char pair_device_param[] = {
 			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00 };
 static const char pair_device_rsp[] = {
@@ -2220,6 +2230,10 @@
 			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff, 0x00 };
 static const char pair_device_invalid_param_rsp_1[] = {
 			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+static const char pair_device_invalid_param_2[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x05 };
+static const char pair_device_invalid_param_rsp_2[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00 };
 
 static const struct generic_data pair_device_not_powered_test_1 = {
 	.send_opcode = MGMT_OP_PAIR_DEVICE,
@@ -2239,6 +2253,15 @@
 	.expect_len = sizeof(pair_device_invalid_param_rsp_1),
 };
 
+static const struct generic_data pair_device_invalid_param_test_2 = {
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_param = pair_device_invalid_param_2,
+	.send_len = sizeof(pair_device_invalid_param_2),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+	.expect_param = pair_device_invalid_param_rsp_2,
+	.expect_len = sizeof(pair_device_invalid_param_rsp_2),
+};
+
 static const void *pair_device_send_param_func(uint16_t *len)
 {
 	struct test_data *data = tester_get_data();
@@ -2521,6 +2544,7 @@
 	.expect_hci_func = client_bdaddr_param_func,
 	.io_cap = 0x03, /* NoInputNoOutput */
 	.client_io_cap = 0x03, /* NoInputNoOutput */
+	.just_works = true,
 };
 
 static const struct generic_data pairing_acceptor_ssp_2 = {
@@ -2534,6 +2558,18 @@
 	.client_io_cap = 0x01, /* DisplayYesNo */
 };
 
+static const struct generic_data pairing_acceptor_ssp_3 = {
+	.setup_settings = settings_powered_connectable_pairable_ssp,
+	.client_enable_ssp = true,
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+	.expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+	.expect_hci_func = client_bdaddr_param_func,
+	.io_cap = 0x01, /* DisplayYesNo */
+	.client_io_cap = 0x01, /* DisplayYesNo */
+	.just_works = true,
+};
+
 static const char unpair_device_param[] = {
 			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00 };
 static const char unpair_device_rsp[] = {
@@ -2737,6 +2773,67 @@
 	.expect_status = MGMT_STATUS_INVALID_PARAMS,
 };
 
+static const void *get_conn_info_send_param_func(uint16_t *len)
+{
+	struct test_data *data = tester_get_data();
+	static uint8_t param[7];
+
+	memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
+	param[6] = 0x00; /* Address type */
+
+	*len = sizeof(param);
+
+	return param;
+}
+
+static const void *get_conn_info_expect_param_func(uint16_t *len)
+{
+	struct test_data *data = tester_get_data();
+	static uint8_t param[10];
+
+	memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
+	param[6] = 0x00; /* Address type */
+	param[7] = 0xff; /* RSSI (= -1) */
+	param[8] = 0xff; /* TX power (= -1) */
+	param[9] = 0x04; /* max TX power */
+
+	*len = sizeof(param);
+
+	return param;
+}
+
+static const void *get_conn_info_error_expect_param_func(uint16_t *len)
+{
+	struct test_data *data = tester_get_data();
+	static uint8_t param[10];
+
+	/* All unset parameters shall be 0 in case of error */
+	memset(param, 0, sizeof(param));
+
+	memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
+	param[6] = 0x00; /* Address type */
+
+	*len = sizeof(param);
+
+	return param;
+}
+
+static const struct generic_data get_conn_info_succes1_test = {
+	.setup_settings = settings_powered_connectable_pairable_ssp,
+	.send_opcode = MGMT_OP_GET_CONN_INFO,
+	.send_func = get_conn_info_send_param_func,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = get_conn_info_expect_param_func,
+};
+
+static const struct generic_data get_conn_info_ncon_test = {
+	.setup_settings = settings_powered_connectable_pairable_ssp,
+	.send_opcode = MGMT_OP_GET_CONN_INFO,
+	.send_func = get_conn_info_send_param_func,
+	.expect_status = MGMT_STATUS_NOT_CONNECTED,
+	.expect_func = get_conn_info_error_expect_param_func,
+};
+
 static void client_cmd_complete(uint16_t opcode, uint8_t status,
 					const void *param, uint8_t len,
 					void *user_data)
@@ -3090,6 +3187,12 @@
 	struct mgmt_cp_user_confirm_reply cp;
 	uint16_t opcode;
 
+	if (test->just_works) {
+		tester_warn("User Confirmation received for just-works case");
+		tester_test_failed();
+		return;
+	}
+
 	memset(&cp, 0, sizeof(cp));
 	memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
 
@@ -3133,6 +3236,9 @@
 	if (test->client_auth_req)
 		bthost_set_auth_req(bthost, test->client_auth_req);
 
+	if (!test->just_works)
+		bthost_set_auth_req(bthost, 0x01);
+
 	if (test->client_reject_ssp)
 		bthost_set_reject_user_confirm(bthost, true);
 
@@ -3422,6 +3528,57 @@
 	bthost_hci_connect(bthost, master_bdaddr, addr_type);
 }
 
+static void connected_event(uint16_t index, uint16_t length, const void *param,
+							void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	const void *send_param = test->send_param;
+	uint16_t send_len = test->send_len;
+
+	tester_print("Sending command 0x%04x", test->send_opcode);
+
+	if (test->send_func)
+		send_param = test->send_func(&send_len);
+
+	mgmt_send(data->mgmt, test->send_opcode, data->mgmt_index, send_len,
+			send_param, command_generic_callback, NULL, NULL);
+	test_add_condition(data);
+
+	/* Complete MGMT_EV_DEVICE_CONNECTED *after* adding new one */
+	test_condition_complete(data);
+}
+
+static void test_command_generic_connect(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned int id;
+	const uint8_t *master_bdaddr;
+	uint8_t addr_type;
+	struct bthost *bthost;
+
+	tester_print("Registering %s notification",
+					mgmt_evstr(MGMT_EV_DEVICE_CONNECTED));
+	id = mgmt_register(data->mgmt_alt, MGMT_EV_DEVICE_CONNECTED,
+				data->mgmt_index, connected_event,
+				NULL, NULL);
+	data->mgmt_alt_ev_id = id;
+	test_add_condition(data);
+
+	master_bdaddr = hciemu_get_master_bdaddr(data->hciemu);
+	if (!master_bdaddr) {
+		tester_warn("No master bdaddr");
+		tester_test_failed();
+		return;
+	}
+
+	addr_type = data->hciemu_type == HCIEMU_TYPE_BREDRLE ? BDADDR_BREDR :
+							BDADDR_LE_PUBLIC;
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_hci_connect(bthost, master_bdaddr, addr_type);
+}
+
 int main(int argc, char *argv[])
 {
 	tester_init(&argc, &argv);
@@ -3892,12 +4049,19 @@
 				&load_ltks_invalid_params_test_3,
 				NULL, test_command_generic);
 
+	test_bredrle("Set IO Capability - Invalid Params 1",
+				&set_io_cap_invalid_param_test_1,
+				NULL, test_command_generic);
+
 	test_bredrle("Pair Device - Not Powered 1",
 				&pair_device_not_powered_test_1,
 				NULL, test_command_generic);
 	test_bredrle("Pair Device - Invalid Parameters 1",
 				&pair_device_invalid_param_test_1,
 				NULL, test_command_generic);
+	test_bredrle("Pair Device - Invalid Parameters 2",
+				&pair_device_invalid_param_test_2,
+				NULL, test_command_generic);
 	test_bredrle("Pair Device - Legacy Success 1",
 				&pair_device_success_test_1,
 				NULL, test_command_generic);
@@ -3950,6 +4114,9 @@
 	test_bredrle("Pairing Acceptor - SSP 2",
 				&pairing_acceptor_ssp_2, setup_ssp_acceptor,
 				test_pairing_acceptor);
+	test_bredrle("Pairing Acceptor - SSP 3",
+				&pairing_acceptor_ssp_3, setup_ssp_acceptor,
+				test_pairing_acceptor);
 
 	test_bredrle("Unpair Device - Not Powered 1",
 				&unpair_device_not_powered_test_1,
@@ -4013,5 +4180,12 @@
 				&set_privacy_nval_param_test,
 				NULL, test_command_generic);
 
+	test_bredrle("Get Conn Info - Success",
+				&get_conn_info_succes1_test, NULL,
+				test_command_generic_connect);
+	test_bredrle("Get Conn Info - Not Connected",
+				&get_conn_info_ncon_test, NULL,
+				test_command_generic);
+
 	return tester_run();
 }
diff --git a/tools/parser/avdtp.c b/tools/parser/avdtp.c
index 5a2ee55..5969067 100644
--- a/tools/parser/avdtp.c
+++ b/tools/parser/avdtp.c
@@ -150,6 +150,13 @@
 	return "Unknown";
 }
 
+static char *vndcodec2str(uint32_t vendor, uint16_t vndcodec)
+{
+	if (vendor == 0x0000004f && vndcodec == 0x0001)
+		return "aptX";
+	return "Unknown";
+}
+
 static char *cat2str(uint8_t cat)
 {
 	switch (cat) {
@@ -212,12 +219,25 @@
 		len = get_u8(frm);
 
 		if (cat == 7) {
-			uint8_t type, codec, tmp;
+			uint8_t type, codec;
+			uint16_t tmp, freq, vndcodec = 0;
+			uint32_t bitrate, vendor = 0;
+			int i;
 
 			type  = get_u8(frm);
 			codec = get_u8(frm);
 
-			printf("%s - %s\n", cat2str(cat), codec2str(type, codec));
+			if (codec == 255) {
+				vendor = btohl(htonl(get_u32(frm)));
+				vndcodec = btohs(htons(get_u16(frm)));
+
+				printf("%s - %s (%s)\n", cat2str(cat),
+						codec2str(type, codec),
+						vndcodec2str(vendor, vndcodec));
+			} else {
+				printf("%s - %s\n", cat2str(cat),
+							codec2str(type, codec));
+			}
 
 			switch (codec) {
 			case 0:
@@ -269,6 +289,143 @@
 				p_indent(level + 1, frm);
 				printf("Bitpool Range %d-%d\n", tmp, get_u8(frm));
 				break;
+			case 1:
+				tmp = get_u8(frm);
+				p_indent(level + 1, frm);
+				printf("Layers: ");
+				if (tmp & 0x80)
+					printf("1 ");
+				if (tmp & 0x40)
+					printf("2 ");
+				if (tmp & 0x20)
+					printf("3 ");
+				printf("\n");
+				p_indent(level + 1, frm);
+				printf("CRC Protection: %s\n",
+						tmp & 0x10 ? "Yes" : "No");
+				p_indent(level + 1, frm);
+				if (tmp & 0x08)
+					printf("Mono ");
+				if (tmp & 0x04)
+					printf("DualChannel ");
+				if (tmp & 0x02)
+					printf("Stereo ");
+				if (tmp & 0x01)
+					printf("JointStereo ");
+				printf("\n");
+				tmp = get_u8(frm);
+				p_indent(level + 1, frm);
+				printf("Media Payload Format: RFC-2250 %s\n",
+						tmp & 0x40 ? "RFC-3119" : "");
+				p_indent(level + 1, frm);
+				if (tmp & 0x20)
+					printf("16kHz ");
+				if (tmp & 0x10)
+					printf("22.05kHz ");
+				if (tmp & 0x08)
+					printf("24kHz ");
+				if (tmp & 0x04)
+					printf("32kHz ");
+				if (tmp & 0x02)
+					printf("44.1kHz ");
+				if (tmp & 0x01)
+					printf("48kHz ");
+				printf("\n");
+				tmp = get_u16(frm);
+				p_indent(level + 1, frm);
+				printf("VBR: %s\n",
+						tmp & 0x8000 ? "Yes" : "No");
+				p_indent(level + 1, frm);
+				printf("Bit Rate Indexes: ");
+				if (tmp & 0x8000) {
+					printf("n/a");
+				} else {
+					for (i = 0; i < 15; i++, tmp >>= 1)
+						if (tmp & 0x0001)
+							printf("%d ", i);
+				}
+				printf("\n");
+				break;
+			case 2:
+				tmp = get_u8(frm);
+				p_indent(level + 1, frm);
+				if (tmp & 0x80)
+					printf("MPEG-2 AAC LC ");
+				if (tmp & 0x40)
+					printf("MPEG-4 AAC LC ");
+				if (tmp & 0x20)
+					printf("MPEG-4 AAC LTP ");
+				if (tmp & 0x10)
+					printf("MPEG-4 AAC scalable ");
+				printf("\n");
+				tmp = get_u16(frm);
+				freq = tmp >> 4;
+				p_indent(level + 1, frm);
+				if (freq & 0x0800)
+					printf("8kHz ");
+				if (freq & 0x0400)
+					printf("11.025kHz ");
+				if (freq & 0x0200)
+					printf("12kHz ");
+				if (freq & 0x0100)
+					printf("16kHz ");
+				if (freq & 0x0080)
+					printf("22.05kHz ");
+				if (freq & 0x0040)
+					printf("24kHz ");
+				if (freq & 0x0020)
+					printf("32kHz ");
+				if (freq & 0x0010)
+					printf("44.1kHz ");
+				if (freq & 0x0008)
+					printf("48kHz ");
+				if (freq & 0x0004)
+					printf("64kHz ");
+				if (freq & 0x0002)
+					printf("88.2kHz ");
+				if (freq & 0x0001)
+					printf("96kHz ");
+				printf("\n");
+				tmp >>= 2;
+				p_indent(level + 1, frm);
+				if (tmp & 0x02)
+					printf("1 ");
+				if (tmp & 0x01)
+					printf("2 ");
+				printf("Channels\n");
+				tmp = get_u8(frm);
+				bitrate = ((tmp & 0x7f) << 16) | get_u16(frm);
+				p_indent(level + 1, frm);
+				printf("%ubps ", bitrate);
+				printf("%s\n", tmp & 0x80 ? "VBR" : "");
+				break;
+			case 255:
+				if (vendor == 0x0000004f &&
+							vndcodec == 0x0001) {
+					tmp = get_u8(frm);
+					p_indent(level + 1, frm);
+					if (tmp & 0x80)
+						printf("16kHz ");
+					if (tmp & 0x40)
+						printf("32kHz ");
+					if (tmp & 0x20)
+						printf("44.1kHz ");
+					if (tmp & 0x10)
+						printf("48kHz ");
+					printf("\n");
+					p_indent(level + 1, frm);
+					if (tmp & 0x02)
+						printf("Stereo ");
+					if (tmp & 0x01)
+						printf("Mono ");
+					printf("\n");
+					break;
+				} else {
+					hex_dump(level + 1, frm, len - 8);
+					frm->ptr += (len - 8);
+					frm->len -= (len - 8);
+				}
+				break;
 			default:
 				hex_dump(level + 1, frm, len - 2);
 				frm->ptr += (len - 2);
diff --git a/tools/smp-tester.c b/tools/smp-tester.c
index e09c802..1e0e1b0 100644
--- a/tools/smp-tester.c
+++ b/tools/smp-tester.c
@@ -81,6 +81,7 @@
 struct smp_data {
 	const struct smp_req_rsp *req;
 	size_t req_count;
+	bool mitm;
 };
 
 static void mgmt_debug(const char *str, void *user_data)
@@ -282,7 +283,7 @@
 };
 
 static const uint8_t smp_nval_req_3[] = { 0x01, 0xff };
-static const uint8_t smp_nval_req_3_rsp[] = { 0x05, 0x08 };
+static const uint8_t smp_nval_req_3_rsp[] = { 0x05, 0x0a };
 
 static const struct smp_req_rsp srv_nval_req_2[] = {
 	{ smp_nval_req_2, sizeof(smp_nval_req_3),
@@ -342,6 +343,29 @@
 	.req_count = G_N_ELEMENTS(cli_basic_req_1),
 };
 
+static const uint8_t smp_basic_req_2[] = {	0x01,	/* Pairing Request */
+						0x04,	/* NoInputNoOutput */
+						0x00,	/* OOB Flag */
+						0x05,	/* Bonding - MITM */
+						0x10,	/* Max key size */
+						0x05,	/* Init. key dist. */
+						0x05,	/* Rsp. key dist. */
+};
+static const struct smp_req_rsp cli_basic_req_2[] = {
+	{ NULL, 0, smp_basic_req_2, sizeof(smp_basic_req_2) },
+	{ smp_basic_req_1_rsp, sizeof(smp_basic_req_1_rsp),
+			smp_confirm_req_1, sizeof(smp_confirm_req_1) },
+	{ smp_confirm_req_1, sizeof(smp_confirm_req_1),
+			smp_random_req_1, sizeof(smp_random_req_1) },
+	{ smp_random_req_1, sizeof(smp_random_req_1), NULL, 0 },
+};
+
+static const struct smp_data smp_client_basic_req_2_test = {
+	.req = cli_basic_req_2,
+	.req_count = G_N_ELEMENTS(cli_basic_req_1),
+	.mitm = true,
+};
+
 static void client_connectable_complete(uint16_t opcode, uint8_t status,
 					const void *param, uint8_t len,
 					void *user_data)
@@ -601,6 +625,7 @@
 static void test_client(const void *test_data)
 {
 	struct test_data *data = tester_get_data();
+	const struct smp_data *smp = data->test_data;
 	struct mgmt_cp_pair_device cp;
 	struct bthost *bthost;
 
@@ -611,7 +636,10 @@
 
 	memcpy(&cp.addr.bdaddr, data->ra, sizeof(data->ra));
 	cp.addr.type = BDADDR_LE_PUBLIC;
-	cp.io_cap = 0x03; /* NoInputNoOutput */
+	if (smp->mitm)
+		cp.io_cap = 0x04; /* KeyboardDisplay */
+	else
+		cp.io_cap = 0x03; /* NoInputNoOutput */
 
 	mgmt_send(data->mgmt, MGMT_OP_PAIR_DEVICE, data->mgmt_index,
 			sizeof(cp), &cp, pair_device_complete, NULL, NULL);
@@ -687,6 +715,9 @@
 	test_smp("SMP Client - Basic Request 1",
 					&smp_client_basic_req_1_test,
 					setup_powered_client, test_client);
+	test_smp("SMP Client - Basic Request 2",
+					&smp_client_basic_req_2_test,
+					setup_powered_client, test_client);
 
 	return tester_run();
 }
diff --git a/unit/test-avrcp.c b/unit/test-avrcp.c
index c41f2e6..e8cf34a 100644
--- a/unit/test-avrcp.c
+++ b/unit/test-avrcp.c
@@ -46,6 +46,7 @@
 struct test_pdu {
 	bool valid;
 	bool fragmented;
+	bool continuing;
 	bool browse;
 	const uint8_t *data;
 	size_t size;
@@ -93,6 +94,14 @@
 		.size = sizeof(data(args)),			\
 	}
 
+#define cont_pdu(args...)					\
+	{							\
+		.valid = true,					\
+		.continuing = true,				\
+		.data = data(args),				\
+		.size = sizeof(data(args)),			\
+	}
+
 #define define_test(name, function, args...)				\
 	do {								\
 		const struct test_pdu pdus[] = {			\
@@ -195,7 +204,8 @@
 	if (g_test_verbose())
 		util_hexdump('>', buf, len, test_debug, "AVRCP: ");
 
-	g_assert_cmpint(len, ==, pdu->size);
+	if (!pdu->continuing)
+		g_assert_cmpint(len, ==, pdu->size);
 
 	g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
 
@@ -486,9 +496,20 @@
 					uint64_t uid, uint8_t number,
 					uint32_t *attrs, void *user_data)
 {
+	struct context *context = user_data;
+
 	DBG("");
 
-	avrcp_get_element_attrs_rsp(session, transaction, NULL, 0);
+	if (g_str_has_prefix(context->data->test_name, "/TP/RCR")) {
+		uint8_t params[1024];
+
+		memset(params, 0x00, sizeof(params) / 2);
+		memset(params + (sizeof(params) / 2), 0xff, sizeof(params) / 2);
+
+		avrcp_get_element_attrs_rsp(session, transaction, params,
+							sizeof(params));
+	} else
+		avrcp_get_element_attrs_rsp(session, transaction, NULL, 0);
 
 	return -EAGAIN;
 }
@@ -1623,5 +1644,43 @@
 				0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
 				0x00, 0x00, 0x01, 0x00));
 
+	/* Request continuing response - TG */
+	define_test("/TP/RCR/BV-02-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
+			cont_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x01, 0x01, 0xf9),
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REQUEST_CONTINUING,
+				0x00, 0x00, 0x01, AVRCP_GET_ELEMENT_ATTRIBUTES),
+			cont_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x02, 0x01, 0xf9),
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REQUEST_CONTINUING,
+				0x00, 0x00, 0x01, AVRCP_GET_ELEMENT_ATTRIBUTES),
+			cont_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x03, 0x00, 0x0e));
+
+	/* Abort continuing response - TG */
+	define_test("/TP/RCR/BV-04-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
+			cont_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x01, 0x01, 0xf9),
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_ABORT_CONTINUING,
+				0x00, 0x00, 0x01, AVRCP_GET_ELEMENT_ATTRIBUTES),
+			raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_ABORT_CONTINUING,
+				0x00, 0x00, 0x00));
+
 	return g_test_run();
 }
diff --git a/unit/test-crypto.c b/unit/test-crypto.c
new file mode 100644
index 0000000..fd2ea78
--- /dev/null
+++ b/unit/test-crypto.c
@@ -0,0 +1,169 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Intel Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "src/shared/crypto.h"
+
+#include <string.h>
+#include <glib.h>
+
+static struct bt_crypto *crypto;
+
+struct test_data {
+	const uint8_t *msg;
+	uint16_t msg_len;
+	const uint8_t *t;
+};
+
+static const uint8_t key[] = {
+	0x3c, 0x4f, 0xcf, 0x09, 0x88, 0x15, 0xf7, 0xab, 0xa6, 0xd2, 0xae, 0x28,
+	0x16, 0x15, 0x7e, 0x2b
+};
+
+static const uint8_t msg_1[] = { 0x00 };
+
+static const uint8_t t_msg_1[] = {
+	0x00, 0x00, 0x00, 0x00, 0xb3, 0xa8, 0x59, 0x41, 0x27, 0xeb, 0xc2, 0xc0
+};
+
+static const struct test_data test_data_1 = {
+	.msg = msg_1,
+	.msg_len = 0,
+	.t = t_msg_1
+};
+
+static const uint8_t msg_2[] = {
+	0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11,
+	0x73, 0x93, 0x17, 0x2a
+
+};
+
+static const uint8_t t_msg_2[] = {
+	0x00, 0x00, 0x00, 0x00, 0x27, 0x39, 0x74, 0xf4, 0x39, 0x2a, 0x23, 0x2a
+};
+
+static const struct test_data test_data_2 = {
+	.msg = msg_2,
+	.msg_len = 16,
+	.t = t_msg_2
+};
+
+static const uint8_t msg_3[] = {
+	0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11,
+	0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+	0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46,
+	0xa3, 0x5c, 0xe4, 0x11
+};
+
+static const uint8_t t_msg_3[12] = {
+	0x00, 0x00, 0x00, 0x00, 0xb7, 0xca, 0x94, 0xab, 0x87, 0xc7, 0x82, 0x18
+};
+
+static const struct test_data test_data_3 = {
+	.msg = msg_3,
+	.msg_len = 40,
+	.t = t_msg_3
+};
+
+static const uint8_t msg_4[] = {
+	0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11,
+	0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+	0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46,
+	0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+	0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b,
+	0xe6, 0x6c, 0x37, 0x10
+};
+
+static const uint8_t t_msg_4[12] = {
+	0x00, 0x00, 0x00, 0x00, 0x44, 0xe1, 0xe6, 0xce, 0x1d, 0xf5, 0x13, 0x68
+};
+
+static const struct test_data test_data_4 = {
+	.msg = msg_4,
+	.msg_len = 64,
+	.t = t_msg_4
+};
+
+static void print_buf(const uint8_t *t, uint8_t len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		g_print("0x%02x, ", t[i]);
+
+	g_print("\n");
+}
+
+static bool result_compare(const uint8_t exp[12], uint8_t res[12])
+{
+	int i;
+	for (i = 0; i < 12; i++)
+		if (exp[i] != res[i])
+			return false;
+
+	return true;
+}
+
+static void test_sign(gconstpointer data)
+{
+	uint8_t t[12];
+	const struct test_data *d = data;
+
+	memset(t, 0, 12);
+	if (!bt_crypto_sign_att(crypto, key, d->msg, d->msg_len, 0, t))
+		g_assert(true);
+
+	if (g_test_verbose()) {
+		g_print("Result T: ");
+		print_buf(t, 12);
+		g_print("Expected T:");
+		print_buf(d->t, 12);
+	}
+
+	g_assert(result_compare(d->t, t));
+}
+
+int main(int argc, char *argv[])
+{
+	int exit_status;
+
+	crypto = bt_crypto_new();
+	if (!crypto)
+		return 0;
+
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_data_func("/crypto/sign_att_1", &test_data_1, test_sign);
+	g_test_add_data_func("/crypto/sign_att_2", &test_data_2, test_sign);
+	g_test_add_data_func("/crypto/sign_att_3", &test_data_3, test_sign);
+	g_test_add_data_func("/crypto/sign_att_4", &test_data_4, test_sign);
+
+	exit_status = g_test_run();
+
+	bt_crypto_unref(crypto);
+
+	return exit_status;
+}
diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index 20aa0b5..a8801b0 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -465,6 +465,9 @@
 									'\r'),
 			type_pdu(HFP_GW_CMD_TYPE_SET, 0),
 			data_end());
+	define_test("/hfp/test_empty", test_fragmented, NULL,
+			raw_pdu('\r'),
+			data_end());
 
 	return g_test_run();
 }
diff --git a/unit/test-queue.c b/unit/test-queue.c
index 7c6d2ad..af7dfd3 100644
--- a/unit/test-queue.c
+++ b/unit/test-queue.c
@@ -58,11 +58,53 @@
 	queue_destroy(queue, NULL);
 }
 
+static void foreach_destroy(void *data, void *user_data)
+{
+	struct queue *queue = user_data;
+
+	queue_destroy(queue, NULL);
+}
+
+static void test_foreach_destroy(void)
+{
+	struct queue *queue;
+
+	queue = queue_new();
+	g_assert(queue != NULL);
+
+	queue_push_tail(queue, UINT_TO_PTR(1));
+	queue_push_tail(queue, UINT_TO_PTR(2));
+
+	queue_foreach(queue, foreach_destroy, queue);
+}
+
+static void foreach_remove_all(void *data, void *user_data)
+{
+	struct queue *queue = user_data;
+
+	queue_remove_all(queue, NULL, NULL, NULL);
+}
+
+static void test_foreach_remove_all(void)
+{
+	struct queue *queue;
+
+	queue = queue_new();
+	g_assert(queue != NULL);
+
+	queue_push_tail(queue, UINT_TO_PTR(1));
+	queue_push_tail(queue, UINT_TO_PTR(2));
+
+	queue_foreach(queue, foreach_remove_all, queue);
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
 
 	g_test_add_func("/queue/basic", test_basic);
+	g_test_add_func("/queue/foreach_destroy", test_foreach_destroy);
+	g_test_add_func("/queue/foreach_remove_all", test_foreach_remove_all);
 
 	return g_test_run();
 }
diff --git a/unit/test-uhid.c b/unit/test-uhid.c
new file mode 100644
index 0000000..85e1356
--- /dev/null
+++ b/unit/test-uhid.c
@@ -0,0 +1,310 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 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 program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "src/shared/uhid.h"
+#include "src/shared/util.h"
+
+struct test_pdu {
+	bool valid;
+	const uint8_t *data;
+	size_t size;
+};
+
+struct test_data {
+	char *test_name;
+	struct test_pdu *pdu_list;
+};
+
+struct context {
+	GMainLoop *main_loop;
+	struct bt_uhid *uhid;
+	guint source;
+	guint process;
+	int fd;
+	unsigned int pdu_offset;
+	const struct test_data *data;
+};
+
+#define event(args...)						\
+	{							\
+		.valid = true,					\
+		.data = (void *) args,				\
+		.size = sizeof(*args),				\
+	}
+
+#define define_test(name, function, args...)				\
+	do {								\
+		const struct test_pdu pdus[] = {			\
+			args, { }					\
+		};							\
+		static struct test_data data;				\
+		data.test_name = g_strdup(name);			\
+		data.pdu_list = g_malloc(sizeof(pdus));			\
+		memcpy(data.pdu_list, pdus, sizeof(pdus));		\
+		g_test_add_data_func(name, &data, function);		\
+	} while (0)
+
+static void test_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	g_print("%s%s\n", prefix, str);
+}
+
+static void test_free(gconstpointer user_data)
+{
+	const struct test_data *data = user_data;
+
+	g_free(data->test_name);
+	g_free(data->pdu_list);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+	struct context *context = user_data;
+
+	if (context->process > 0)
+		g_source_remove(context->process);
+
+	g_main_loop_quit(context->main_loop);
+
+	return FALSE;
+}
+
+static gboolean send_pdu(gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	ssize_t len;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	len = write(context->fd, pdu->data, pdu->size);
+
+	if (g_test_verbose())
+		util_hexdump('<', pdu->data, len, test_debug, "uHID: ");
+
+	g_assert_cmpint(len, ==, pdu->size);
+
+	context->process = 0;
+	return FALSE;
+}
+
+static void context_process(struct context *context)
+{
+	if (!context->data->pdu_list[context->pdu_offset].valid) {
+		context_quit(context);
+		return;
+	}
+
+	context->process = g_idle_add(send_pdu, context);
+}
+
+static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	unsigned char buf[sizeof(struct uhid_event)];
+	ssize_t len;
+	int fd;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		context->source = 0;
+		g_print("%s: cond %x\n", __func__, cond);
+		return FALSE;
+	}
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	len = read(fd, buf, sizeof(buf));
+
+	g_assert(len > 0);
+
+	if (g_test_verbose())
+		util_hexdump('>', buf, len, test_debug, "uHID: ");
+
+	g_assert_cmpint(len, ==, pdu->size);
+
+	g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+
+	context_process(context);
+
+	return TRUE;
+}
+
+static struct context *create_context(gconstpointer data)
+{
+	struct context *context = g_new0(struct context, 1);
+	GIOChannel *channel;
+	int err, sv[2];
+
+	context->main_loop = g_main_loop_new(NULL, FALSE);
+	g_assert(context->main_loop);
+
+	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+	g_assert(err == 0);
+
+	context->uhid = bt_uhid_new(sv[0]);
+	g_assert(context->uhid != NULL);
+
+	channel = g_io_channel_unix_new(sv[1]);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	context->source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				test_handler, context);
+	g_assert(context->source > 0);
+
+	g_io_channel_unref(channel);
+
+	context->fd = sv[1];
+	context->data = data;
+
+	return context;
+}
+
+static void destroy_context(struct context *context)
+{
+	if (context->source > 0)
+		g_source_remove(context->source);
+
+	bt_uhid_unref(context->uhid);
+
+	g_main_loop_unref(context->main_loop);
+
+	test_free(context->data);
+	g_free(context);
+}
+
+static void execute_context(struct context *context)
+{
+	g_main_loop_run(context->main_loop);
+
+	destroy_context(context);
+}
+
+static const struct uhid_event ev_create = {
+	.type = UHID_CREATE,
+};
+
+static const struct uhid_event ev_destroy = {
+	.type = UHID_DESTROY,
+};
+
+static const struct uhid_event ev_feature_answer = {
+	.type = UHID_FEATURE_ANSWER,
+};
+
+static const struct uhid_event ev_input = {
+	.type = UHID_INPUT,
+};
+
+static const struct uhid_event ev_output = {
+	.type = UHID_OUTPUT,
+};
+
+static const struct uhid_event ev_feature = {
+	.type = UHID_FEATURE,
+};
+
+static void test_client(gconstpointer data)
+{
+	struct context *context = create_context(data);
+
+	if (g_str_equal(context->data->test_name, "/uhid/command/create"))
+		bt_uhid_send(context->uhid, &ev_create);
+
+	if (g_str_equal(context->data->test_name, "/uhid/command/destroy"))
+		bt_uhid_send(context->uhid, &ev_destroy);
+
+	if (g_str_equal(context->data->test_name,
+						"/uhid/command/feature_answer"))
+		bt_uhid_send(context->uhid, &ev_feature_answer);
+
+	if (g_str_equal(context->data->test_name, "/uhid/command/input"))
+		bt_uhid_send(context->uhid, &ev_input);
+
+	execute_context(context);
+}
+
+static void handle_output(struct uhid_event *ev, void *user_data)
+{
+	g_assert_cmpint(ev->type, ==, UHID_OUTPUT);
+
+	context_quit(user_data);
+}
+
+static void handle_feature(struct uhid_event *ev, void *user_data)
+{
+	g_assert_cmpint(ev->type, ==, UHID_FEATURE);
+
+	context_quit(user_data);
+}
+
+static void test_server(gconstpointer data)
+{
+	struct context *context = create_context(data);
+
+	bt_uhid_register(context->uhid, UHID_OUTPUT, handle_output, context);
+	bt_uhid_register(context->uhid, UHID_FEATURE, handle_feature, context);
+
+	g_idle_add(send_pdu, context);
+
+	execute_context(context);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	define_test("/uhid/command/create", test_client, event(&ev_create));
+	define_test("/uhid/command/destroy", test_client, event(&ev_destroy));
+	define_test("/uhid/command/feature_answer", test_client,
+						event(&ev_feature_answer));
+	define_test("/uhid/command/input", test_client, event(&ev_input));
+
+	define_test("/uhid/event/output", test_server, event(&ev_output));
+	define_test("/uhid/event/feature", test_server, event(&ev_feature));
+
+	return g_test_run();
+}