1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * BlueZ - Bluetooth protocol stack for Linux
6 * Copyright (C) 2014 Google Inc.
21 #include <dbus/dbus.h>
23 #include "lib/bluetooth.h"
27 #include "gdbus/gdbus.h"
28 #include "btio/btio.h"
35 #include "src/shared/io.h"
36 #include "src/shared/queue.h"
37 #include "src/shared/att.h"
38 #include "src/shared/gatt-db.h"
39 #include "src/shared/gatt-client.h"
40 #include "src/shared/util.h"
41 #include "gatt-client.h"
42 #include "dbus-common.h"
45 #define NELEM(x) (sizeof(x) / sizeof((x)[0]))
48 #define GATT_SERVICE_IFACE "org.bluez.GattService1"
49 #define GATT_CHARACTERISTIC_IFACE "org.bluez.GattCharacteristic1"
50 #define GATT_DESCRIPTOR_IFACE "org.bluez.GattDescriptor1"
52 struct btd_gatt_client {
53 struct btd_device *device;
58 struct bt_gatt_client *gatt;
60 struct queue *services;
61 struct queue *all_notify_clients;
63 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
69 struct btd_gatt_client *client;
71 uint16_t start_handle;
76 struct queue *incl_services;
77 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
79 struct queue *pending_ext_props;
84 typedef bool (*async_dbus_op_complete_t)(void *data);
86 struct async_dbus_op {
92 async_dbus_op_complete_t complete;
98 void (*destroy)(void *data);
102 struct characteristic {
103 struct service *service;
104 struct gatt_db_attribute *attr;
106 uint16_t value_handle;
109 uint16_t ext_props_handle;
113 unsigned int ready_id;
114 unsigned int exchange_id;
115 struct sock_io *write_io;
116 struct sock_io *notify_io;
118 struct async_dbus_op *read_op;
119 struct async_dbus_op *write_op;
124 struct queue *notify_clients;
128 struct characteristic *chrc;
129 struct gatt_db_attribute *attr;
134 struct async_dbus_op *read_op;
135 struct async_dbus_op *write_op;
138 static bool uuid_cmp(const bt_uuid_t *uuid, uint16_t u16)
142 bt_uuid16_create(&uuid16, u16);
144 return bt_uuid_cmp(uuid, &uuid16) == 0;
147 static gboolean descriptor_get_uuid(const GDBusPropertyTable *property,
148 DBusMessageIter *iter, void *data)
150 char uuid[MAX_LEN_UUID_STR + 1];
151 const char *ptr = uuid;
152 struct descriptor *desc = data;
154 bt_uuid_to_string(&desc->uuid, uuid, sizeof(uuid));
155 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
160 static gboolean descriptor_get_characteristic(
161 const GDBusPropertyTable *property,
162 DBusMessageIter *iter, void *data)
164 struct descriptor *desc = data;
165 const char *str = desc->chrc->path;
167 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &str);
172 static void read_cb(struct gatt_db_attribute *attrib, int err,
173 const uint8_t *value, size_t length,
176 DBusMessageIter *array = user_data;
181 dbus_message_iter_append_fixed_array(array, DBUS_TYPE_BYTE, &value,
185 static gboolean descriptor_get_value(const GDBusPropertyTable *property,
186 DBusMessageIter *iter, void *data)
188 struct descriptor *desc = data;
189 DBusMessageIter array;
191 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
193 gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_cb, &array);
195 dbus_message_iter_close_container(iter, &array);
200 static void read_check_cb(struct gatt_db_attribute *attrib, int err,
201 const uint8_t *value, size_t length,
204 gboolean *ret = user_data;
214 static gboolean descriptor_value_exists(const GDBusPropertyTable *property,
217 struct descriptor *desc = data;
220 gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_check_cb, &ret);
225 static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len)
227 DBusMessageIter array;
229 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
232 dbus_message_iter_recurse(iter, &array);
233 dbus_message_iter_get_fixed_array(&array, value, len);
238 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
239 static int parse_type_value_arg(DBusMessageIter *iter, uint8_t *type, uint8_t **value,
242 DBusMessageIter array;
244 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BYTE)
246 dbus_message_iter_get_basic(iter, type);
247 dbus_message_iter_next(iter);
248 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
251 dbus_message_iter_recurse(iter, &array);
252 dbus_message_iter_get_fixed_array(&array, value, len);
258 static void async_dbus_op_free(void *data)
260 struct async_dbus_op *op = data;
262 queue_destroy(op->msgs, (void *)dbus_message_unref);
267 static struct async_dbus_op *async_dbus_op_ref(struct async_dbus_op *op)
269 __sync_fetch_and_add(&op->ref_count, 1);
274 static void async_dbus_op_unref(void *data)
276 struct async_dbus_op *op = data;
278 if (__sync_sub_and_fetch(&op->ref_count, 1))
281 async_dbus_op_free(op);
284 static void message_append_byte_array(DBusMessage *msg, const uint8_t *bytes,
287 DBusMessageIter iter, array;
289 dbus_message_iter_init_append(msg, &iter);
290 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "y", &array);
291 dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &bytes,
293 dbus_message_iter_close_container(&iter, &array);
296 static DBusMessage *create_gatt_dbus_error(DBusMessage *msg, uint8_t att_ecode)
298 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
299 return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
300 "Operation failed with ATT error: 0x%02x",
304 case BT_ATT_ERROR_READ_NOT_PERMITTED:
305 return btd_error_not_permitted(msg, "Read not permitted");
306 case BT_ATT_ERROR_WRITE_NOT_PERMITTED:
307 return btd_error_not_permitted(msg, "Write not permitted");
308 case BT_ATT_ERROR_AUTHENTICATION:
309 case BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION:
310 case BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE:
311 return btd_error_not_permitted(msg, "Not paired");
312 case BT_ATT_ERROR_INVALID_OFFSET:
313 return btd_error_invalid_args_str(msg, "Invalid offset");
314 case BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN:
315 return btd_error_invalid_args_str(msg, "Invalid Length");
316 case BT_ATT_ERROR_AUTHORIZATION:
317 return btd_error_not_authorized(msg);
318 case BT_ATT_ERROR_REQUEST_NOT_SUPPORTED:
319 return btd_error_not_supported(msg);
321 return btd_error_failed(msg, "Operation failed");
323 return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
324 "Operation failed with ATT error: 0x%02x",
332 static void write_descriptor_cb(struct gatt_db_attribute *attr, int err,
335 struct descriptor *desc = user_data;
340 g_dbus_emit_property_changed(btd_get_dbus_connection(), desc->path,
341 GATT_DESCRIPTOR_IFACE, "Value");
344 static void async_dbus_op_reply(struct async_dbus_op *op, int err,
345 const uint8_t *value, ssize_t length)
347 const struct queue_entry *entry;
352 for (entry = queue_get_entries(op->msgs); entry; entry = entry->next) {
353 DBusMessage *msg = entry->data;
356 reply = err > 0 ? create_gatt_dbus_error(msg, err) :
357 btd_error_failed(msg, strerror(-err));
361 reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
363 error("Failed to allocate D-Bus message reply");
368 message_append_byte_array(reply, value, length);
371 g_dbus_send_message(btd_get_dbus_connection(), reply);
375 static void read_op_cb(struct gatt_db_attribute *attrib, int err,
376 const uint8_t *value, size_t length,
379 struct async_dbus_op *op = user_data;
381 async_dbus_op_reply(op, err, value, length);
384 static void desc_read_cb(bool success, uint8_t att_ecode,
385 const uint8_t *value, uint16_t length,
388 struct async_dbus_op *op = user_data;
389 struct descriptor *desc = op->data;
395 gatt_db_attribute_reset(desc->attr);
397 if (!gatt_db_attribute_write(desc->attr, op->offset, value, length, 0,
398 NULL, write_descriptor_cb, desc)) {
399 error("Failed to store attribute");
400 att_ecode = BT_ATT_ERROR_UNLIKELY;
404 /* Read the stored data from db */
405 if (!gatt_db_attribute_read(desc->attr, op->offset, 0, NULL, read_op_cb,
407 error("Failed to read database");
408 att_ecode = BT_ATT_ERROR_UNLIKELY;
412 desc->read_op = NULL;
417 async_dbus_op_reply(op, att_ecode, NULL, 0);
418 desc->read_op = NULL;
421 static int parse_options(DBusMessageIter *iter, uint16_t *offset,
424 DBusMessageIter dict;
426 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
429 dbus_message_iter_recurse(iter, &dict);
431 while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
433 DBusMessageIter value, entry;
436 dbus_message_iter_recurse(&dict, &entry);
437 dbus_message_iter_get_basic(&entry, &key);
439 dbus_message_iter_next(&entry);
440 dbus_message_iter_recurse(&entry, &value);
442 var = dbus_message_iter_get_arg_type(&value);
443 if (strcasecmp(key, "offset") == 0) {
444 if (var != DBUS_TYPE_UINT16)
446 dbus_message_iter_get_basic(&value, offset);
449 if (type && strcasecmp(key, "type") == 0) {
450 if (var != DBUS_TYPE_STRING)
452 dbus_message_iter_get_basic(&value, type);
455 dbus_message_iter_next(&dict);
461 static struct async_dbus_op *async_dbus_op_new(DBusMessage *msg, void *data)
463 struct async_dbus_op *op;
465 op = new0(struct async_dbus_op, 1);
466 op->msgs = queue_new();
467 queue_push_tail(op->msgs, dbus_message_ref(msg));
473 static struct async_dbus_op *read_value(struct bt_gatt_client *gatt,
474 DBusMessage *msg, uint16_t handle,
476 bt_gatt_client_read_callback_t callback,
479 struct async_dbus_op *op;
481 op = async_dbus_op_new(msg, data);
484 op->id = bt_gatt_client_read_long_value(gatt, handle, offset, callback,
485 async_dbus_op_ref(op),
486 async_dbus_op_unref);
490 async_dbus_op_free(op);
495 static DBusMessage *descriptor_read_value(DBusConnection *conn,
496 DBusMessage *msg, void *user_data)
498 struct descriptor *desc = user_data;
499 struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
500 DBusMessageIter iter;
504 return btd_error_failed(msg, "Not connected");
506 dbus_message_iter_init(msg, &iter);
508 if (parse_options(&iter, &offset, NULL))
509 return btd_error_invalid_args(msg);
512 if (desc->read_op->offset != offset)
513 return btd_error_in_progress(msg);
514 queue_push_tail(desc->read_op->msgs, dbus_message_ref(msg));
518 desc->read_op = read_value(gatt, msg, desc->handle, offset,
521 return btd_error_failed(msg, "Failed to send read request");
526 static void write_result_cb(bool success, bool reliable_error,
527 uint8_t att_ecode, void *user_data)
529 struct async_dbus_op *op = user_data;
532 if (op->complete && !op->complete(op->data)) {
545 async_dbus_op_reply(op, err, NULL, -1);
548 static void write_cb(bool success, uint8_t att_ecode, void *user_data)
550 write_result_cb(success, false, att_ecode, user_data);
553 static struct async_dbus_op *start_long_write(DBusMessage *msg, uint16_t handle,
554 struct bt_gatt_client *gatt,
555 bool reliable, const uint8_t *value,
556 size_t value_len, uint16_t offset,
558 async_dbus_op_complete_t complete)
560 struct async_dbus_op *op;
562 op = async_dbus_op_new(msg, data);
563 op->complete = complete;
566 op->id = bt_gatt_client_write_long_value(gatt, reliable, handle, offset,
572 async_dbus_op_free(op);
579 static struct async_dbus_op *start_write_request(DBusMessage *msg,
581 struct bt_gatt_client *gatt,
582 const uint8_t *value, size_t value_len,
584 async_dbus_op_complete_t complete)
586 struct async_dbus_op *op;
588 op = async_dbus_op_new(msg, data);
589 op->complete = complete;
591 op->id = bt_gatt_client_write_value(gatt, handle, value, value_len,
595 async_dbus_op_free(op);
601 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
602 static struct async_dbus_op *start_write_cmd(DBusMessage *msg, uint16_t handle,
603 struct bt_gatt_client *gatt, bool signed_write,
604 const uint8_t *value, size_t value_len,
605 void *data, async_dbus_op_complete_t complete)
607 struct async_dbus_op *op;
609 op = async_dbus_op_new(msg, data);
610 op->complete = complete;
612 op->id = bt_gatt_client_write_without_response_async(gatt, handle,
613 signed_write, value, value_len,
614 write_cb, op, async_dbus_op_free);
617 async_dbus_op_free(op);
625 static bool desc_write_complete(void *data)
627 struct descriptor *desc = data;
629 desc->write_op = NULL;
632 * The descriptor might have been unregistered during the read. Return
638 static DBusMessage *descriptor_write_value(DBusConnection *conn,
639 DBusMessage *msg, void *user_data)
641 struct descriptor *desc = user_data;
642 struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
643 DBusMessageIter iter;
644 uint8_t *value = NULL;
649 return btd_error_failed(msg, "Not connected");
652 return btd_error_in_progress(msg);
654 dbus_message_iter_init(msg, &iter);
656 if (parse_value_arg(&iter, &value, &value_len))
657 return btd_error_invalid_args(msg);
659 dbus_message_iter_next(&iter);
661 if (parse_options(&iter, &offset, NULL))
662 return btd_error_invalid_args(msg);
665 * Don't allow writing to Client Characteristic Configuration
666 * descriptors. We achieve this through the StartNotify and StopNotify
667 * methods on GattCharacteristic1.
669 if (uuid_cmp(&desc->uuid, GATT_CLIENT_CHARAC_CFG_UUID))
670 return btd_error_not_permitted(msg, "Write not permitted");
673 * Based on the value length and the MTU, either use a write or a long
676 if (value_len <= bt_gatt_client_get_mtu(gatt) - 3 && !offset)
677 desc->write_op = start_write_request(msg, desc->handle,
680 desc_write_complete);
682 desc->write_op = start_long_write(msg, desc->handle, gatt,
684 value_len, offset, desc,
685 desc_write_complete);
688 return btd_error_failed(msg, "Failed to initiate write");
693 static const GDBusPropertyTable descriptor_properties[] = {
694 { "UUID", "s", descriptor_get_uuid },
695 { "Characteristic", "o", descriptor_get_characteristic, },
696 { "Value", "ay", descriptor_get_value, NULL, descriptor_value_exists },
700 static const GDBusMethodTable descriptor_methods[] = {
701 { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
702 GDBUS_ARGS({ "value", "ay" }),
703 descriptor_read_value) },
704 { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
705 { "options", "a{sv}" }),
707 descriptor_write_value) },
711 static void descriptor_free(void *data)
713 struct descriptor *desc = data;
719 static struct descriptor *descriptor_create(struct gatt_db_attribute *attr,
720 struct characteristic *chrc)
722 struct descriptor *desc;
724 desc = new0(struct descriptor, 1);
727 desc->handle = gatt_db_attribute_get_handle(attr);
729 bt_uuid_to_uuid128(gatt_db_attribute_get_type(attr), &desc->uuid);
731 desc->path = g_strdup_printf("%s/desc%04x", chrc->path, desc->handle);
733 if (!g_dbus_register_interface(btd_get_dbus_connection(), desc->path,
734 GATT_DESCRIPTOR_IFACE,
735 descriptor_methods, NULL,
736 descriptor_properties,
737 desc, descriptor_free)) {
738 error("Unable to register GATT descriptor with handle 0x%04x",
740 descriptor_free(desc);
745 DBG("Exported GATT characteristic descriptor: %s", desc->path);
747 if (uuid_cmp(&desc->uuid, GATT_CHARAC_EXT_PROPER_UUID))
748 chrc->ext_props_handle = desc->handle;
753 static void unregister_descriptor(void *data)
755 struct descriptor *desc = data;
756 struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
758 DBG("Removing GATT descriptor: %s", desc->path);
761 bt_gatt_client_cancel(gatt, desc->read_op->id);
764 bt_gatt_client_cancel(gatt, desc->write_op->id);
768 g_dbus_unregister_interface(btd_get_dbus_connection(), desc->path,
769 GATT_DESCRIPTOR_IFACE);
772 static gboolean characteristic_get_uuid(const GDBusPropertyTable *property,
773 DBusMessageIter *iter, void *data)
775 char uuid[MAX_LEN_UUID_STR + 1];
776 const char *ptr = uuid;
777 struct characteristic *chrc = data;
779 bt_uuid_to_string(&chrc->uuid, uuid, sizeof(uuid));
780 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
785 static gboolean characteristic_get_service(const GDBusPropertyTable *property,
786 DBusMessageIter *iter, void *data)
788 struct characteristic *chrc = data;
789 const char *str = chrc->service->path;
791 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &str);
796 static gboolean characteristic_get_value(const GDBusPropertyTable *property,
797 DBusMessageIter *iter, void *data)
799 struct characteristic *chrc = data;
800 DBusMessageIter array;
802 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
804 gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_cb, &array);
806 dbus_message_iter_close_container(iter, &array);
811 static gboolean characteristic_value_exists(const GDBusPropertyTable *property,
814 struct characteristic *chrc = data;
817 gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_check_cb, &ret);
822 static gboolean characteristic_get_notifying(const GDBusPropertyTable *property,
823 DBusMessageIter *iter, void *data)
825 struct characteristic *chrc = data;
826 dbus_bool_t notifying = chrc->notifying ? TRUE : FALSE;
828 dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, ¬ifying);
834 characteristic_notifying_exists(const GDBusPropertyTable *property, void *data)
836 struct characteristic *chrc = data;
838 return (chrc->props & BT_GATT_CHRC_PROP_NOTIFY ||
839 chrc->props & BT_GATT_CHRC_PROP_INDICATE);
842 struct chrc_prop_data {
847 static struct chrc_prop_data chrc_props[] = {
848 /* Default Properties */
849 { BT_GATT_CHRC_PROP_BROADCAST, "broadcast" },
850 { BT_GATT_CHRC_PROP_READ, "read" },
851 { BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP, "write-without-response" },
852 { BT_GATT_CHRC_PROP_WRITE, "write" },
853 { BT_GATT_CHRC_PROP_NOTIFY, "notify" },
854 { BT_GATT_CHRC_PROP_INDICATE, "indicate" },
855 { BT_GATT_CHRC_PROP_AUTH, "authenticated-signed-writes" },
856 { BT_GATT_CHRC_PROP_EXT_PROP, "extended-properties" }
859 static struct chrc_prop_data chrc_ext_props[] = {
860 /* Extended Properties */
861 { BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE, "reliable-write" },
862 { BT_GATT_CHRC_EXT_PROP_WRITABLE_AUX, "writable-auxiliaries" }
865 static gboolean characteristic_get_flags(const GDBusPropertyTable *property,
866 DBusMessageIter *iter, void *data)
868 struct characteristic *chrc = data;
869 DBusMessageIter array;
872 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array);
874 for (i = 0; i < NELEM(chrc_props); i++) {
875 if (chrc->props & chrc_props[i].prop)
876 dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
880 for (i = 0; i < NELEM(chrc_ext_props); i++) {
881 if (chrc->ext_props & chrc_ext_props[i].prop)
882 dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
883 &chrc_ext_props[i].str);
886 dbus_message_iter_close_container(iter, &array);
892 characteristic_get_write_acquired(const GDBusPropertyTable *property,
893 DBusMessageIter *iter, void *data)
895 struct characteristic *chrc = data;
896 dbus_bool_t locked = chrc->write_io ? TRUE : FALSE;
898 dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &locked);
904 characteristic_write_acquired_exists(const GDBusPropertyTable *property,
907 struct characteristic *chrc = data;
909 return (chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP);
913 characteristic_get_notify_acquired(const GDBusPropertyTable *property,
914 DBusMessageIter *iter, void *data)
916 struct characteristic *chrc = data;
917 dbus_bool_t locked = chrc->notify_io ? TRUE : FALSE;
919 dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &locked);
925 characteristic_notify_acquired_exists(const GDBusPropertyTable *property,
928 struct characteristic *chrc = data;
930 return (chrc->props & BT_GATT_CHRC_PROP_NOTIFY);
933 static gboolean characteristic_get_mtu(const GDBusPropertyTable *property,
934 DBusMessageIter *iter, void *data)
936 struct characteristic *chrc = data;
937 struct bt_gatt_client *gatt = chrc->service->client->gatt;
941 att = bt_gatt_client_get_att(gatt);
942 mtu = att ? bt_att_get_mtu(att) : BT_ATT_DEFAULT_LE_MTU;
944 dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &mtu);
949 static void write_characteristic_cb(struct gatt_db_attribute *attr, int err,
952 struct characteristic *chrc = user_data;
957 g_dbus_emit_property_changed_full(btd_get_dbus_connection(),
958 chrc->path, GATT_CHARACTERISTIC_IFACE,
959 "Value", G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH);
963 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
964 static void notify_characteristic_cb(struct gatt_db_attribute *attr, int err,
968 error("Failed to notify_characteristic_cb : %d", err);
974 static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
975 uint16_t length, void *user_data)
977 struct async_dbus_op *op = user_data;
978 struct characteristic *chrc = op->data;
984 gatt_db_attribute_reset(chrc->attr);
986 if (!gatt_db_attribute_write(chrc->attr, op->offset, value, length, 0,
987 NULL, write_characteristic_cb, chrc)) {
988 error("Failed to store attribute");
989 att_ecode = BT_ATT_ERROR_UNLIKELY;
993 /* Read the stored data from db */
994 if (!gatt_db_attribute_read(chrc->attr, op->offset, 0, NULL, read_op_cb,
996 error("Failed to read database");
997 att_ecode = BT_ATT_ERROR_UNLIKELY;
1001 chrc->read_op = NULL;
1006 async_dbus_op_reply(op, att_ecode, NULL, 0);
1007 chrc->read_op = NULL;
1010 static DBusMessage *characteristic_read_value(DBusConnection *conn,
1011 DBusMessage *msg, void *user_data)
1013 struct characteristic *chrc = user_data;
1014 struct bt_gatt_client *gatt = chrc->service->client->gatt;
1015 DBusMessageIter iter;
1016 uint16_t offset = 0;
1019 return btd_error_failed(msg, "Not connected");
1021 dbus_message_iter_init(msg, &iter);
1023 if (parse_options(&iter, &offset, NULL))
1024 return btd_error_invalid_args(msg);
1026 if (chrc->read_op) {
1027 if (chrc->read_op->offset != offset)
1028 return btd_error_in_progress(msg);
1029 queue_push_tail(chrc->read_op->msgs, dbus_message_ref(msg));
1033 chrc->read_op = read_value(gatt, msg, chrc->value_handle, offset,
1034 chrc_read_cb, chrc);
1036 return btd_error_failed(msg, "Failed to send read request");
1041 static bool chrc_write_complete(void *data)
1043 struct characteristic *chrc = data;
1045 chrc->write_op = NULL;
1048 * The characteristic might have been unregistered during the read.
1051 return !!chrc->service;
1054 static DBusMessage *characteristic_write_value(DBusConnection *conn,
1055 DBusMessage *msg, void *user_data)
1057 struct characteristic *chrc = user_data;
1058 struct bt_gatt_client *gatt = chrc->service->client->gatt;
1059 DBusMessageIter iter;
1060 uint8_t *value = NULL;
1062 bool supported = false;
1063 uint16_t offset = 0;
1064 const char *type = NULL;
1067 return btd_error_failed(msg, "Not connected");
1070 return btd_error_not_permitted(msg, "Write acquired");
1073 return btd_error_in_progress(msg);
1075 dbus_message_iter_init(msg, &iter);
1077 if (parse_value_arg(&iter, &value, &value_len))
1078 return btd_error_invalid_args(msg);
1080 dbus_message_iter_next(&iter);
1082 if (parse_options(&iter, &offset, &type))
1083 return btd_error_invalid_args(msg);
1086 * Decide which write to use based on characteristic properties. For now
1087 * we don't perform signed writes since gatt-client doesn't support them
1088 * and the user can always encrypt the through pairing. The procedure to
1089 * use is determined based on the following priority:
1091 * * "reliable-write" property set -> reliable long-write.
1092 * * "write" property set -> write request.
1093 * - If value is larger than MTU - 3: long-write
1094 * * "write-without-response" property set -> write command.
1096 #ifndef TIZEN_FEATURE_BLUEZ_MODIFY
1097 if ((!type && (chrc->ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE))
1098 || (type && !strcasecmp(type, "reliable"))) {
1100 chrc->write_op = start_long_write(msg, chrc->value_handle, gatt,
1101 true, value, value_len, offset,
1102 chrc, chrc_write_complete);
1108 if ((!type && chrc->props & BT_GATT_CHRC_PROP_WRITE) ||
1109 (type && !strcasecmp(type, "request"))) {
1113 mtu = bt_gatt_client_get_mtu(gatt);
1115 return btd_error_failed(msg, "No ATT transport");
1117 if (value_len <= mtu - 3 && !offset)
1118 chrc->write_op = start_write_request(msg,
1120 gatt, value, value_len,
1121 chrc, chrc_write_complete);
1123 chrc->write_op = start_long_write(msg,
1124 chrc->value_handle, gatt,
1125 false, value, value_len, offset,
1126 chrc, chrc_write_complete);
1132 if ((type && strcasecmp(type, "command")) || offset || (!type &&
1133 !(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP)))
1138 if (bt_gatt_client_write_without_response(gatt,
1140 chrc->props & BT_GATT_CHRC_PROP_AUTH,
1142 return dbus_message_new_method_return(msg);
1146 return btd_error_failed(msg, "Failed to initiate write");
1148 return btd_error_not_supported(msg);
1151 static bool sock_read(struct io *io, void *user_data)
1153 struct characteristic *chrc = user_data;
1154 struct bt_gatt_client *gatt = chrc->service->client->gatt;
1158 int fd = io_get_fd(io);
1162 iov.iov_len = sizeof(buf);
1164 memset(&msg, 0, sizeof(msg));
1168 bytes_read = recvmsg(fd, &msg, MSG_DONTWAIT);
1169 if (bytes_read < 0) {
1170 error("recvmsg: %s", strerror(errno));
1174 if (!gatt || bytes_read == 0)
1177 bt_gatt_client_write_without_response(gatt, chrc->value_handle,
1178 chrc->props & BT_GATT_CHRC_PROP_AUTH,
1184 static void sock_io_destroy(struct sock_io *io)
1187 io->destroy(io->data);
1190 dbus_message_unref(io->msg);
1196 static void destroy_sock(struct characteristic *chrc,
1199 queue_remove(chrc->service->client->ios, io);
1201 if (chrc->write_io && io == chrc->write_io->io) {
1202 sock_io_destroy(chrc->write_io);
1203 chrc->write_io = NULL;
1204 g_dbus_emit_property_changed(btd_get_dbus_connection(),
1206 GATT_CHARACTERISTIC_IFACE,
1208 } else if (chrc->notify_io) {
1209 sock_io_destroy(chrc->notify_io);
1210 chrc->notify_io = NULL;
1211 g_dbus_emit_property_changed(btd_get_dbus_connection(),
1213 GATT_CHARACTERISTIC_IFACE,
1218 static bool sock_hup(struct io *io, void *user_data)
1220 struct characteristic *chrc = user_data;
1222 DBG("%s: io %p", chrc->path, io);
1224 destroy_sock(chrc, io);
1229 static DBusMessage *create_sock(struct characteristic *chrc,
1232 struct bt_gatt_client *gatt = chrc->service->client->gatt;
1239 if (!gatt || !bt_gatt_client_is_ready(gatt))
1240 return btd_error_failed(msg, "Not connected");
1242 if (socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC,
1244 return btd_error_failed(msg, strerror(errno));
1246 dir = dbus_message_has_member(msg, "AcquireWrite");
1248 io = io_new(fds[!dir]);
1252 return btd_error_failed(msg, strerror(EIO));
1255 io_set_close_on_destroy(io, true);
1257 if (!io_set_read_handler(io, sock_read, chrc, NULL))
1260 if (!io_set_disconnect_handler(io, sock_hup, chrc, NULL))
1263 mtu = bt_gatt_client_get_mtu(gatt);
1265 reply = g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD, &fds[dir],
1266 DBUS_TYPE_UINT16, &mtu,
1272 chrc->write_io->io = io;
1273 g_dbus_emit_property_changed(btd_get_dbus_connection(),
1275 GATT_CHARACTERISTIC_IFACE,
1278 chrc->notify_io->io = io;
1279 g_dbus_emit_property_changed(btd_get_dbus_connection(),
1281 GATT_CHARACTERISTIC_IFACE,
1285 queue_push_tail(chrc->service->client->ios, io);
1287 DBG("%s: sender %s io %p", dbus_message_get_member(msg),
1288 dbus_message_get_sender(msg), io);
1295 return btd_error_failed(msg, strerror(EIO));
1298 static void characteristic_ready(bool success, uint8_t ecode, void *user_data)
1300 struct characteristic *chrc = user_data;
1305 if (chrc->write_io && chrc->write_io->msg) {
1306 reply = create_sock(chrc, chrc->write_io->msg);
1308 g_dbus_send_message(btd_get_dbus_connection(), reply);
1310 dbus_message_unref(chrc->write_io->msg);
1311 chrc->write_io->msg = NULL;
1314 if (chrc->notify_io && chrc->notify_io->msg) {
1315 reply = create_sock(chrc, chrc->notify_io->msg);
1317 g_dbus_send_message(btd_get_dbus_connection(), reply);
1319 dbus_message_unref(chrc->notify_io->msg);
1320 chrc->notify_io->msg = NULL;
1324 static DBusMessage *characteristic_acquire_write(DBusConnection *conn,
1325 DBusMessage *msg, void *user_data)
1327 struct characteristic *chrc = user_data;
1328 struct bt_gatt_client *gatt = chrc->service->client->gatt;
1331 return btd_error_failed(msg, "Not connected");
1334 return btd_error_not_permitted(msg, "Write acquired");
1336 if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP))
1337 return btd_error_not_supported(msg);
1339 chrc->write_io = new0(struct sock_io, 1);
1341 if (!bt_gatt_client_is_ready(gatt)) {
1342 /* GATT not ready, wait until it becomes ready */
1343 if (!chrc->ready_id)
1344 chrc->ready_id = bt_gatt_client_ready_register(gatt,
1345 characteristic_ready,
1347 chrc->write_io->msg = dbus_message_ref(msg);
1351 return create_sock(chrc, msg);
1354 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
1355 static DBusMessage *characteristic_write_value_by_type(DBusConnection *conn,
1356 DBusMessage *msg, void *user_data)
1358 struct characteristic *chrc = user_data;
1359 struct bt_gatt_client *gatt = chrc->service->client->gatt;
1360 DBusMessageIter iter;
1361 uint8_t *value = NULL;
1363 bool supported = false;
1364 uint8_t write_type = 0;
1365 uint16_t offset = 0;
1368 return btd_error_failed(msg, "Not connected");
1371 return btd_error_in_progress(msg);
1373 dbus_message_iter_init(msg, &iter);
1375 if (parse_type_value_arg(&iter, &write_type, &value, &value_len))
1376 return btd_error_invalid_args(msg);
1378 if (parse_options(&iter, &offset,NULL))
1379 return btd_error_invalid_args(msg);
1381 if ((write_type & chrc->props) == BT_GATT_CHRC_PROP_WRITE) {
1385 mtu = bt_gatt_client_get_mtu(gatt);
1387 return btd_error_failed(msg, "No ATT transport");
1389 if (value_len <= mtu - 3 && !offset)
1390 chrc->write_op = start_write_request(msg,
1392 gatt, value, value_len,
1393 chrc, chrc_write_complete);
1395 chrc->write_op = start_long_write(msg,
1396 chrc->value_handle, gatt,
1397 false, value, value_len, offset,
1398 chrc, chrc_write_complete);
1402 } else if ((write_type & chrc->props) ==
1403 BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP) {
1405 chrc->write_op = start_write_cmd(msg, chrc->value_handle, gatt,
1406 false, value, value_len,
1407 chrc, chrc_write_complete);
1410 } else if ((write_type & chrc->props) == BT_GATT_CHRC_PROP_AUTH) {
1412 chrc->write_op = start_write_cmd(msg, chrc->value_handle, gatt,
1413 true, value, value_len,
1414 chrc, chrc_write_complete);
1420 return btd_error_failed(msg, "Failed to initiate write");
1422 return btd_error_not_supported(msg);
1426 struct notify_client {
1427 struct characteristic *chrc;
1431 unsigned int notify_id;
1434 static void notify_client_free(struct notify_client *client)
1436 DBG("owner %s", client->owner);
1438 g_dbus_remove_watch(btd_get_dbus_connection(), client->watch);
1439 bt_gatt_client_unregister_notify(client->chrc->service->client->gatt,
1441 free(client->owner);
1445 static void notify_client_unref(void *data)
1447 struct notify_client *client = data;
1449 DBG("owner %s", client->owner);
1451 if (__sync_sub_and_fetch(&client->ref_count, 1))
1454 notify_client_free(client);
1457 static struct notify_client *notify_client_ref(struct notify_client *client)
1459 DBG("owner %s", client->owner);
1461 __sync_fetch_and_add(&client->ref_count, 1);
1466 static bool match_notifying(const void *a, const void *b)
1468 const struct notify_client *client = a;
1470 return !!client->notify_id;
1473 static void update_notifying(struct characteristic *chrc)
1475 if (!chrc->notifying)
1478 if (queue_find(chrc->notify_clients, match_notifying, NULL))
1481 chrc->notifying = false;
1483 g_dbus_emit_property_changed(btd_get_dbus_connection(), chrc->path,
1484 GATT_CHARACTERISTIC_IFACE,
1488 static void notify_client_disconnect(DBusConnection *conn, void *user_data)
1490 struct notify_client *client = user_data;
1491 struct characteristic *chrc = client->chrc;
1493 DBG("owner %s", client->owner);
1495 queue_remove(chrc->notify_clients, client);
1496 queue_remove(chrc->service->client->all_notify_clients, client);
1498 update_notifying(chrc);
1500 notify_client_unref(client);
1503 static struct notify_client *notify_client_create(struct characteristic *chrc,
1506 struct notify_client *client;
1508 client = new0(struct notify_client, 1);
1509 client->chrc = chrc;
1510 client->owner = strdup(owner);
1511 if (!client->owner) {
1516 client->watch = g_dbus_add_disconnect_watch(btd_get_dbus_connection(),
1517 owner, notify_client_disconnect,
1519 if (!client->watch) {
1520 free(client->owner);
1525 return notify_client_ref(client);
1528 static bool match_notify_sender(const void *a, const void *b)
1530 const struct notify_client *client = a;
1531 const char *sender = b;
1533 return strcmp(client->owner, sender) == 0;
1536 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
1537 void gatt_characteristic_value_changed(struct notify_client *client, const uint8_t *data, uint16_t data_len, void *user_data)
1539 struct characteristic *chrc = user_data;
1540 char *chrc_path = strdup(chrc->path);
1541 dbus_int32_t result = 0;
1543 g_dbus_emit_signal_to_dest(btd_get_dbus_connection(),
1544 client->owner, chrc_path,
1545 GATT_CHARACTERISTIC_IFACE, "GattValueChanged",
1546 DBUS_TYPE_INT32, &result,
1547 DBUS_TYPE_STRING, &chrc_path,
1548 DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, data_len,
1557 static void notify_cb(uint16_t value_handle, const uint8_t *value,
1558 uint16_t length, void *user_data)
1560 struct async_dbus_op *op = user_data;
1561 struct notify_client *client = op->data;
1562 struct characteristic *chrc = client->chrc;
1565 * Even if the value didn't change, we want to send a PropertiesChanged
1566 * signal so that we propagate the notification/indication to
1569 gatt_db_attribute_reset(chrc->attr);
1570 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
1571 gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL,
1572 notify_characteristic_cb, chrc);
1574 gatt_characteristic_value_changed(client, value, length, chrc);
1576 gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL,
1577 write_characteristic_cb, chrc);
1581 static void create_notify_reply(struct async_dbus_op *op, bool success,
1589 err = att_ecode ? att_ecode : -ENOENT;
1591 async_dbus_op_reply(op, err, NULL, -1);
1594 static void register_notify_cb(uint16_t att_ecode, void *user_data)
1596 struct async_dbus_op *op = user_data;
1597 struct notify_client *client = op->data;
1598 struct characteristic *chrc = client->chrc;
1601 queue_remove(chrc->notify_clients, client);
1602 queue_remove(chrc->service->client->all_notify_clients, client);
1603 notify_client_free(client);
1605 create_notify_reply(op, false, att_ecode);
1610 if (!chrc->notifying) {
1611 chrc->notifying = true;
1612 g_dbus_emit_property_changed(btd_get_dbus_connection(),
1613 chrc->path, GATT_CHARACTERISTIC_IFACE,
1617 create_notify_reply(op, true, 0);
1620 static void notify_io_cb(uint16_t value_handle, const uint8_t *value,
1621 uint16_t length, void *user_data)
1625 struct notify_client *client = user_data;
1626 struct characteristic *chrc = client->chrc;
1629 /* Drop notification if the pipe is not ready */
1630 if (!chrc->notify_io || !chrc->notify_io->io)
1633 iov.iov_base = (void *) value;
1634 iov.iov_len = length;
1636 memset(&msg, 0, sizeof(msg));
1640 err = sendmsg(io_get_fd(chrc->notify_io->io), &msg, MSG_NOSIGNAL);
1642 error("sendmsg: %s", strerror(errno));
1645 static void register_notify_io_cb(uint16_t att_ecode, void *user_data)
1647 struct notify_client *client = user_data;
1648 struct characteristic *chrc = client->chrc;
1649 struct bt_gatt_client *gatt = chrc->service->client->gatt;
1652 queue_remove(chrc->notify_clients, client);
1653 notify_client_free(client);
1657 if (!bt_gatt_client_is_ready(gatt)) {
1658 if (!chrc->ready_id)
1659 chrc->ready_id = bt_gatt_client_ready_register(gatt,
1660 characteristic_ready,
1665 characteristic_ready(true, 0, chrc);
1668 static void notify_io_destroy(void *data)
1670 struct notify_client *client = data;
1672 if (queue_remove(client->chrc->notify_clients, client))
1673 notify_client_unref(client);
1676 static DBusMessage *characteristic_acquire_notify(DBusConnection *conn,
1677 DBusMessage *msg, void *user_data)
1679 struct characteristic *chrc = user_data;
1680 struct bt_gatt_client *gatt = chrc->service->client->gatt;
1681 const char *sender = dbus_message_get_sender(msg);
1682 struct notify_client *client;
1685 return btd_error_failed(msg, "Not connected");
1687 if (chrc->notify_io)
1688 return btd_error_not_permitted(msg, "Notify acquired");
1690 /* Each client can only have one active notify session. */
1691 if (!queue_isempty(chrc->notify_clients))
1692 return btd_error_in_progress(msg);
1694 if (!(chrc->props & BT_GATT_CHRC_PROP_NOTIFY))
1695 return btd_error_not_supported(msg);
1697 client = notify_client_create(chrc, sender);
1699 return btd_error_failed(msg, "Failed allocate notify session");
1701 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
1702 chrc->notify_io = new0(struct sock_io, 1);
1703 chrc->notify_io->data = client;
1704 chrc->notify_io->msg = dbus_message_ref(msg);
1705 chrc->notify_io->destroy = notify_io_destroy;
1707 client->notify_id = bt_gatt_client_register_notify(gatt,
1709 register_notify_io_cb,
1712 if (!client->notify_id) {
1713 notify_client_unref(client);
1714 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
1715 dbus_message_unref(chrc->notify_io->msg);
1716 g_free(chrc->notify_io);
1717 chrc->notify_io = NULL;
1719 return btd_error_failed(msg, "Failed to subscribe");
1722 queue_push_tail(chrc->notify_clients, client);
1724 #ifndef TIZEN_FEATURE_BLUEZ_MODIFY
1725 chrc->notify_io = new0(struct sock_io, 1);
1726 chrc->notify_io->data = client;
1727 chrc->notify_io->msg = dbus_message_ref(msg);
1728 chrc->notify_io->destroy = notify_io_destroy;
1734 static DBusMessage *characteristic_start_notify(DBusConnection *conn,
1735 DBusMessage *msg, void *user_data)
1737 struct characteristic *chrc = user_data;
1738 struct bt_gatt_client *gatt = chrc->service->client->gatt;
1739 const char *sender = dbus_message_get_sender(msg);
1740 struct async_dbus_op *op;
1741 struct notify_client *client;
1742 struct btd_device *device = chrc->service->client->device;
1744 if (device_is_disconnecting(device)) {
1745 error("Device is disconnecting. StartNotify is not allowed.");
1746 return btd_error_not_connected(msg);
1749 if (chrc->notify_io)
1750 return btd_error_not_permitted(msg, "Notify acquired");
1752 if (!(chrc->props & BT_GATT_CHRC_PROP_NOTIFY ||
1753 chrc->props & BT_GATT_CHRC_PROP_INDICATE))
1754 return btd_error_not_supported(msg);
1756 /* Each client can only have one active notify session. */
1757 client = queue_find(chrc->notify_clients, match_notify_sender, sender);
1759 return client->notify_id ?
1760 g_dbus_create_reply(msg, DBUS_TYPE_INVALID) :
1761 btd_error_in_progress(msg);
1763 client = notify_client_create(chrc, sender);
1765 return btd_error_failed(msg, "Failed allocate notify session");
1767 queue_push_tail(chrc->notify_clients, client);
1768 queue_push_tail(chrc->service->client->all_notify_clients, client);
1771 * If the device is currently not connected, return success. We will
1772 * automatically try and register all clients when a GATT client becomes
1778 reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1783 * Clean up and respond with an error instead of timing out to
1784 * avoid any ambiguities.
1786 error("Failed to construct D-Bus message reply");
1790 op = async_dbus_op_new(msg, client);
1792 client->notify_id = bt_gatt_client_register_notify(gatt,
1794 register_notify_cb, notify_cb,
1795 op, async_dbus_op_free);
1796 if (client->notify_id)
1799 async_dbus_op_free(op);
1802 queue_remove(chrc->notify_clients, client);
1803 queue_remove(chrc->service->client->all_notify_clients, client);
1805 /* Directly free the client */
1806 notify_client_free(client);
1808 return btd_error_failed(msg, "Failed to register notify session");
1811 static DBusMessage *characteristic_stop_notify(DBusConnection *conn,
1812 DBusMessage *msg, void *user_data)
1814 struct characteristic *chrc = user_data;
1815 struct bt_gatt_client *gatt = chrc->service->client->gatt;
1816 const char *sender = dbus_message_get_sender(msg);
1817 struct notify_client *client;
1819 if (chrc->notify_io) {
1820 destroy_sock(chrc, chrc->notify_io->io);
1821 return dbus_message_new_method_return(msg);
1825 client = queue_remove_if(chrc->notify_clients, match_notify_sender,
1828 return btd_error_failed(msg, "No notify session started");
1830 queue_remove(chrc->service->client->all_notify_clients, client);
1831 bt_gatt_client_unregister_notify(gatt, client->notify_id);
1832 update_notifying(chrc);
1834 notify_client_unref(client);
1836 return dbus_message_new_method_return(msg);
1839 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
1840 static void append_desc_path(void *data, void *user_data)
1842 struct descriptor *desc = data;
1843 DBusMessageIter *array = user_data;
1845 dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH,
1849 static gboolean characteristic_get_descriptors(
1850 const GDBusPropertyTable *property,
1851 DBusMessageIter *iter, void *data)
1853 struct characteristic *chrc = data;
1854 DBusMessageIter array;
1856 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array);
1858 queue_foreach(chrc->descs, append_desc_path, &array);
1860 dbus_message_iter_close_container(iter, &array);
1866 static const GDBusPropertyTable characteristic_properties[] = {
1867 { "UUID", "s", characteristic_get_uuid, NULL, NULL },
1868 { "Service", "o", characteristic_get_service, NULL, NULL },
1869 { "Value", "ay", characteristic_get_value, NULL,
1870 characteristic_value_exists },
1871 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
1872 { "ChangedValue", "ay", characteristic_get_value, NULL,
1873 characteristic_value_exists },
1875 { "Notifying", "b", characteristic_get_notifying, NULL,
1876 characteristic_notifying_exists },
1877 { "Flags", "as", characteristic_get_flags, NULL, NULL },
1878 { "WriteAcquired", "b", characteristic_get_write_acquired, NULL,
1879 characteristic_write_acquired_exists },
1880 { "NotifyAcquired", "b", characteristic_get_notify_acquired, NULL,
1881 characteristic_notify_acquired_exists },
1882 { "MTU", "q", characteristic_get_mtu, NULL, NULL },
1883 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
1884 { "Descriptors", "ao", characteristic_get_descriptors },
1889 static const GDBusMethodTable characteristic_methods[] = {
1890 { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
1891 GDBUS_ARGS({ "value", "ay" }),
1892 characteristic_read_value) },
1893 { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
1894 { "options", "a{sv}" }),
1896 characteristic_write_value) },
1897 { GDBUS_ASYNC_METHOD("AcquireWrite",
1898 GDBUS_ARGS({ "options", "a{sv}" }),
1899 GDBUS_ARGS({ "fd", "h" },
1901 characteristic_acquire_write) },
1902 { GDBUS_ASYNC_METHOD("AcquireNotify",
1903 GDBUS_ARGS({ "options", "a{sv}" }),
1904 GDBUS_ARGS({ "fd", "h" },
1906 characteristic_acquire_notify) },
1907 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
1908 { GDBUS_ASYNC_METHOD("WriteValuebyType",
1909 GDBUS_ARGS({ "type", "y" }, { "value", "ay" },
1910 { "options", "a{sv}" }),
1912 characteristic_write_value_by_type) },
1914 { GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL,
1915 characteristic_start_notify) },
1916 { GDBUS_METHOD("StopNotify", NULL, NULL,
1917 characteristic_stop_notify) },
1921 static void remove_client(void *data)
1923 struct notify_client *ntfy_client = data;
1924 struct btd_gatt_client *client = ntfy_client->chrc->service->client;
1926 queue_remove(client->all_notify_clients, ntfy_client);
1928 notify_client_unref(ntfy_client);
1931 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
1932 static const GDBusSignalTable service_signals[] = {
1933 { GDBUS_SIGNAL("GattServiceAdded",
1934 GDBUS_ARGS({ "Service Path","s"})) },
1937 static const GDBusSignalTable characteristic_signals[] = {
1938 { GDBUS_SIGNAL("GattValueChanged",
1939 GDBUS_ARGS({ "Result", "i"},
1940 { "Characteristic Path","s"},
1941 { "GattData", "ay"})) },
1946 static void characteristic_free(void *data)
1948 struct characteristic *chrc = data;
1949 struct bt_gatt_client *gatt = chrc->service->client->gatt;
1952 /* List should be empty here */
1953 queue_destroy(chrc->descs, NULL);
1955 if (chrc->write_io) {
1956 queue_remove(chrc->service->client->ios, chrc->write_io->io);
1957 sock_io_destroy(chrc->write_io);
1960 if (chrc->notify_io) {
1961 queue_remove(chrc->service->client->ios, chrc->notify_io->io);
1962 sock_io_destroy(chrc->notify_io);
1965 queue_destroy(chrc->notify_clients, remove_client);
1967 att = bt_gatt_client_get_att(gatt);
1969 bt_att_unregister_exchange(att, chrc->exchange_id);
1975 static void att_exchange(uint16_t mtu, void *user_data)
1977 struct characteristic *chrc = user_data;
1979 g_dbus_emit_property_changed(btd_get_dbus_connection(), chrc->path,
1980 GATT_CHARACTERISTIC_IFACE, "MTU");
1983 static struct characteristic *characteristic_create(
1984 struct gatt_db_attribute *attr,
1985 struct service *service)
1987 struct characteristic *chrc;
1988 struct bt_gatt_client *gatt = service->client->gatt;
1992 chrc = new0(struct characteristic, 1);
1993 chrc->descs = queue_new();
1994 chrc->notify_clients = queue_new();
1995 chrc->service = service;
1997 #ifndef TIZEN_FEATURE_BLUEZ_MODIFY
1998 gatt_db_attribute_get_char_data(attr, &chrc->handle,
1999 &chrc->value_handle,
2004 if (!gatt_db_attribute_get_char_data(attr, &chrc->handle,
2005 &chrc->value_handle,
2009 queue_destroy(chrc->descs, NULL);
2010 queue_destroy(chrc->notify_clients, NULL);
2016 chrc->attr = gatt_db_get_attribute(service->client->db,
2017 chrc->value_handle);
2019 error("Attribute 0x%04x not found", chrc->value_handle);
2020 characteristic_free(chrc);
2024 bt_uuid_to_uuid128(&uuid, &chrc->uuid);
2026 chrc->path = g_strdup_printf("%s/char%04x", service->path,
2029 if (!g_dbus_register_interface(btd_get_dbus_connection(), chrc->path,
2030 GATT_CHARACTERISTIC_IFACE,
2031 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
2032 characteristic_methods, characteristic_signals,
2034 characteristic_methods, NULL,
2036 characteristic_properties,
2037 chrc, characteristic_free)) {
2038 error("Unable to register GATT characteristic with handle "
2039 "0x%04x", chrc->handle);
2040 characteristic_free(chrc);
2045 att = bt_gatt_client_get_att(gatt);
2047 chrc->exchange_id = bt_att_register_exchange(att, att_exchange,
2050 DBG("Exported GATT characteristic: %s", chrc->path);
2055 static void unregister_characteristic(void *data)
2057 struct characteristic *chrc = data;
2058 struct bt_gatt_client *gatt = chrc->service->client->gatt;
2060 DBG("Removing GATT characteristic: %s", chrc->path);
2063 bt_gatt_client_cancel(gatt, chrc->read_op->id);
2066 bt_gatt_client_cancel(gatt, chrc->write_op->id);
2068 queue_remove_all(chrc->descs, NULL, NULL, unregister_descriptor);
2070 g_dbus_unregister_interface(btd_get_dbus_connection(), chrc->path,
2071 GATT_CHARACTERISTIC_IFACE);
2074 static gboolean service_get_uuid(const GDBusPropertyTable *property,
2075 DBusMessageIter *iter, void *data)
2077 char uuid[MAX_LEN_UUID_STR + 1];
2078 const char *ptr = uuid;
2079 struct service *service = data;
2081 bt_uuid_to_string(&service->uuid, uuid, sizeof(uuid));
2082 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
2087 static gboolean service_get_device(const GDBusPropertyTable *property,
2088 DBusMessageIter *iter, void *data)
2090 struct service *service = data;
2091 const char *str = device_get_path(service->client->device);
2093 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &str);
2098 static gboolean service_get_primary(const GDBusPropertyTable *property,
2099 DBusMessageIter *iter, void *data)
2101 struct service *service = data;
2102 dbus_bool_t primary;
2104 primary = service->primary ? TRUE : FALSE;
2106 dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &primary);
2111 static void append_incl_service_path(void *data, void *user_data)
2113 struct service *incl_service = data;
2114 DBusMessageIter *array = user_data;
2116 dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH,
2117 &incl_service->path);
2120 static gboolean service_get_includes(const GDBusPropertyTable *property,
2121 DBusMessageIter *iter, void *data)
2123 struct service *service = data;
2124 DBusMessageIter array;
2126 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{o}", &array);
2128 queue_foreach(service->incl_services, append_incl_service_path, &array);
2130 dbus_message_iter_close_container(iter, &array);
2136 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
2137 static void append_chrc_path(void *data, void *user_data)
2139 struct characteristic *chrc = data;
2140 DBusMessageIter *array = user_data;
2142 dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH,
2146 static gboolean service_get_characteristics(const GDBusPropertyTable *property,
2147 DBusMessageIter *iter, void *data)
2149 struct service *service = data;
2150 DBusMessageIter array;
2152 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array);
2154 if (service->chrcs_ready)
2155 queue_foreach(service->chrcs, append_chrc_path, &array);
2157 dbus_message_iter_close_container(iter, &array);
2163 static const GDBusPropertyTable service_properties[] = {
2164 { "UUID", "s", service_get_uuid },
2165 { "Device", "o", service_get_device },
2166 { "Primary", "b", service_get_primary },
2167 { "Includes", "ao", service_get_includes },
2168 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
2169 { "Characteristics", "ao", service_get_characteristics },
2174 static void service_free(void *data)
2176 struct service *service = data;
2178 queue_destroy(service->chrcs, NULL); /* List should be empty here */
2179 queue_destroy(service->incl_services, NULL);
2180 g_free(service->path);
2184 static struct service *service_create(struct gatt_db_attribute *attr,
2185 struct btd_gatt_client *client)
2187 struct service *service;
2188 const char *device_path = device_get_path(client->device);
2191 service = new0(struct service, 1);
2192 service->chrcs = queue_new();
2193 service->incl_services = queue_new();
2194 service->client = client;
2196 gatt_db_attribute_get_service_data(attr, &service->start_handle,
2197 &service->end_handle,
2200 bt_uuid_to_uuid128(&uuid, &service->uuid);
2202 service->path = g_strdup_printf("%s/service%04x", device_path,
2203 service->start_handle);
2205 if (!g_dbus_register_interface(btd_get_dbus_connection(), service->path,
2207 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
2208 NULL, service_signals,
2213 service, service_free)) {
2214 error("Unable to register GATT service with handle 0x%04x for "
2216 service->start_handle,
2218 service_free(service);
2223 DBG("Exported GATT service: %s", service->path);
2225 /* Set service active so we can skip discovering next time */
2226 gatt_db_service_set_active(attr, true);
2228 /* Mark the service as claimed since it going to be exported */
2229 gatt_db_service_set_claimed(attr, true);
2234 static void on_service_removed(void *data, void *user_data)
2236 struct service *service = data;
2237 struct service *removed_service = user_data;
2239 if (queue_remove(service->incl_services, removed_service))
2240 g_dbus_emit_property_changed(btd_get_dbus_connection(),
2246 static void unregister_service(void *data)
2248 struct service *service = data;
2249 struct btd_gatt_client *client = service->client;
2251 DBG("Removing GATT service: %s", service->path);
2253 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
2254 if (service->idle_id)
2255 g_source_remove(service->idle_id);
2258 queue_remove_all(service->chrcs, NULL, NULL, unregister_characteristic);
2259 queue_remove_all(service->incl_services, NULL, NULL, NULL);
2261 queue_foreach(client->services, on_service_removed, service);
2263 g_dbus_unregister_interface(btd_get_dbus_connection(), service->path,
2264 GATT_SERVICE_IFACE);
2267 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
2268 static void notify_service_added(struct service *service)
2272 if (service == NULL) {
2273 error("service is NULL");
2277 if (service->path == NULL) {
2278 error("service->path is NULL");
2281 if (!service->chrcs_ready) {
2282 error("service is not ready");
2286 svc_path = g_strdup(service->path);
2288 g_dbus_emit_signal(btd_get_dbus_connection(), service->path,
2289 GATT_SERVICE_IFACE, "GattServiceAdded",
2290 DBUS_TYPE_STRING, &svc_path,
2298 static void notify_chrcs(struct service *service)
2301 if (service->chrcs_ready ||
2302 !queue_isempty(service->pending_ext_props))
2305 service->chrcs_ready = true;
2307 g_dbus_emit_property_changed(btd_get_dbus_connection(), service->path,
2310 if (service->primary == true) {
2311 DBG("Notify Service Added");
2312 notify_service_added(service);
2317 struct export_data {
2322 static void export_desc(struct gatt_db_attribute *attr, void *user_data)
2324 struct descriptor *desc;
2325 struct export_data *data = user_data;
2326 struct characteristic *charac = data->root;
2331 desc = descriptor_create(attr, charac);
2333 data->failed = true;
2337 queue_push_tail(charac->descs, desc);
2340 static bool create_descriptors(struct gatt_db_attribute *attr,
2341 struct characteristic *charac)
2343 struct export_data data;
2346 data.failed = false;
2348 gatt_db_service_foreach_desc(attr, export_desc, &data);
2350 return !data.failed;
2353 static void export_char(struct gatt_db_attribute *attr, void *user_data)
2355 struct characteristic *charac;
2356 struct export_data *data = user_data;
2357 struct service *service = data->root;
2362 charac = characteristic_create(attr, service);
2366 if (!create_descriptors(attr, charac)) {
2367 unregister_characteristic(charac);
2371 queue_push_tail(service->chrcs, charac);
2373 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
2374 if (charac->ext_props_handle)
2375 queue_push_tail(service->pending_ext_props, charac);
2381 data->failed = true;
2384 static bool create_characteristics(struct gatt_db_attribute *attr,
2385 struct service *service)
2387 struct export_data data;
2389 data.root = service;
2390 data.failed = false;
2392 gatt_db_service_foreach_char(attr, export_char, &data);
2394 return !data.failed;
2397 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
2398 static gboolean set_chrcs_ready(gpointer user_data)
2400 struct service *service = user_data;
2402 service->idle_id = 0;
2403 notify_chrcs(service);
2408 static void export_service(struct gatt_db_attribute *attr, void *user_data)
2410 struct btd_gatt_client *client = user_data;
2411 struct service *service;
2413 if (gatt_db_service_get_claimed(attr))
2416 service = service_create(attr, client);
2420 if (!create_characteristics(attr, service)) {
2421 error("Exporting characteristics failed");
2422 unregister_service(service);
2426 queue_push_tail(client->services, service);
2428 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
2430 * Asynchronously update the "Characteristics" property of the service.
2431 * If there are any pending reads to obtain the value of the "Extended
2432 * Properties" descriptor then wait until they are complete.
2434 if (!service->chrcs_ready && queue_isempty(service->pending_ext_props))
2435 service->idle_id = g_idle_add(set_chrcs_ready, service);
2439 static bool match_service_handle(const void *a, const void *b)
2441 const struct service *service = a;
2442 uint16_t start_handle = PTR_TO_UINT(b);
2444 return service->start_handle == start_handle;
2447 struct update_incl_data {
2448 struct service *service;
2452 static void update_included_service(struct gatt_db_attribute *attrib,
2455 struct update_incl_data *update_data = user_data;
2456 struct btd_gatt_client *client = update_data->service->client;
2457 struct service *service = update_data->service;
2458 struct service *incl_service;
2459 uint16_t start_handle;
2461 gatt_db_attribute_get_incl_data(attrib, NULL, &start_handle, NULL);
2463 incl_service = queue_find(client->services, match_service_handle,
2464 UINT_TO_PTR(start_handle));
2469 /* Check if service is already on list */
2470 if (queue_find(service->incl_services, NULL, incl_service))
2473 queue_push_tail(service->incl_services, incl_service);
2474 update_data->changed = true;
2477 static void update_included_services(void *data, void *user_data)
2479 struct btd_gatt_client *client = user_data;
2480 struct service *service = data;
2481 struct gatt_db_attribute *attr;
2482 struct update_incl_data inc_data = {
2487 attr = gatt_db_get_attribute(client->db, service->start_handle);
2488 gatt_db_service_foreach_incl(attr, update_included_service, &inc_data);
2490 if (inc_data.changed)
2491 g_dbus_emit_property_changed(btd_get_dbus_connection(),
2497 static void create_services(struct btd_gatt_client *client)
2499 DBG("Exporting objects for GATT services: %s", client->devaddr);
2501 gatt_db_foreach_service(client->db, NULL, export_service, client);
2503 queue_foreach(client->services, update_included_services, client);
2506 struct btd_gatt_client *btd_gatt_client_new(struct btd_device *device)
2508 struct btd_gatt_client *client;
2514 db = btd_device_get_gatt_db(device);
2518 client = new0(struct btd_gatt_client, 1);
2519 client->services = queue_new();
2520 client->all_notify_clients = queue_new();
2521 client->ios = queue_new();
2522 client->device = device;
2523 ba2str(device_get_address(device), client->devaddr);
2525 client->db = gatt_db_ref(db);
2530 void btd_gatt_client_destroy(struct btd_gatt_client *client)
2535 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
2536 if (client->wait_charcs_id)
2537 g_source_remove(client->wait_charcs_id);
2539 queue_destroy(client->services, unregister_service);
2540 queue_destroy(client->all_notify_clients, NULL);
2541 queue_destroy(client->ios, NULL);
2542 bt_gatt_client_unref(client->gatt);
2543 gatt_db_unref(client->db);
2547 static void register_notify(void *data, void *user_data)
2549 struct notify_client *notify_client = data;
2550 struct btd_gatt_client *client = user_data;
2551 struct async_dbus_op *op;
2553 DBG("Re-register subscribed notification client");
2555 op = new0(struct async_dbus_op, 1);
2556 op->data = notify_client;
2558 notify_client->notify_id = bt_gatt_client_register_notify(client->gatt,
2559 notify_client->chrc->value_handle,
2560 register_notify_cb, notify_cb,
2561 op, async_dbus_op_free);
2562 if (notify_client->notify_id)
2565 async_dbus_op_free(op);
2567 DBG("Failed to re-register notification client");
2569 queue_remove(notify_client->chrc->notify_clients, notify_client);
2570 queue_remove(client->all_notify_clients, notify_client);
2572 notify_client_free(notify_client);
2575 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
2576 static void check_chrcs_ready(void *data, void *user_data)
2578 gboolean *chrcs_ready = user_data;
2579 struct service *service = data;
2582 * Return FALSE if charcteristics are not ready or if there is
2583 * any pending request to read char. extended properties exist.
2585 if (!service->chrcs_ready ||
2586 !queue_isempty(service->pending_ext_props))
2587 *chrcs_ready = FALSE;
2590 static gboolean check_all_chrcs_ready(gpointer user_data)
2592 struct btd_gatt_client *client = user_data;
2593 gboolean all_chrcs_ready = TRUE;
2594 static int count = 0;
2596 queue_foreach(client->services, check_chrcs_ready, &all_chrcs_ready);
2599 * By adding below condition, forcing to call check_chrcs_ready()
2600 * function to check whether all char./extended properties are ready.
2601 * Above function would be called max. for 500 times (Assuming more
2602 * no of services). Emit signal only when all characteristics are ready.
2604 if (all_chrcs_ready == FALSE && count < 500) {
2610 * There are chances when all of the primary services are not found,
2611 * instead service changed is received while reading REQs in progress,
2612 * so emit signal after service changed operation is completed (if any).
2614 if (bt_gatt_client_svc_changed_received(client->gatt))
2617 device_set_gatt_connected(client->device, TRUE);
2619 client->wait_charcs_id = 0;
2627 void btd_gatt_client_ready(struct btd_gatt_client *client)
2632 if (!client->gatt) {
2633 struct bt_gatt_client *gatt;
2635 gatt = btd_device_get_gatt_client(client->device);
2636 client->gatt = bt_gatt_client_clone(gatt);
2637 if (!client->gatt) {
2638 error("GATT client not initialized");
2643 client->ready = true;
2645 DBG("GATT client ready");
2647 create_services(client);
2649 DBG("Features 0x%02x", client->features);
2651 if (!client->features) {
2652 client->features = bt_gatt_client_get_features(client->gatt);
2653 DBG("Update Features 0x%02x", client->features);
2654 if (client->features & BT_GATT_CHRC_CLI_FEAT_EATT)
2655 btd_gatt_client_eatt_connect(client);
2658 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
2660 * In case of more number of services and services having
2661 * characteristics extended properties; GattConnected signal
2662 * should be emitted only after all the characteristics are ready.
2663 * This piece of code checks all the characteristics periodically and
2664 * emit the signal if characteristics are ready.
2666 if (client->wait_charcs_id > 0)
2667 g_source_remove(client->wait_charcs_id);
2669 client->wait_charcs_id = g_timeout_add(10,
2670 check_all_chrcs_ready, client);
2675 static void eatt_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
2677 struct btd_gatt_client *client = user_data;
2682 device_attach_att(client->device, io);
2685 void btd_gatt_client_eatt_connect(struct btd_gatt_client *client)
2687 struct bt_att *att = bt_gatt_client_get_att(client->gatt);
2688 struct btd_device *dev = client->device;
2689 struct btd_adapter *adapter = device_get_adapter(dev);
2691 GError *gerr = NULL;
2695 if (!(client->features & BT_GATT_CHRC_CLI_FEAT_EATT))
2697 if (bt_att_get_channels(att) == btd_opts.gatt_channels)
2700 ba2str(device_get_address(dev), addr);
2702 for (i = bt_att_get_channels(att); i < btd_opts.gatt_channels; i++) {
2703 int defer_timeout = i + 1 < btd_opts.gatt_channels ? 1 : 0;
2705 DBG("Connection attempt to: %s defer %s", addr,
2706 defer_timeout ? "true" : "false");
2708 /* Attempt to connect using the Ext-Flowctl */
2709 io = bt_io_connect(eatt_connect_cb, client, NULL, &gerr,
2710 BT_IO_OPT_SOURCE_BDADDR,
2711 btd_adapter_get_address(adapter),
2712 BT_IO_OPT_SOURCE_TYPE,
2713 btd_adapter_get_address_type(adapter),
2714 BT_IO_OPT_DEST_BDADDR,
2715 device_get_address(dev),
2716 BT_IO_OPT_DEST_TYPE,
2717 device_get_le_address_type(dev),
2718 BT_IO_OPT_MODE, BT_IO_MODE_EXT_FLOWCTL,
2719 BT_IO_OPT_PSM, BT_ATT_EATT_PSM,
2720 BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
2721 BT_IO_OPT_MTU, btd_opts.gatt_mtu,
2722 BT_IO_OPT_DEFER_TIMEOUT, defer_timeout,
2727 /* Fallback to legacy LE Mode */
2728 io = bt_io_connect(eatt_connect_cb, client, NULL, &gerr,
2729 BT_IO_OPT_SOURCE_BDADDR,
2730 btd_adapter_get_address(adapter),
2731 BT_IO_OPT_SOURCE_TYPE,
2732 btd_adapter_get_address_type(adapter),
2733 BT_IO_OPT_DEST_BDADDR,
2734 device_get_address(dev),
2735 BT_IO_OPT_DEST_TYPE,
2736 device_get_le_address_type(dev),
2737 BT_IO_OPT_PSM, BT_ATT_EATT_PSM,
2738 BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
2739 BT_IO_OPT_MTU, btd_opts.gatt_mtu,
2742 error("EATT bt_io_connect(%s): %s", addr,
2749 g_io_channel_unref(io);
2753 void btd_gatt_client_connected(struct btd_gatt_client *client)
2755 struct bt_gatt_client *gatt;
2757 gatt = btd_device_get_gatt_client(client->device);
2759 error("GATT client not initialized");
2763 DBG("Device connected.");
2765 bt_gatt_client_unref(client->gatt);
2766 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
2767 /* The Client data is not getting updated
2768 * while clone logic is used, because of which
2769 * the notification callbacks and other debug callbacks
2770 * are not getting set properly, untill the clone logic
2771 * is fixed, the ref logic is used */
2772 client->gatt = bt_gatt_client_ref(gatt);
2774 client->gatt = bt_gatt_client_clone(gatt);
2777 * Services have already been created before. Re-enable notifications
2778 * for any pre-registered notification sessions.
2780 queue_foreach(client->all_notify_clients, register_notify, client);
2783 void btd_gatt_client_service_added(struct btd_gatt_client *client,
2784 struct gatt_db_attribute *attrib)
2786 if (!client || !attrib || !client->ready)
2789 export_service(attrib, client);
2791 queue_foreach(client->services, update_included_services, client);
2794 void btd_gatt_client_service_removed(struct btd_gatt_client *client,
2795 struct gatt_db_attribute *attrib)
2797 uint16_t start_handle, end_handle;
2799 if (!client || !attrib || !client->ready)
2802 gatt_db_attribute_get_service_handles(attrib, &start_handle,
2805 DBG("GATT Services Removed - start: 0x%04x, end: 0x%04x", start_handle,
2807 queue_remove_all(client->services, match_service_handle,
2808 UINT_TO_PTR(start_handle),
2809 unregister_service);
2812 static void clear_notify_id(void *data, void *user_data)
2814 struct notify_client *client = data;
2816 client->notify_id = 0;
2819 static void client_shutdown(void *data)
2824 void btd_gatt_client_disconnected(struct btd_gatt_client *client)
2826 if (!client || !client->gatt)
2829 DBG("Device disconnected. Cleaning up.");
2831 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
2832 if (client->wait_charcs_id) {
2833 g_source_remove(client->wait_charcs_id);
2834 client->wait_charcs_id = 0;
2837 queue_remove_all(client->ios, NULL, NULL, client_shutdown);
2840 * TODO: Once GATT over BR/EDR is properly supported, we should pass the
2841 * correct bdaddr_type based on the transport over which GATT is being
2844 queue_foreach(client->all_notify_clients, clear_notify_id, NULL);
2846 bt_gatt_client_unref(client->gatt);
2847 client->gatt = NULL;
2850 struct foreach_service_data {
2851 btd_gatt_client_service_path_t func;
2855 static void client_service_foreach(void *data, void *user_data)
2857 struct service *service = data;
2858 struct foreach_service_data *foreach_data = user_data;
2860 foreach_data->func(service->path, foreach_data->user_data);
2863 void btd_gatt_client_foreach_service(struct btd_gatt_client *client,
2864 btd_gatt_client_service_path_t func,
2867 struct foreach_service_data data;
2873 data.user_data = user_data;
2875 queue_foreach(client->services, client_service_foreach, &data);