Merge BlueZ 5.38 to master

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

Change-Id: I3f6dbf0f923ad44d4c59a2c3525bf92ea932057f
diff --git a/ChangeLog b/ChangeLog
index c7a4fc0..ed3bff6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+ver 5.38:
+	Fix issue with stack overflow and UUID handling.
+	Fix issue with ObjectManager interface and GATT.
+	Fix issue with GATT database and error handling.
+	Fix issue with GATT client notifications.
+	Fix issue with GATT object ordering.
+	Fix issue with GATT default MTU exchange.
+	Fix issue with device attribute clearing.
+	Fix issue with AVRCP capabilities request.
+
 ver 5.37:
 	Fix issue with registering external profiles.
 	Fix issue with connecting external profiles.
diff --git a/Makefile.am b/Makefile.am
index 1a48a71..bac371b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -80,7 +80,7 @@
 lib_LTLIBRARIES += lib/libbluetooth.la
 
 lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
-lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:10:18
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:11:18
 lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
 endif
 
diff --git a/TODO b/TODO
index 8c710ef..d88349e 100644
--- a/TODO
+++ b/TODO
@@ -136,11 +136,6 @@
   Priority: Medium
   Complexity: C1
 
-- Persist client attribute cache across reboots.
-
-  Priority: Medium
-  Complexity: C4
-
 - Move all daemon plugins and profiles that are GATT based to use
   shared/gatt-client instead of attrib/*. This is a complicated task that
   potentially needs a new plugin/profile probing interface and a lot of
@@ -162,12 +157,6 @@
   Priority: Low
   Complexity: C1
 
-- Implement the server portion of doc/gatt-api.txt using shared/gatt-server once
-  it exists.
-
-  Priority: Medium
-  Complexity: C4
-
 - Send out indications from the "Service Changed" characteristic upon
   reconnection if a bonded device is not connected when the local database is
   modified.
@@ -195,94 +184,5 @@
   Priority: Medium
   Complexity: C1
 
-- The recently added support for ATT signed writes requires the following kernel
-  modules to be enabled:
-
-     CONFIG_CRYPTO_USER_API
-     CONFIG_CRYPTO_USER_API_HASH
-     CONFIG_CRYPTO_USER_API_SKCIPHER
-
-  Currently, if these are not enabled, bt_att_new silently returns NULL. We
-  should handle this more gracefully by not supporting signed writes if we can't
-  initialize bt_crypto while succeeding bt_att initialization regardless.
-
-  This behavior should be documented in the README.
-
-  Priority: High
-  Complexity: C1
-
-
-ATT/GATT (old/outdated)
-=======================
-
-- At the moment authentication and authorization is not supported at the
-  same time, read/write requirements in the attribute server needs to
-  be extended. According to Bluetooth Specification a server shall check
-  authentication and authorization requirements before any other check is
-  performed.
-
-  Priority: Medium
-  Complexity: C1
-
-- Implement ATT PDU validation. Malformed PDUs can cause division by zero
-  when decoding PDUs. A proper error PDU should be returned for this case.
-  See decoding function in att.c file.
-
-  Priority: Medium
-  Complexity: C1
-
-- Refactor read_by_group() and read_by_type() in src/attrib-server.c
-  (they've grown simply too big). First step could be to move out the
-  long for-loops to new functions called e.g. get_groups() and get_types().
-
-  Priority: Low
-  Complexity: C1
-
-- Agent for characteristics: Agent interface should be extended to support
-  authorization per characteristic if the remote is not in the trusted list.
-
-  Priority: Low
-  Complexity: C1
-
-- gatttool should have the ability to wait for req responses before
-  quitting (some servers require a small sleep even with cmd's). Maybe a
-  --delay-exit or --timeout command line switch.
-
-  Priority: Low
-  Complexity: C1
-
-- Client needs to export a property in the Device Characteristic hierarchy
-  to manage characteristic value changes reports in the remote device.
-  Currently, Client Characteristic Configuration attribute is not exposed
-  as an object. The user needs to use gatttool to change the value of the
-  this attribute to receive notification/indications. Export this attribute
-  as a property is a proposal that needs further discussion.
-
-  Priority: Low
-  Complexity: C1
-
-- Attribute server should process queued GATT/ATT commands if the
-  client disconnects. The client can simply send a command and quit,
-  without wait for a response(ex: Write Command). For this scenario
-  that the client disconnects the link quickly the queued received
-  command is ignored.
-
-  Priority: Low
-  Complecity: C1
-
-- Implement Server characteristic Configuration support in the attribute
-  server to manage characteristic value broadcasting. There is a single
-  instance of the Server Characteristic Configuration for all clients.
-  See Volume 3, Part G, section 3.3.3.4 for more information.
-
-  Priority: Low
-  Complexity: C1
-
-- Long write is not implemented. Attribute server, client and command line
-  tool shall be changed to support this feature.
-
-  Priority: Low
-  Complexity: C2
-
 Management Interface
 ====================
diff --git a/client/main.c b/client/main.c
index b695744..1063755 100644
--- a/client/main.c
+++ b/client/main.c
@@ -321,45 +321,6 @@
 	return FALSE;
 }
 
-static void proxy_added(GDBusProxy *proxy, void *user_data)
-{
-	const char *interface;
-
-	interface = g_dbus_proxy_get_interface(proxy);
-
-	if (!strcmp(interface, "org.bluez.Device1")) {
-		if (device_is_child(proxy, default_ctrl) == TRUE) {
-			dev_list = g_list_append(dev_list, proxy);
-
-			print_device(proxy, COLORED_NEW);
-		}
-	} else if (!strcmp(interface, "org.bluez.Adapter1")) {
-		ctrl_list = g_list_append(ctrl_list, proxy);
-
-		if (!default_ctrl)
-			default_ctrl = proxy;
-
-		print_adapter(proxy, COLORED_NEW);
-	} else if (!strcmp(interface, "org.bluez.AgentManager1")) {
-		if (!agent_manager) {
-			agent_manager = proxy;
-
-			if (auto_register_agent)
-				agent_register(dbus_conn, agent_manager,
-							auto_register_agent);
-		}
-	} else if (!strcmp(interface, "org.bluez.GattService1")) {
-		if (service_is_child(proxy))
-			gatt_add_service(proxy);
-	} else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
-		gatt_add_characteristic(proxy);
-	} else if (!strcmp(interface, "org.bluez.GattDescriptor1")) {
-		gatt_add_descriptor(proxy);
-	} else if (!strcmp(interface, "org.bluez.GattManager1")) {
-		gatt_add_manager(proxy);
-	}
-}
-
 static void set_default_device(GDBusProxy *proxy, const char *attribute)
 {
 	char *desc = NULL;
@@ -393,6 +354,64 @@
 	g_free(desc);
 }
 
+static void device_added(GDBusProxy *proxy)
+{
+	DBusMessageIter iter;
+
+	dev_list = g_list_append(dev_list, proxy);
+
+	print_device(proxy, COLORED_NEW);
+
+	if (default_dev)
+		return;
+
+	if (g_dbus_proxy_get_property(proxy, "Connected", &iter)) {
+		dbus_bool_t connected;
+
+		dbus_message_iter_get_basic(&iter, &connected);
+
+		if (connected)
+			set_default_device(proxy, NULL);
+	}
+}
+
+static void proxy_added(GDBusProxy *proxy, void *user_data)
+{
+	const char *interface;
+
+	interface = g_dbus_proxy_get_interface(proxy);
+
+	if (!strcmp(interface, "org.bluez.Device1")) {
+		if (device_is_child(proxy, default_ctrl) == TRUE)
+			device_added(proxy);
+
+	} else if (!strcmp(interface, "org.bluez.Adapter1")) {
+		ctrl_list = g_list_append(ctrl_list, proxy);
+
+		if (!default_ctrl)
+			default_ctrl = proxy;
+
+		print_adapter(proxy, COLORED_NEW);
+	} else if (!strcmp(interface, "org.bluez.AgentManager1")) {
+		if (!agent_manager) {
+			agent_manager = proxy;
+
+			if (auto_register_agent)
+				agent_register(dbus_conn, agent_manager,
+							auto_register_agent);
+		}
+	} else if (!strcmp(interface, "org.bluez.GattService1")) {
+		if (service_is_child(proxy))
+			gatt_add_service(proxy);
+	} else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
+		gatt_add_characteristic(proxy);
+	} else if (!strcmp(interface, "org.bluez.GattDescriptor1")) {
+		gatt_add_descriptor(proxy);
+	} else if (!strcmp(interface, "org.bluez.GattManager1")) {
+		gatt_add_manager(proxy);
+	}
+}
+
 static void set_default_attribute(GDBusProxy *proxy)
 {
 	const char *path;
@@ -1314,25 +1333,10 @@
 	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
 }
 
-static void cmd_remove(const char *arg)
+static void remove_device(GDBusProxy *proxy)
 {
-	GDBusProxy *proxy;
 	char *path;
 
-	if (!arg || !strlen(arg)) {
-		rl_printf("Missing device address argument\n");
-		return;
-	}
-
-	if (check_default_ctrl() == FALSE)
-		return;
-
-	proxy = find_proxy_by_address(dev_list, arg);
-	if (!proxy) {
-		rl_printf("Device %s not available\n", arg);
-		return;
-	}
-
 	path = g_strdup(g_dbus_proxy_get_path(proxy));
 
 	if (g_dbus_proxy_method_call(default_ctrl, "RemoveDevice",
@@ -1341,8 +1345,40 @@
 						path, g_free) == FALSE) {
 		rl_printf("Failed to remove device\n");
 		g_free(path);
+	}
+}
+
+static void cmd_remove(const char *arg)
+{
+	GDBusProxy *proxy;
+
+	if (!arg || !strlen(arg)) {
+		rl_printf("Missing device address argument\n");
 		return;
 	}
+
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	if (strcmp(arg, "*") == 0) {
+		GList *list;
+
+		for (list = g_list_first(dev_list); list; list = g_list_next(list)) {
+			GDBusProxy *proxy = list->data;
+
+			remove_device(proxy);
+		}
+
+		return;
+	}
+
+	proxy = find_proxy_by_address(dev_list, arg);
+	if (!proxy) {
+		rl_printf("Device %s not available\n", arg);
+		return;
+	}
+
+	remove_device(proxy);
 }
 
 static void connect_reply(DBusMessage *message, void *user_data)
diff --git a/configure.ac b/configure.ac
index 7ec7c32..bf40126 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(bluez, 5.37)
+AC_INIT(bluez, 5.38)
 
 AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
 					tar-pax no-dist-gzip dist-xz])
diff --git a/doc/adapter-api.txt b/doc/adapter-api.txt
index 284214e..4dbf879 100644
--- a/doc/adapter-api.txt
+++ b/doc/adapter-api.txt
@@ -102,6 +102,7 @@
 			right after call to StartDiscovery.
 
 			Possible errors: org.bluez.Error.NotReady
+					 org.bluez.Error.NotSupported
 					 org.bluez.Error.Failed
 
 Properties	string Address [readonly]
diff --git a/doc/advertising-api.txt b/doc/advertising-api.txt
index 4ee37bb..1c18ae3 100644
--- a/doc/advertising-api.txt
+++ b/doc/advertising-api.txt
@@ -3,7 +3,7 @@
 
 Advertising packets are structured data which is broadcast on the LE Advertising
 channels and available for all devices in range.  Because of the limited space
-available in LE Advertising packets (32 bytes), each packet's contents must be
+available in LE Advertising packets (31 bytes), each packet's contents must be
 carefully controlled.
 
 BlueZ acts as a store for the Advertisement Data which is meant to be sent.
diff --git a/doc/device-api.txt b/doc/device-api.txt
index a8076a2..96832e8 100644
--- a/doc/device-api.txt
+++ b/doc/device-api.txt
@@ -212,10 +212,7 @@
 			Service advertisement data. Keys are the UUIDs in
 			string format followed by its byte array value.
 
-		array{object} GattServices [readonly, optional]
+		bool ServicesResolved [readonly, experimental]
 
-			List of GATT service object paths. Each referenced
-			object exports the org.bluez.GattService1 interface and
-			represents a remote GATT service. This property will be
-			updated once all remote GATT services of this device
-			have been discovered and exported over D-Bus.
+			Indicate whether or not service discovery has been
+			resolved.
diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
index d832c73..232ffa0 100644
--- a/doc/gatt-api.txt
+++ b/doc/gatt-api.txt
@@ -45,14 +45,6 @@
 			belongs to. Only present on services from remote
 			devices.
 
-		array{object} Characteristics [read-only]
-
-			Array of object paths representing the characteristics
-			of this service. This property is set only when the
-			characteristic discovery has been completed, however the
-			characteristic objects will become available via
-			ObjectManager as soon as they get discovered.
-
 		array{object} Includes [read-only]: Not implemented
 
 			Array of object paths representing the included
@@ -153,15 +145,6 @@
 				"encrypt-authenticated-read"
 				"encrypt-authenticated-write"
 
-		array{object} Descriptors [read-only]
-
-			Array of object paths representing the descriptors
-			of this service. This property is set only when the
-			descriptor discovery has been completed, however the
-			descriptor objects will become available via
-			ObjectManager as soon as they get discovered.
-
-
 Characteristic Descriptors hierarchy
 ====================================
 
@@ -270,9 +253,9 @@
 containing two separate GATT services may look like this:
 
 -> /com/example
+  |   - org.freedesktop.DBus.ObjectManager
   |
   -> /com/example/service0
-  | |   - org.freedesktop.DBus.ObjectManager
   | |   - org.freedesktop.DBus.Properties
   | |   - org.bluez.GattService1
   | |
@@ -289,7 +272,6 @@
   |       - org.bluez.GattDescriptor1
   |
   -> /com/example/service1
-    |   - org.freedesktop.DBus.ObjectManager
     |   - org.freedesktop.DBus.Properties
     |   - org.bluez.GattService1
     |
@@ -309,21 +291,21 @@
 Interface	org.bluez.GattManager1 [Experimental]
 Object path	[variable prefix]/{hci0,hci1,...}
 
-Methods		void RegisterService(object service, dict options)
+Methods		void RegisterApplication(object application, dict options)
 
-			Registers a local GATT service hierarchy as described
+			Registers a local GATT services hierarchy as described
 			above.
 
-			"service" object path together with the D-Bus system
-			bus connection ID define the identification of the
-			application registering a GATT based service.
+			The application object path together with the D-Bus
+			system bus connection ID define the identification of
+			the application registering a GATT based service.
 
 			Possible errors: org.bluez.Error.InvalidArguments
 					 org.bluez.Error.AlreadyExists
 
-		void UnregisterService(object service)
+		void UnregisterApplication(object application)
 
-			This unregisters the service that has been
+			This unregisters the services that has been
 			previously registered. The object path parameter
 			must match the same value that has been used
 			on registration.
diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt
index d5b6c90..8393bbf 100644
--- a/doc/mgmt-api.txt
+++ b/doc/mgmt-api.txt
@@ -25,7 +25,8 @@
 Linux kernel v3.19	Version 1.8
 Linux kernel v4.1	Version 1.9
 Linux kernel v4.2	Version 1.10
-Linux kernel v4.5	Version 1.11 (not yet released)
+Linux kernel v4.5	Version 1.11
+Linux kernel v4.6	Version 1.12 (not yet released)
 
 Version 1.1 introduces Set Device ID command.
 
@@ -71,6 +72,8 @@
 
 Version 1.11 introduces Get Advertising Size Information command.
 
+Version 1.12 introduces a new limited privacy mode (value 0x02 passed to
+the Set Privacy command).
 
 Example
 =======
@@ -250,7 +253,7 @@
 				Name (249 Octets)
 				Short_Name (11 Octets)
 
-	This command is used to retreive the current state and basic
+	This command is used to retrieve the current state and basic
 	information of a controller. It is typically used right after
 	getting the response to the Read Controller Index List command
 	or an Index Added event.
@@ -762,8 +765,8 @@
 
 	This command is used to feed the kernel with currently known
 	link keys. The command does not need to be called again upon the
-	receiption of New Link Key events since the kernel updates its
-	list automatically.
+	receipt of New Link Key events since the kernel updates its list
+	automatically.
 
 	The Debug_Keys parameter is used to tell the kernel whether to
 	accept the usage of debug keys or not. The allowed values for
@@ -826,7 +829,7 @@
 
 	This command is used to feed the kernel with currently known
 	(SMP) Long Term Keys. The command does not need to be called
-	again upon the receiption of New Long Term Key events since the
+	again upon the receipt of New Long Term Key events since the
 	kernel updates its list automatically.
 
 	Possible values for the Address_Type parameter:
@@ -1023,7 +1026,7 @@
 
 	This command is used to trigger pairing with a remote device.
 	The IO_Capability command parameter is used to temporarily (for
-	this pairing event only) override the global IO Capaility (set
+	this pairing event only) override the global IO Capability (set
 	using the Set IO Capability command).
 
 	Possible values for the Address_Type parameter:
@@ -1567,7 +1570,7 @@
 
 		0x0000	Disable Device ID
 		0x0001	Bluetooth SIG
-		0x0002	USB Implementer’s Forum
+		0x0002	USB Implementer's Forum
 
 	The information is put into the EIR data. If the controller does
 	not support EIR or if SSP is disabled, this command will still
@@ -1600,7 +1603,7 @@
 
 	Using value 0x01 means that when connectable setting is disabled,
 	the advertising happens with undirected non-connectable advertising
-	packets and a non-resovable random address is used. If connectable
+	packets and a non-resolvable random address is used. If connectable
 	setting is enabled, then undirected connectable advertising packets
 	and the identity address or resolvable private address are used.
 
@@ -1821,13 +1824,12 @@
 	means resolvable private address is used when the controller is
 	discoverable and also when pairing is initiated.
 
-	With value 0x02 the kernel will use privacy mode with resolvable
-	private address. In case the conroller is bondable and discoverable
-	the identity address is used. Also when pairing is initiated, the
-	connection will be established with the identity address.
+	With value 0x02 the kernel will use a limited privacy mode with a
+	resolvable private address except when the controller is bondable
+	and discoverable, in which case the identity address is used.
 
 	Exposing the identity address when bondable and discoverable or
-	during initated pairing can be a privacy issue. For dual-mode
+	during initiated pairing can be a privacy issue. For dual-mode
 	controllers this can be neglected since its public address will
 	be exposed over BR/EDR anyway. The benefit of exposing the
 	identity address for pairing purposes is that it makes matching
@@ -1841,7 +1843,7 @@
 	When the controller has a public address (mandatory for dual-mode
 	controllers) it is used as identity address. In case the controller
 	is single mode LE only without a public address, it is required
-	to configure a static random andress first. The privacy mode can
+	to configure a static random address first. The privacy mode can
 	only be enabled when an identity address is available.
 
 	The Identity_Resolving_Key is the local key assigned for the local
@@ -1870,7 +1872,7 @@
 
 	This command is used to feed the kernel with currently known
 	identity resolving keys. The command does not need to be called
-	again upon the receiption of New Identity Resolving Key events
+	again upon the receipt of New Identity Resolving Key events
 	since the kernel updates its list automatically.
 
 	Possible values for the Address_Type parameter:
@@ -2063,7 +2065,7 @@
 					Address (6 Octets)
 					Address_Type (1 Octet)
 					Min_Connection_Interval (2 Octets)
-					Max_Connection_Interval (2 Octes)
+					Max_Connection_Interval (2 Octets)
 					Connection_Latency (2 Octets)
 					Supervision_Timeout (2 Octets)
 				}
@@ -2133,7 +2135,7 @@
 				Supported_Options (4 Octets)
 				Missing_Options (4 Octets)
 
-	This command is used to retreive the supported configuration
+	This command is used to retrieve the supported configuration
 	options of a controller and the missing configuration options.
 
 	The missing options are required to be configured before the
@@ -2194,7 +2196,7 @@
 	can be expected that it will be announced via Index Added event.
 
 	Wrongly configured controllers might still cause an error when
-	trying to power them via Set Powered commmand.
+	trying to power them via Set Powered command.
 
 	This command generates a Command Complete event on success or a
 	Command Status event on failure.
@@ -2431,7 +2433,7 @@
 		0x01	Unconfigured Controller (BR/EDR and/or LE)
 		0x02	Alternate MAC/PHY Controller (AMP)
 
-	The 0x00 and 0x01 types indiciate a primary BR/EDR and/or LE
+	The 0x00 and 0x01 types indicate a primary BR/EDR and/or LE
 	controller. The difference is just if they need extra configuration
 	or if they are fully configured.
 
@@ -2522,7 +2524,7 @@
 	not be set.
 
 	The Flags bit 5 indicates support for automatically adding the
-	Apperance value to the scan response data. Users of this flag
+	Appearance value to the scan response data. Users of this flag
 	will decrease the Max_Scan_Rsp_len by 4 octets. The Appearance
 	field will be added in front of the scan response data provided
 	by the user. If the appearance value is not supported, then this
@@ -2728,7 +2730,7 @@
 				Max_Scan_Rsp_Len (1 Octet)
 
 	The Read Advertising Features command returns the overall maximum
-	size of advertising data and scan respone data fields. That size is
+	size of advertising data and scan response data fields. That size is
 	valid when no Flags are used. However when certain Flags are used,
 	then the size might decrease. This command can be used to request
 	detailed information about the maximum available size.
@@ -2781,9 +2783,9 @@
 		7	BR/EDR/LE (interleaved discovery)
 
 	The limited discovery uses active scanning for Low Energy scanning
-	and will search for devices with the limited discoverabilty flag
+	and will search for devices with the limited discoverability flag
 	configured. On BR/EDR it uses LIAC and filters on the limited
-	discoverabilty flag of the class of device.
+	discoverability flag of the class of device.
 
 	When the discovery procedure starts the Discovery event will
 	notify this similar to Start Discovery.
@@ -2922,7 +2924,7 @@
 					PIN_Length (1 Octet)
 				}
 
-	This event indicates that a new link key has bee generated for a
+	This event indicates that a new link key has been generated for a
 	remote device.
 
 	The Store_Hint parameter indicates whether the host is expected
@@ -2948,7 +2950,7 @@
 		0x07	Unauthenticated Combination key from P-256
 		0x08	Authenticated Combination key from P-256
 
-	Receiving this event indicates that a pairing procecure has
+	Receiving this event indicates that a pairing procedure has
 	been completed.
 
 
@@ -2996,7 +2998,7 @@
 		0x03	Authenticated key from P-256
 		0x04	Debug key from P-256
 
-	Receiving this event indicates that a pairing procecure has
+	Receiving this event indicates that a pairing procedure has
 	been completed.
 
 
@@ -3301,7 +3303,7 @@
 		2	LE Random
 
 	For devices using resolvable random addresses with a known
-	identity resolving key, the event paramters will contain
+	identity resolving key, the event parameters will contain
 	the identity. After receiving this event, the device will
 	become essentially private again.
 
@@ -3479,7 +3481,7 @@
 					Address (6 Octets)
 					Address_Type (1 Octet)
 					Min_Connection_Interval (2 Octets)
-					Max_Connection_Interval (2 Octes)
+					Max_Connection_Interval (2 Octets)
 					Connection_Latency (2 Octets)
 					Supervision_Timeout (2 Octets)
 				}
diff --git a/gdbus/client.c b/gdbus/client.c
index 48711ae..a011e19 100644
--- a/gdbus/client.c
+++ b/gdbus/client.c
@@ -853,6 +853,21 @@
 	if (client == NULL)
 		return FALSE;
 
+	msg = dbus_message_new_method_call(client->service_name,
+				proxy->obj_path, proxy->interface, method);
+	if (msg == NULL)
+		return FALSE;
+
+	if (setup) {
+		DBusMessageIter iter;
+
+		dbus_message_iter_init_append(msg, &iter);
+		setup(&iter, user_data);
+	}
+
+	if (!function)
+		return g_dbus_send_message(client->dbus_conn, msg);
+
 	data = g_try_new0(struct method_call_data, 1);
 	if (data == NULL)
 		return FALSE;
@@ -861,19 +876,6 @@
 	data->user_data = user_data;
 	data->destroy = destroy;
 
-	msg = dbus_message_new_method_call(client->service_name,
-				proxy->obj_path, proxy->interface, method);
-	if (msg == NULL) {
-		g_free(data);
-		return FALSE;
-	}
-
-	if (setup) {
-		DBusMessageIter iter;
-
-		dbus_message_iter_init_append(msg, &iter);
-		setup(&iter, data->user_data);
-	}
 
 	if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
 					&call, METHOD_CALL_TIMEOUT) == FALSE) {
@@ -1073,9 +1075,6 @@
 
 		dbus_message_iter_next(&dict);
 	}
-
-	if (client->ready)
-		client->ready(client, client->ready_data);
 }
 
 static void get_managed_objects_reply(DBusPendingCall *call, void *user_data)
@@ -1096,6 +1095,9 @@
 	parse_managed_objects(client, reply);
 
 done:
+	if (client->ready)
+		client->ready(client, client->ready_data);
+
 	dbus_message_unref(reply);
 
 	dbus_pending_call_unref(client->get_objects_call);
diff --git a/lib/bluetooth.c b/lib/bluetooth.c
index 4a569c0..cb510ac 100644
--- a/lib/bluetooth.c
+++ b/lib/bluetooth.c
@@ -998,7 +998,7 @@
 	case 367:
 		return "Podo Labs, Inc";
 	case 368:
-		return "Roche Diabetes Care AG";
+		return "F. Hoffmann-La Roche AG";
 	case 369:
 		return "Amazon Fulfillment Service";
 	case 370:
@@ -1745,6 +1745,164 @@
 		return "Bytestorm Ltd.";
 	case 741:
 		return "Espressif Incorporated ( 乐鑫信息科技(上海)有限公司 )";
+	case 742:
+		return "Unwire";
+	case 743:
+		return "Connected Yard, Inc.";
+	case 744:
+		return "American Music Environments";
+	case 745:
+		return "Sensogram Technologies, Inc.";
+	case 746:
+		return "Fujitsu Limited";
+	case 747:
+		return "Ardic Technology";
+	case 748:
+		return "Delta Systems, Inc";
+	case 749:
+		return "HTC Corporation";
+	case 750:
+		return "Citizen Holdings Co., Ltd.";
+	case 751:
+		return "SMART-INNOVATION.inc";
+	case 752:
+		return "Blackrat Software";
+	case 753:
+		return "The Idea Cave, LLC";
+	case 754:
+		return "GoPro, Inc.";
+	case 755:
+		return "AuthAir, Inc";
+	case 756:
+		return "Vensi, Inc.";
+	case 757:
+		return "Indagem Tech LLC";
+	case 758:
+		return "Intemo Technologies";
+	case 759:
+		return "DreamVisions co., Ltd.";
+	case 760:
+		return "Runteq Oy Ltd";
+	case 761:
+		return "IMAGINATION TECHNOLOGIES LTD";
+	case 762:
+		return "CoSTAR Technologies";
+	case 763:
+		return "Clarius Mobile Health Corp.";
+	case 764:
+		return "Shanghai Frequen Microelectronics Co., Ltd.";
+	case 765:
+		return "Uwanna, Inc.";
+	case 766:
+		return "Lierda Science & Technology Group Co., Ltd.";
+	case 767:
+		return "Silicon Laboratories";
+	case 768:
+		return "World Moto Inc.";
+	case 769:
+		return "Giatec Scientific Inc.";
+	case 770:
+		return "Loop Devices, Inc";
+	case 771:
+		return "IACA electronique";
+	case 772:
+		return "Martians Inc";
+	case 773:
+		return "Swipp ApS";
+	case 774:
+		return "Life Laboratory Inc.";
+	case 775:
+		return "FUJI INDUSTRIAL CO.,LTD.";
+	case 776:
+		return "Surefire, LLC";
+	case 777:
+		return "Dolby Labs";
+	case 778:
+		return "Ellisys";
+	case 779:
+		return "Magnitude Lighting Converters";
+	case 780:
+		return "Hilti AG";
+	case 781:
+		return "Devdata S.r.l.";
+	case 782:
+		return "Deviceworx";
+	case 783:
+		return "Shortcut Labs";
+	case 784:
+		return "SGL Italia S.r.l.";
+	case 785:
+		return "PEEQ DATA";
+	case 786:
+		return "Ducere Technologies Pvt Ltd";
+	case 787:
+		return "DiveNav, Inc.";
+	case 788:
+		return "RIIG AI Sp. z o.o.";
+	case 789:
+		return "Thermo Fisher Scientific";
+	case 790:
+		return "AG Measurematics Pvt. Ltd.";
+	case 791:
+		return "CHUO Electronics CO., LTD.";
+	case 792:
+		return "Aspenta International";
+	case 793:
+		return "Eugster Frismag AG";
+	case 794:
+		return "Amber wireless GmbH";
+	case 795:
+		return "HQ Inc";
+	case 796:
+		return "Lab Sensor Solutions";
+	case 797:
+		return "Enterlab ApS";
+	case 798:
+		return "Eyefi, Inc.";
+	case 799:
+		return "MetaSystem S.p.A";
+	case 800:
+		return "SONO ELECTRONICS. CO., LTD";
+	case 801:
+		return "Jewelbots";
+	case 802:
+		return "Compumedics Limited";
+	case 803:
+		return "Rotor Bike Components";
+	case 804:
+		return "Astro, Inc.";
+	case 805:
+		return "Amotus Solutions";
+	case 806:
+		return "Healthwear Technologies (Changzhou)Ltd";
+	case 807:
+		return "Essex Electronics";
+	case 808:
+		return "Grundfos A/S";
+	case 809:
+		return "Eargo, Inc.";
+	case 810:
+		return "Electronic Design Lab";
+	case 811:
+		return "ESYLUX";
+	case 812:
+		return "NIPPON SMT.CO.,Ltd";
+	case 813:
+		return "BM innovations GmbH";
+	case 814:
+		return "indoormap";
+	case 815:
+		return "OttoQ Inc";
+	case 816:
+		return "North Pole Engineering";
+	case 817:
+		return "3flares Technologies Inc.";
+	case 818:
+		return "Electrocompaniet A.S.";
+	case 819:
+		return "Mul-T-Lock";
+	case 820:
+		return "Corentium AS";
 	case 65535:
 		return "internal use";
 	default:
diff --git a/lib/mgmt.h b/lib/mgmt.h
index d76bdbb..f6a976a 100644
--- a/lib/mgmt.h
+++ b/lib/mgmt.h
@@ -528,6 +528,8 @@
 	uint8_t  max_scan_rsp_len;
 } __packed;
 
+#define MGMT_OP_START_LIMITED_DISCOVERY	0x0041
+
 #define MGMT_EV_CMD_COMPLETE		0x0001
 struct mgmt_ev_cmd_complete {
 	uint16_t opcode;
diff --git a/lib/uuid.c b/lib/uuid.c
index 20b67d0..ac071fa 100644
--- a/lib/uuid.c
+++ b/lib/uuid.c
@@ -183,14 +183,14 @@
 static inline int is_base_uuid128(const char *string)
 {
 	uint16_t uuid;
-	char dummy;
+	char dummy[2];
 
 	if (!is_uuid128(string))
 		return 0;
 
 	return sscanf(string,
 		"0000%04hx-0000-1000-8000-00805%1[fF]9%1[bB]34%1[fF]%1[bB]",
-		&uuid, &dummy, &dummy, &dummy, &dummy) == 5;
+		&uuid, dummy, dummy, dummy, dummy) == 5;
 }
 
 static inline int is_uuid32(const char *string)
diff --git a/monitor/avctp.c b/monitor/avctp.c
index a024a0f..81e9166 100644
--- a/monitor/avctp.c
+++ b/monitor/avctp.c
@@ -2430,7 +2430,12 @@
 		if (!l2cap_frame_get_u8(frame, &len))
 			return false;
 
-		printf("Folder: ");
+		if (!len) {
+			print_field("%*cFolder: <empty>", indent, ' ');
+			continue;
+		}
+
+		printf("%*cFolder: ", indent+8, ' ');
 		for (; len > 0; len--) {
 			uint8_t c;
 
diff --git a/monitor/uuid.c b/monitor/uuid.c
index b123ebe..54adb0d 100644
--- a/monitor/uuid.c
+++ b/monitor/uuid.c
@@ -513,6 +513,27 @@
 	{ 0xfe7d, "Aterica Health Inc."				},
 	{ 0xfe7c, "Stollmann E+V GmbH"				},
 	{ 0xfe7b, "Orion Labs, Inc."				},
+	{ 0xfe7a, "Bragi GmbH"					},
+	{ 0xfe79, "Zebra Technologies"				},
+	{ 0xfe78, "Hewlett-Packard Company"			},
+	{ 0xfe77, "Hewlett-Packard Company"			},
+	{ 0xfe76, "TangoMe"					},
+	{ 0xfe75, "TangoMe"					},
+	{ 0xfe74, "unwire"					},
+	{ 0xfe73, "St. Jude Medical, Inc."			},
+	{ 0xfe72, "St. Jude Medical, Inc."			},
+	{ 0xfe71, "Plume Design Inc"				},
+	{ 0xfe70, "Beijing Jingdong Century Trading Co., Ltd."	},
+	{ 0xfe6f, "LINE Corporation"				},
+	{ 0xfe6e, "The University of Tokyo"			},
+	{ 0xfe6d, "The University of Tokyo"			},
+	{ 0xfe6c, "TASER International, Inc."			},
+	{ 0xfe6b, "TASER International, Inc."			},
+	{ 0xfe6a, "Kontakt Micro-Location Sp. z o.o."		},
+	{ 0xfe69, "Qualcomm Life Inc"				},
+	{ 0xfe68, "Qualcomm Life Inc"				},
+	{ 0xfe67, "Lab Sensor Solutions"			},
+	{ 0xfe66, "Intel Corporation"				},
 	/* SDO defined */
 	{ 0xfffe, "Alliance for Wireless Power (A4WP)"		},
 	{ 0xfffd, "Fast IDentity Online Alliance (FIDO)"	},
diff --git a/obexd/client/session.c b/obexd/client/session.c
index ef998f9..5f981bf 100644
--- a/obexd/client/session.c
+++ b/obexd/client/session.c
@@ -1179,6 +1179,7 @@
 	if (!data->remaining || !data->remaining[0]) {
 		error("obc_session_setpath: invalid path %s", path);
 		g_set_error(err, OBEX_IO_ERROR, -EINVAL, "Invalid argument");
+		setpath_data_free(data);
 		return 0;
 	}
 
diff --git a/profiles/audio/a2dp-codecs.h b/profiles/audio/a2dp-codecs.h
index e9da0bf..4fb5c0c 100644
--- a/profiles/audio/a2dp-codecs.h
+++ b/profiles/audio/a2dp-codecs.h
@@ -234,6 +234,11 @@
 	uint8_t channel_mode:4;
 } __attribute__ ((packed)) a2dp_aptx_t;
 
+typedef struct {
+	a2dp_vendor_codec_t info;
+	uint8_t unknown[2];
+} __attribute__ ((packed)) a2dp_ldac_t;
+
 #else
 #error "Unknown byte order"
 #endif
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 7b60012..787643c 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2655,8 +2655,10 @@
 		uint8_t len;
 
 		len = pdu->params[i++];
+		if (!len)
+			continue;
 
-		if (i + len > operand_count || len == 0) {
+		if (i + len > operand_count) {
 			error("Invalid folder length");
 			break;
 		}
@@ -3843,12 +3845,11 @@
 	btd_service_connecting_complete(service, 0);
 
 	/* Only create player if category 1 is supported */
-	if (!(controller->features & AVRCP_FEATURE_CATEGORY_1))
-		return;
-
-	player = create_ct_player(session, 0);
-	if (player == NULL)
-		return;
+	if (controller->features & AVRCP_FEATURE_CATEGORY_1) {
+		player = create_ct_player(session, 0);
+		if (player == NULL)
+			return;
+	}
 
 	if (controller->version < 0x0103)
 		return;
diff --git a/src/adapter.c b/src/adapter.c
index d40e4be..a81a144 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -2304,6 +2304,9 @@
 	if (!(adapter->current_settings & MGMT_SETTING_POWERED))
 		return btd_error_not_ready(msg);
 
+	if (MGMT_VERSION(mgmt_version, mgmt_revision) < MGMT_VERSION(1, 8))
+		return btd_error_not_supported(msg);
+
 	/* parse parameters */
 	if (!parse_discovery_filter_dict(&discovery_filter, msg))
 		return btd_error_invalid_args(msg);
@@ -3088,12 +3091,12 @@
 static struct irk_info *get_irk_info(GKeyFile *key_file, const char *peer,
 							uint8_t bdaddr_type)
 {
-	struct irk_info *irk;
+	struct irk_info *irk = NULL;
 	char *str;
 
 	str = g_key_file_get_string(key_file, "IdentityResolvingKey", "Key", NULL);
 	if (!str || strlen(str) < 32)
-		return NULL;
+		goto failed;
 
 	irk = g_new0(struct irk_info, 1);
 
@@ -3105,6 +3108,7 @@
 	else
 		str2buf(&str[0], irk->val, sizeof(irk->val));
 
+failed:
 	g_free(str);
 
 	return irk;
diff --git a/src/adapter.h b/src/adapter.h
index 0c95f5d..f2947fe 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -225,3 +225,4 @@
 			void *data);
 
 bool btd_le_connect_before_pairing(void);
+
diff --git a/src/advertising.c b/src/advertising.c
index 59c8c3d..d2019de 100644
--- a/src/advertising.c
+++ b/src/advertising.c
@@ -500,7 +500,7 @@
 		return btd_error_failed(ad->reg, "Failed");
 	}
 
-	cp->flags = flags;
+	cp->flags = htobl(flags);
 	cp->instance = ad->instance;
 	cp->adv_data_len = adv_data_len;
 	memcpy(cp->data, adv_data, adv_data_len);
diff --git a/src/device.c b/src/device.c
index d31604a..ed8ffba 100644
--- a/src/device.c
+++ b/src/device.c
@@ -238,7 +238,6 @@
 	 * attribute cache support can be built.
 	 */
 	struct gatt_db *db;			/* GATT db cache */
-	bool gatt_cache_used;			/* true if discovery skipped */
 	struct bt_gatt_client *client;		/* GATT client instance */
 	struct bt_gatt_server *server;		/* GATT server instance */
 
@@ -953,38 +952,14 @@
 	return TRUE;
 }
 
-static void append_service_path(const char *path, void *user_data)
-{
-	DBusMessageIter *array = user_data;
-
-	dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH, &path);
-}
-
-static gboolean dev_property_get_gatt_services(
-					const GDBusPropertyTable *property,
+static gboolean
+dev_property_get_svc_resolved(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *data)
 {
-	struct btd_device *dev = data;
-	DBusMessageIter array;
+	struct btd_device *device = data;
+	gboolean val = device->svc_refreshed;
 
-	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array);
-
-	btd_gatt_client_foreach_service(dev->client_dbus, append_service_path,
-									&array);
-
-	dbus_message_iter_close_container(iter, &array);
-
-	return TRUE;
-}
-
-static gboolean dev_property_exists_gatt_services(
-					const GDBusPropertyTable *property,
-					void *data)
-{
-	struct btd_device *dev = data;
-
-	if (!dev->client || !bt_gatt_client_is_ready(dev->client))
-		return FALSE;
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
 
 	return TRUE;
 }
@@ -2158,6 +2133,9 @@
 	key_file = g_key_file_new();
 	g_key_file_load_from_file(key_file, filename, 0, NULL);
 
+	/* Remove current attributes since it might have changed */
+	g_key_file_remove_group(key_file, "Attributes", NULL);
+
 	saver.key_file = key_file;
 	saver.device = device;
 
@@ -2217,6 +2195,17 @@
 	browse_request_free(req);
 }
 
+static void device_set_svc_refreshed(struct btd_device *device, bool value)
+{
+	if (device->svc_refreshed == value)
+		return;
+
+	device->svc_refreshed = value;
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+					DEVICE_INTERFACE, "ServicesResolved");
+}
+
 static void device_svc_resolved(struct btd_device *dev, uint8_t bdaddr_type,
 								int err)
 {
@@ -2232,7 +2221,7 @@
 	 * device.
 	 */
 	if (state->connected)
-		dev->svc_refreshed = true;
+		device_set_svc_refreshed(dev, true);
 
 	g_slist_free_full(dev->eir_uuids, g_free);
 	dev->eir_uuids = NULL;
@@ -2575,8 +2564,7 @@
 	{ "TxPower", "n", dev_property_get_tx_power, NULL,
 					dev_property_exists_tx_power,
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "GattServices", "ao", dev_property_get_gatt_services, NULL,
-					dev_property_exists_gatt_services,
+	{ "ServicesResolved", "b", dev_property_get_svc_resolved, NULL, NULL,
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 
 	{ }
@@ -2628,9 +2616,10 @@
 		return;
 
 	state->connected = false;
-	device->svc_refreshed = false;
 	device->general_connect = FALSE;
 
+	device_set_svc_refreshed(device, false);
+
 	if (device->disconn_timer > 0) {
 		g_source_remove(device->disconn_timer);
 		device->disconn_timer = 0;
@@ -3060,7 +3049,7 @@
 							&uuid, 0, properties,
 							NULL, NULL, NULL);
 	if (!att || gatt_db_attribute_get_handle(att) != value_handle) {
-		warn("saving characteristic to db failed");
+		warn("loading characteristic to db failed");
 		return -EIO;
 	}
 
@@ -3087,13 +3076,13 @@
 
 	att = gatt_db_get_attribute(db, start);
 	if (!att) {
-		warn("saving included service to db failed - no such service");
+		warn("loading included service to db failed - no such service");
 		return -EIO;
 	}
 
 	att = gatt_db_service_add_included(service, att);
 	if (!att) {
-		warn("saving included service to db failed");
+		warn("loading included service to db failed");
 		return -EIO;
 	}
 
@@ -3130,7 +3119,7 @@
 	att = gatt_db_insert_service(db, start, &uuid, primary,
 							end - start + 1);
 	if (!att) {
-		DBG("ERROR saving service to db!");
+		error("Unable load service into db!");
 		return -EIO;
 	}
 
@@ -3213,8 +3202,10 @@
 		}
 
 		g_free(value);
-		if (ret)
+		if (ret) {
+			gatt_db_clear(db);
 			return ret;
+		}
 	}
 
 	if (current_service)
@@ -4572,8 +4563,6 @@
 	device_register_primaries(device, services, -1);
 
 	device_add_gatt_services(device);
-
-	device_svc_resolved(device, device->bdaddr_type, 0);
 }
 
 static void gatt_client_init(struct btd_device *device);
@@ -4594,18 +4583,7 @@
 
 	btd_gatt_client_ready(device->client_dbus);
 
-	/*
-	 * Update the GattServices property. Do this asynchronously since this
-	 * should happen after the "Characteristics" and "Descriptors"
-	 * properties of all services have been asynchronously updated by
-	 * btd_gatt_client.
-	 *
-	 * Service discovery will be skipped and exported objects won't change
-	 * if the attribute cache was populated when bt_gatt_client gets
-	 * initialized, so no need to to send this signal if that's the case.
-	 */
-	if (!device->gatt_cache_used)
-		g_idle_add(gatt_services_changed, device);
+	device_svc_resolved(device, device->bdaddr_type, 0);
 }
 
 static void gatt_client_service_changed(uint16_t start_handle,
@@ -4658,8 +4636,6 @@
 		return;
 	}
 
-	device->gatt_cache_used = !gatt_db_isempty(device->db);
-
 	btd_gatt_client_connected(device->client_dbus);
 }
 
@@ -4835,10 +4811,10 @@
 		bonding_request_free(device->bonding);
 	}
 
-	if (device->connect) {
-		if (!device->le_state.svc_resolved && !err)
-			device_browse_gatt(device, NULL);
+	if (!err)
+		device_browse_gatt(device, NULL);
 
+	if (device->connect) {
 		if (err < 0)
 			reply = btd_error_failed(device->connect,
 							strerror(-err));
@@ -4984,12 +4960,12 @@
 	if (!req)
 		return -EBUSY;
 
-	if (device->attrib) {
+	if (device->client) {
 		/*
 		 * If discovery has not yet completed, then wait for gatt-client
 		 * to become ready.
 		 */
-		if (!device->le_state.svc_resolved)
+		if (!bt_gatt_client_is_ready(device->client))
 			return 0;
 
 		/*
diff --git a/src/gatt-client.c b/src/gatt-client.c
index 39f6646..cfe14da 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -71,9 +71,7 @@
 	bt_uuid_t uuid;
 	char *path;
 	struct queue *chrcs;
-	bool chrcs_ready;
 	struct queue *pending_ext_props;
-	guint idle_id;
 };
 
 struct characteristic {
@@ -1205,9 +1203,6 @@
 	const char *sender = dbus_message_get_sender(msg);
 	struct notify_client *client;
 
-	if (!chrc->notifying)
-		return btd_error_failed(msg, "Not notifying");
-
 	client = queue_remove_if(chrc->notify_clients, match_notify_sender,
 							(void *) sender);
 	if (!client)
@@ -1222,31 +1217,6 @@
 	return dbus_message_new_method_return(msg);
 }
 
-static void append_desc_path(void *data, void *user_data)
-{
-	struct descriptor *desc = data;
-	DBusMessageIter *array = user_data;
-
-	dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH,
-								&desc->path);
-}
-
-static gboolean characteristic_get_descriptors(
-					const GDBusPropertyTable *property,
-					DBusMessageIter *iter, void *data)
-{
-	struct characteristic *chrc = data;
-	DBusMessageIter array;
-
-	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array);
-
-	queue_foreach(chrc->descs, append_desc_path, &array);
-
-	dbus_message_iter_close_container(iter, &array);
-
-	return TRUE;
-}
-
 static const GDBusPropertyTable characteristic_properties[] = {
 	{ "UUID", "s", characteristic_get_uuid, NULL, NULL,
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
@@ -1259,8 +1229,6 @@
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 	{ "Flags", "as", characteristic_get_flags, NULL, NULL,
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "Descriptors", "ao", characteristic_get_descriptors, NULL, NULL,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 	{ }
 };
 
@@ -1404,31 +1372,6 @@
 	return TRUE;
 }
 
-static void append_chrc_path(void *data, void *user_data)
-{
-	struct characteristic *chrc = data;
-	DBusMessageIter *array = user_data;
-
-	dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH,
-								&chrc->path);
-}
-
-static gboolean service_get_characteristics(const GDBusPropertyTable *property,
-					DBusMessageIter *iter, void *data)
-{
-	struct service *service = data;
-	DBusMessageIter array;
-
-	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array);
-
-	if (service->chrcs_ready)
-		queue_foreach(service->chrcs, append_chrc_path, &array);
-
-	dbus_message_iter_close_container(iter, &array);
-
-	return TRUE;
-}
-
 static const GDBusPropertyTable service_properties[] = {
 	{ "UUID", "s", service_get_uuid, NULL, NULL,
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
@@ -1436,8 +1379,6 @@
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 	{ "Primary", "b", service_get_primary, NULL, NULL,
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "Characteristics", "ao", service_get_characteristics, NULL, NULL,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 	{ }
 };
 
@@ -1503,29 +1444,12 @@
 
 	DBG("Removing GATT service: %s", service->path);
 
-	if (service->idle_id)
-		g_source_remove(service->idle_id);
-
 	queue_remove_all(service->chrcs, NULL, NULL, unregister_characteristic);
 
 	g_dbus_unregister_interface(btd_get_dbus_connection(), service->path,
 							GATT_SERVICE_IFACE);
 }
 
-static void notify_chrcs(struct service *service)
-{
-
-	if (service->chrcs_ready ||
-				!queue_isempty(service->pending_ext_props))
-		return;
-
-	service->chrcs_ready = true;
-
-	g_dbus_emit_property_changed(btd_get_dbus_connection(), service->path,
-							GATT_SERVICE_IFACE,
-							"Characteristics");
-}
-
 struct export_data {
 	void *root;
 	bool failed;
@@ -1575,8 +1499,6 @@
 						"Flags");
 
 	queue_remove(service->pending_ext_props, chrc);
-
-	notify_chrcs(service);
 }
 
 static void read_ext_props(void *data, void *user_data)
@@ -1650,16 +1572,6 @@
 	return true;
 }
 
-static gboolean set_chrcs_ready(gpointer user_data)
-{
-	struct service *service = user_data;
-
-	service->idle_id = 0;
-	notify_chrcs(service);
-
-	return FALSE;
-}
-
 static void export_service(struct gatt_db_attribute *attr, void *user_data)
 {
 	struct btd_gatt_client *client = user_data;
@@ -1679,18 +1591,16 @@
 	}
 
 	queue_push_tail(client->services, service);
-
-	/*
-	 * Asynchronously update the "Characteristics" property of the service.
-	 * If there are any pending reads to obtain the value of the "Extended
-	 * Properties" descriptor then wait until they are complete.
-	 */
-	if (!service->chrcs_ready && queue_isempty(service->pending_ext_props))
-		service->idle_id = g_idle_add(set_chrcs_ready, service);
 }
 
 static void create_services(struct btd_gatt_client *client)
 {
+	/* Don't attempt to create any objects if experimental is disabled */
+	if (!(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL)) {
+		info("GATT service objects disabled");
+		return;
+	}
+
 	DBG("Exporting objects for GATT services: %s", client->devaddr);
 
 	gatt_db_foreach_service(client->db, NULL, export_service, client);
@@ -1765,8 +1675,14 @@
 		return;
 
 	if (!client->gatt) {
-		error("GATT client not initialized");
-		return;
+		struct bt_gatt_client *gatt;
+
+		gatt = btd_device_get_gatt_client(client->device);
+		client->gatt = bt_gatt_client_clone(gatt);
+		if (!client->gatt) {
+			error("GATT client not initialized");
+			return;
+		}
 	}
 
 	client->ready = true;
@@ -1789,7 +1705,7 @@
 	DBG("Device connected.");
 
 	bt_gatt_client_unref(client->gatt);
-	client->gatt = bt_gatt_client_ref(gatt);
+	client->gatt = bt_gatt_client_clone(gatt);
 
 	/*
 	 * Services have already been created before. Re-enable notifications
@@ -1840,47 +1756,6 @@
 	client->notify_id = 0;
 }
 
-static void cancel_desc_ops(void *data, void *user_data)
-{
-	struct descriptor *desc = data;
-	struct bt_gatt_client *gatt = user_data;
-
-	if (desc->read_id) {
-		bt_gatt_client_cancel(gatt, desc->read_id);
-		desc->read_id = 0;
-	}
-
-	if (desc->write_id) {
-		bt_gatt_client_cancel(gatt, desc->write_id);
-		desc->write_id = 0;
-	}
-}
-
-static void cancel_chrc_ops(void *data, void *user_data)
-{
-	struct characteristic *chrc = data;
-	struct bt_gatt_client *gatt = user_data;
-
-	if (chrc->read_id) {
-		bt_gatt_client_cancel(gatt, chrc->read_id);
-		chrc->read_id = 0;
-	}
-
-	if (chrc->write_id) {
-		bt_gatt_client_cancel(gatt, chrc->write_id);
-		chrc->write_id = 0;
-	}
-
-	queue_foreach(chrc->descs, cancel_desc_ops, user_data);
-}
-
-static void cancel_ops(void *data, void *user_data)
-{
-	struct service *service = data;
-
-	queue_foreach(service->chrcs, cancel_chrc_ops, user_data);
-}
-
 void btd_gatt_client_disconnected(struct btd_gatt_client *client)
 {
 	if (!client || !client->gatt)
@@ -1894,7 +1769,6 @@
 	 * done.
 	 */
 	queue_foreach(client->all_notify_clients, clear_notify_id, NULL);
-	queue_foreach(client->services, cancel_ops, client->gatt);
 
 	bt_gatt_client_unref(client->gatt);
 	client->gatt = NULL;
diff --git a/src/gatt-database.c b/src/gatt-database.c
index e8ce7d5..b8da955 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -77,17 +77,24 @@
 	struct queue *ccc_callbacks;
 	struct gatt_db_attribute *svc_chngd;
 	struct gatt_db_attribute *svc_chngd_ccc;
-	struct queue *services;
+	struct queue *apps;
 	struct queue *profiles;
 };
 
-struct external_service {
+struct gatt_app {
 	struct btd_gatt_database *database;
-	bool failed;
 	char *owner;
-	char *path;	/* Path to GattService1 */
+	char *path;
 	DBusMessage *reg;
 	GDBusClient *client;
+	bool failed;
+	struct queue *services;
+	struct queue *proxies;
+};
+
+struct external_service {
+	struct gatt_app *app;
+	char *path;	/* Path to GattService1 */
 	GDBusProxy *proxy;
 	struct gatt_db_attribute *attrib;
 	uint16_t attr_cnt;
@@ -342,26 +349,42 @@
 	queue_destroy(service->chrcs, chrc_free);
 	queue_destroy(service->descs, desc_free);
 
-	gatt_db_remove_service(service->database->db, service->attrib);
+	if (service->attrib)
+		gatt_db_remove_service(service->app->database->db,
+							service->attrib);
 
-	if (service->client) {
-		g_dbus_client_set_disconnect_watch(service->client, NULL, NULL);
-		g_dbus_client_set_proxy_handlers(service->client, NULL, NULL,
-								NULL, NULL);
-		g_dbus_client_set_ready_watch(service->client, NULL, NULL);
+	if (service->app->client)
 		g_dbus_proxy_unref(service->proxy);
-		g_dbus_client_unref(service->client);
-	}
 
-	if (service->reg)
-		dbus_message_unref(service->reg);
-
-	g_free(service->owner);
 	g_free(service->path);
 
 	free(service);
 }
 
+static void app_free(void *data)
+{
+	struct gatt_app *app = data;
+
+	queue_destroy(app->services, service_free);
+	queue_destroy(app->proxies, NULL);
+
+	if (app->client) {
+		g_dbus_client_set_disconnect_watch(app->client, NULL, NULL);
+		g_dbus_client_set_proxy_handlers(app->client, NULL, NULL,
+								NULL, NULL);
+		g_dbus_client_set_ready_watch(app->client, NULL, NULL);
+		g_dbus_client_unref(app->client);
+	}
+
+	if (app->reg)
+		dbus_message_unref(app->reg);
+
+	g_free(app->owner);
+	g_free(app->path);
+
+	free(app);
+}
+
 static void profile_remove(void *data)
 {
 	struct btd_profile *p = data;
@@ -434,7 +457,7 @@
 	gatt_db_unregister(database->db, database->db_id);
 
 	queue_destroy(database->device_states, device_state_free);
-	queue_destroy(database->services, service_free);
+	queue_destroy(database->apps, app_free);
 	queue_destroy(database->profiles, profile_free);
 	queue_destroy(database->ccc_callbacks, ccc_cb_free);
 	database->device_states = NULL;
@@ -1012,118 +1035,65 @@
 	const char *sender;
 };
 
-static bool match_service(const void *a, const void *b)
+static bool match_app(const void *a, const void *b)
 {
-	const struct external_service *service = a;
+	const struct gatt_app *app = a;
 	const struct svc_match_data *data = b;
 
-	return g_strcmp0(service->path, data->path) == 0 &&
-				g_strcmp0(service->owner, data->sender) == 0;
+	return g_strcmp0(app->path, data->path) == 0 &&
+				g_strcmp0(app->owner, data->sender) == 0;
 }
 
-static gboolean service_free_idle_cb(void *data)
+static gboolean app_free_idle_cb(void *data)
 {
-	service_free(data);
+	app_free(data);
 
 	return FALSE;
 }
 
-static void service_remove_helper(void *data)
-{
-	struct external_service *service = data;
-
-	queue_remove(service->database->services, service);
-
-	/*
-	 * Do not run in the same loop, this may be a disconnect
-	 * watch call and GDBusClient should not be destroyed.
-	 */
-	g_idle_add(service_free_idle_cb, service);
-}
-
 static void client_disconnect_cb(DBusConnection *conn, void *user_data)
 {
+	struct gatt_app *app = user_data;
+	struct btd_gatt_database *database = app->database;
+
 	DBG("Client disconnected");
 
-	service_remove_helper(user_data);
+	if (queue_remove(database->apps, app))
+		app_free(app);
 }
 
-static void remove_service(void *data)
+static void remove_app(void *data)
 {
-	struct external_service *service = data;
+	struct gatt_app *app = data;
 
 	/*
 	 * Set callback to NULL to avoid potential race condition
-	 * when calling remove_service and GDBusClient unref.
+	 * when calling remove_app and GDBusClient unref.
 	 */
-	g_dbus_client_set_disconnect_watch(service->client, NULL, NULL);
+	g_dbus_client_set_disconnect_watch(app->client, NULL, NULL);
 
 	/*
 	 * Set proxy handlers to NULL, so that this gets called only once when
 	 * the first proxy that belongs to this service gets removed.
 	 */
-	g_dbus_client_set_proxy_handlers(service->client, NULL, NULL,
-								NULL, NULL);
+	g_dbus_client_set_proxy_handlers(app->client, NULL, NULL, NULL, NULL);
 
-	service_remove_helper(service);
+
+	queue_remove(app->database->apps, app);
+
+	/*
+	 * Do not run in the same loop, this may be a disconnect
+	 * watch call and GDBusClient should not be destroyed.
+	 */
+	g_idle_add(app_free_idle_cb, app);
 }
 
-static struct external_chrc *chrc_create(struct external_service *service,
-							GDBusProxy *proxy,
-							const char *path)
+static bool match_service_by_path(const void *a, const void *b)
 {
-	struct external_chrc *chrc;
+	const struct external_service *service = a;
+	const char *path = b;
 
-	chrc = new0(struct external_chrc, 1);
-	chrc->pending_reads = queue_new();
-	chrc->pending_writes = queue_new();
-
-	chrc->path = g_strdup(path);
-	if (!chrc->path) {
-		queue_destroy(chrc->pending_reads, NULL);
-		queue_destroy(chrc->pending_writes, NULL);
-		free(chrc);
-		return NULL;
-	}
-
-	chrc->service = service;
-	chrc->proxy = g_dbus_proxy_ref(proxy);
-
-	return chrc;
-}
-
-static struct external_desc *desc_create(struct external_service *service,
-							GDBusProxy *proxy,
-							const char *chrc_path)
-{
-	struct external_desc *desc;
-
-	desc = new0(struct external_desc, 1);
-	desc->pending_reads = queue_new();
-	desc->pending_writes = queue_new();
-
-	desc->chrc_path = g_strdup(chrc_path);
-	if (!desc->chrc_path) {
-		queue_destroy(desc->pending_reads, NULL);
-		queue_destroy(desc->pending_writes, NULL);
-		free(desc);
-		return NULL;
-	}
-
-	desc->service = service;
-	desc->proxy = g_dbus_proxy_ref(proxy);
-
-	return desc;
-}
-
-static bool incr_attr_count(struct external_service *service, uint16_t incr)
-{
-	if (service->attr_cnt > UINT16_MAX - incr)
-		return false;
-
-	service->attr_cnt += incr;
-
-	return true;
+	return strcmp(service->path, path) == 0;
 }
 
 static bool parse_path(GDBusProxy *proxy, const char *name, const char **path)
@@ -1141,15 +1111,14 @@
 	return true;
 }
 
-static bool check_service_path(GDBusProxy *proxy,
-					struct external_service *service)
+static bool incr_attr_count(struct external_service *service, uint16_t incr)
 {
-	const char *service_path;
-
-	if (!parse_path(proxy, "Service", &service_path))
+	if (service->attr_cnt > UINT16_MAX - incr)
 		return false;
 
-	return g_strcmp0(service_path, service->path) == 0;
+	service->attr_cnt += incr;
+
+	return true;
 }
 
 static bool parse_chrc_flags(DBusMessageIter *array, uint8_t *props,
@@ -1259,149 +1228,232 @@
 	return parse_chrc_flags(&array, props, ext_props);
 }
 
+static struct external_chrc *chrc_create(struct gatt_app *app,
+							GDBusProxy *proxy,
+							const char *path)
+{
+	struct external_service *service;
+	struct external_chrc *chrc;
+	const char *service_path;
+
+	if (!parse_path(proxy, "Service", &service_path)) {
+		error("Failed to obtain service path for characteristic");
+		return NULL;
+	}
+
+	service = queue_find(app->services, match_service_by_path,
+								service_path);
+	if (!service) {
+		error("Unable to find service for characteristic: %s", path);
+		return NULL;
+	}
+
+	chrc = new0(struct external_chrc, 1);
+	chrc->pending_reads = queue_new();
+	chrc->pending_writes = queue_new();
+
+	chrc->path = g_strdup(path);
+	if (!chrc->path)
+		goto fail;
+
+	chrc->service = service;
+	chrc->proxy = g_dbus_proxy_ref(proxy);
+
+	/*
+	 * Add 2 for the characteristic declaration and the value
+	 * attribute.
+	 */
+	if (!incr_attr_count(chrc->service, 2)) {
+		error("Failed to increment attribute count");
+		goto fail;
+	}
+
+	/*
+	 * Parse characteristic flags (i.e. properties) here since they
+	 * are used to determine if any special descriptors should be
+	 * created.
+	 */
+	if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, NULL)) {
+		error("Failed to parse characteristic properties");
+		goto fail;
+	}
+
+	if ((chrc->props & BT_GATT_CHRC_PROP_NOTIFY ||
+				chrc->props & BT_GATT_CHRC_PROP_INDICATE) &&
+				!incr_attr_count(chrc->service, 1)) {
+		error("Failed to increment attribute count for CCC");
+		goto fail;
+	}
+
+	if (chrc->ext_props && !incr_attr_count(chrc->service, 1)) {
+		error("Failed to increment attribute count for CEP");
+		goto fail;
+	}
+
+	queue_push_tail(chrc->service->chrcs, chrc);
+
+	return chrc;
+
+fail:
+	chrc_free(chrc);
+	return NULL;
+}
+
+static bool match_chrc(const void *a, const void *b)
+{
+	const struct external_chrc *chrc = a;
+	const char *path = b;
+
+	return strcmp(chrc->path, path) == 0;
+}
+
+static bool match_service_by_chrc(const void *a, const void *b)
+{
+	const struct external_service *service = a;
+	const char *path = b;
+
+	return queue_find(service->chrcs, match_chrc, path);
+}
+
+static struct external_desc *desc_create(struct gatt_app *app,
+							GDBusProxy *proxy)
+{
+	struct external_service *service;
+	struct external_desc *desc;
+	const char *chrc_path;
+
+	if (!parse_path(proxy, "Characteristic", &chrc_path)) {
+		error("Failed to obtain characteristic path for descriptor");
+		return NULL;
+	}
+
+	service = queue_find(app->services, match_service_by_chrc, chrc_path);
+	if (!service) {
+		error("Unable to find service for characteristic: %s",
+								chrc_path);
+		return NULL;
+	}
+
+	desc = new0(struct external_desc, 1);
+	desc->pending_reads = queue_new();
+	desc->pending_writes = queue_new();
+
+	desc->chrc_path = g_strdup(chrc_path);
+	if (!desc->chrc_path)
+		goto fail;
+
+	desc->service = service;
+	desc->proxy = g_dbus_proxy_ref(proxy);
+
+	/* Add 1 for the descriptor attribute */
+	if (!incr_attr_count(desc->service, 1)) {
+		error("Failed to increment attribute count");
+		goto fail;
+	}
+
+	/*
+	 * Parse descriptors flags here since they are used to
+	 * determine the permission the descriptor should have
+	 */
+	if (!parse_flags(proxy, NULL, NULL, &desc->perm)) {
+		error("Failed to parse characteristic properties");
+		goto fail;
+	}
+
+	queue_push_tail(desc->service->descs, desc);
+
+	return desc;
+
+fail:
+	desc_free(desc);
+	return NULL;
+}
+
+static bool check_service_path(GDBusProxy *proxy,
+					struct external_service *service)
+{
+	const char *service_path;
+
+	if (!parse_path(proxy, "Service", &service_path))
+		return false;
+
+	return g_strcmp0(service_path, service->path) == 0;
+}
+
+static struct external_service *create_service(struct gatt_app *app,
+						GDBusProxy *proxy,
+						const char *path)
+{
+	struct external_service *service;
+
+	if (!path || !g_str_has_prefix(path, "/"))
+		return NULL;
+
+	service = queue_find(app->services, match_service_by_path, path);
+	if (service) {
+		error("Duplicated service: %s", path);
+		return NULL;
+	}
+
+	service = new0(struct external_service, 1);
+
+	service->app = app;
+
+	service->path = g_strdup(path);
+	if (!service->path)
+		goto fail;
+
+	service->proxy = g_dbus_proxy_ref(proxy);
+	service->chrcs = queue_new();
+	service->descs = queue_new();
+
+	/* Add 1 for the service declaration */
+	if (!incr_attr_count(service, 1)) {
+		error("Failed to increment attribute count");
+		goto fail;
+	}
+
+	queue_push_tail(app->services, service);
+
+	return service;
+
+fail:
+	service_free(service);
+	return NULL;
+}
+
 static void proxy_added_cb(GDBusProxy *proxy, void *user_data)
 {
-	struct external_service *service = user_data;
+	struct gatt_app *app = user_data;
 	const char *iface, *path;
 
-	if (service->failed || service->attrib)
+	if (app->failed)
 		return;
 
+	queue_push_tail(app->proxies, proxy);
+
 	iface = g_dbus_proxy_get_interface(proxy);
 	path = g_dbus_proxy_get_path(proxy);
 
-	if (!g_str_has_prefix(path, service->path))
-		return;
-
-	if (g_strcmp0(iface, GATT_SERVICE_IFACE) == 0) {
-		if (service->proxy)
-			return;
-
-		/*
-		 * TODO: We may want to support adding included services in a
-		 * single hierarchy.
-		 */
-		if (g_strcmp0(path, service->path) != 0) {
-			error("Multiple services added within hierarchy");
-			service->failed = true;
-			return;
-		}
-
-		/* Add 1 for the service declaration */
-		if (!incr_attr_count(service, 1)) {
-			error("Failed to increment attribute count");
-			service->failed = true;
-			return;
-		}
-
-		service->proxy = g_dbus_proxy_ref(proxy);
-	} else if (g_strcmp0(iface, GATT_CHRC_IFACE) == 0) {
-		struct external_chrc *chrc;
-
-		if (g_strcmp0(path, service->path) == 0) {
-			error("Characteristic path same as service path");
-			service->failed = true;
-			return;
-		}
-
-		chrc = chrc_create(service, proxy, path);
-		if (!chrc) {
-			service->failed = true;
-			return;
-		}
-
-		/*
-		 * Add 2 for the characteristic declaration and the value
-		 * attribute.
-		 */
-		if (!incr_attr_count(service, 2)) {
-			error("Failed to increment attribute count");
-			service->failed = true;
-			return;
-		}
-
-		/*
-		 * Parse characteristic flags (i.e. properties) here since they
-		 * are used to determine if any special descriptors should be
-		 * created.
-		 */
-		if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, NULL)) {
-			error("Failed to parse characteristic properties");
-			service->failed = true;
-			return;
-		}
-
-		if ((chrc->props & BT_GATT_CHRC_PROP_NOTIFY ||
-				chrc->props & BT_GATT_CHRC_PROP_INDICATE) &&
-				!incr_attr_count(service, 1)) {
-			error("Failed to increment attribute count for CCC");
-			service->failed = true;
-			return;
-		}
-
-		if (chrc->ext_props && !incr_attr_count(service, 1)) {
-			error("Failed to increment attribute count for CEP");
-			service->failed = true;
-			return;
-		}
-
-		queue_push_tail(service->chrcs, chrc);
-	} else if (g_strcmp0(iface, GATT_DESC_IFACE) == 0) {
-		struct external_desc *desc;
-		const char *chrc_path;
-
-		if (!parse_path(proxy, "Characteristic", &chrc_path)) {
-			error("Failed to obtain characteristic path for "
-								"descriptor");
-			service->failed = true;
-			return;
-		}
-
-		desc = desc_create(service, proxy, chrc_path);
-		if (!desc) {
-			service->failed = true;
-			return;
-		}
-
-		/* Add 1 for the descriptor attribute */
-		if (!incr_attr_count(service, 1)) {
-			error("Failed to increment attribute count");
-			service->failed = true;
-			return;
-		}
-
-		/*
-		 * Parse descriptors flags here since they are used to
-		 * determine the permission the descriptor should have
-		 */
-		if (!parse_flags(proxy, NULL, NULL, &desc->perm)) {
-			error("Failed to parse characteristic properties");
-			service->failed = true;
-			return;
-		}
-
-		queue_push_tail(service->descs, desc);
-	} else {
-		DBG("Ignoring unrelated interface: %s", iface);
-		return;
-	}
-
-	DBG("Object added to service - path: %s, iface: %s", path, iface);
+	DBG("Object received: %s, iface: %s", path, iface);
 }
 
 static void proxy_removed_cb(GDBusProxy *proxy, void *user_data)
 {
-	struct external_service *service = user_data;
+	struct gatt_app *app = user_data;
+	struct external_service *service;
 	const char *path;
 
 	path = g_dbus_proxy_get_path(proxy);
 
-	if (!g_str_has_prefix(path, service->path))
+	service = queue_remove_if(app->services, match_service_by_path,
+							(void *) path);
+	if (!service)
 		return;
 
 	DBG("Proxy removed - removing service: %s", service->path);
 
-	remove_service(service);
+	service_free(service);
 }
 
 static bool parse_uuid(GDBusProxy *proxy, bt_uuid_t *uuid)
@@ -1769,7 +1821,7 @@
 	len = MIN(BT_ATT_MAX_VALUE_LEN, len);
 	value = len ? value : NULL;
 
-	send_notification_to_devices(chrc->service->database,
+	send_notification_to_devices(chrc->service->app->database,
 				gatt_db_attribute_get_handle(chrc->attrib),
 				value, len,
 				gatt_db_attribute_get_handle(chrc->ccc),
@@ -1783,7 +1835,7 @@
 				!(chrc->props & BT_GATT_CHRC_PROP_INDICATE))
 		return true;
 
-	chrc->ccc = service_add_ccc(service->attrib, service->database,
+	chrc->ccc = service_add_ccc(service->attrib, service->app->database,
 						ccc_write_cb, chrc, NULL);
 	if (!chrc->ccc) {
 		error("Failed to create CCC entry for characteristic");
@@ -1913,6 +1965,36 @@
 	send_read(attrib, chrc->proxy, chrc->pending_reads, id);
 }
 
+static void write_without_response_setup_cb(DBusMessageIter *iter,
+							void *user_data)
+{
+	struct iovec *iov = user_data;
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
+	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+						&iov->iov_base, iov->iov_len);
+	dbus_message_iter_close_container(iter, &array);
+}
+
+static void send_write_without_response(struct gatt_db_attribute *attrib,
+					GDBusProxy *proxy, unsigned int id,
+					const uint8_t *value, size_t len)
+{
+	struct iovec iov;
+	uint8_t ecode = 0;
+
+	iov.iov_base = (uint8_t *) value;
+	iov.iov_len = len;
+
+	if (!g_dbus_proxy_method_call(proxy, "WriteValue",
+					write_without_response_setup_cb,
+					NULL, &iov, NULL))
+		ecode = BT_ATT_ERROR_UNLIKELY;
+
+	gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
 static void chrc_write_cb(struct gatt_db_attribute *attrib,
 					unsigned int id, uint16_t offset,
 					const uint8_t *value, size_t len,
@@ -1926,6 +2008,12 @@
 		return;
 	}
 
+	if (chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP) {
+		send_write_without_response(attrib, chrc->proxy, id, value,
+									len);
+		return;
+	}
+
 	send_write(attrib, chrc->proxy, chrc->pending_writes, id, value, len);
 }
 
@@ -1992,7 +2080,7 @@
 	return !desc->handled;
 }
 
-static bool create_service_entry(struct external_service *service)
+static bool database_add_service(struct external_service *service)
 {
 	bt_uuid_t uuid;
 	bool primary;
@@ -2008,7 +2096,7 @@
 		return false;
 	}
 
-	service->attrib = gatt_db_add_service(service->database->db, &uuid,
+	service->attrib = gatt_db_add_service(service->app->database->db, &uuid,
 						primary, service->attr_cnt);
 	if (!service->attrib)
 		return false;
@@ -2036,98 +2124,195 @@
 	return true;
 
 fail:
-	gatt_db_remove_service(service->database->db, service->attrib);
+	gatt_db_remove_service(service->app->database->db, service->attrib);
 	service->attrib = NULL;
 
 	return false;
 }
 
+static bool database_add_app(struct gatt_app *app)
+{
+	const struct queue_entry *entry;
+
+	if (queue_isempty(app->services))
+		return false;
+
+	entry = queue_get_entries(app->services);
+	while (entry) {
+		if (!database_add_service(entry->data)) {
+			error("Failed to add service");
+			return false;
+		}
+
+		entry = entry->next;
+	}
+
+	return true;
+}
+
+static void register_service(void *data, void *user_data)
+{
+	struct gatt_app *app = user_data;
+	GDBusProxy *proxy = data;
+	const char *iface = g_dbus_proxy_get_interface(proxy);
+	const char *path = g_dbus_proxy_get_path(proxy);
+
+	if (app->failed)
+		return;
+
+	if (g_strcmp0(iface, GATT_SERVICE_IFACE) == 0) {
+		struct external_service *service;
+
+		service = create_service(app, proxy, path);
+		if (!service) {
+			app->failed = true;
+			return;
+		}
+	}
+}
+
+static void register_characteristic(void *data, void *user_data)
+{
+	struct gatt_app *app = user_data;
+	GDBusProxy *proxy = data;
+	const char *iface = g_dbus_proxy_get_interface(proxy);
+	const char *path = g_dbus_proxy_get_path(proxy);
+
+	if (app->failed)
+		return;
+
+	iface = g_dbus_proxy_get_interface(proxy);
+	path = g_dbus_proxy_get_path(proxy);
+
+	if (g_strcmp0(iface, GATT_CHRC_IFACE) == 0) {
+		struct external_chrc *chrc;
+
+		chrc = chrc_create(app, proxy, path);
+		if (!chrc) {
+			app->failed = true;
+			return;
+		}
+	}
+}
+
+static void register_descriptor(void *data, void *user_data)
+{
+	struct gatt_app *app = user_data;
+	GDBusProxy *proxy = data;
+	const char *iface = g_dbus_proxy_get_interface(proxy);
+
+	if (app->failed)
+		return;
+
+	if (g_strcmp0(iface, GATT_DESC_IFACE) == 0) {
+		struct external_desc *desc;
+
+		desc = desc_create(app, proxy);
+		if (!desc) {
+			app->failed = true;
+			return;
+		}
+	}
+}
+
 static void client_ready_cb(GDBusClient *client, void *user_data)
 {
-	struct external_service *service = user_data;
+	struct gatt_app *app = user_data;
 	DBusMessage *reply;
 	bool fail = false;
 
-	if (!service->proxy || service->failed) {
+	/*
+	 * Process received objects
+	 */
+	if (queue_isempty(app->proxies)) {
+		error("No object received");
+		fail = true;
+		reply = btd_error_failed(app->reg,
+					"No object received");
+		goto reply;
+	}
+
+	queue_foreach(app->proxies, register_service, app);
+	queue_foreach(app->proxies, register_characteristic, app);
+	queue_foreach(app->proxies, register_descriptor, app);
+
+	if (!app->services || app->failed) {
 		error("No valid external GATT objects found");
 		fail = true;
-		reply = btd_error_failed(service->reg,
+		reply = btd_error_failed(app->reg,
 					"No valid service object found");
 		goto reply;
 	}
 
-	if (!create_service_entry(service)) {
+	if (!database_add_app(app)) {
 		error("Failed to create GATT service entry in local database");
 		fail = true;
-		reply = btd_error_failed(service->reg,
+		reply = btd_error_failed(app->reg,
 					"Failed to create entry in database");
 		goto reply;
 	}
 
-	DBG("GATT service registered: %s", service->path);
+	DBG("GATT application registered: %s:%s", app->owner, app->path);
 
-	reply = dbus_message_new_method_return(service->reg);
+	reply = dbus_message_new_method_return(app->reg);
 
 reply:
 	g_dbus_send_message(btd_get_dbus_connection(), reply);
-	dbus_message_unref(service->reg);
-	service->reg = NULL;
+	dbus_message_unref(app->reg);
+	app->reg = NULL;
 
 	if (fail)
-		remove_service(service);
+		remove_app(app);
 }
 
-static struct external_service *create_service(DBusConnection *conn,
-					DBusMessage *msg, const char *path)
+static struct gatt_app *create_app(DBusConnection *conn, DBusMessage *msg,
+							const char *path)
 {
-	struct external_service *service;
+	struct gatt_app *app;
 	const char *sender = dbus_message_get_sender(msg);
 
 	if (!path || !g_str_has_prefix(path, "/"))
 		return NULL;
 
-	service = new0(struct external_service, 1);
+	app = new0(struct gatt_app, 1);
 
-	service->client = g_dbus_client_new_full(conn, sender, path, path);
-	if (!service->client)
+	app->client = g_dbus_client_new_full(conn, sender, path, path);
+	if (!app->client)
 		goto fail;
 
-	service->owner = g_strdup(sender);
-	if (!service->owner)
+	app->owner = g_strdup(sender);
+	if (!app->owner)
 		goto fail;
 
-	service->path = g_strdup(path);
-	if (!service->path)
+	app->path = g_strdup(path);
+	if (!app->path)
 		goto fail;
 
-	service->chrcs = queue_new();
-	service->descs = queue_new();
+	app->services = queue_new();
+	app->proxies = queue_new();
+	app->reg = dbus_message_ref(msg);
 
-	service->reg = dbus_message_ref(msg);
+	g_dbus_client_set_disconnect_watch(app->client, client_disconnect_cb,
+									app);
+	g_dbus_client_set_proxy_handlers(app->client, proxy_added_cb,
+					proxy_removed_cb, NULL, app);
+	g_dbus_client_set_ready_watch(app->client, client_ready_cb, app);
 
-	g_dbus_client_set_disconnect_watch(service->client,
-						client_disconnect_cb, service);
-	g_dbus_client_set_proxy_handlers(service->client, proxy_added_cb,
-							proxy_removed_cb, NULL,
-							service);
-	g_dbus_client_set_ready_watch(service->client, client_ready_cb,
-								service);
-
-	return service;
+	return app;
 
 fail:
-	service_free(service);
+	app_free(app);
 	return NULL;
 }
 
-static DBusMessage *manager_register_service(DBusConnection *conn,
+static DBusMessage *manager_register_app(DBusConnection *conn,
 					DBusMessage *msg, void *user_data)
 {
 	struct btd_gatt_database *database = user_data;
 	const char *sender = dbus_message_get_sender(msg);
 	DBusMessageIter args;
 	const char *path;
-	struct external_service *service;
+	struct gatt_app *app;
 	struct svc_match_data match_data;
 
 	if (!dbus_message_iter_init(msg, &args))
@@ -2141,33 +2326,33 @@
 	match_data.path = path;
 	match_data.sender = sender;
 
-	if (queue_find(database->services, match_service, &match_data))
+	if (queue_find(database->apps, match_app, &match_data))
 		return btd_error_already_exists(msg);
 
 	dbus_message_iter_next(&args);
 	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
 		return btd_error_invalid_args(msg);
 
-	service = create_service(conn, msg, path);
-	if (!service)
-		return btd_error_failed(msg, "Failed to register service");
+	app = create_app(conn, msg, path);
+	if (!app)
+		return btd_error_failed(msg, "Failed to register application");
 
-	DBG("Registering service - path: %s", path);
+	DBG("Registering application: %s:%s", sender, path);
 
-	service->database = database;
-	queue_push_tail(database->services, service);
+	app->database = database;
+	queue_push_tail(database->apps, app);
 
 	return NULL;
 }
 
-static DBusMessage *manager_unregister_service(DBusConnection *conn,
+static DBusMessage *manager_unregister_app(DBusConnection *conn,
 					DBusMessage *msg, void *user_data)
 {
 	struct btd_gatt_database *database = user_data;
 	const char *sender = dbus_message_get_sender(msg);
 	const char *path;
 	DBusMessageIter args;
-	struct external_service *service;
+	struct gatt_app *app;
 	struct svc_match_data match_data;
 
 	if (!dbus_message_iter_init(msg, &args))
@@ -2181,12 +2366,11 @@
 	match_data.path = path;
 	match_data.sender = sender;
 
-	service = queue_remove_if(database->services, match_service,
-								&match_data);
-	if (!service)
+	app = queue_remove_if(database->apps, match_app, &match_data);
+	if (!app)
 		return btd_error_does_not_exist(msg);
 
-	service_free(service);
+	app_free(app);
 
 	return dbus_message_new_method_return(msg);
 }
@@ -2389,12 +2573,13 @@
 }
 
 static const GDBusMethodTable manager_methods[] = {
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterService",
-			GDBUS_ARGS({ "service", "o" }, { "options", "a{sv}" }),
-			NULL, manager_register_service) },
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterService",
-					GDBUS_ARGS({ "service", "o" }),
-					NULL, manager_unregister_service) },
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterApplication",
+			GDBUS_ARGS({ "application", "o" },
+			{ "options", "a{sv}" }), NULL,
+			manager_register_app) },
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterApplication",
+					GDBUS_ARGS({ "application", "o" }),
+					NULL, manager_unregister_app) },
 	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterProfile",
 			GDBUS_ARGS({ "profile", "o" }, { "UUIDs", "as" },
 			{ "options", "a{sv}" }), NULL,
@@ -2418,7 +2603,7 @@
 	database->adapter = btd_adapter_ref(adapter);
 	database->db = gatt_db_new();
 	database->device_states = queue_new();
-	database->services = queue_new();
+	database->apps = queue_new();
 	database->profiles = queue_new();
 	database->ccc_callbacks = queue_new();
 
diff --git a/src/shared/att.c b/src/shared/att.c
index 3a84783..abcf3bb 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -535,6 +535,16 @@
 		disconn->callback(err, disconn->user_data);
 }
 
+static void disc_att_send_op(void *data)
+{
+	struct att_send_op *op = data;
+
+	if (op->callback)
+		op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data);
+
+	destroy_att_send_op(op);
+}
+
 static bool disconnect_cb(struct io *io, void *user_data)
 {
 	struct bt_att *att = user_data;
@@ -557,7 +567,20 @@
 	io_destroy(att->io);
 	att->io = NULL;
 
-	bt_att_cancel_all(att);
+	/* Notify request callbacks */
+	queue_remove_all(att->req_queue, NULL, NULL, disc_att_send_op);
+	queue_remove_all(att->ind_queue, NULL, NULL, disc_att_send_op);
+	queue_remove_all(att->write_queue, NULL, NULL, disc_att_send_op);
+
+	if (att->pending_req) {
+		disc_att_send_op(att->pending_req);
+		att->pending_req = NULL;
+	}
+
+	if (att->pending_ind) {
+		disc_att_send_op(att->pending_ind);
+		att->pending_ind = NULL;
+	}
 
 	bt_att_ref(att);
 
@@ -573,10 +596,11 @@
 {
 	int security;
 
-	security = bt_att_get_security(att);
-	if (security != BT_ATT_SECURITY_AUTO)
+	if (att->io_sec_level != BT_ATT_SECURITY_AUTO)
 		return false;
 
+	security = bt_att_get_security(att);
+
 	if (ecode == BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION &&
 					security < BT_ATT_SECURITY_MEDIUM)
 		security = BT_ATT_SECURITY_MEDIUM;
@@ -979,7 +1003,7 @@
 
 	att->io_on_l2cap = is_io_l2cap_based(att->fd);
 	if (!att->io_on_l2cap)
-		att->io_sec_level = BT_SECURITY_LOW;
+		att->io_sec_level = BT_ATT_SECURITY_LOW;
 
 	return bt_att_ref(att);
 
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 06ac763..1dd6b22 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -291,25 +291,6 @@
 	uint16_t end;
 };
 
-static bool match_notify_data_handle_range(const void *a, const void *b)
-{
-	const struct notify_data *notify_data = a;
-	struct notify_chrc *chrc = notify_data->chrc;
-	const struct handle_range *range = b;
-
-	return chrc->value_handle >= range->start &&
-					chrc->value_handle <= range->end;
-}
-
-static bool match_notify_chrc_handle_range(const void *a, const void *b)
-{
-	const struct notify_chrc *chrc = a;
-	const struct handle_range *range = b;
-
-	return chrc->value_handle >= range->start &&
-					chrc->value_handle <= range->end;
-}
-
 static void notify_data_cleanup(void *data)
 {
 	struct notify_data *notify_data = data;
@@ -320,32 +301,6 @@
 	notify_data_unref(notify_data);
 }
 
-static void gatt_client_remove_all_notify_in_range(
-				struct bt_gatt_client *client,
-				uint16_t start_handle, uint16_t end_handle)
-{
-	struct handle_range range;
-
-	range.start = start_handle;
-	range.end = end_handle;
-
-	queue_remove_all(client->notify_list, match_notify_data_handle_range,
-						&range, notify_data_cleanup);
-}
-
-static void gatt_client_remove_notify_chrcs_in_range(
-				struct bt_gatt_client *client,
-				uint16_t start_handle, uint16_t end_handle)
-{
-	struct handle_range range;
-
-	range.start = start_handle;
-	range.end = end_handle;
-
-	queue_remove_all(client->notify_chrcs, match_notify_chrc_handle_range,
-						&range, notify_chrc_free);
-}
-
 struct discovery_op;
 
 typedef void (*discovery_op_complete_func_t)(struct discovery_op *op,
@@ -357,11 +312,12 @@
 	struct bt_gatt_client *client;
 	struct queue *pending_svcs;
 	struct queue *pending_chrcs;
-	struct queue *tmp_queue;
+	struct queue *svcs;
 	struct gatt_db_attribute *cur_svc;
 	bool success;
 	uint16_t start;
 	uint16_t end;
+	uint16_t last;
 	int ref_count;
 	discovery_op_complete_func_t complete_func;
 	discovery_op_fail_func_t failure_func;
@@ -371,10 +327,25 @@
 {
 	queue_destroy(op->pending_svcs, NULL);
 	queue_destroy(op->pending_chrcs, free);
-	queue_destroy(op->tmp_queue, NULL);
+	queue_destroy(op->svcs, NULL);
 	free(op);
 }
 
+static void discovery_op_complete(struct discovery_op *op, bool success,
+								uint8_t err)
+{
+	/* Reset remaining range */
+	if (success) {
+		if (op->last != UINT16_MAX)
+			gatt_db_clear_range(op->client->db, op->last + 1,
+								UINT16_MAX);
+	} else
+		gatt_db_clear(op->client->db);
+
+	op->success = success;
+	op->complete_func(op, success, err);
+}
+
 static struct discovery_op *discovery_op_create(struct bt_gatt_client *client,
 				uint16_t start, uint16_t end,
 				discovery_op_complete_func_t complete_func,
@@ -385,7 +356,7 @@
 	op = new0(struct discovery_op, 1);
 	op->pending_svcs = queue_new();
 	op->pending_chrcs = queue_new();
-	op->tmp_queue = queue_new();
+	op->svcs = queue_new();
 	op->client = client;
 	op->complete_func = complete_func;
 	op->failure_func = failure_func;
@@ -501,17 +472,11 @@
 	/* Move on to the next service */
 	attr = queue_pop_head(op->pending_svcs);
 	if (!attr) {
-		struct queue *tmp_queue;
-
-		tmp_queue = op->pending_svcs;
-		op->pending_svcs = op->tmp_queue;
-		op->tmp_queue = tmp_queue;
-
 		/*
 		 * We have processed all include definitions. Move on to
 		 * characteristics.
 		 */
-		attr = queue_pop_head(op->pending_svcs);
+		attr = queue_pop_head(op->svcs);
 		if (!attr)
 			goto failed;
 
@@ -535,7 +500,7 @@
 		goto failed;
 	}
 
-	queue_push_tail(op->tmp_queue, attr);
+	queue_push_tail(op->svcs, attr);
 	op->cur_svc = attr;
 	if (!gatt_db_attribute_get_service_handles(attr, &start, &end))
 		goto failed;
@@ -556,8 +521,7 @@
 	discovery_op_unref(op);
 
 failed:
-	op->success = false;
-	op->complete_func(op, false, att_ecode);
+	discovery_op_complete(op, false, att_ecode);
 }
 
 struct chrc {
@@ -588,8 +552,12 @@
 							chrc_data->properties,
 							NULL, NULL, NULL);
 
-		if (!attr)
+		if (!attr) {
+			util_debug(client->debug_callback, client->debug_data,
+				"Failed to insert characteristic at 0x%04x",
+				chrc_data->value_handle);
 			goto failed;
+		}
 
 		if (gatt_db_attribute_get_handle(attr) !=
 							chrc_data->value_handle)
@@ -698,7 +666,7 @@
 	/* Done with the current service */
 	gatt_db_service_set_active(op->cur_svc, true);
 
-	attr = queue_pop_head(op->pending_svcs);
+	attr = queue_pop_head(op->svcs);
 	if (!attr)
 		goto done;
 
@@ -727,8 +695,7 @@
 	success = false;
 
 done:
-	op->success = success;
-	op->complete_func(op, success, att_ecode);
+	discovery_op_complete(op, success, att_ecode);
 }
 
 static void discover_chrcs_cb(bool success, uint8_t att_ecode,
@@ -805,7 +772,7 @@
 	/* Done with the current service */
 	gatt_db_service_set_active(op->cur_svc, true);
 
-	attr = queue_pop_head(op->pending_svcs);
+	attr = queue_pop_head(op->svcs);
 	if (!attr)
 		goto done;
 
@@ -834,8 +801,7 @@
 	success = false;
 
 done:
-	op->success = success;
-	op->complete_func(op, success, att_ecode);
+	discovery_op_complete(op, success, att_ecode);
 }
 
 static void discover_secondary_cb(bool success, uint8_t att_ecode,
@@ -903,6 +869,10 @@
 		/* Skip if service already active */
 		if (!gatt_db_service_get_active(attr))
 			queue_push_tail(op->pending_svcs, attr);
+
+		/* Update last handle */
+		if (end > op->last)
+			op->last = end;
 	}
 
 next:
@@ -916,10 +886,10 @@
 	}
 
 	/*
-	 * Store the service in the tmp queue to be reused during
+	 * Store the service in the svcs queue to be reused during
 	 * characteristics discovery later.
 	 */
-	queue_push_tail(op->tmp_queue, attr);
+	queue_push_tail(op->svcs, attr);
 	op->cur_svc = attr;
 
 	if (!gatt_db_attribute_get_service_handles(attr, &start, &end)) {
@@ -940,8 +910,7 @@
 	discovery_op_unref(op);
 
 done:
-	op->success = success;
-	op->complete_func(op, success, att_ecode);
+	discovery_op_complete(op, success, att_ecode);
 }
 
 static void discover_primary_cb(bool success, uint8_t att_ecode,
@@ -1007,6 +976,10 @@
 		/* Skip if service already active */
 		if (!gatt_db_service_get_active(attr))
 			queue_push_tail(op->pending_svcs, attr);
+
+		/* Update last handle */
+		if (end > op->last)
+			op->last = end;
 	}
 
 secondary:
@@ -1034,8 +1007,7 @@
 	success = false;
 
 done:
-	op->success = success;
-	op->complete_func(op, success, att_ecode);
+	discovery_op_complete(op, success, att_ecode);
 }
 
 static void notify_client_ready(struct bt_gatt_client *client, bool success,
@@ -1116,9 +1088,6 @@
 {
 	struct notify_data *notify_data = data;
 
-	/* Increment the per-characteristic ref count of notify handlers */
-	__sync_fetch_and_add(&notify_data->chrc->notify_count, 1);
-
 	notify_data->att_id = 0;
 	notify_data->callback(0, notify_data->user_data);
 }
@@ -1174,7 +1143,6 @@
 	struct notify_data *notify_data = user_data;
 	uint16_t att_ecode;
 
-	assert(!notify_data->chrc->notify_count);
 	assert(notify_data->chrc->ccc_write_id);
 
 	notify_data->chrc->ccc_write_id = 0;
@@ -1264,6 +1232,9 @@
 
 	notify_data->id = client->next_reg_id++;
 
+	/* Increment the per-characteristic ref count of notify handlers */
+	__sync_fetch_and_add(&notify_data->chrc->notify_count, 1);
+
 	/*
 	 * If a write to the CCC descriptor is in progress, then queue this
 	 * request.
@@ -1274,9 +1245,9 @@
 	}
 
 	/*
-	 * If the ref count is not zero, then notifications are already enabled.
+	 * If the ref count > 1, then notifications are already enabled.
 	 */
-	if (chrc->notify_count > 0 || !chrc->ccc_handle) {
+	if (chrc->notify_count > 1 || !chrc->ccc_handle) {
 		complete_notify_request(notify_data);
 		return notify_data->id;
 	}
@@ -1406,22 +1377,6 @@
 {
 	struct discovery_op *op;
 
-	/* On full database reset just re-run attribute discovery */
-	if (start_handle == 0x0001 && end_handle == 0xffff)
-		goto discover;
-
-	/* Invalidate and remove all effected notify callbacks */
-	gatt_client_remove_all_notify_in_range(client, start_handle,
-								end_handle);
-	gatt_client_remove_notify_chrcs_in_range(client, start_handle,
-								end_handle);
-
-	/* Remove all services that overlap the modified range since we'll
-	 * rediscover them
-	 */
-	gatt_db_clear_range(client->db, start_handle, end_handle);
-
-discover:
 	op = discovery_op_create(client, start_handle, end_handle,
 						service_changed_complete,
 						service_changed_failure);
@@ -1528,6 +1483,11 @@
 	if (!op)
 		return false;
 
+	/* Check if MTU needs to be send */
+	mtu = MAX(BT_ATT_DEFAULT_LE_MTU, mtu);
+	if (mtu == BT_ATT_DEFAULT_LE_MTU)
+		goto discover;
+
 	/* Configure the MTU */
 	client->mtu_req_id = bt_gatt_exchange_mtu(client->att,
 						MAX(BT_ATT_DEFAULT_LE_MTU, mtu),
@@ -1542,6 +1502,20 @@
 	client->in_init = true;
 
 	return true;
+
+discover:
+	client->discovery_req = bt_gatt_discover_all_primary_services(
+							client->att, NULL,
+							discover_primary_cb,
+							discovery_op_ref(op),
+							discovery_op_unref);
+	if (!client->discovery_req) {
+		discovery_op_free(op);
+		return false;
+	}
+
+	client->in_init = true;
+	return true;
 }
 
 struct pdu_data {
@@ -1555,7 +1529,6 @@
 	struct notify_data *notify_data = user_data;
 	struct notify_data *next_data;
 
-	assert(!notify_data->chrc->notify_count);
 	assert(notify_data->chrc->ccc_write_id);
 
 	notify_data->chrc->ccc_write_id = 0;
@@ -1581,6 +1554,7 @@
 	 */
 	if (notify_data->att_id) {
 		bt_att_cancel(notify_data->client->att, notify_data->att_id);
+		notify_data->att_id = 0;
 		goto done;
 	}
 
@@ -1685,15 +1659,11 @@
 		notify_client_ready(client, false, 0);
 }
 
-struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db,
-							struct bt_att *att,
-							uint16_t mtu)
+static struct bt_gatt_client *gatt_client_new(struct gatt_db *db,
+							struct bt_att *att)
 {
 	struct bt_gatt_client *client;
 
-	if (!att || !db)
-		return NULL;
-
 	client = new0(struct bt_gatt_client, 1);
 	client->disc_id = bt_att_register_disconnect(att, att_disconnect_cb,
 								client, NULL);
@@ -1719,14 +1689,49 @@
 	client->att = bt_att_ref(att);
 	client->db = gatt_db_ref(db);
 
-	if (!gatt_client_init(client, mtu))
-		goto fail;
-
-	return bt_gatt_client_ref(client);
+	return client;
 
 fail:
 	bt_gatt_client_free(client);
 	return NULL;
+
+}
+
+struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db,
+							struct bt_att *att,
+							uint16_t mtu)
+{
+	struct bt_gatt_client *client;
+
+	if (!att || !db)
+		return NULL;
+
+	client = gatt_client_new(db, att);
+	if (!client)
+		return NULL;
+
+	if (!gatt_client_init(client, mtu)) {
+		bt_gatt_client_free(client);
+		return NULL;
+	}
+
+	return bt_gatt_client_ref(client);
+}
+
+struct bt_gatt_client *bt_gatt_client_clone(struct bt_gatt_client *client)
+{
+	struct bt_gatt_client *clone;
+
+	if (!client || !client->ready)
+		return NULL;
+
+	clone = gatt_client_new(client->db, client->att);
+	if (!clone)
+		return NULL;
+
+	clone->ready = client->ready;
+
+	return bt_gatt_client_ref(clone);
 }
 
 struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client)
@@ -2156,7 +2161,9 @@
 		goto done;
 	}
 
-	if (opcode != BT_ATT_OP_READ_BLOB_RSP || (!pdu && length)) {
+	if ((!op->offset && opcode != BT_ATT_OP_READ_RSP)
+			|| (op->offset && opcode != BT_ATT_OP_READ_BLOB_RSP)
+			|| (!pdu && length)) {
 		success = false;
 		goto done;
 	}
@@ -2209,7 +2216,9 @@
 {
 	struct request *req;
 	struct read_long_op *op;
+	uint8_t att_op;
 	uint8_t pdu[4];
+	uint16_t pdu_len;
 
 	if (!client)
 		return 0;
@@ -2233,12 +2242,32 @@
 	req->destroy = destroy_read_long_op;
 
 	put_le16(value_handle, pdu);
-	put_le16(offset, pdu + 2);
+	pdu_len = sizeof(value_handle);
 
-	req->att_id = bt_att_send(client->att, BT_ATT_OP_READ_BLOB_REQ,
-							pdu, sizeof(pdu),
-							read_long_cb, req,
-							request_unref);
+	/*
+	 * Core v4.2, part F, section 1.3.4.4.5:
+	 * If the attribute value has a fixed length that is less than or equal
+	 * to (ATT_MTU - 3) octets in length, then an Error Response can be sent
+	 * with the error code «Attribute Not Long».
+	 *
+	 * To remove need for caller to handle "Attribute Not Long" error when
+	 * reading characteristics with short values, use Read Request for
+	 * reading first part of characteristics value instead of Read Blob
+	 * Request. Both are allowed in this case.
+	 */
+
+	if (op->offset) {
+		att_op = BT_ATT_OP_READ_BLOB_REQ;
+		pdu_len += sizeof(op->offset);
+
+		put_le16(op->offset, pdu + 2);
+	} else {
+		att_op = BT_ATT_OP_READ_REQ;
+	}
+
+	req->att_id = bt_att_send(client->att, att_op, pdu, pdu_len,
+					read_long_cb, req, request_unref);
+
 	if (!req->att_id) {
 		op->destroy = NULL;
 		request_unref(req);
@@ -2959,8 +2988,8 @@
 	if (!notify_data)
 		return false;
 
-	assert(notify_data->chrc->notify_count > 0);
-	assert(!notify_data->chrc->ccc_write_id);
+	/* Remove data if it has been queued */
+	queue_remove(notify_data->chrc->reg_notify_queue, notify_data);
 
 	complete_unregister_notify(notify_data);
 	return true;
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index befa43f..aceb570 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -32,6 +32,7 @@
 struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db,
 							struct bt_att *att,
 							uint16_t mtu);
+struct bt_gatt_client *bt_gatt_client_clone(struct bt_gatt_client *client);
 
 struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client);
 void bt_gatt_client_unref(struct bt_gatt_client *client);
diff --git a/test/example-gatt-client b/test/example-gatt-client
index 724a45d..5a02505 100755
--- a/test/example-gatt-client
+++ b/test/example-gatt-client
@@ -1,8 +1,11 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 
 import argparse
 import dbus
-import gobject
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
 import sys
 
 from dbus.mainloop.glib import DBusGMainLoop
@@ -195,7 +198,7 @@
     global bus
     bus = dbus.SystemBus()
     global mainloop
-    mainloop = gobject.MainLoop()
+    mainloop = GObject.MainLoop()
 
     om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'), DBUS_OM_IFACE)
     om.connect_to_signal('InterfacesRemoved', interfaces_removed_cb)
@@ -204,10 +207,10 @@
         if not process_hr_service(service_path):
             sys.exit(1)
     except dbus.DBusException as e:
-        print e.message
+        print(e)
         sys.exit(1)
 
-    print 'Heart Rate Service ready'
+    print('Heart Rate Service ready')
 
     start_client()
 
diff --git a/test/example-gatt-server b/test/example-gatt-server
index 47219b8..93cf068 100755
--- a/test/example-gatt-server
+++ b/test/example-gatt-server
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 
 import dbus
 import dbus.exceptions
@@ -6,7 +6,11 @@
 import dbus.service
 
 import array
-import gobject
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+import sys
 
 from random import randint
 
@@ -37,6 +41,38 @@
     _dbus_error_name = 'org.bluez.Error.Failed'
 
 
+class Application(dbus.service.Object):
+    def __init__(self, bus):
+        self.path = '/'
+        self.services = []
+        dbus.service.Object.__init__(self, bus, self.path)
+        self.add_service(HeartRateService(bus, 0))
+        self.add_service(BatteryService(bus, 1))
+        self.add_service(TestService(bus, 2))
+
+    def get_path(self):
+        return dbus.ObjectPath(self.path)
+
+    def add_service(self, service):
+        self.services.append(service)
+
+    @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
+    def GetManagedObjects(self):
+        response = {}
+        print('GetManagedObjects')
+
+        for service in self.services:
+            response[service.get_path()] = service.get_properties()
+            chrcs = service.get_characteristics()
+            for chrc in chrcs:
+                response[chrc.get_path()] = chrc.get_properties()
+                descs = chrc.get_descriptors()
+                for desc in descs:
+                    response[desc.get_path()] = desc.get_properties()
+
+        return response
+
+
 class Service(dbus.service.Object):
     PATH_BASE = '/org/bluez/example/service'
 
@@ -83,21 +119,6 @@
 
         return self.get_properties[GATT_SERVICE_IFACE]
 
-    @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
-    def GetManagedObjects(self):
-        response = {}
-        print('GetManagedObjects')
-
-        response[self.get_path()] = self.get_properties()
-        chrcs = self.get_characteristics()
-        for chrc in chrcs:
-            response[chrc.get_path()] = chrc.get_properties()
-            descs = chrc.get_descriptors()
-            for desc in descs:
-                response[desc.get_path()] = desc.get_properties()
-
-        return response
-
 
 class Characteristic(dbus.service.Object):
     def __init__(self, bus, index, uuid, flags, service):
@@ -267,7 +288,7 @@
         if not self.notifying:
             return
 
-        gobject.timeout_add(1000, self.hr_msrmt_cb)
+        GObject.timeout_add(1000, self.hr_msrmt_cb)
 
     def StartNotify(self):
         if self.notifying:
@@ -354,7 +375,7 @@
                 service)
         self.notifying = False
         self.battery_lvl = 100
-        gobject.timeout_add(5000, self.drain_battery)
+        GObject.timeout_add(5000, self.drain_battery)
 
     def notify_battery_level(self):
         if not self.notifying:
@@ -462,7 +483,7 @@
 
     def __init__(self, bus, index, characteristic):
         self.writable = 'writable-auxiliaries' in characteristic.flags
-        self.value = array.array('B', 'This is a characteristic for testing')
+        self.value = array.array('B', b'This is a characteristic for testing')
         self.value = self.value.tolist()
         Descriptor.__init__(
                 self, bus, index,
@@ -523,12 +544,12 @@
                 dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
         ]
 
-def register_service_cb():
-    print('GATT service registered')
+def register_app_cb():
+    print('GATT application registered')
 
 
-def register_service_error_cb(error):
-    print('Failed to register service: ' + str(error))
+def register_app_error_cb(error):
+    print('Failed to register application: ' + str(error))
     mainloop.quit()
 
 
@@ -537,8 +558,8 @@
                                DBUS_OM_IFACE)
     objects = remote_om.GetManagedObjects()
 
-    for o, props in objects.iteritems():
-        if props.has_key(GATT_MANAGER_IFACE):
+    for o, props in objects.items():
+        if GATT_MANAGER_IFACE in props.keys():
             return o
 
     return None
@@ -559,21 +580,13 @@
             bus.get_object(BLUEZ_SERVICE_NAME, adapter),
             GATT_MANAGER_IFACE)
 
-    hr_service = HeartRateService(bus, 0)
-    bat_service = BatteryService(bus, 1)
-    test_service = TestService(bus, 2)
+    app = Application(bus)
 
-    mainloop = gobject.MainLoop()
+    mainloop = GObject.MainLoop()
 
-    service_manager.RegisterService(hr_service.get_path(), {},
-                                    reply_handler=register_service_cb,
-                                    error_handler=register_service_error_cb)
-    service_manager.RegisterService(bat_service.get_path(), {},
-                                    reply_handler=register_service_cb,
-                                    error_handler=register_service_error_cb)
-    service_manager.RegisterService(test_service.get_path(), {},
-                                    reply_handler=register_service_cb,
-                                    error_handler=register_service_error_cb)
+    service_manager.RegisterApplication(app.get_path(), {},
+                                    reply_handler=register_app_cb,
+                                    error_handler=register_app_error_cb)
 
     mainloop.run()
 
diff --git a/test/test-discovery b/test/test-discovery
index 9a2c6b9..cea7768 100755
--- a/test/test-discovery
+++ b/test/test-discovery
@@ -115,6 +115,18 @@
 	option_list = [
 			make_option("-i", "--device", action="store",
 					type="string", dest="dev_id"),
+			make_option("-u", "--uuids", action="store",
+					type="string", dest="uuids",
+					help="Filtered service UUIDs [uuid1,uuid2,...]"),
+			make_option("-r", "--rssi", action="store",
+					type="int", dest="rssi",
+					help="RSSI threshold value"),
+			make_option("-p", "--pathloss", action="store",
+					type="int", dest="pathloss",
+					help="Pathloss threshold value"),
+			make_option("-t", "--transport", action="store",
+					type="string", dest="transport",
+					help="Type of scan to run (le/bredr/auto)"),
 			make_option("-c", "--compact",
 					action="store_true", dest="compact"),
 			]
@@ -144,6 +156,26 @@
 		if "org.bluez.Device1" in interfaces:
 			devices[path] = interfaces["org.bluez.Device1"]
 
+	scan_filter = dict()
+
+	if options.uuids:
+		uuids = []
+		uuid_list = options.uuids.split(',')
+		for uuid in uuid_list:
+			uuids.append(uuid)
+
+		scan_filter.update({ "UUIDs": uuids })
+
+	if options.rssi:
+		scan_filter.update({ "RSSI": dbus.Int16(options.rssi) })
+
+	if options.pathloss:
+		scan_filter.update({ "Pathloss": dbus.UInt16(options.pathloss) })
+
+	if options.transport:
+		scan_filter.update({ "Transport": options.transport })
+
+	adapter.SetDiscoveryFilter(scan_filter)
 	adapter.StartDiscovery()
 
 	mainloop = GObject.MainLoop()
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 0f6a1bd..2153bec 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -658,7 +658,7 @@
 
 static void cmd_write_value(struct client *cli, char *cmd_str)
 {
-	int opt, i;
+	int opt, i, val;
 	char *argvbuf[516];
 	char **argv = argvbuf;
 	int argc = 1;
@@ -726,19 +726,14 @@
 		}
 
 		for (i = 1; i < argc; i++) {
-			if (strlen(argv[i]) != 2) {
-				printf("Invalid value byte: %s\n",
-								argv[i]);
-				goto done;
-			}
-
-			value[i-1] = strtol(argv[i], &endptr, 0);
+			val = strtol(argv[i], &endptr, 0);
 			if (endptr == argv[i] || *endptr != '\0'
-							|| errno == ERANGE) {
+				|| errno == ERANGE || val < 0 || val > 255) {
 				printf("Invalid value byte: %s\n",
 								argv[i]);
 				goto done;
 			}
+			value[i-1] = val;
 		}
 	}
 
@@ -793,7 +788,7 @@
 
 static void cmd_write_long_value(struct client *cli, char *cmd_str)
 {
-	int opt, i;
+	int opt, i, val;
 	char *argvbuf[516];
 	char **argv = argvbuf;
 	int argc = 1;
@@ -865,21 +860,15 @@
 		}
 
 		for (i = 2; i < argc; i++) {
-			if (strlen(argv[i]) != 2) {
-				printf("Invalid value byte: %s\n",
-								argv[i]);
-				free(value);
-				return;
-			}
-
-			value[i-2] = strtol(argv[i], &endptr, 0);
+			val = strtol(argv[i], &endptr, 0);
 			if (endptr == argv[i] || *endptr != '\0'
-							|| errno == ERANGE) {
+				|| errno == ERANGE || val < 0 || val > 255) {
 				printf("Invalid value byte: %s\n",
 								argv[i]);
 				free(value);
 				return;
 			}
+			value[i-2] = val;
 		}
 	}
 
@@ -909,7 +898,7 @@
 
 static void cmd_write_prepare(struct client *cli, char *cmd_str)
 {
-	int opt, i;
+	int opt, i, val;
 	char *argvbuf[516];
 	char **argv = argvbuf;
 	int argc = 0;
@@ -1002,18 +991,14 @@
 	}
 
 	for (i = 2; i < argc; i++) {
-		if (strlen(argv[i]) != 2) {
+		val = strtol(argv[i], &endptr, 0);
+		if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE
+						|| val < 0 || val > 255) {
 			printf("Invalid value byte: %s\n", argv[i]);
 			free(value);
 			return;
 		}
-
-		value[i-2] = strtol(argv[i], &endptr, 0);
-		if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE) {
-			printf("Invalid value byte: %s\n", argv[i]);
-			free(value);
-			return;
-		}
+		value[i-2] = val;
 	}
 
 done:
diff --git a/tools/btmgmt.c b/tools/btmgmt.c
index f82461e..4c86dcb 100644
--- a/tools/btmgmt.c
+++ b/tools/btmgmt.c
@@ -2254,19 +2254,21 @@
 	{ "help",	0, 0, 'h' },
 	{ "le-only",	1, 0, 'l' },
 	{ "bredr-only",	1, 0, 'b' },
+	{ "limited",	1, 0, 'L' },
 	{ 0, 0, 0, 0 }
 };
 
 static void cmd_find(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
 {
 	struct mgmt_cp_start_discovery cp;
+	uint8_t op = MGMT_OP_START_DISCOVERY;
 	uint8_t type = SCAN_TYPE_DUAL;
 	int opt;
 
 	if (index == MGMT_INDEX_NONE)
 		index = 0;
 
-	while ((opt = getopt_long(argc, argv, "+lbh", find_options,
+	while ((opt = getopt_long(argc, argv, "+lbLh", find_options,
 								NULL)) != -1) {
 		switch (opt) {
 		case 'l':
@@ -2277,6 +2279,9 @@
 			type |= SCAN_TYPE_BREDR;
 			type &= ~SCAN_TYPE_LE;
 			break;
+		case 'L':
+			op = MGMT_OP_START_LIMITED_DISCOVERY;
+			break;
 		case 'h':
 			find_usage();
 			optind = 0;
@@ -2295,8 +2300,8 @@
 	memset(&cp, 0, sizeof(cp));
 	cp.type = type;
 
-	if (mgmt_send(mgmt, MGMT_OP_START_DISCOVERY, index, sizeof(cp), &cp,
-						find_rsp, NULL, NULL) == 0) {
+	if (mgmt_send(mgmt, op, index, sizeof(cp), &cp, find_rsp,
+							NULL, NULL) == 0) {
 		error("Unable to send start_discovery cmd");
 		return noninteractive_quit(EXIT_FAILURE);
 	}
@@ -2587,7 +2592,7 @@
 	str2ba(argv[0], &cp.bdaddr);
 	cp.type = type;
 
-	if (mgmt_send(mgmt, MGMT_OP_CANCEL_PAIR_DEVICE, index, sizeof(cp), &cp,
+	if (mgmt_reply(mgmt, MGMT_OP_CANCEL_PAIR_DEVICE, index, sizeof(cp), &cp,
 					cancel_pair_rsp, NULL, NULL) == 0) {
 		error("Unable to send cancel_pair_device cmd");
 		return noninteractive_quit(EXIT_FAILURE);
diff --git a/tools/eddystone.c b/tools/eddystone.c
index 23a34f9..9569ee0 100644
--- a/tools/eddystone.c
+++ b/tools/eddystone.c
@@ -149,7 +149,6 @@
 
 	cmd.data[0] = 0x02;		/* Field length */
 	cmd.data[1] = 0x01;		/* Flags */
-	cmd.data[2] = 0x02;		/* LE General Discoverable Mode */
 	cmd.data[2] |= 0x04;		/* BR/EDR Not Supported */
 
 	cmd.data[3] = 0x03;		/* Field length */
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 9baa9e1..329d1af 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -55,6 +55,7 @@
 static DBusConnection *connection;
 
 struct characteristic {
+	char *service;
 	char *uuid;
 	char *path;
 	uint8_t *value;
@@ -63,10 +64,12 @@
 };
 
 struct descriptor {
+	struct characteristic *chr;
 	char *uuid;
 	char *path;
 	uint8_t *value;
 	int vlen;
+	const char **props;
 };
 
 /*
@@ -75,6 +78,7 @@
  * property of the GattCharacteristic1.
  */
 static const char *ias_alert_level_props[] = { "write-without-response", NULL };
+static const char *desc_props[] = { "read", "write", NULL };
 
 static gboolean desc_get_uuid(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *user_data)
@@ -86,13 +90,20 @@
 	return TRUE;
 }
 
-static gboolean desc_get_value(const GDBusPropertyTable *property,
+static gboolean desc_get_characteristic(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *user_data)
 {
 	struct descriptor *desc = user_data;
-	DBusMessageIter array;
 
-	printf("Descriptor(%s): Get(\"Value\")\n", desc->uuid);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+						&desc->chr->path);
+
+	return TRUE;
+}
+
+static bool desc_read(struct descriptor *desc, DBusMessageIter *iter)
+{
+	DBusMessageIter array;
 
 	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
 					DBUS_TYPE_BYTE_AS_STRING, &array);
@@ -103,20 +114,25 @@
 
 	dbus_message_iter_close_container(iter, &array);
 
-	return TRUE;
+	return true;
 }
 
-static void desc_set_value(const GDBusPropertyTable *property,
-				DBusMessageIter *iter,
-				GDBusPendingPropertySet id, void *user_data)
+static gboolean desc_get_value(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
 {
 	struct descriptor *desc = user_data;
+
+	printf("Descriptor(%s): Get(\"Value\")\n", desc->uuid);
+
+	return desc_read(desc, iter);
+}
+
+static void desc_write(struct descriptor *desc, DBusMessageIter *iter)
+{
 	DBusMessageIter array;
 	const uint8_t *value;
 	int vlen;
 
-	printf("Descriptor(%s): Set(\"Value\", ...)\n", desc->uuid);
-
 	dbus_message_iter_recurse(iter, &array);
 	dbus_message_iter_get_fixed_array(&array, &value, &vlen);
 
@@ -124,16 +140,47 @@
 	desc->value = g_memdup(value, vlen);
 	desc->vlen = vlen;
 
-	g_dbus_pending_property_success(id);
-
 	g_dbus_emit_property_changed(connection, desc->path,
 					GATT_DESCRIPTOR_IFACE, "Value");
+}
 
+static void desc_set_value(const GDBusPropertyTable *property,
+				DBusMessageIter *iter,
+				GDBusPendingPropertySet id, void *user_data)
+{
+	struct descriptor *desc = user_data;
+
+	printf("Descriptor(%s): Set(\"Value\", ...)\n", desc->uuid);
+
+	desc_write(desc, iter);
+
+	g_dbus_pending_property_success(id);
+}
+
+static gboolean desc_get_props(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct descriptor *desc = data;
+	DBusMessageIter array;
+	int i;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_STRING_AS_STRING, &array);
+
+	for (i = 0; desc->props[i]; i++)
+		dbus_message_iter_append_basic(&array,
+					DBUS_TYPE_STRING, &desc->props[i]);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
 }
 
 static const GDBusPropertyTable desc_properties[] = {
-	{ "UUID",		"s",	desc_get_uuid },
-	{ "Value",		"ay",	desc_get_value, desc_set_value, NULL },
+	{ "UUID",		"s", desc_get_uuid },
+	{ "Characteristic",	"o", desc_get_characteristic },
+	{ "Value",		"ay", desc_get_value, desc_set_value, NULL },
+	{ "Flags",		"as", desc_get_props, NULL, NULL },
 	{ }
 };
 
@@ -147,13 +194,20 @@
 	return TRUE;
 }
 
-static gboolean chr_get_value(const GDBusPropertyTable *property,
+static gboolean chr_get_service(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *user_data)
 {
 	struct characteristic *chr = user_data;
-	DBusMessageIter array;
 
-	printf("Characteristic(%s): Get(\"Value\")\n", chr->uuid);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+							&chr->service);
+
+	return TRUE;
+}
+
+static bool chr_read(struct characteristic *chr, DBusMessageIter *iter)
+{
+	DBusMessageIter array;
 
 	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
 					DBUS_TYPE_BYTE_AS_STRING, &array);
@@ -163,7 +217,17 @@
 
 	dbus_message_iter_close_container(iter, &array);
 
-	return TRUE;
+	return true;
+}
+
+static gboolean chr_get_value(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct characteristic *chr = user_data;
+
+	printf("Characteristic(%s): Get(\"Value\")\n", chr->uuid);
+
+	return chr_read(chr, iter);
 }
 
 static gboolean chr_get_props(const GDBusPropertyTable *property,
@@ -185,14 +249,28 @@
 	return TRUE;
 }
 
+static void chr_write(struct characteristic *chr, DBusMessageIter *iter)
+{
+	DBusMessageIter array;
+	uint8_t *value;
+	int len;
+
+	dbus_message_iter_recurse(iter, &array);
+	dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+	g_free(chr->value);
+	chr->value = g_memdup(value, len);
+	chr->vlen = len;
+
+	g_dbus_emit_property_changed(connection, chr->path, GATT_CHR_IFACE,
+								"Value");
+}
+
 static void chr_set_value(const GDBusPropertyTable *property,
 				DBusMessageIter *iter,
 				GDBusPendingPropertySet id, void *user_data)
 {
 	struct characteristic *chr = user_data;
-	DBusMessageIter array;
-	uint8_t *value;
-	int len;
 
 	printf("Characteristic(%s): Set('Value', ...)\n", chr->uuid);
 
@@ -204,25 +282,31 @@
 		return;
 	}
 
-	dbus_message_iter_recurse(iter, &array);
-	dbus_message_iter_get_fixed_array(&array, &value, &len);
-
-	g_free(chr->value);
-	chr->value = g_memdup(value, len);
-	chr->vlen = len;
+	chr_write(chr, iter);
 
 	g_dbus_pending_property_success(id);
-	g_dbus_emit_property_changed(connection, chr->path,
-						GATT_CHR_IFACE, "Value");
 }
 
 static const GDBusPropertyTable chr_properties[] = {
-	{ "UUID",	"s",	chr_get_uuid },
-	{ "Value", "ay", chr_get_value, chr_set_value, NULL },
-	{ "Flags", "as", chr_get_props, NULL, NULL },
+	{ "UUID",	"s", chr_get_uuid },
+	{ "Service",	"o", chr_get_service },
+	{ "Value",	"ay", chr_get_value, chr_set_value, NULL },
+	{ "Flags",	"as", chr_get_props, NULL, NULL },
 	{ }
 };
 
+static gboolean service_get_primary(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	dbus_bool_t primary = TRUE;
+
+	printf("Get Primary: %s\n", primary ? "True" : "False");
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &primary);
+
+	return TRUE;
+}
+
 static gboolean service_get_uuid(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *user_data)
 {
@@ -256,6 +340,7 @@
 }
 
 static const GDBusPropertyTable service_properties[] = {
+	{ "Primary", "b", service_get_primary },
 	{ "UUID", "s", service_get_uuid },
 	{ "Includes", "ao", service_get_includes, NULL,
 					service_exist_includes },
@@ -267,6 +352,7 @@
 	struct characteristic *chr = user_data;
 
 	g_free(chr->uuid);
+	g_free(chr->service);
 	g_free(chr->value);
 	g_free(chr->path);
 	g_free(chr);
@@ -282,10 +368,108 @@
 	g_free(desc);
 }
 
+static DBusMessage *chr_read_value(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	struct characteristic *chr = user_data;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return g_dbus_create_error(msg, DBUS_ERROR_NO_MEMORY,
+							"No Memory");
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	chr_read(chr, &iter);
+
+	return reply;
+}
+
+static DBusMessage *chr_write_value(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	struct characteristic *chr = user_data;
+	DBusMessageIter iter;
+
+	dbus_message_iter_init(msg, &iter);
+
+	chr_write(chr, &iter);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *chr_start_notify(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	return g_dbus_create_error(msg, DBUS_ERROR_NOT_SUPPORTED,
+							"Not Supported");
+}
+
+static DBusMessage *chr_stop_notify(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	return g_dbus_create_error(msg, DBUS_ERROR_NOT_SUPPORTED,
+							"Not Supported");
+}
+
+static const GDBusMethodTable chr_methods[] = {
+	{ GDBUS_ASYNC_METHOD("ReadValue", NULL, GDBUS_ARGS({ "value", "ay" }),
+						chr_read_value) },
+	{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }),
+						NULL, chr_write_value) },
+	{ GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chr_start_notify) },
+	{ GDBUS_METHOD("StopNotify", NULL, NULL, chr_stop_notify) },
+	{ }
+};
+
+static DBusMessage *desc_read_value(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	struct descriptor *desc = user_data;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return g_dbus_create_error(msg, DBUS_ERROR_NO_MEMORY,
+							"No Memory");
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	desc_read(desc, &iter);
+
+	return reply;
+}
+
+static DBusMessage *desc_write_value(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	struct descriptor *desc = user_data;
+	DBusMessageIter iter;
+
+	dbus_message_iter_init(msg, &iter);
+
+	desc_write(desc, &iter);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable desc_methods[] = {
+	{ GDBUS_ASYNC_METHOD("ReadValue", NULL, GDBUS_ARGS({ "value", "ay" }),
+						desc_read_value) },
+	{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }),
+						NULL,
+						desc_write_value) },
+	{ }
+};
+
 static gboolean register_characteristic(const char *chr_uuid,
 						const uint8_t *value, int vlen,
 						const char **props,
 						const char *desc_uuid,
+						const char **desc_props,
 						const char *service_path)
 {
 	struct characteristic *chr;
@@ -297,10 +481,11 @@
 	chr->value = g_memdup(value, vlen);
 	chr->vlen = vlen;
 	chr->props = props;
+	chr->service = g_strdup(service_path);
 	chr->path = g_strdup_printf("%s/characteristic%d", service_path, id++);
 
 	if (!g_dbus_register_interface(connection, chr->path, GATT_CHR_IFACE,
-					NULL, NULL, chr_properties,
+					chr_methods, NULL, chr_properties,
 					chr, chr_iface_destroy)) {
 		printf("Couldn't register characteristic interface\n");
 		chr_iface_destroy(chr);
@@ -312,11 +497,13 @@
 
 	desc = g_new0(struct descriptor, 1);
 	desc->uuid = g_strdup(desc_uuid);
+	desc->chr = chr;
+	desc->props = desc_props;
 	desc->path = g_strdup_printf("%s/descriptor%d", chr->path, id++);
 
 	if (!g_dbus_register_interface(connection, desc->path,
 					GATT_DESCRIPTOR_IFACE,
-					NULL, NULL, desc_properties,
+					desc_methods, NULL, desc_properties,
 					desc, desc_iface_destroy)) {
 		printf("Couldn't register descriptor interface\n");
 		g_dbus_unregister_interface(connection, chr->path,
@@ -360,6 +547,7 @@
 						&level, sizeof(level),
 						ias_alert_level_props,
 						READ_WRITE_DESCRIPTOR_UUID,
+						desc_props,
 						service_path)) {
 		printf("Couldn't register Alert Level characteristic (IAS)\n");
 		g_dbus_unregister_interface(connection, service_path,
@@ -372,63 +560,55 @@
 	printf("Registered service: %s\n", service_path);
 }
 
-static void register_external_service_reply(DBusPendingCall *call,
-							void *user_data)
+static void register_app_reply(DBusMessage *reply, void *user_data)
 {
-	DBusMessage *reply = dbus_pending_call_steal_reply(call);
 	DBusError derr;
 
 	dbus_error_init(&derr);
 	dbus_set_error_from_message(&derr, reply);
 
 	if (dbus_error_is_set(&derr))
-		printf("RegisterService: %s\n", derr.message);
+		printf("RegisterApplication: %s\n", derr.message);
 	else
-		printf("RegisterService: OK\n");
+		printf("RegisterApplication: OK\n");
 
-	dbus_message_unref(reply);
 	dbus_error_free(&derr);
 }
 
-static void register_external_service(gpointer a, gpointer b)
+static void register_app_setup(DBusMessageIter *iter, void *user_data)
 {
-	DBusConnection *conn = b;
-	const char *path = a;
-	DBusMessage *msg;
-	DBusPendingCall *call;
-	DBusMessageIter iter, dict;
+	const char *path = "/";
+	DBusMessageIter dict;
 
-	msg = dbus_message_new_method_call("org.bluez", "/org/bluez",
-					GATT_MGR_IFACE, "RegisterService");
-	if (!msg) {
-		printf("Couldn't allocate D-Bus message\n");
-		return;
-	}
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
 
-	dbus_message_iter_init_append(msg, &iter);
-
-	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
-
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
 
 	/* TODO: Add options dictionary */
 
-	dbus_message_iter_close_container(&iter, &dict);
-
-	if (!g_dbus_send_message_with_reply(conn, msg, &call, -1)) {
-		dbus_message_unref(msg);
-		return;
-	}
-
-	dbus_pending_call_set_notify(call, register_external_service_reply,
-								NULL, NULL);
-
-	dbus_pending_call_unref(call);
+	dbus_message_iter_close_container(iter, &dict);
 }
 
-static void connect_handler(DBusConnection *conn, void *user_data)
+static void register_app(GDBusProxy *proxy)
 {
-	g_slist_foreach(services, register_external_service, conn);
+	if (!g_dbus_proxy_method_call(proxy, "RegisterApplication",
+					register_app_setup, register_app_reply,
+					NULL, NULL)) {
+		printf("Unable to call RegisterApplication\n");
+		return;
+	}
+}
+
+static void proxy_added_cb(GDBusProxy *proxy, void *user_data)
+{
+	const char *iface;
+
+	iface = g_dbus_proxy_get_interface(proxy);
+
+	if (g_strcmp0(iface, GATT_MGR_IFACE))
+		return;
+
+	register_app(proxy);
 }
 
 static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
@@ -520,9 +700,10 @@
 
 	create_services();
 
-	client = g_dbus_client_new(connection, "org.bluez", "/org/bluez");
+	client = g_dbus_client_new(connection, "org.bluez", "/");
 
-	g_dbus_client_set_connect_watch(client, connect_handler, NULL);
+	g_dbus_client_set_proxy_handlers(client, proxy_added_cb, NULL, NULL,
+									NULL);
 
 	g_main_loop_run(main_loop);
 
diff --git a/tools/ibeacon.c b/tools/ibeacon.c
index 9d48e66..6208e8a 100644
--- a/tools/ibeacon.c
+++ b/tools/ibeacon.c
@@ -149,7 +149,6 @@
 
 	cmd.data[0] = 0x02;		/* Field length */
 	cmd.data[1] = 0x01;		/* Flags */
-	cmd.data[2] = 0x02;		/* LE General Discoverable Mode */
 	cmd.data[2] |= 0x04;		/* BR/EDR Not Supported */
 
 	cmd.data[3] = 0x1a;		/* Field length */
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 326a32c..944bfba 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -2892,14 +2892,14 @@
 	define_test_client("/TP/GAR/CL/BV-04-C", test_client, service_db_1,
 			&test_long_read_1,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
-			raw_pdu(0x0d, 0x01, 0x02, 0x03));
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x0b, 0x01, 0x02, 0x03));
 
 	define_test_client("/TP/GAR/CL/BV-04-C/512B", test_client, service_db_1,
 			&test_long_read_2,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
-			raw_pdu(0x0d, 0xff, 0xff, 0xff, 0xff,
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x0b, 0xff, 0xff, 0xff, 0xff,
 				0xff, 0xff, 0xff, 0xff, 0xff,
 				0xff, 0xff, 0xff, 0xff, 0xff,
 				0xff, 0xff, 0xff, 0xff, 0xff,
@@ -3014,46 +3014,46 @@
 	define_test_client("/TP/GAR/CL/BI-12-C", test_client, service_db_1,
 			&test_long_read_3,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
-			raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x02));
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x02));
 
 	define_test_client("/TP/GAR/CL/BI-13-C", test_client, service_db_1,
 			&test_long_read_4,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
-			raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x07));
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x07));
 
 	define_test_client("/TP/GAR/CL/BI-14-C", test_client, service_db_1,
 			&test_long_read_5,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x00, 0x00, 0x00, 0x00),
-			raw_pdu(0x01, 0x0c, 0x00, 0x00, 0x01));
+			raw_pdu(0x0a, 0x00, 0x00),
+			raw_pdu(0x01, 0x0a, 0x00, 0x00, 0x01));
 
 	define_test_client("/TP/GAR/CL/BI-15-C", test_client, service_db_1,
 			&test_long_read_6,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
-			raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x08));
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x08));
 
 	define_test_client("/TP/GAR/CL/BI-16-C", test_client, service_db_1,
 			&test_long_read_7,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
-			raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x05));
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x05));
 
 	define_test_client("/TP/GAR/CL/BI-16-C/auto", test_client, service_db_1,
 			&test_long_read_1,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
-			raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x05),
-			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x05),
+			raw_pdu(0x0a, 0x03, 0x00),
 			raw_pdu(0x0d, 0x01, 0x02, 0x03));
 
 	define_test_client("/TP/GAR/CL/BI-17-C", test_client, service_db_1,
 			&test_long_read_8,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
-			raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x0c));
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x0c));
 
 	define_test_client("/TP/GAR/CL/BI-18-C", test_client, service_db_1,
 			&test_multiple_read_2,
@@ -3299,14 +3299,14 @@
 	define_test_client("/TP/GAR/CL/BV-07-C", test_client, service_db_1,
 			&test_long_read_9,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
-			raw_pdu(0x0d, 0x01, 0x02, 0x03));
+			raw_pdu(0x0a, 0x04, 0x00),
+			raw_pdu(0x0b, 0x01, 0x02, 0x03));
 
 	define_test_client("/TP/GAR/CL/BV-07-C/512B", test_client, service_db_1,
 			&test_long_read_10,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
-			raw_pdu(0x0d, 0xff, 0xff, 0xff, 0xff,
+			raw_pdu(0x0a, 0x04, 0x00),
+			raw_pdu(0x0b, 0xff, 0xff, 0xff, 0xff,
 				0xff, 0xff, 0xff, 0xff, 0xff,
 				0xff, 0xff, 0xff, 0xff, 0xff,
 				0xff, 0xff, 0xff, 0xff, 0xff,
@@ -3415,46 +3415,46 @@
 	define_test_client("/TP/GAR/CL/BI-28-C", test_client, service_db_1,
 			&test_long_read_11,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
-			raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x02));
+			raw_pdu(0x0a, 0x04, 0x00),
+			raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x02));
 
 	define_test_client("/TP/GAR/CL/BI-29-C", test_client, service_db_1,
 			&test_long_read_12,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
-			raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x07));
+			raw_pdu(0x0a, 0x04, 0x00),
+			raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x07));
 
 	define_test_client("/TP/GAR/CL/BI-30-C", test_client, service_db_1,
 			&test_long_read_5,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x00, 0x00, 0x00, 0x00),
-			raw_pdu(0x01, 0x0c, 0x00, 0x00, 0x01));
+			raw_pdu(0x0a, 0x00, 0x00),
+			raw_pdu(0x01, 0x0a, 0x00, 0x00, 0x01));
 
 	define_test_client("/TP/GAR/CL/BI-31-C", test_client, service_db_1,
 			&test_long_read_13,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
-			raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x08));
+			raw_pdu(0x0a, 0x04, 0x00),
+			raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x08));
 
 	define_test_client("/TP/GAR/CL/BI-32-C", test_client, service_db_1,
 			&test_long_read_14,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
-			raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x05));
+			raw_pdu(0x0a, 0x04, 0x00),
+			raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x05));
 
 	define_test_client("/TP/GAR/CL/BI-32-C/auto", test_client, service_db_1,
 			&test_long_read_9,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
-			raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x05),
-			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
-			raw_pdu(0x0d, 0x01, 0x02, 0x03));
+			raw_pdu(0x0a, 0x04, 0x00),
+			raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x05),
+			raw_pdu(0x0a, 0x04, 0x00),
+			raw_pdu(0x0b, 0x01, 0x02, 0x03));
 
 	define_test_client("/TP/GAR/CL/BI-33-C", test_client, service_db_1,
 			&test_long_read_15,
 			SERVICE_DATA_1_PDUS,
-			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
-			raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x0c));
+			raw_pdu(0x0a, 0x04, 0x00),
+			raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x0c));
 
 	define_test_client("/TP/GAR/CL/BI-34-C", test_client, service_db_1,
 			&test_read_12,