Diff between 6d410a0e2e2e801e8555a42f799ede5787565bf7 and 183d47c1760107fbe3958c3f7d63cc17cef745ae

Changed Files

File Additions Deletions Status
client/gatt.c +235 -0 modified
client/gatt.h +1 -0 modified
client/main.c +17 -0 modified

Full Patch

diff --git a/client/gatt.c b/client/gatt.c
index cbcff30..6728b1c 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -47,6 +47,7 @@
 #include "gatt.h"
 
 #define APP_PATH "/org/bluez/app"
+#define DEVICE_INTERFACE "org.bluez.Device1"
 #define PROFILE_INTERFACE "org.bluez.GattProfile1"
 #define SERVICE_INTERFACE "org.bluez.GattService1"
 #define CHRC_INTERFACE "org.bluez.GattCharacteristic1"
@@ -72,6 +73,7 @@ struct desc {
 
 struct chrc {
 	struct service *service;
+	GDBusProxy *proxy;
 	char *path;
 	uint16_t handle;
 	char *uuid;
@@ -89,6 +91,7 @@ struct chrc {
 
 struct service {
 	DBusConnection *conn;
+	GDBusProxy *proxy;
 	char *path;
 	uint16_t handle;
 	char *uuid;
@@ -2653,3 +2656,235 @@ void gatt_unregister_desc(DBusConnection *conn, GDBusProxy *proxy,
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
+
+static GDBusProxy *select_service(GDBusProxy *proxy)
+{
+	GList *l;
+
+	for (l = services; l; l = g_list_next(l)) {
+		GDBusProxy *p = l->data;
+
+		if (proxy == p || g_str_has_prefix(g_dbus_proxy_get_path(proxy),
+						g_dbus_proxy_get_path(p)))
+			return p;
+	}
+
+	return NULL;
+}
+
+static void clone_chrc(struct GDBusProxy *proxy)
+{
+	struct service *service;
+	struct chrc *chrc;
+	DBusMessageIter iter;
+	DBusMessageIter array;
+	const char *uuid;
+	char **flags;
+	int i;
+
+	if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+		return;
+
+	dbus_message_iter_get_basic(&iter, &uuid);
+
+	if (g_dbus_proxy_get_property(proxy, "Flags", &iter) == FALSE)
+		return;
+
+	flags = g_new0(char *, dbus_message_iter_get_element_count(&iter) + 1);
+
+	dbus_message_iter_recurse(&iter, &array);
+
+	for (i = 0; dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING;
+									i++) {
+		const char *flag;
+
+		dbus_message_iter_get_basic(&array, &flag);
+
+		flags[i] = g_strdup(flag);
+
+		dbus_message_iter_next(&array);
+	}
+
+	service = g_list_last(local_services)->data;
+
+	chrc = g_new0(struct chrc, 1);
+	chrc->service = service;
+	chrc->proxy = proxy;
+	chrc->uuid = g_strdup(uuid);
+	chrc->path = g_strdup_printf("%s/chrc%u", service->path,
+					g_list_length(service->chrcs));
+	chrc->flags = flags;
+
+	if (g_dbus_register_interface(service->conn, chrc->path, CHRC_INTERFACE,
+					chrc_methods, NULL, chrc_properties,
+					chrc, chrc_free) == FALSE) {
+		bt_shell_printf("Failed to register characteristic object\n");
+		chrc_free(chrc);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	service->chrcs = g_list_append(service->chrcs, chrc);
+
+	print_chrc(chrc, COLORED_NEW);
+}
+
+static void clone_chrcs(struct GDBusProxy *proxy)
+{
+	GList *l;
+
+	for (l = characteristics; l; l = g_list_next(l)) {
+		GDBusProxy *p = l->data;
+
+		if (g_str_has_prefix(g_dbus_proxy_get_path(p),
+						g_dbus_proxy_get_path(proxy)))
+			clone_chrc(p);
+	}
+}
+
+static void clone_service(struct GDBusProxy *proxy)
+{
+	struct service *service;
+	DBusMessageIter iter;
+	const char *uuid;
+	dbus_bool_t primary;
+
+	if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+		return;
+
+	dbus_message_iter_get_basic(&iter, &uuid);
+
+	if (g_dbus_proxy_get_property(proxy, "Primary", &iter) == FALSE)
+		return;
+
+	dbus_message_iter_get_basic(&iter, &primary);
+
+	if (!strcmp(uuid, "00001800-0000-1000-8000-00805f9b34fb") ||
+			!strcmp(uuid, "00001801-0000-1000-8000-00805f9b34fb"))
+		return;
+
+	service = g_new0(struct service, 1);
+	service->conn = bt_shell_get_env("DBUS_CONNECTION");
+	service->proxy = proxy;
+	service->path = g_strdup_printf("%s/service%u", APP_PATH,
+					g_list_length(local_services));
+	service->uuid = g_strdup(uuid);
+	service->primary = primary;
+
+	if (g_dbus_register_interface(service->conn, service->path,
+					SERVICE_INTERFACE, NULL, NULL,
+					service_properties, service,
+					service_free) == FALSE) {
+		bt_shell_printf("Failed to register service object\n");
+		service_free(service);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print_service(service, COLORED_NEW);
+
+	local_services = g_list_append(local_services, service);
+
+	clone_chrcs(proxy);
+}
+
+static void clone_device(struct GDBusProxy *proxy)
+{
+	GList *l;
+
+	for (l = services; l; l = g_list_next(l)) {
+		struct GDBusProxy *p = l->data;
+
+		if (g_str_has_prefix(g_dbus_proxy_get_path(p),
+						g_dbus_proxy_get_path(proxy)))
+			clone_service(p);
+	}
+}
+
+static void service_clone(const char *input, void *user_data)
+{
+	struct GDBusProxy *proxy = user_data;
+
+	if (!strcmp(input, "yes"))
+		return clone_service(proxy);
+	else if (!strcmp(input, "no"))
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	else if (!strcmp(input, "all"))
+		return clone_device(proxy);
+
+	bt_shell_printf("Invalid option: %s\n", input);
+
+	return bt_shell_noninteractive_quit(EXIT_FAILURE);
+}
+
+static void device_clone(const char *input, void *user_data)
+{
+	struct GDBusProxy *proxy = user_data;
+
+	if (!strcmp(input, "yes"))
+		return clone_device(proxy);
+	else if (!strcmp(input, "no"))
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+
+	bt_shell_printf("Invalid option: %s\n", input);
+
+	return bt_shell_noninteractive_quit(EXIT_FAILURE);
+}
+
+static const char *proxy_get_name(struct GDBusProxy *proxy)
+{
+	DBusMessageIter iter;
+	const char *uuid;
+	const char *str;
+
+	if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+		return NULL;
+
+	dbus_message_iter_get_basic(&iter, &uuid);
+
+	str = bt_uuidstr_to_str(uuid);
+
+	return str ? str : uuid;
+}
+
+static const char *proxy_get_alias(struct GDBusProxy *proxy)
+{
+	DBusMessageIter iter;
+	const char *alias;
+
+	if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == FALSE)
+		return NULL;
+
+	dbus_message_iter_get_basic(&iter, &alias);
+
+	return alias;
+}
+
+void gatt_clone_attribute(GDBusProxy *proxy, int argc, char *argv[])
+{
+	GDBusProxy *service = NULL;
+
+	if (argc > 1) {
+		proxy = gatt_select_attribute(proxy, argv[1]);
+		if (!proxy) {
+			bt_shell_printf("Unable to find attribute %s\n",
+								argv[1]);
+			return bt_shell_noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+
+	if (!strcmp(g_dbus_proxy_get_interface(proxy), DEVICE_INTERFACE)) {
+		bt_shell_prompt_input(proxy_get_alias(proxy),
+					"Clone (yes/no):",
+					device_clone, proxy);
+	}
+
+	/* Only clone services */
+	service = select_service(proxy);
+	if (service) {
+		bt_shell_prompt_input(proxy_get_name(proxy),
+					"Clone (yes/no/all):",
+					service_clone, service);
+		return;
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_FAILURE);
+}
diff --git a/client/gatt.h b/client/gatt.h
index d4d7bed..09ca618 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -37,6 +37,7 @@ char *gatt_attribute_generator(const char *text, int state);
 void gatt_read_attribute(GDBusProxy *proxy, int argc, char *argv[]);
 void gatt_write_attribute(GDBusProxy *proxy, int argc, char *argv[]);
 void gatt_notify_attribute(GDBusProxy *proxy, bool enable);
+void gatt_clone_attribute(GDBusProxy *proxy, int argc, char *argv[]);
 
 void gatt_acquire_write(GDBusProxy *proxy, const char *arg);
 void gatt_release_write(GDBusProxy *proxy, const char *arg);
diff --git a/client/main.c b/client/main.c
index e917945..1f6e045 100644
--- a/client/main.c
+++ b/client/main.c
@@ -2105,6 +2105,19 @@ static void cmd_notify(int argc, char *argv[])
 	gatt_notify_attribute(default_attr, enable ? true : false);
 }
 
+static void cmd_clone(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+
+	proxy = default_attr ? default_attr : default_dev;
+	if (!proxy) {
+		bt_shell_printf("Not connected\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	gatt_clone_attribute(proxy, argc, argv);
+}
+
 static void cmd_register_app(int argc, char *argv[])
 {
 	if (check_default_ctrl() == FALSE)
@@ -2624,6 +2637,8 @@ static const struct bt_shell_menu gatt_menu = {
 					"Release Notify file descriptor" },
 	{ "notify",       "<on/off>", cmd_notify, "Notify attribute value",
 							NULL },
+	{ "clone",	  "[dev/attribute/UUID]", cmd_clone,
+						"Clone a device or attribute" },
 	{ "register-application", "[UUID ...]", cmd_register_app,
 						"Register profile to connect" },
 	{ "unregister-application", NULL, cmd_unregister_app,
@@ -2757,6 +2772,8 @@ int main(int argc, char *argv[])
 	dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
 	g_dbus_attach_object_manager(dbus_conn);
 
+	bt_shell_set_env("DBUS_CONNECTION", dbus_conn);
+
 	client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
 
 	g_dbus_client_set_connect_watch(client, connect_handler, NULL);