From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> When acting as server it is useful to select where to allocate the handle for an attribute so it can be restored in the same position when restarting the daemon or rebooting the system. In order to do that the application also needs to know in which handle the attribute is allocated the very first time it is registered, this also allows for a better integration with PTS and tools like auto-pts which needs to know the handles where the attributes have been allocated. --- doc/gatt-api.txt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt index 8dda60b8a..491eb026a 100644 --- a/doc/gatt-api.txt +++ b/doc/gatt-api.txt @@ -50,6 +50,14 @@ Properties string UUID [read-only] Array of object paths representing the included services of this service. + uint16 Handle [read-write, optional] (Server Only) + + Service handle. When available in the server it + would attempt to use to allocate into the database + which may fail, to auto allocate the value 0x0000 + shall be used which will cause the allocated handle to + be set once registered. + Characteristic hierarchy ======================== @@ -257,6 +265,14 @@ Properties string UUID [read-only] "secure-write" (Server only) "authorize" + uint16 Handle [read-write, optional] (Server Only) + + Characteristic handle. When available in the server it + would attempt to use to allocate into the database + which may fail, to auto allocate the value 0x0000 + shall be used which will cause the allocated handle to + be set once registered. + Characteristic Descriptors hierarchy ==================================== @@ -332,6 +348,14 @@ Properties string UUID [read-only] "secure-write" (Server Only) "authorize" + uint16 Handle [read-write, optional] (Server Only) + + Characteristic handle. When available in the server it + would attempt to use to allocate into the database + which may fail, to auto allocate the value 0x0000 + shall be used which will cause the allocated handle to + be set once registered. + GATT Profile hierarchy ===================== -- 2.17.2
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> Handle 0 should be allocate on the next handle just as gatt_db_add_service. --- src/shared/gatt-db.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c index d28301ac4..b0ab2994e 100644 --- a/src/shared/gatt-db.c +++ b/src/shared/gatt-db.c @@ -525,9 +525,12 @@ struct gatt_db_attribute *gatt_db_insert_service(struct gatt_db *db, after = NULL; - if (!db || handle < 1) + if (!db) return NULL; + if (!handle) + handle = db->next_handle; + if (num_handles < 1 || (handle + num_handles - 1) > UINT16_MAX) return NULL; @@ -585,8 +588,7 @@ struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db, bool primary, uint16_t num_handles) { - return gatt_db_insert_service(db, db->next_handle, uuid, primary, - num_handles); + return gatt_db_insert_service(db, 0, uuid, primary, num_handles); } unsigned int gatt_db_register(struct gatt_db *db, -- 2.17.2
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> This parses the Handle property and if it exists attempt to insert attributes in their locations. --- src/gatt-database.c | 47 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/src/gatt-database.c b/src/gatt-database.c index 2c2f035b1..025f777eb 100644 --- a/src/gatt-database.c +++ b/src/gatt-database.c @@ -1839,6 +1839,25 @@ static bool parse_primary(GDBusProxy *proxy, bool *primary) return true; } +static bool parse_handle(GDBusProxy *proxy, uint16_t *handle) +{ + DBusMessageIter iter; + + /* Handle property is optional */ + if (!g_dbus_proxy_get_property(proxy, "Handle", &iter)) { + *handle = 0; + return true; + } + + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT16) + return false; + + dbus_message_iter_get_basic(&iter, &handle); + + return true; +} + static uint8_t dbus_error_to_att_ecode(const char *error_name) { @@ -2601,14 +2620,21 @@ fail: static bool database_add_desc(struct external_service *service, struct external_desc *desc) { + uint16_t handle; bt_uuid_t uuid; + if (!parse_handle(desc->proxy, &handle)) { + error("Failed to read \"Handle\" property of descriptor"); + return false; + } + if (!parse_uuid(desc->proxy, &uuid)) { error("Failed to read \"UUID\" property of descriptor"); return false; } - desc->attrib = gatt_db_service_add_descriptor(service->attrib, &uuid, + desc->attrib = gatt_db_service_insert_descriptor(service->attrib, + handle, &uuid, desc->perm, desc_read_cb, desc_write_cb, desc); @@ -2750,9 +2776,15 @@ static void database_add_includes(struct external_service *service) static bool database_add_chrc(struct external_service *service, struct external_chrc *chrc) { + uint16_t handle; bt_uuid_t uuid; const struct queue_entry *entry; + if (!parse_handle(chrc->proxy, &handle)) { + error("Failed to read \"Handle\" property of characteristic"); + return false; + } + if (!parse_uuid(chrc->proxy, &uuid)) { error("Failed to read \"UUID\" property of characteristic"); return false; @@ -2763,8 +2795,8 @@ static bool database_add_chrc(struct external_service *service, return false; } - chrc->attrib = gatt_db_service_add_characteristic(service->attrib, - &uuid, chrc->perm, + chrc->attrib = gatt_db_service_insert_characteristic(service->attrib, + handle, &uuid, chrc->perm, chrc->props, chrc_read_cb, chrc_write_cb, chrc); if (!chrc->attrib) { @@ -2807,6 +2839,7 @@ static bool database_add_service(struct external_service *service) { bt_uuid_t uuid; bool primary; + uint16_t handle; const struct queue_entry *entry; if (!parse_uuid(service->proxy, &uuid)) { @@ -2824,7 +2857,13 @@ static bool database_add_service(struct external_service *service) return false; } - service->attrib = gatt_db_add_service(service->app->database->db, &uuid, + if (!parse_handle(service->proxy, &handle)) { + error("Failed to read \"Handle\" property of service"); + return false; + } + + service->attrib = gatt_db_insert_service(service->app->database->db, + handle, &uuid, primary, service->attr_cnt); if (!service->attrib) return false; -- 2.17.2
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> If the application has set an attribute with Handle set to 0 inform the allocated handle by writting the Handle property. --- src/gatt-database.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/gatt-database.c b/src/gatt-database.c index 025f777eb..b159786ea 100644 --- a/src/gatt-database.c +++ b/src/gatt-database.c @@ -2617,6 +2617,18 @@ fail: gatt_db_attribute_write_result(attrib, id, BT_ATT_ERROR_UNLIKELY); } +static void write_handle(struct GDBusProxy *proxy, uint16_t handle) +{ + DBusMessageIter iter; + + /* Check if the attribute has the Handle property */ + if (!g_dbus_proxy_get_property(proxy, "Handle", &iter)) + return; + + g_dbus_proxy_set_property_basic(proxy, "Handle", DBUS_TYPE_UINT16, + &handle, NULL, NULL, NULL); +} + static bool database_add_desc(struct external_service *service, struct external_desc *desc) { @@ -2645,6 +2657,11 @@ static bool database_add_desc(struct external_service *service, desc->handled = true; + if (!handle) { + handle = gatt_db_attribute_get_handle(desc->attrib); + write_handle(desc->proxy, handle); + } + return true; } @@ -2810,6 +2827,11 @@ static bool database_add_chrc(struct external_service *service, if (!database_add_cep(service, chrc)) return false; + if (!handle) { + handle = gatt_db_attribute_get_handle(chrc->attrib); + write_handle(chrc->proxy, handle); + } + /* Handle the descriptors that belong to this characteristic. */ for (entry = queue_get_entries(service->descs); entry; entry = entry->next) { @@ -2868,6 +2890,11 @@ static bool database_add_service(struct external_service *service) if (!service->attrib) return false; + if (!handle) { + handle = gatt_db_attribute_get_handle(service->attrib); + write_handle(service->proxy, handle); + } + database_add_includes(service); entry = queue_get_entries(service->chrcs); -- 2.17.2
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> This enable bluetoothd to write back the actual value of attribute handles. --- client/gatt.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/client/gatt.c b/client/gatt.c index b6b517bdf..0137aa9e5 100644 --- a/client/gatt.c +++ b/client/gatt.c @@ -62,6 +62,7 @@ struct desc { struct chrc *chrc; char *path; + uint16_t handle; char *uuid; char **flags; int value_len; @@ -72,6 +73,7 @@ struct desc { struct chrc { struct service *service; char *path; + uint16_t handle; char *uuid; char **flags; bool notifying; @@ -88,6 +90,7 @@ struct chrc { struct service { DBusConnection *conn; char *path; + uint16_t handle; char *uuid; bool primary; GList *chrcs; @@ -1259,6 +1262,34 @@ static void service_free(void *data) g_free(service); } +static gboolean service_get_handle(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct service *service = data; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, + &service->handle); + + return TRUE; +} + +static void service_set_handle(const GDBusPropertyTable *property, + DBusMessageIter *value, GDBusPendingPropertySet id, + void *data) +{ + struct service *service = data; + + if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16) { + g_dbus_pending_property_error(id, "org.bluez.InvalidArguments", + "Invalid arguments in method call"); + return; + } + + dbus_message_iter_get_basic(value, &service->handle); + + g_dbus_pending_property_success(id); +} + static gboolean service_get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { @@ -1326,6 +1357,7 @@ static gboolean service_exist_includes(const GDBusPropertyTable *property, static const GDBusPropertyTable service_properties[] = { + { "Handle", "q", service_get_handle, service_set_handle }, { "UUID", "s", service_get_uuid }, { "Primary", "b", service_get_primary }, { "Includes", "ao", service_get_includes, @@ -1493,6 +1525,33 @@ void gatt_unregister_include(DBusConnection *conn, GDBusProxy *proxy, return bt_shell_noninteractive_quit(EXIT_SUCCESS); } +static gboolean chrc_get_handle(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct chrc *chrc = data; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &chrc->handle); + + return TRUE; +} + +static void chrc_set_handle(const GDBusPropertyTable *property, + DBusMessageIter *value, GDBusPendingPropertySet id, + void *data) +{ + struct chrc *chrc = data; + + if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16) { + g_dbus_pending_property_error(id, "org.bluez.InvalidArguments", + "Invalid arguments in method call"); + return; + } + + dbus_message_iter_get_basic(value, &chrc->handle); + + g_dbus_pending_property_success(id); +} + static gboolean chrc_get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { @@ -1616,6 +1675,7 @@ static gboolean chrc_notify_acquired_exists(const GDBusPropertyTable *property, } static const GDBusPropertyTable chrc_properties[] = { + { "Handle", "s", chrc_get_handle, chrc_set_handle, NULL }, { "UUID", "s", chrc_get_uuid, NULL, NULL }, { "Service", "o", chrc_get_service, NULL, NULL }, { "Value", "ay", chrc_get_value, NULL, NULL }, @@ -2342,6 +2402,33 @@ static const GDBusMethodTable desc_methods[] = { { } }; +static gboolean desc_get_handle(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct desc *desc = data; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &desc->handle); + + return TRUE; +} + +static void desc_set_handle(const GDBusPropertyTable *property, + DBusMessageIter *value, GDBusPendingPropertySet id, + void *data) +{ + struct desc *desc = data; + + if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16) { + g_dbus_pending_property_error(id, "org.bluez.InvalidArguments", + "Invalid arguments in method call"); + return; + } + + dbus_message_iter_get_basic(value, &desc->handle); + + g_dbus_pending_property_success(id); +} + static gboolean desc_get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { @@ -2400,6 +2487,7 @@ static gboolean desc_get_flags(const GDBusPropertyTable *property, } static const GDBusPropertyTable desc_properties[] = { + { "Handle", "q", desc_get_handle, desc_set_handle, NULL }, { "UUID", "s", desc_get_uuid, NULL, NULL }, { "Characteristic", "o", desc_get_chrc, NULL, NULL }, { "Value", "ay", desc_get_value, NULL, NULL }, -- 2.17.2
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> This emulates application setting their attribute handles. --- client/gatt.c | 53 +++++++++++++++++++++++++++++++-------------- client/main.c | 13 ++++++----- src/gatt-database.c | 24 +++++++++++++++----- 3 files changed, 63 insertions(+), 27 deletions(-) diff --git a/client/gatt.c b/client/gatt.c index 0137aa9e5..16ba2e306 100644 --- a/client/gatt.c +++ b/client/gatt.c @@ -120,21 +120,24 @@ static void print_service(struct service *service, const char *description) text = bt_uuidstr_to_str(service->uuid); if (!text) - bt_shell_printf("%s%s%s%s Service\n\t%s\n\t%s\n", + bt_shell_printf("%s%s%s%s Service\n\t%s\n\tHandle %u\n\t%s\n", description ? "[" : "", description ? : "", description ? "] " : "", service->primary ? "Primary" : "Secondary", - service->path, service->uuid); + service->path, service->handle, + service->uuid); else - bt_shell_printf("%s%s%s%s Service\n\t%s\n\t%s\n\t%s\n", + bt_shell_printf("%s%s%s%s Service\n\t%s\n\tHandle %u\n\t%s\n\t" + "%s\n", description ? "[" : "", description ? : "", description ? "] " : "", service->primary ? "Primary" : "Secondary", - service->path, service->uuid, text); + service->path, service->handle, + service->uuid, text); } static void print_inc_service(struct service *service, const char *description) @@ -143,21 +146,25 @@ static void print_inc_service(struct service *service, const char *description) text = bt_uuidstr_to_str(service->uuid); if (!text) - bt_shell_printf("%s%s%s%s Included Service\n\t%s\n\t%s\n", + bt_shell_printf("%s%s%s%s Included Service\n\t%s\n\tHandle %u" + "\n\t%s\n", description ? "[" : "", description ? : "", description ? "] " : "", service->primary ? "Primary" : "Secondary", - service->path, service->uuid); + service->path, service->handle, + service->uuid); else - bt_shell_printf("%s%s%s%s Included Service\n\t%s\n\t%s\n\t%s\n", + bt_shell_printf("%s%s%s%s Included Service\n\t%s\n\tHandle %u" + "\n\t%s\n\t%s\n", description ? "[" : "", description ? : "", description ? "] " : "", service->primary ? "Primary" : "Secondary", - service->path, service->uuid, text); + service->path, service->handle, + service->uuid, text); } static void print_service_proxy(GDBusProxy *proxy, const char *description) @@ -210,17 +217,20 @@ static void print_chrc(struct chrc *chrc, const char *description) text = bt_uuidstr_to_str(chrc->uuid); if (!text) - bt_shell_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n", + bt_shell_printf("%s%s%sCharacteristic\n\t%s\n\tHandle %u\n\t" + "%s\n", description ? "[" : "", description ? : "", description ? "] " : "", - chrc->path, chrc->uuid); + chrc->path, chrc->handle, chrc->uuid); else - bt_shell_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n\t%s\n", + bt_shell_printf("%s%s%sCharacteristic\n\t%s\n\tHandle %u\n\t" + "%s\n\t%s\n", description ? "[" : "", description ? : "", description ? "] " : "", - chrc->path, chrc->uuid, text); + chrc->path, chrc->handle, chrc->uuid, + text); } static void print_characteristic(GDBusProxy *proxy, const char *description) @@ -300,17 +310,19 @@ static void print_desc(struct desc *desc, const char *description) text = bt_uuidstr_to_str(desc->uuid); if (!text) - bt_shell_printf("%s%s%sDescriptor\n\t%s\n\t%s\n", + bt_shell_printf("%s%s%sDescriptor\n\t%s\n\tHandle %u\n\t%s\n", description ? "[" : "", description ? : "", description ? "] " : "", - desc->path, desc->uuid); + desc->path, desc->handle, desc->uuid); else - bt_shell_printf("%s%s%sDescriptor\n\t%s\n\t%s\n\t%s\n", + bt_shell_printf("%s%s%sDescriptor\n\t%s\n\tHandle %u\n\t%s\n\t" + "%s\n", description ? "[" : "", description ? : "", description ? "] " : "", - desc->path, desc->uuid, text); + desc->path, desc->handle, desc->uuid, + text); } static void print_descriptor(GDBusProxy *proxy, const char *description) @@ -1394,6 +1406,9 @@ void gatt_register_service(DBusConnection *conn, GDBusProxy *proxy, service->path = g_strdup_printf("%s/service%p", APP_PATH, service); service->primary = primary; + if (argc > 2) + service->handle = atoi(argv[2]); + if (g_dbus_register_interface(conn, service->path, SERVICE_INTERFACE, NULL, NULL, service_properties, service, @@ -2271,6 +2286,9 @@ void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy, chrc->flags = g_strsplit(argv[2], ",", -1); chrc->authorization_req = attr_authorization_flag_exists(chrc->flags); + if (argc > 3) + chrc->handle = atoi(argv[3]); + if (g_dbus_register_interface(conn, chrc->path, CHRC_INTERFACE, chrc_methods, NULL, chrc_properties, chrc, chrc_free) == FALSE) { @@ -2535,6 +2553,9 @@ void gatt_register_desc(DBusConnection *conn, GDBusProxy *proxy, desc->path = g_strdup_printf("%s/desc%p", desc->chrc->path, desc); desc->flags = g_strsplit(argv[2], ",", -1); + if (argc > 3) + desc->handle = atoi(argv[3]); + if (g_dbus_register_interface(conn, desc->path, DESC_INTERFACE, desc_methods, NULL, desc_properties, desc, desc_free) == FALSE) { diff --git a/client/main.c b/client/main.c index a4647f334..07c8ec008 100644 --- a/client/main.c +++ b/client/main.c @@ -2619,22 +2619,23 @@ static const struct bt_shell_menu gatt_menu = { "Register profile to connect" }, { "unregister-application", NULL, cmd_unregister_app, "Unregister profile" }, - { "register-service", "<UUID>", cmd_register_service, + { "register-service", "<UUID> [handle]", cmd_register_service, "Register application service." }, { "unregister-service", "<UUID/object>", cmd_unregister_service, "Unregister application service" }, - { "register-includes", "<UUID>", cmd_register_includes, + { "register-includes", "<UUID> [handle]", cmd_register_includes, "Register as Included service in." }, { "unregister-includes", "<Service-UUID><Inc-UUID>", cmd_unregister_includes, "Unregister Included service." }, - { "register-characteristic", "<UUID> <Flags=read,write,notify...> " - , cmd_register_characteristic, - "Register application characteristic" }, + { "register-characteristic", + "<UUID> <Flags=read,write,notify...> [handle]", + cmd_register_characteristic, + "Register application characteristic" }, { "unregister-characteristic", "<UUID/object>", cmd_unregister_characteristic, "Unregister application characteristic" }, - { "register-descriptor", "<UUID> <Flags=read,write...>", + { "register-descriptor", "<UUID> <Flags=read,write...> [handle]", cmd_register_descriptor, "Register application descriptor" }, { "unregister-descriptor", "<UUID/object>", diff --git a/src/gatt-database.c b/src/gatt-database.c index b159786ea..cca70c947 100644 --- a/src/gatt-database.c +++ b/src/gatt-database.c @@ -1843,17 +1843,16 @@ static bool parse_handle(GDBusProxy *proxy, uint16_t *handle) { DBusMessageIter iter; + *handle = 0; + /* Handle property is optional */ - if (!g_dbus_proxy_get_property(proxy, "Handle", &iter)) { - *handle = 0; + if (!g_dbus_proxy_get_property(proxy, "Handle", &iter)) return true; - } - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT16) return false; - dbus_message_iter_get_basic(&iter, &handle); + dbus_message_iter_get_basic(&iter, handle); return true; } @@ -2634,6 +2633,7 @@ static bool database_add_desc(struct external_service *service, { uint16_t handle; bt_uuid_t uuid; + char str[MAX_LEN_UUID_STR]; if (!parse_handle(desc->proxy, &handle)) { error("Failed to read \"Handle\" property of descriptor"); @@ -2662,6 +2662,10 @@ static bool database_add_desc(struct external_service *service, write_handle(desc->proxy, handle); } + bt_uuid_to_string(&uuid, str, sizeof(str)); + + DBG("handle 0x%04x UUID %s", handle, str); + return true; } @@ -2795,6 +2799,7 @@ static bool database_add_chrc(struct external_service *service, { uint16_t handle; bt_uuid_t uuid; + char str[MAX_LEN_UUID_STR]; const struct queue_entry *entry; if (!parse_handle(chrc->proxy, &handle)) { @@ -2832,6 +2837,10 @@ static bool database_add_chrc(struct external_service *service, write_handle(chrc->proxy, handle); } + bt_uuid_to_string(&uuid, str, sizeof(str)); + + DBG("handle 0x%04x UUID %s", handle, str); + /* Handle the descriptors that belong to this characteristic. */ for (entry = queue_get_entries(service->descs); entry; entry = entry->next) { @@ -2863,6 +2872,7 @@ static bool database_add_service(struct external_service *service) bool primary; uint16_t handle; const struct queue_entry *entry; + char str[MAX_LEN_UUID_STR]; if (!parse_uuid(service->proxy, &uuid)) { error("Failed to read \"UUID\" property of service"); @@ -2895,6 +2905,10 @@ static bool database_add_service(struct external_service *service) write_handle(service->proxy, handle); } + bt_uuid_to_string(&uuid, str, sizeof(str)); + + DBG("handle 0x%04x UUID %s", handle, str); + database_add_includes(service); entry = queue_get_entries(service->chrcs); -- 2.17.2