diff options
author | Luiz Augusto von Dentz <luiz.von.dentz@intel.com> | 2019-02-11 18:36:27 +0200 |
---|---|---|
committer | Luiz Augusto von Dentz <luiz.von.dentz@intel.com> | 2019-02-13 17:09:13 +0200 |
commit | 183d47c1760107fbe3958c3f7d63cc17cef745ae (patch) | |
tree | feaf5bbc401f54378e632cfef81d8835d0509e8d /client/gatt.c | |
parent | 6d410a0e2e2e801e8555a42f799ede5787565bf7 (diff) | |
download | bluez-183d47c1760107fbe3958c3f7d63cc17cef745ae.tar.gz |
client: Add gatt.clone command
This adds clone command to gatt submenu which can be use to clone
services as follow:
Clone all services from the connected device:
> gatt.clone
Clone a given service
> gatt.select-attribute <attribute/uuid>
> gatt.clone
In either case there is a prompt to confirm since this may add a lot of
service the user must confim before proceding.
Then finally:
> gatt.register-application
Diffstat (limited to 'client/gatt.c')
-rw-r--r-- | client/gatt.c | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/client/gatt.c b/client/gatt.c index cbcff30da..6728b1c33 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); +} |