Merge BlueZ upstream master branch

Merged several post-5.28-release fixes from BlueZ upstream .

BUG=none
TEST=emerge bluez
diff --git a/Makefile.plugins b/Makefile.plugins
index 52b51c5..a5d62d0 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -1,4 +1,7 @@
 
+builtin_modules += chromium
+builtin_sources += plugins/chromium.c
+
 builtin_modules += hostname
 builtin_sources += plugins/hostname.c
 
diff --git a/Makefile.tools b/Makefile.tools
index e42e42d..16cc571 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -121,7 +121,8 @@
 bin_PROGRAMS += tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \
 			tools/rfcomm tools/rctest tools/l2test tools/l2ping \
 			tools/sdptool tools/ciptool tools/bccmd \
-			tools/bluemoon tools/hex2hcd tools/mpris-proxy
+			tools/bluemoon tools/hex2hcd tools/mpris-proxy \
+			tools/btmgmt tools/btgatt-client tools/btgatt-server
 
 tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
 						tools/hciattach_st.c \
@@ -190,6 +191,17 @@
 
 tools_hex2hcd_SOURCES = tools/hex2hcd.c
 
+tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c
+tools_btmgmt_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
+
+tools_btgatt_client_SOURCES = tools/btgatt-client.c src/uuid-helper.c
+tools_btgatt_client_LDADD = src/libshared-mainloop.la \
+						lib/libbluetooth-internal.la
+
+tools_btgatt_server_SOURCES = tools/btgatt-server.c src/uuid-helper.c
+tools_btgatt_server_LDADD = src/libshared-mainloop.la \
+						lib/libbluetooth-internal.la
+
 tools_mpris_proxy_SOURCES = tools/mpris-proxy.c
 tools_mpris_proxy_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
 
@@ -220,11 +232,10 @@
 noinst_PROGRAMS += tools/bdaddr tools/avinfo tools/avtest \
 			tools/scotest tools/amptest tools/hwdb \
 			tools/hcieventmask tools/hcisecfilter \
-			tools/btmgmt tools/btinfo tools/btattach \
+			tools/btinfo tools/btattach \
 			tools/btsnoop tools/btproxy \
 			tools/btiotest tools/mcaptest tools/cltest \
-			tools/oobtest tools/seq2bseq tools/ibeacon \
-			tools/btgatt-client tools/btgatt-server
+			tools/oobtest tools/seq2bseq tools/ibeacon
 
 tools_bdaddr_SOURCES = tools/bdaddr.c src/oui.h src/oui.c
 tools_bdaddr_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
@@ -241,9 +252,6 @@
 
 tools_hcieventmask_LDADD = lib/libbluetooth-internal.la
 
-tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c
-tools_btmgmt_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
-
 tools_btinfo_SOURCES = tools/btinfo.c monitor/bt.h
 tools_btinfo_LDADD = src/libshared-mainloop.la
 
@@ -276,14 +284,6 @@
 tools_ibeacon_SOURCES = tools/ibeacon.c monitor/bt.h
 tools_ibeacon_LDADD = src/libshared-mainloop.la
 
-tools_btgatt_client_SOURCES = tools/btgatt-client.c src/uuid-helper.c
-tools_btgatt_client_LDADD = src/libshared-mainloop.la \
-						lib/libbluetooth-internal.la
-
-tools_btgatt_server_SOURCES = tools/btgatt-server.c src/uuid-helper.c
-tools_btgatt_server_LDADD = src/libshared-mainloop.la \
-						lib/libbluetooth-internal.la
-
 EXTRA_DIST += tools/bdaddr.1
 endif
 
diff --git a/README.chromium b/README.chromium
new file mode 100644
index 0000000..c931b6b
--- /dev/null
+++ b/README.chromium
@@ -0,0 +1,14 @@
+Homepage: http://www.bluez.org
+Upstream Repo: http://git.kernel.org/cgit/bluetooth/bluez.git
+License: GPLv2
+License File: COPYING
+
+Description:
+
+User-space daemon for the official Linux Bluetooth stack. It provides high-level
+APIs for core Bluetooth functionality as well as several profiles used by OS
+features, such as audio and input.
+
+This repository mirrors the master branch of the upstream project while
+containing Chromium OS only additions that won't be upstream, as well as fixes
+that were sent upstream but were first merged locally.
diff --git a/acinclude.m4 b/acinclude.m4
index bc39c6d..04b7670 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -11,21 +11,18 @@
 ])
 
 AC_DEFUN([COMPILER_FLAGS], [
-	with_cflags=""
-	if (test "$USE_MAINTAINER_MODE" = "yes"); then
-		with_cflags="$with_cflags -Wall -Werror -Wextra"
-		with_cflags="$with_cflags -Wno-unused-parameter"
-		with_cflags="$with_cflags -Wno-missing-field-initializers"
-		with_cflags="$with_cflags -Wdeclaration-after-statement"
-		with_cflags="$with_cflags -Wmissing-declarations"
-		with_cflags="$with_cflags -Wredundant-decls"
-		with_cflags="$with_cflags -Wcast-align"
-		with_cflags="$with_cflags -Wswitch-enum"
-		with_cflags="$with_cflags -Wformat -Wformat-security"
-		with_cflags="$with_cflags -DG_DISABLE_DEPRECATED"
-		with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28"
-		with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_28"
-	fi
+	with_cflags="-Wall -Werror -Wextra"
+	with_cflags="$with_cflags -Wno-unused-parameter"
+	with_cflags="$with_cflags -Wno-missing-field-initializers"
+	with_cflags="$with_cflags -Wdeclaration-after-statement"
+	with_cflags="$with_cflags -Wmissing-declarations"
+	with_cflags="$with_cflags -Wredundant-decls"
+	with_cflags="$with_cflags -Wcast-align"
+	with_cflags="$with_cflags -Wswitch-enum"
+	with_cflags="$with_cflags -Wformat -Wformat-security"
+	with_cflags="$with_cflags -DG_DISABLE_DEPRECATED"
+	with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28"
+	with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_28"
 	AC_SUBST([WARNING_CFLAGS], $with_cflags)
 ])
 
diff --git a/lib/sdp.h b/lib/sdp.h
index cc10e9f..69a2122 100644
--- a/lib/sdp.h
+++ b/lib/sdp.h
@@ -34,7 +34,7 @@
 #include <stdint.h>
 #include <bluetooth/bluetooth.h>
 
-#define SDP_UNIX_PATH "/var/run/sdp"
+#define SDP_UNIX_PATH "/var/run/bluetooth/sdp"
 #define SDP_RESPONSE_TIMEOUT	20
 #define SDP_REQ_BUFFER_SIZE	2048
 #define SDP_RSP_BUFFER_SIZE	65535
diff --git a/plugins/chromium.c b/plugins/chromium.c
new file mode 100644
index 0000000..3d362652
--- /dev/null
+++ b/plugins/chromium.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus/gdbus.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include "lib/mgmt.h"
+#include "src/adapter.h"
+#include "src/dbus-common.h"
+#include "src/device.h"
+#include "src/error.h"
+#include "src/log.h"
+#include "src/plugin.h"
+#include "src/shared/mgmt.h"
+
+#define DBUS_PATH "/org/bluez"
+#define DBUS_PLUGIN_INTERFACE "org.chromium.Bluetooth"
+#define DBUS_PLUGIN_DEVICE_INTERFACE "org.chromium.BluetoothDevice"
+
+#define DBUS_BLUEZ_SERVICE "org.bluez"
+#define DBUS_OBJECT_MANAGER_INTERFACE "org.freedesktop.DBus.ObjectManager"
+
+#define DBUS_BLUEZ_DEVICE_INTERFACE "org.bluez.Device1"
+
+static struct mgmt *mgmt_if = NULL;
+
+static bool supports_le_services = false;
+static bool supports_conn_info = false;
+
+static int interfaces_added_watch_id = 0;
+static int interfaces_removed_watch_id = 0;
+
+static gboolean chromium_property_get_supports_le_services(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	dbus_bool_t value = supports_le_services;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static gboolean chromium_property_get_supports_conn_info(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	dbus_bool_t value = supports_conn_info;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+/* Helper functions and struct to find a device and the adapter it belongs to
+ * for a given DBus object path.
+ */
+struct find_device_context {
+	const char *device_path;
+	struct btd_adapter *adapter;
+	struct btd_device *device;
+};
+
+static void find_by_path_device_cb(struct btd_device *device, void *data) {
+	struct find_device_context *context = data;
+
+	if (strcmp(context->device_path, device_get_path(device)) == 0)
+		context->device = device;
+}
+
+static void find_by_path_adapter_cb(struct btd_adapter *adapter,
+							gpointer user_data) {
+	struct find_device_context *context = user_data;
+
+	context->adapter = adapter;
+	btd_adapter_for_each_device(adapter, find_by_path_device_cb, context);
+}
+
+static gboolean find_device_by_path(const char *device_path,
+					struct btd_adapter **out_adapter,
+					struct btd_device **out_device) {
+	struct find_device_context context;
+
+	context.device_path = device_path;
+	context.device = NULL;
+
+	adapter_foreach(find_by_path_adapter_cb, &context);
+	if (context.adapter == NULL || context.device == NULL)
+	    return FALSE;
+
+	*out_adapter = context.adapter;
+	*out_device = context.device;
+	return TRUE;
+}
+
+static void get_conn_info_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data) {
+	DBusMessage *msg = user_data;
+	DBusMessage *reply;
+	const struct mgmt_rp_get_conn_info *rp;
+	int16_t rssi, tx_power, max_tx_power;
+
+	if (status == 0) {
+		DBusMessageIter iter;
+
+		reply = dbus_message_new_method_return(msg);
+		if (reply == NULL) {
+			dbus_message_unref(msg);
+			error("Failed to create dbus reply message.");
+			return;
+		}
+
+		rp = param;
+		rssi = rp->rssi;
+		tx_power = rp->tx_power;
+		max_tx_power = rp->max_tx_power;
+
+		dbus_message_iter_init_append(reply, &iter);
+		dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT16, &rssi);
+		dbus_message_iter_append_basic(
+					&iter, DBUS_TYPE_INT16, &tx_power);
+		dbus_message_iter_append_basic(
+					&iter, DBUS_TYPE_INT16, &max_tx_power);
+	} else {
+		reply = btd_error_failed(msg, mgmt_errstr(status));
+		if (!reply) {
+			dbus_message_unref(msg);
+			error("Failed to create dbus error reply message.");
+			return;
+		}
+	}
+
+	if (!g_dbus_send_message(btd_get_dbus_connection(), reply))
+		error("DBus send failed.");
+	dbus_message_unref(msg);
+}
+
+static DBusMessage *get_conn_info(DBusConnection *conn, DBusMessage *msg,
+								void *user_data)
+{
+	const char *device_path = dbus_message_get_path(msg);
+	struct btd_adapter *adapter = NULL;
+	struct btd_device *device = NULL;
+	struct mgmt_cp_get_conn_info cp;
+
+	if (!mgmt_if)
+		return btd_error_not_ready(msg);
+
+	if (!supports_conn_info)
+		return btd_error_not_supported(msg);
+
+	if (!find_device_by_path(device_path, &adapter, &device))
+		return btd_error_does_not_exist(msg);
+
+	if (!btd_device_is_connected(device))
+		return btd_error_not_connected(msg);
+
+	memset(&cp, 0, sizeof(cp));
+	cp.addr.type = btd_device_get_bdaddr_type(device);
+	cp.addr.bdaddr = *device_get_address(device);
+
+	dbus_message_ref(msg);
+	if (mgmt_send(mgmt_if, MGMT_OP_GET_CONN_INFO,
+			btd_adapter_get_index(adapter), sizeof(cp), &cp,
+			get_conn_info_complete, msg, NULL) == 0)
+		return btd_error_failed(msg,
+				"Failed to send get_conn_info mgmt command");
+	return NULL;
+}
+
+static const GDBusMethodTable device_methods[] = {
+	/* GetConnInfo is a simple DBus wrapper over the get_conn_info mgmt API.
+	 */
+	{ GDBUS_ASYNC_METHOD("GetConnInfo", NULL, GDBUS_ARGS({"TXPower", "y"},
+					{"MaximumTXPower", "y"}, {"RSSI", "y"}),
+		get_conn_info) },
+	{ }
+};
+
+static bool is_interface_entry_bluez_device(DBusMessageIter *array_iter) {
+	int arg_type;
+	DBusMessageIter dict_iter;
+	char *interface = NULL;
+
+	arg_type = dbus_message_iter_get_arg_type(array_iter);
+	if (arg_type == 'e') {
+		dbus_message_iter_recurse(array_iter, &dict_iter);
+		arg_type = dbus_message_iter_get_arg_type(&dict_iter);
+		if (arg_type == 's')
+			dbus_message_iter_get_basic(&dict_iter, &interface);
+		else
+			error("Expected string in InterfaceAdded signal.");
+
+	} else if (arg_type == 's') {
+		dbus_message_iter_get_basic(array_iter, &interface);
+	} else {
+		error("Expected string in InterfaceRemoved signal.");
+	}
+
+	return interface &&
+			strcmp(interface, DBUS_BLUEZ_DEVICE_INTERFACE) == 0;
+}
+
+/* Given an InterfaceAdded or InterfaceRemoved ObjectManager signal, return
+ * the object path if it contains the BlueZ device interface; otherwise, return
+ * null.
+ *
+ * The documentation for these ObjectManager signals can be found at
+ * http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
+ */
+static const char *get_device_path_from_interface_msg(DBusMessage *msg) {
+	int arg_type;
+	char *object_path = NULL;
+	DBusMessageIter args_iter, array_iter;
+
+	dbus_message_iter_init(msg, &args_iter);
+	arg_type = dbus_message_iter_get_arg_type(&args_iter);
+	if (arg_type != 'o') {
+		error("Expected object path in ObjectManager signal.");
+		return NULL;
+	}
+
+	dbus_message_iter_get_basic(&args_iter, &object_path);
+	dbus_message_iter_next(&args_iter);
+	if (!object_path)
+		return NULL;
+
+	arg_type = dbus_message_iter_get_arg_type(&args_iter);
+	if (arg_type != 'a') {
+		error("Expected array in ObjectManager signal.");
+		return NULL;
+	}
+
+	dbus_message_iter_recurse(&args_iter, &array_iter);
+	while (dbus_message_iter_has_next(&array_iter)) {
+		if (is_interface_entry_bluez_device(&array_iter))
+		    return object_path;
+		dbus_message_iter_next(&array_iter);
+	}
+
+	return NULL;
+}
+
+static gboolean interfaces_added(DBusConnection *conn, DBusMessage *msg,
+								void *user_data)
+{
+	const char *device_path = get_device_path_from_interface_msg(msg);
+
+	if (!device_path)
+		return TRUE;
+
+	g_dbus_register_interface(btd_get_dbus_connection(),
+					device_path, DBUS_PLUGIN_DEVICE_INTERFACE,
+					device_methods, NULL, NULL, NULL, NULL);
+
+	return TRUE;
+}
+
+static gboolean interfaces_removed(DBusConnection *conn, DBusMessage *msg,
+								void *user_data)
+{
+	const char *device_path = get_device_path_from_interface_msg(msg);
+
+	if (!device_path)
+		return TRUE;
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+				device_path, DBUS_PLUGIN_DEVICE_INTERFACE);
+
+	return TRUE;
+}
+
+static void remove_dbus_watches() {
+	if (interfaces_added_watch_id)
+		g_dbus_remove_watch(btd_get_dbus_connection(),
+						interfaces_added_watch_id);
+
+	if (interfaces_removed_watch_id)
+		g_dbus_remove_watch(btd_get_dbus_connection(),
+						interfaces_removed_watch_id);
+}
+
+static const GDBusPropertyTable chromium_properties[] = {
+	{ "SupportsLEServices", "b",
+				chromium_property_get_supports_le_services },
+	{ "SupportsConnInfo", "b",
+				chromium_property_get_supports_conn_info },
+	{ }
+};
+
+static void read_version_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_version *rp = param;
+	uint8_t mgmt_version, mgmt_revision;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to read version information: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	if (length < sizeof(*rp)) {
+		error("Wrong size of read version response");
+		return;
+	}
+
+	mgmt_version = rp->version;
+	mgmt_revision = btohs(rp->revision);
+
+	supports_le_services = (mgmt_version > 1 ||
+		(mgmt_version == 1 && mgmt_revision >= 4));
+	supports_conn_info = (mgmt_revision > 1 ||
+		(mgmt_version == 1 && mgmt_revision >= 5));
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+		DBUS_PATH, DBUS_PLUGIN_INTERFACE, "SupportsLEServices");
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+		DBUS_PATH, DBUS_PLUGIN_INTERFACE, "SupportsConnInfo");
+}
+
+static int chromium_init(void)
+{
+	DBG("");
+
+	mgmt_if = mgmt_new_default();
+	if (!mgmt_if)
+		error("Failed to access management interface");
+	else if (!mgmt_send(mgmt_if, MGMT_OP_READ_VERSION,
+					MGMT_INDEX_NONE, 0, NULL,
+					read_version_complete, NULL, NULL))
+		error("Failed to read management version information");
+
+	g_dbus_register_interface(btd_get_dbus_connection(),
+		DBUS_PATH, DBUS_PLUGIN_INTERFACE,
+		NULL, NULL, chromium_properties, NULL, NULL);
+
+	/* Listen for new device objects being added so we can add the plugin
+	 * interface to them.
+	 */
+	interfaces_added_watch_id = g_dbus_add_signal_watch(
+			btd_get_dbus_connection(), DBUS_BLUEZ_SERVICE,
+			"/", DBUS_OBJECT_MANAGER_INTERFACE, "InterfacesAdded",
+			interfaces_added, NULL, NULL);
+	if (!interfaces_added_watch_id) {
+		error("Failed to add watch for InterfacesAdded signal");
+		return 0;
+	}
+
+	interfaces_removed_watch_id = g_dbus_add_signal_watch(
+			btd_get_dbus_connection(), DBUS_BLUEZ_SERVICE,
+			"/", DBUS_OBJECT_MANAGER_INTERFACE, "InterfacesRemoved",
+			interfaces_removed, NULL, NULL);
+	if (!interfaces_removed_watch_id) {
+		error("Failed to add watch for InterfaceRemoved signal");
+		remove_dbus_watches();
+	}
+
+	return 0;
+}
+
+static void chromium_exit(void)
+{
+	DBG("");
+
+	mgmt_unref(mgmt_if);
+	mgmt_if = NULL;
+
+	remove_dbus_watches();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(chromium, VERSION, BLUETOOTH_PLUGIN_PRIORITY_HIGH,
+						chromium_init, chromium_exit)
diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
index ac53ba9..0a4e3d8 100644
--- a/plugins/sixaxis.c
+++ b/plugins/sixaxis.c
@@ -289,7 +289,7 @@
 
 	device = btd_adapter_get_device(adapter, &device_bdaddr, BDADDR_BREDR);
 
-	if (g_slist_find_custom(btd_device_get_uuids(device), HID_UUID,
+	if (g_slist_find_custom(btd_device_get_sdp_uuids(device), HID_UUID,
 						(GCompareFunc)strcasecmp)) {
 		DBG("device %s already known, skipping", device_addr);
 		return true;
diff --git a/src/adapter.c b/src/adapter.c
index 1839286..42077b7 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -170,6 +170,7 @@
 	char *short_name;		/* controller short name */
 	uint32_t supported_settings;	/* controller supported settings */
 	uint32_t current_settings;	/* current controller settings */
+	bool desired_powered;		/* powered status desired by clients */
 
 	char *path;			/* adapter object path */
 	uint8_t major_class;		/* configured major class */
@@ -422,6 +423,10 @@
 		g_key_file_set_string(key_file, "General", "Alias",
 							adapter->stored_alias);
 
+	/* Always store the powered status */
+	g_key_file_set_boolean(key_file, "General", "Powered",
+						adapter->desired_powered);
+
 	ba2str(&adapter->bdaddr, address);
 	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/settings", address);
 
@@ -455,6 +460,14 @@
 	        g_dbus_emit_property_changed(dbus_conn, adapter->path,
 					ADAPTER_INTERFACE, "Powered");
 
+		/*
+		 * Don't store the adapter information during daemon shutdown.
+		 * It will store the adapter as powered off as part of the
+		 * shutdown.
+		 */
+		if (!powering_down)
+			store_adapter_info(adapter);
+
 		if (adapter->current_settings & MGMT_SETTING_POWERED) {
 			adapter_start(adapter);
 		} else {
@@ -2056,6 +2069,7 @@
 				GDBusPendingPropertySet id, void *user_data)
 {
 	struct btd_adapter *adapter = user_data;
+	dbus_bool_t enabled;
 
 	if (powering_down) {
 		g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed",
@@ -2063,6 +2077,9 @@
 		return;
 	}
 
+	dbus_message_iter_get_basic(iter, &enabled);
+	adapter->desired_powered = enabled;
+
 	property_set_mode(adapter, MGMT_SETTING_POWERED, iter, id);
 }
 
@@ -2912,9 +2929,13 @@
 
 		/* TODO: register services from pre-loaded list of primaries */
 
-		list = btd_device_get_uuids(device);
+		list = btd_device_get_sdp_uuids(device);
 		if (list)
-			device_probe_profiles(device, list);
+			device_probe_profiles(device, list, false);
+
+		list = btd_device_get_gatt_uuids(device);
+		if (list)
+			device_probe_profiles(device, list, true);
 
 device_exist:
 		if (key_info) {
@@ -4343,6 +4364,8 @@
 		mode = get_mode(str);
 		g_key_file_set_boolean(key_file, "General", "Discoverable",
 					mode == MODE_DISCOVERABLE);
+		g_key_file_set_boolean(key_file, "General", "Powered",
+			mode == MODE_DISCOVERABLE || mode == MODE_CONNECTABLE);
 	}
 
 	if (read_local_name(&adapter->bdaddr, str) == 0)
@@ -4425,6 +4448,7 @@
 	char address[18];
 	struct stat st;
 	GError *gerr = NULL;
+	gboolean powered;
 
 	ba2str(&adapter->bdaddr, address);
 
@@ -4475,6 +4499,18 @@
 		gerr = NULL;
 	}
 
+	/* Get power status */
+	powered = g_key_file_get_boolean(key_file, "General", "Powered", &gerr);
+	if (gerr) {
+		powered = false;
+		g_error_free(gerr);
+		gerr = NULL;
+	}
+
+	/* Update the power status for this adapter */
+	adapter->desired_powered = powered;
+	set_mode(adapter, MGMT_OP_SET_POWERED, powered ? 0x01 : 0x00);
+
 	g_key_file_free(key_file);
 }
 
diff --git a/src/bluetooth.conf b/src/bluetooth.conf
index ad8891a..074bcd6 100644
--- a/src/bluetooth.conf
+++ b/src/bluetooth.conf
@@ -8,6 +8,10 @@
   <!-- ../system.conf have denied everything, so we just punch some holes -->
 
   <policy user="root">
+    <allow send_destination="org.bluez"/>
+  </policy>
+
+  <policy user="bluetooth">
     <allow own="org.bluez"/>
     <allow send_destination="org.bluez"/>
     <allow send_interface="org.bluez.Agent1"/>
@@ -21,13 +25,11 @@
     <allow send_interface="org.freedesktop.DBus.ObjectManager"/>
   </policy>
 
-  <policy at_console="true">
+  <policy user="chronos">
     <allow send_destination="org.bluez"/>
   </policy>
 
-  <!-- allow users of lp group (printing subsystem) to 
-       communicate with bluetoothd -->
-  <policy group="lp">
+  <policy user="cras">
     <allow send_destination="org.bluez"/>
   </policy>
 
diff --git a/src/device.c b/src/device.c
index a28d6fb..05a4bd9 100644
--- a/src/device.c
+++ b/src/device.c
@@ -169,6 +169,7 @@
 	bool bonded;
 	bool connected;
 	bool svc_resolved;
+	GSList *uuids;
 };
 
 struct btd_device {
@@ -193,7 +194,7 @@
 	uint16_t	appearance;
 	char		*modalias;
 	struct btd_adapter	*adapter;
-	GSList		*uuids;
+	GSList		*legacy_uuids;
 	GSList		*primaries;		/* List of primary services */
 	GSList		*services;		/* List of btd_service */
 	GSList		*pending;		/* Pending services */
@@ -335,6 +336,28 @@
 								list, len);
 }
 
+static char **store_service_uuids(GKeyFile *key_file, gchar *key, GSList *uuids)
+{
+	GSList *l;
+	char **array;
+	int i;
+
+	if (!uuids) {
+		g_key_file_remove_key(key_file, "General", key, NULL);
+		return NULL;
+	}
+
+	array = g_new0(char *, g_slist_length(uuids) + 1);
+
+	for (i = 0, l = uuids; l; l = g_slist_next(l), i++)
+		array[i] = l->data;
+
+	g_key_file_set_string_list(key_file, "General", key,
+						(const char **)array, i);
+
+	return array;
+}
+
 static gboolean store_device_info_cb(gpointer user_data)
 {
 	struct btd_device *device = user_data;
@@ -344,7 +367,7 @@
 	char device_addr[18];
 	char *str;
 	char class[9];
-	char **uuids = NULL;
+	char **sdp_uuids, **gatt_uuids;
 	gsize length = 0;
 
 	device->store_id = 0;
@@ -387,18 +410,17 @@
 	g_key_file_set_boolean(key_file, "General", "Blocked",
 							device->blocked);
 
-	if (device->uuids) {
-		GSList *l;
-		int i;
-
-		uuids = g_new0(char *, g_slist_length(device->uuids) + 1);
-		for (i = 0, l = device->uuids; l; l = g_slist_next(l), i++)
-			uuids[i] = l->data;
-		g_key_file_set_string_list(key_file, "General", "Services",
-						(const char **)uuids, i);
-	} else {
+	/*
+	 * Remove the legacy-format "Services" entry if we have new data to
+	 * populate for either GATT or SDP after a connection.
+	 */
+	if (device->bredr_state.uuids || device->le_state.uuids)
 		g_key_file_remove_key(key_file, "General", "Services", NULL);
-	}
+
+	sdp_uuids = store_service_uuids(key_file, "SDPServices",
+						device->bredr_state.uuids);
+	gatt_uuids = store_service_uuids(key_file, "GATTServices",
+						device->le_state.uuids);
 
 	if (device->vendor_src) {
 		g_key_file_set_integer(key_file, "DeviceID", "Source",
@@ -420,7 +442,8 @@
 	g_free(str);
 
 	g_key_file_free(key_file);
-	g_free(uuids);
+	g_free(sdp_uuids);
+	g_free(gatt_uuids);
 
 	return FALSE;
 }
@@ -585,7 +608,9 @@
 	btd_gatt_client_destroy(device->client_dbus);
 	device->client_dbus = NULL;
 
-	g_slist_free_full(device->uuids, g_free);
+	g_slist_free_full(device->bredr_state.uuids, g_free);
+	g_slist_free_full(device->le_state.uuids, g_free);
+	g_slist_free_full(device->legacy_uuids, g_free);
 	g_slist_free_full(device->primaries, g_free);
 	g_slist_free_full(device->attios, g_free);
 	g_slist_free_full(device->attios_offline, g_free);
@@ -994,26 +1019,48 @@
 	return TRUE;
 }
 
+static void insert_uuid(gpointer data, gpointer user_data)
+{
+	char *uuid = data;
+	GSList **uuids = user_data;
+
+	if (g_slist_find_custom(*uuids, uuid, bt_uuid_strcmp))
+		return;
+
+	*uuids = g_slist_insert_sorted(*uuids, uuid, bt_uuid_strcmp);
+}
+
 static gboolean dev_property_get_uuids(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *data)
 {
 	struct btd_device *dev = data;
 	DBusMessageIter entry;
-	GSList *l;
+	GSList *uuids, *l;
+	bool needs_free = false;
 
 	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
 				DBUS_TYPE_STRING_AS_STRING, &entry);
 
-	if (dev->bredr_state.svc_resolved || dev->le_state.svc_resolved)
-		l = dev->uuids;
-	else if (dev->eir_uuids)
-		l = dev->eir_uuids;
-	else
-		l = dev->uuids;
+	if (!dev->bredr_state.svc_resolved && !dev->le_state.svc_resolved
+							&& dev->eir_uuids)
+		uuids = dev->eir_uuids;
+	else if (!dev->bredr_state.uuids && !dev->le_state.uuids)
+		uuids = dev->legacy_uuids;
+	else {
+		uuids = NULL;
+		g_slist_foreach(dev->bredr_state.uuids, insert_uuid, &uuids);
+		g_slist_foreach(dev->le_state.uuids, insert_uuid, &uuids);
 
-	for (; l != NULL; l = l->next)
+		if (uuids)
+			needs_free = true;
+	}
+
+	for (l = uuids; l; l = g_slist_next(l))
 		dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
-							&l->data);
+								&l->data);
+
+	if (needs_free)
+		g_slist_free(l);
 
 	dbus_message_iter_close_container(iter, &entry);
 
@@ -1141,7 +1188,8 @@
 	if (!silent) {
 		g_dbus_emit_property_changed(dbus_conn, device->path,
 						DEVICE_INTERFACE, "Blocked");
-		device_probe_profiles(device, device->uuids);
+		device_probe_profiles(device, device->bredr_state.uuids, false);
+		device_probe_profiles(device, device->le_state.uuids, true);
 	}
 
 	return 0;
@@ -2276,6 +2324,66 @@
 	return str;
 }
 
+static bool device_add_uuid(GSList **uuids, const char *uuid)
+{
+	if (g_slist_find_custom(*uuids, uuid, bt_uuid_strcmp))
+		return false;
+
+	*uuids = g_slist_insert_sorted(*uuids, g_strdup(uuid), bt_uuid_strcmp);
+
+	return true;
+}
+
+static bool device_add_uuids(struct btd_device *device, GSList **uuids,
+							GSList *new_uuids)
+{
+	GSList *l;
+	bool changed = false;
+
+	for (l = new_uuids; l; l = g_slist_next(l)) {
+		if (device_add_uuid(uuids, l->data))
+			changed = true;
+	}
+
+	if (changed)
+		g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "UUIDs");
+
+	return changed;
+}
+
+static bool device_add_sdp_uuid(struct btd_device *device, const char *uuid)
+{
+	return device_add_uuid(&device->bredr_state.uuids, uuid);
+}
+
+static bool device_add_sdp_uuids(struct btd_device *device, GSList *uuids)
+{
+	return device_add_uuids(device, &device->bredr_state.uuids, uuids);
+}
+
+static bool device_add_gatt_uuid(struct btd_device *device, const char *uuid)
+{
+	return device_add_uuid(&device->le_state.uuids, uuid);
+}
+
+static bool device_add_gatt_uuids(struct btd_device *device, GSList *uuids)
+{
+	return device_add_uuids(device, &device->le_state.uuids, uuids);
+}
+
+static void device_remove_gatt_uuid(struct btd_device *device, const char *uuid)
+{
+	GSList *l;
+
+	l = g_slist_find_custom(device->le_state.uuids, uuid, bt_uuid_strcmp);
+	if (!l)
+		return;
+
+	g_free(l->data);
+	device->le_state.uuids = g_slist_delete_link(device->le_state.uuids, l);
+}
+
 static void load_info(struct btd_device *device, const char *local,
 			const char *peer, GKeyFile *key_file)
 {
@@ -2365,30 +2473,58 @@
 	if (blocked)
 		device_block(device, FALSE);
 
-	/* Load device profile list */
+	/*
+	 * Load legacy, mixed profile list, to display UUIDs before a
+	 * connection. We immediately load the values and then clear the stored
+	 * value.
+	 */
 	uuids = g_key_file_get_string_list(key_file, "General", "Services",
-						NULL, NULL);
+								NULL, NULL);
 	if (uuids) {
 		char **uuid;
 
-		for (uuid = uuids; *uuid; uuid++) {
-			GSList *match;
+		for (uuid = uuids; *uuid; uuid++)
+			device->legacy_uuids = g_slist_append(
+							device->legacy_uuids,
+							g_strdup(*uuid));
 
-			match = g_slist_find_custom(device->uuids, *uuid,
-							bt_uuid_strcmp);
-			if (match)
-				continue;
+		g_strfreev(uuids);
+	}
 
-			device->uuids = g_slist_insert_sorted(device->uuids,
-								g_strdup(*uuid),
-								bt_uuid_strcmp);
-		}
+	/* Load classic device profile list */
+	uuids = g_key_file_get_string_list(key_file, "General", "SDPServices",
+								NULL, NULL);
+	if (uuids) {
+		char **uuid;
+
+		for (uuid = uuids; *uuid; uuid++)
+			device_add_sdp_uuid(device, *uuid);
+
 		g_strfreev(uuids);
 
 		/* Discovered services restored from storage */
 		device->bredr_state.svc_resolved = true;
 	}
 
+	/* Load GATT-based device profile list */
+	uuids = g_key_file_get_string_list(key_file, "General", "GATTServices",
+								NULL, NULL);
+	if (uuids) {
+		char **uuid;
+
+		for (uuid = uuids; *uuid; uuid++)
+			device_add_gatt_uuid(device, *uuid);
+
+		g_strfreev(uuids);
+
+		/*
+		 * TODO: The GATT-based service UUIDs have been restored from
+		 * storage but we don't have a populated gatt-db yet. Here we
+		 * should mark le_state.svc_resolved as true, if we're bonded
+		 * and we have a populated gatt_db.
+		 */
+	}
+
 	/* Load device id */
 	source = g_key_file_get_integer(key_file, "DeviceID", "Source", NULL);
 	if (source) {
@@ -2522,32 +2658,11 @@
 	*new_services = g_slist_append(*new_services, prim);
 }
 
-static void device_add_uuids(struct btd_device *device, GSList *uuids)
-{
-	GSList *l;
-	bool changed = false;
-
-	for (l = uuids; l != NULL; l = g_slist_next(l)) {
-		GSList *match = g_slist_find_custom(device->uuids, l->data,
-							bt_uuid_strcmp);
-		if (match)
-			continue;
-
-		changed = true;
-		device->uuids = g_slist_insert_sorted(device->uuids,
-						g_strdup(l->data),
-						bt_uuid_strcmp);
-	}
-
-	if (changed)
-		g_dbus_emit_property_changed(dbus_conn, device->path,
-						DEVICE_INTERFACE, "UUIDs");
-}
-
 struct gatt_probe_data {
 	struct btd_device *dev;
 	bool all_services;
-	GSList *uuids;
+	GSList *new_uuids;
+	GSList *old_uuids;
 	struct gatt_db_attribute *cur_attr;
 	char cur_uuid[MAX_LEN_UUID_STR];
 };
@@ -2597,20 +2712,31 @@
 {
 	struct gatt_probe_data *data = user_data;
 	bt_uuid_t uuid;
-	GSList *l = NULL;
+	gpointer dup_uuid;
 
 	gatt_db_attribute_get_service_uuid(attr, &uuid);
 	bt_uuid_to_string(&uuid, data->cur_uuid, sizeof(data->cur_uuid));
 
 	data->cur_attr = attr;
 
-	/*
-	 * If we're probing for all services, store the UUID since device->uuids
-	 * was cleared.
-	 */
-	if (data->all_services)
-		data->uuids = g_slist_append(data->uuids,
+	if (data->all_services) {
+		GSList *l;
+
+		/*
+		 * Check if the service is already in the old UUIDs list. If so,
+		 * remove it.
+		 */
+		l = g_slist_find_custom(data->old_uuids, data->cur_uuid,
+								bt_uuid_strcmp);
+		if (l) {
+			g_free(l->data);
+			data->old_uuids = g_slist_delete_link(data->old_uuids,
+									l);
+		} else {
+			data->new_uuids = g_slist_append(data->new_uuids,
 						g_strdup(data->cur_uuid));
+		}
+	}
 
 	/* Don't probe the profiles if a matching service already exists. */
 	if (find_service_with_uuid(data->dev->services, data->cur_uuid)) {
@@ -2624,8 +2750,14 @@
 	if (data->all_services)
 		return;
 
-	l = g_slist_append(l, g_strdup(data->cur_uuid));
-	device_add_uuids(data->dev, l);
+	dup_uuid = g_strdup(data->cur_uuid);
+	if (!device_add_gatt_uuid(data->dev, dup_uuid)) {
+		g_free(dup_uuid);
+		return;
+	}
+
+	g_dbus_emit_property_changed(dbus_conn, data->dev->path,
+						DEVICE_INTERFACE, "UUIDs");
 }
 
 static void device_probe_gatt_profile(struct btd_device *device,
@@ -2638,13 +2770,33 @@
 	data.dev = device;
 
 	dev_probe_gatt_profile(attr, &data);
-	g_slist_free_full(data.uuids, g_free);
+}
+
+static void remove_invalid_services(struct gatt_probe_data *data)
+{
+	struct btd_device *dev = data->dev;
+	struct btd_service *service;
+	GSList *l, *svc;
+
+	for (l = data->old_uuids; l; l = g_slist_next(l)) {
+		device_remove_gatt_uuid(dev, l->data);
+
+		svc = find_service_with_uuid(dev->services, l->data);
+		if (!svc)
+			continue;
+
+		service = svc->data;
+		dev->services = g_slist_delete_link(dev->services, svc);
+		dev->pending = g_slist_remove(dev->pending, service);
+		service_remove(service);
+	}
 }
 
 static void device_probe_gatt_profiles(struct btd_device *device)
 {
 	struct gatt_probe_data data;
 	char addr[18];
+	GSList *l;
 
 	ba2str(&device->bdaddr, addr);
 
@@ -2658,11 +2810,24 @@
 	data.dev = device;
 	data.all_services = true;
 
+	/* Copy current list of GATT UUIDs */
+	for (l = device->le_state.uuids; l; l = g_slist_next(l))
+		data.old_uuids = g_slist_append(data.old_uuids,
+							g_strdup(l->data));
+
 	gatt_db_foreach_service(device->db, NULL, dev_probe_gatt_profile,
 									&data);
 
-	device_add_uuids(device, data.uuids);
-	g_slist_free_full(data.uuids, g_free);
+	/* Whatever remains in data.old_uuids is stale and should be removed */
+	remove_invalid_services(&data);
+
+	/* Update the list with new UUIDs */
+	if (!device_add_gatt_uuids(device, data.new_uuids) && data.old_uuids)
+		g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "UUIDs");
+
+	g_slist_free_full(data.new_uuids, g_free);
+	g_slist_free_full(data.old_uuids, g_free);
 }
 
 static void device_accept_gatt_profiles(struct btd_device *device)
@@ -2751,21 +2916,14 @@
 	return !(prim->range.start == start && prim->range.end == end);
 }
 
-static gint prim_uuid_cmp(gconstpointer a, gconstpointer b)
-{
-	const struct gatt_primary *prim = a;
-	const char *uuid = b;
-
-	return bt_uuid_strcmp(prim->uuid, uuid);
-}
-
 static void gatt_service_removed(struct gatt_db_attribute *attr,
 								void *user_data)
 {
 	struct btd_device *device = user_data;
 	GSList *l;
-	struct gatt_primary *prim;
 	uint16_t start, end;
+	bt_uuid_t uuid;
+	char uuid_str[MAX_LEN_UUID_STR];
 
 	/*
 	 * NOTE: shared/gatt-client clears the database in case of failure. This
@@ -2783,26 +2941,26 @@
 	 *       In this case the gatt-client will not be ready.
 	 */
 
-	gatt_db_attribute_get_service_handles(attr, &start, &end);
+	gatt_db_attribute_get_service_data(attr, &start, &end, NULL, &uuid);
 
 	DBG("start: 0x%04x, end: 0x%04x", start, end);
 
 	/* Remove the corresponding gatt_primary */
 	l = g_slist_find_custom(device->primaries, attr, prim_attr_cmp);
-	if (!l)
-		return;
+	if (l) {
+		g_free(l->data);
+		device->primaries = g_slist_delete_link(device->primaries, l);
+	}
 
-	prim = l->data;
-	device->primaries = g_slist_delete_link(device->primaries, l);
+	bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
 
 	/*
 	 * Remove the corresponding UUIDs entry and profile, only if this is
 	 * the last service with this UUID.
 	 */
-	l = g_slist_find_custom(device->uuids, prim->uuid, bt_uuid_strcmp);
-
-	if (l && !g_slist_find_custom(device->primaries, prim->uuid,
-							prim_uuid_cmp)) {
+	l = g_slist_find_custom(device->le_state.uuids, uuid_str,
+								bt_uuid_strcmp);
+	if (l && !gatt_db_get_service_with_uuid(device->db, &uuid)) {
 		/*
 		 * If this happend since the db was cleared for a non-bonded
 		 * device, then don't remove the btd_service just yet. We do
@@ -2815,13 +2973,12 @@
 			device_remove_gatt_profile(device, attr);
 
 		g_free(l->data);
-		device->uuids = g_slist_delete_link(device->uuids, l);
+		device->le_state.uuids = g_slist_delete_link(
+						device->le_state.uuids, l);
 		g_dbus_emit_property_changed(dbus_conn, device->path,
 						DEVICE_INTERFACE, "UUIDs");
 	}
 
-	g_free(prim);
-
 	store_device_info(device);
 
 	btd_gatt_client_service_removed(device->client_dbus, attr);
@@ -3076,8 +3233,13 @@
 	dev->trusted = dup->trusted;
 	dev->blocked = dup->blocked;
 
-	for (l = dup->uuids; l; l = g_slist_next(l))
-		dev->uuids = g_slist_append(dev->uuids, g_strdup(l->data));
+	for (l = dup->bredr_state.uuids; l; l = g_slist_next(l))
+		dev->bredr_state.uuids = g_slist_append(dev->bredr_state.uuids,
+							g_strdup(l->data));
+
+	for (l = dup->le_state.uuids; l; l = g_slist_next(l))
+		dev->le_state.uuids = g_slist_append(dev->le_state.uuids,
+							g_strdup(l->data));
 
 	if (dev->name[0] == '\0')
 		strcpy(dev->name, dup->name);
@@ -3328,9 +3490,14 @@
 	return FALSE;
 }
 
-GSList *btd_device_get_uuids(struct btd_device *device)
+GSList *btd_device_get_sdp_uuids(struct btd_device *device)
 {
-	return device->uuids;
+	return device->bredr_state.uuids;
+}
+
+GSList *btd_device_get_gatt_uuids(struct btd_device *device)
+{
+	return device->le_state.uuids;
 }
 
 struct probe_data {
@@ -3368,7 +3535,8 @@
 	if (profile->device_probe == NULL)
 		return;
 
-	if (!device_match_profile(device, profile, device->uuids))
+	if (!device_match_profile(device, profile, device->bredr_state.uuids) &&
+		!device_match_profile(device, profile, device->le_state.uuids))
 		return;
 
 	service = service_create(device, profile);
@@ -3406,7 +3574,8 @@
 	service_remove(service);
 }
 
-void device_probe_profiles(struct btd_device *device, GSList *uuids)
+void device_probe_profiles(struct btd_device *device, GSList *uuids,
+								bool gatt)
 {
 	struct probe_data d = { device, uuids };
 	char addr[18];
@@ -3423,7 +3592,10 @@
 	btd_profile_foreach(dev_probe, &d);
 
 add_uuids:
-	device_add_uuids(device, uuids);
+	if (gatt)
+		device_add_gatt_uuids(device, uuids);
+	else
+		device_add_sdp_uuids(device, uuids);
 }
 
 static void store_sdp_record(GKeyFile *key_file, sdp_record_t *rec)
@@ -3519,7 +3691,8 @@
 	req->records = sdp_list_append(req->records, sdp_copy_record(rec));
 
 	/* Check if UUID is duplicated */
-	l = g_slist_find_custom(req->device->uuids, uuid, bt_uuid_strcmp);
+	l = g_slist_find_custom(req->device->bredr_state.uuids, uuid,
+								bt_uuid_strcmp);
 	if (l == NULL) {
 		l = g_slist_find_custom(req->profiles_added, uuid,
 							bt_uuid_strcmp);
@@ -3750,7 +3923,7 @@
 	 * the full list of services and populate a client-role gatt_db over
 	 * BR/EDR.
 	 */
-	device_probe_profiles(device, req->profiles_added);
+	device_probe_profiles(device, req->profiles_added, false);
 
 	/* Propagate services changes */
 	g_dbus_emit_property_changed(dbus_conn, req->device->path,
@@ -4437,8 +4610,8 @@
 		else
 			delta = rssi - device->rssi;
 
-		/* only report changes of 8 dBm or more */
-		if (delta < 8)
+		/* only report changes of 2 dBm or more */
+		if (delta < 2)
 			return;
 
 		DBG("rssi %d delta %d", rssi, delta);
@@ -5094,13 +5267,14 @@
 	GSList *uuid_list;
 	char *new_uuid;
 
-	if (g_slist_find_custom(device->uuids, uuid, bt_uuid_strcmp))
+	if (g_slist_find_custom(device->bredr_state.uuids, uuid,
+								bt_uuid_strcmp))
 		return;
 
 	new_uuid = g_strdup(uuid);
 	uuid_list = g_slist_append(NULL, new_uuid);
 
-	device_probe_profiles(device, uuid_list);
+	device_probe_profiles(device, uuid_list, false);
 
 	g_free(new_uuid);
 	g_slist_free(uuid_list);
diff --git a/src/device.h b/src/device.h
index a7fefee..f250bfa 100644
--- a/src/device.h
+++ b/src/device.h
@@ -60,8 +60,10 @@
 };
 
 int device_addr_type_cmp(gconstpointer a, gconstpointer b);
-GSList *btd_device_get_uuids(struct btd_device *device);
-void device_probe_profiles(struct btd_device *device, GSList *profiles);
+GSList *btd_device_get_sdp_uuids(struct btd_device *device);
+GSList *btd_device_get_gatt_uuids(struct btd_device *device);
+void device_probe_profiles(struct btd_device *device, GSList *profiles,
+								bool gatt);
 const sdp_record_t *btd_device_get_record(struct btd_device *device,
 						const char *uuid);
 struct gatt_primary *btd_device_get_primary(struct btd_device *device,
diff --git a/src/gatt-client.c b/src/gatt-client.c
index 9811bd8..373f27a 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -564,21 +564,16 @@
 }
 
 static const GDBusPropertyTable descriptor_properties[] = {
-	{ "UUID", "s", descriptor_get_uuid, NULL, NULL,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "Characteristic", "o", descriptor_get_characteristic, NULL, NULL,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "Value", "ay", descriptor_get_value, NULL, descriptor_value_exists,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "UUID", "s", descriptor_get_uuid, NULL, NULL },
+	{ "Characteristic", "o", descriptor_get_characteristic, NULL, NULL },
+	{ "Value", "ay", descriptor_get_value, NULL, descriptor_value_exists },
 	{ }
 };
 
 static const GDBusMethodTable descriptor_methods[] = {
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", NULL,
-						GDBUS_ARGS({ "value", "ay" }),
+	{ GDBUS_ASYNC_METHOD("ReadValue", NULL, GDBUS_ARGS({ "value", "ay" }),
 						descriptor_read_value) },
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue",
-						GDBUS_ARGS({ "value", "ay" }),
+	{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }),
 						NULL,
 						descriptor_write_value) },
 	{ }
@@ -1246,33 +1241,25 @@
 }
 
 static const GDBusPropertyTable characteristic_properties[] = {
-	{ "UUID", "s", characteristic_get_uuid, NULL, NULL,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "Service", "o", characteristic_get_service, NULL, NULL,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "UUID", "s", characteristic_get_uuid, NULL, NULL },
+	{ "Service", "o", characteristic_get_service, NULL, NULL },
 	{ "Value", "ay", characteristic_get_value, NULL,
-					characteristic_value_exists,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "Notifying", "b", characteristic_get_notifying, NULL, NULL,
-					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 },
+					characteristic_value_exists },
+	{ "Notifying", "b", characteristic_get_notifying, NULL, NULL },
+	{ "Flags", "as", characteristic_get_flags, NULL, NULL },
+	{ "Descriptors", "ao", characteristic_get_descriptors, NULL, NULL },
 	{ }
 };
 
 static const GDBusMethodTable characteristic_methods[] = {
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", NULL,
-						GDBUS_ARGS({ "value", "ay" }),
+	{ GDBUS_ASYNC_METHOD("ReadValue", NULL, GDBUS_ARGS({ "value", "ay" }),
 						characteristic_read_value) },
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue",
-						GDBUS_ARGS({ "value", "ay" }),
+	{ GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }),
 						NULL,
 						characteristic_write_value) },
-	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("StartNotify", NULL, NULL,
+	{ GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL,
 						characteristic_start_notify) },
-	{ GDBUS_EXPERIMENTAL_METHOD("StopNotify", NULL, NULL,
+	{ GDBUS_METHOD("StopNotify", NULL, NULL,
 						characteristic_stop_notify) },
 	{ }
 };
@@ -1425,14 +1412,10 @@
 }
 
 static const GDBusPropertyTable service_properties[] = {
-	{ "UUID", "s", service_get_uuid, NULL, NULL,
-					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "Device", "o", service_get_device, NULL, NULL,
-					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 },
+	{ "UUID", "s", service_get_uuid, NULL, NULL },
+	{ "Device", "o", service_get_device, NULL, NULL },
+	{ "Primary", "b", service_get_primary, NULL, NULL },
+	{ "Characteristics", "ao", service_get_characteristics, NULL, NULL },
 	{ }
 };