diff --git a/src/device.c b/src/device.c
index 999f09f..dc362d9 100644
--- a/src/device.c
+++ b/src/device.c
#define RSSI_THRESHOLD 8
+#define GATT_PRIM_SVC_UUID_STR "2800"
+#define GATT_SND_SVC_UUID_STR "2801"
+#define GATT_INCLUDE_UUID_STR "2802"
+#define GATT_CHARAC_UUID_STR "2803"
+
static DBusConnection *dbus_conn = NULL;
static unsigned service_state_cb_id;
g_key_file_free(key_file);
}
+struct gatt_saver {
+ struct btd_device *device;
+ GKeyFile *key_file;
+};
+
+static void store_desc(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct gatt_saver *saver = user_data;
+ GKeyFile *key_file = saver->key_file;
+ char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR];
+ const bt_uuid_t *uuid;
+ uint16_t handle_num;
+
+ handle_num = gatt_db_attribute_get_handle(attr);
+ sprintf(handle, "%04hx", handle_num);
+
+ uuid = gatt_db_attribute_get_type(attr);
+ bt_uuid_to_string(uuid, uuid_str, sizeof(uuid_str));
+ sprintf(value, "%s", uuid_str);
+ g_key_file_set_string(key_file, "Attributes", handle, value);
+}
+
+static void store_chrc(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct gatt_saver *saver = user_data;
+ GKeyFile *key_file = saver->key_file;
+ char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR];
+ uint16_t handle_num, value_handle;
+ uint8_t properties;
+ bt_uuid_t uuid;
+
+ if (!gatt_db_attribute_get_char_data(attr, &handle_num, &value_handle,
+ &properties, &uuid)) {
+ warn("Error storing characteristic - can't get data");
+ return;
+ }
+
+ sprintf(handle, "%04hx", handle_num);
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+ sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hx:%s", value_handle,
+ properties, uuid_str);
+ g_key_file_set_string(key_file, "Attributes", handle, value);
+
+ gatt_db_service_foreach_desc(attr, store_desc, saver);
+}
+
+static void store_incl(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct gatt_saver *saver = user_data;
+ GKeyFile *key_file = saver->key_file;
+ struct gatt_db_attribute *service;
+ char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR];
+ uint16_t handle_num, start, end;
+ bt_uuid_t uuid;
+
+ if (!gatt_db_attribute_get_incl_data(attr, &handle_num, &start, &end)) {
+ warn("Error storing included service - can't get data");
+ return;
+ }
+
+ service = gatt_db_get_attribute(saver->device->db, start);
+ if (!service) {
+ warn("Error storing included service - can't find it");
+ return;
+ }
+
+ sprintf(handle, "%04hx", handle_num);
+
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+ sprintf(value, GATT_INCLUDE_UUID_STR ":%04hx:%04hx:%s", start,
+ end, uuid_str);
+
+ g_key_file_set_string(key_file, "Attributes", handle, value);
+}
+
+static void store_service(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct gatt_saver *saver = user_data;
+ GKeyFile *key_file = saver->key_file;
+ char uuid_str[MAX_LEN_UUID_STR], handle[6], value[256];
+ uint16_t start, end;
+ bt_uuid_t uuid;
+ bool primary;
+ char *type;
+
+ if (!gatt_db_attribute_get_service_data(attr, &start, &end, &primary,
+ &uuid)) {
+ warn("Error storing service - can't get data");
+ return;
+ }
+
+ sprintf(handle, "%04hx", start);
+
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+
+ if (primary)
+ type = GATT_PRIM_SVC_UUID_STR;
+ else
+ type = GATT_SND_SVC_UUID_STR;
+
+ sprintf(value, "%s:%04hx:%s", type, end, uuid_str);
+
+ g_key_file_set_string(key_file, "Attributes", handle, value);
+
+ gatt_db_service_foreach_incl(attr, store_incl, saver);
+ gatt_db_service_foreach_char(attr, store_chrc, saver);
+}
+
+static void store_gatt_db(struct btd_device *device)
+{
+ struct btd_adapter *adapter = device->adapter;
+ char filename[PATH_MAX];
+ char src_addr[18], dst_addr[18];
+ GKeyFile *key_file;
+ char *data;
+ gsize length = 0;
+ struct gatt_saver saver;
+
+ if (device_address_is_private(device)) {
+ warn("Can't store GATT db for private addressed device %s",
+ device->path);
+ return;
+ }
+
+ ba2str(btd_adapter_get_address(adapter), src_addr);
+ ba2str(&device->bdaddr, dst_addr);
+
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", src_addr,
+ dst_addr);
+ create_file(filename, S_IRUSR | S_IWUSR);
+
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+ saver.key_file = key_file;
+ saver.device = device;
+
+ gatt_db_foreach_service(device->db, NULL, store_service, &saver);
+
+ data = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(filename, data, length, NULL);
+
+ g_free(data);
+ g_key_file_free(key_file);
+}
+
+
static void browse_request_complete(struct browse_req *req, uint8_t bdaddr_type,
int err)
{
if (!dev->temporary)
store_device_info(dev);
- if (bdaddr_type != BDADDR_BREDR && err == 0)
+ if (bdaddr_type != BDADDR_BREDR && err == 0) {
store_services(dev);
+ store_gatt_db(dev);
+ }
if (!req)
return;
*new_services = g_slist_append(*new_services, prim);
}
+static int load_desc(char *handle, char *value,
+ struct gatt_db_attribute *service)
+{
+ char uuid_str[MAX_LEN_UUID_STR];
+ struct gatt_db_attribute *att;
+ uint16_t handle_int;
+ bt_uuid_t uuid;
+
+ if (sscanf(handle, "%04hx", &handle_int) != 1)
+ return -EIO;
+
+ if (sscanf(value, "%s", uuid_str) != 1)
+ return -EIO;
+
+ bt_string_to_uuid(&uuid, uuid_str);
+
+ DBG("loading descriptor handle: 0x%04x, uuid: %s", handle_int,
+ uuid_str);
+
+ att = gatt_db_service_insert_descriptor(service, handle_int, &uuid,
+ 0, NULL, NULL, NULL);
+ if (!att || gatt_db_attribute_get_handle(att) != handle_int) {
+ warn("loading descriptor to db failed");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int load_chrc(char *handle, char *value,
+ struct gatt_db_attribute *service)
+{
+ uint16_t properties, value_handle, handle_int;
+ char uuid_str[MAX_LEN_UUID_STR];
+ struct gatt_db_attribute *att;
+ bt_uuid_t uuid;
+
+ if (sscanf(handle, "%04hx", &handle_int) != 1)
+ return -EIO;
+
+ if (sscanf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hx:%s", &value_handle,
+ &properties, uuid_str) != 3)
+ return -EIO;
+
+ bt_string_to_uuid(&uuid, uuid_str);
+
+ /* Log debug message. */
+ DBG("loading characteristic handle: 0x%04x, value handle: 0x%04x,"
+ " properties 0x%04x uuid: %s", handle_int,
+ value_handle, properties, uuid_str);
+
+ att = gatt_db_service_insert_characteristic(service, value_handle,
+ &uuid, 0, properties,
+ NULL, NULL, NULL);
+ if (!att || gatt_db_attribute_get_handle(att) != value_handle) {
+ warn("saving characteristic to db failed");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int load_incl(struct gatt_db *db, char *handle, char *value,
+ struct gatt_db_attribute *service)
+{
+ char uuid_str[MAX_LEN_UUID_STR];
+ struct gatt_db_attribute *att;
+ uint16_t start, end;
+
+ if (sscanf(handle, "%04hx", &start) != 1)
+ return -EIO;
+
+ if (sscanf(value, GATT_INCLUDE_UUID_STR ":%04hx:%04hx:%s", &start, &end,
+ uuid_str) != 3)
+ return -EIO;
+
+ /* Log debug message. */
+ DBG("loading included service: 0x%04x, end: 0x%04x, uuid: %s", start,
+ end, uuid_str);
+
+ att = gatt_db_get_attribute(db, start);
+ if (!att) {
+ warn("saving included service to db failed - no such service");
+ return -EIO;
+ }
+
+ att = gatt_db_service_add_included(service, att);
+ if (!att) {
+ warn("saving included service to db failed");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int load_service(struct gatt_db *db, char *handle, char *value)
+{
+ struct gatt_db_attribute *att;
+ uint16_t start, end;
+ char type[MAX_LEN_UUID_STR], uuid_str[MAX_LEN_UUID_STR];
+ bt_uuid_t uuid;
+ bool primary;
+
+ if (sscanf(handle, "%04hx", &start) != 1)
+ return -EIO;
+
+ if (sscanf(value, "%[^:]:%04hx:%s", type, &end, uuid_str) != 3)
+ return -EIO;
+
+ if (g_str_equal(type, GATT_PRIM_SVC_UUID_STR))
+ primary = true;
+ else if (g_str_equal(type, GATT_SND_SVC_UUID_STR))
+ primary = false;
+ else
+ return -EIO;
+
+ bt_string_to_uuid(&uuid, uuid_str);
+
+ /* Log debug message. */
+ DBG("loading service: 0x%04x, end: 0x%04x, uuid: %s",
+ start, end, uuid_str);
+
+ att = gatt_db_insert_service(db, start, &uuid, primary,
+ end - start + 1);
+ if (!att) {
+ DBG("ERROR saving service to db!");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int load_gatt_db_impl(GKeyFile *key_file, char **keys,
+ struct gatt_db *db)
+{
+ struct gatt_db_attribute *current_service;
+ char **handle, *value, type[MAX_LEN_UUID_STR];
+ int ret;
+
+ /* first load service definitions */
+ for (handle = keys; *handle; handle++) {
+ value = g_key_file_get_string(key_file, "Attributes", *handle,
+ NULL);
+
+ if (sscanf(value, "%[^:]:", type) != 1) {
+ warn("Missing Type in attribute definition");
+ g_free(value);
+ return -EIO;
+ }
+
+ if (g_str_equal(type, GATT_PRIM_SVC_UUID_STR) ||
+ g_str_equal(type, GATT_SND_SVC_UUID_STR)) {
+ ret = load_service(db, *handle, value);
+ if (ret) {
+ g_free(value);
+ return ret;
+ }
+ }
+
+ g_free(value);
+ }
+
+ current_service = NULL;
+ /* then fill them with data*/
+ for (handle = keys; *handle; handle++) {
+ value = g_key_file_get_string(key_file, "Attributes", *handle,
+ NULL);
+
+ if (sscanf(value, "%[^:]:", type) != 1) {
+ warn("Missing Type in attribute definition");
+ g_free(value);
+ return -EIO;
+ }
+
+ if (g_str_equal(type, GATT_PRIM_SVC_UUID_STR) ||
+ g_str_equal(type, GATT_SND_SVC_UUID_STR)) {
+ uint16_t tmp;
+ uint16_t start, end;
+ bool primary;
+ bt_uuid_t uuid;
+ char uuid_str[MAX_LEN_UUID_STR];
+
+ if (sscanf(*handle, "%04hx", &tmp) != 1) {
+ warn("Unable to parse attribute handle");
+ g_free(value);
+ return -EIO;
+ }
+
+ if (current_service)
+ gatt_db_service_set_active(current_service,
+ true);
+
+ current_service = gatt_db_get_attribute(db, tmp);
+
+ gatt_db_attribute_get_service_data(current_service,
+ &start, &end,
+ &primary, &uuid);
+
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+ } else if (g_str_equal(type, GATT_INCLUDE_UUID_STR)) {
+ ret = load_incl(db, *handle, value, current_service);
+ } else if (g_str_equal(type, GATT_CHARAC_UUID_STR)) {
+ ret = load_chrc(*handle, value, current_service);
+ } else {
+ ret = load_desc(*handle, value, current_service);
+ }
+
+ g_free(value);
+ if (ret)
+ return ret;
+ }
+
+ if (current_service)
+ gatt_db_service_set_active(current_service, true);
+
+ return 0;
+}
+
+static void load_gatt_db(struct btd_device *device, const char *local,
+ const char *peer)
+{
+ char **keys, filename[PATH_MAX];
+ GKeyFile *key_file;
+
+ DBG("Restoring %s gatt database from file", peer);
+
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer);
+
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, filename, 0, NULL);
+ keys = g_key_file_get_keys(key_file, "Attributes", NULL, NULL);
+
+ if (!keys) {
+ warn("No cache for %s", peer);
+ g_key_file_free(key_file);
+ return;
+ }
+
+ if (load_gatt_db_impl(key_file, keys, device->db))
+ warn("Unable to load gatt db from file for %s", peer);
+
+ g_strfreev(keys);
+ g_key_file_free(key_file);
+
+ g_slist_free_full(device->primaries, g_free);
+ device->primaries = NULL;
+ gatt_db_foreach_service(device->db, NULL, add_primary,
+ &device->primaries);
+}
+
static void device_add_uuids(struct btd_device *device, GSList *uuids)
{
GSList *l;
uint16_t mtu;
uint16_t cid;
struct btd_gatt_database *database;
+ const bdaddr_t *src, *dst;
+ char srcaddr[18], dstaddr[18];
bt_io_get(io, &gerr, BT_IO_OPT_SEC_LEVEL, &sec_level,
BT_IO_OPT_IMTU, &mtu,
database = btd_adapter_get_database(dev->adapter);
+ src = btd_adapter_get_address(dev->adapter);
+ ba2str(src, srcaddr);
+
+ dst = device_get_address(dev);
+ ba2str(dst, dstaddr);
+
+ load_gatt_db(dev, srcaddr, dstaddr);
+
gatt_client_init(dev);
gatt_server_init(dev, btd_gatt_database_get_db(database));