@@ -322,7 +322,8 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
src/dbus-common.c src/dbus-common.h \
src/eir.h src/eir.c \
src/adv_monitor.h src/adv_monitor.c \
- src/battery.h src/battery.c
+ src/battery.h src/battery.c \
+ src/settings.h src/settings.c
src_bluetoothd_LDADD = lib/libbluetooth-internal.la \
gdbus/libgdbus-internal.la \
src/libshared-glib.la \
@@ -63,6 +63,7 @@
#include "textfile.h"
#include "storage.h"
#include "eir.h"
+#include "settings.h"
#define DISCONNECT_TIMER 2
#define DISCOVERY_TIMER 1
@@ -74,11 +75,6 @@
#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;
@@ -2517,171 +2513,10 @@ static void store_services(struct btd_device *device)
g_key_file_free(key_file);
}
-struct gatt_saver {
- struct btd_device *device;
- uint16_t ext_props;
- GKeyFile *key_file;
-};
-
-static void db_hash_read_value_cb(struct gatt_db_attribute *attrib,
- int err, const uint8_t *value,
- size_t length, void *user_data)
-{
- const uint8_t **hash = user_data;
-
- if (err || (length != 16))
- return;
-
- *hash = value;
-}
-
-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;
- bt_uuid_t ext_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));
-
- bt_uuid16_create(&ext_uuid, GATT_CHARAC_EXT_PROPER_UUID);
- if (!bt_uuid_cmp(uuid, &ext_uuid) && saver->ext_props)
- sprintf(value, "%04hx:%s", saver->ext_props, uuid_str);
- else
- 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, hash_uuid;
-
- if (!gatt_db_attribute_get_char_data(attr, &handle_num, &value_handle,
- &properties, &saver->ext_props,
- &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));
-
- /* Store Database Hash value if available */
- bt_uuid16_create(&hash_uuid, GATT_CHARAC_DB_HASH);
- if (!bt_uuid_cmp(&uuid, &hash_uuid)) {
- const uint8_t *hash = NULL;
-
- attr = gatt_db_get_attribute(saver->device->db, value_handle);
-
- gatt_db_attribute_read(attr, 0, BT_ATT_OP_READ_REQ, NULL,
- db_hash_read_value_cb, &hash);
- if (hash)
- sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hhx:"
- "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
- "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
- "%02hhx%02hhx:%s", value_handle, properties,
- hash[0], hash[1], hash[2], hash[3],
- hash[4], hash[5], hash[6], hash[7],
- hash[8], hash[9], hash[10], hash[11],
- hash[12], hash[13], hash[14], hash[15],
- uuid_str);
- else
- sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hhx:%s",
- value_handle, properties, uuid_str);
-
- } else
- sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hhx:%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);
-
- gatt_db_attribute_get_service_uuid(service, &uuid);
- 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)
{
char filename[PATH_MAX];
char dst_addr[18];
- GKeyFile *key_file;
- GError *gerr = NULL;
- char *data;
- gsize length = 0;
- struct gatt_saver saver;
if (device_address_is_private(device)) {
DBG("Can't store GATT db for private addressed device %s",
@@ -2699,33 +2534,9 @@ static void store_gatt_db(struct btd_device *device)
dst_addr);
create_file(filename, 0600);
- key_file = g_key_file_new();
- if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) {
- error("Unable to load key file from %s: (%s)", filename,
- gerr->message);
- g_clear_error(&gerr);
- }
-
- /* Remove current attributes since it might have changed */
- g_key_file_remove_group(key_file, "Attributes", 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);
- if (!g_file_set_contents(filename, data, length, &gerr)) {
- error("Unable set contents for %s: (%s)", filename,
- gerr->message);
- g_error_free(gerr);
- }
-
- g_free(data);
- g_key_file_free(key_file);
+ btd_settings_gatt_db_store(device->db, filename);
}
-
static void browse_request_complete(struct browse_req *req, uint8_t type,
uint8_t bdaddr_type, int err)
{
@@ -3797,288 +3608,11 @@ static void add_primary(struct gatt_db_attribute *attr, void *user_data)
*new_services = g_slist_append(*new_services, prim);
}
-static void load_desc_value(struct gatt_db_attribute *attrib,
- int err, void *user_data)
-{
- if (err)
- warn("loading descriptor value to db failed");
-}
-
-static ssize_t str2val(const char *str, uint8_t *val, size_t len)
-{
- const char *pos = str;
- size_t i;
-
- for (i = 0; i < len; i++) {
- if (sscanf(pos, "%2hhx", &val[i]) != 1)
- break;
- pos += 2;
- }
-
- return i;
-}
-
-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;
- uint16_t val;
- bt_uuid_t uuid, ext_uuid;
-
- if (sscanf(handle, "%04hx", &handle_int) != 1)
- return -EIO;
-
- /* Check if there is any value stored, otherwise it is just the UUID */
- if (sscanf(value, "%04hx:%36s", &val, uuid_str) != 2) {
- if (sscanf(value, "%36s", uuid_str) != 1)
- return -EIO;
- val = 0;
- }
-
- DBG("loading descriptor handle: 0x%04x, value: 0x%04x, value uuid: %s",
- handle_int, val, uuid_str);
-
- bt_string_to_uuid(&uuid, uuid_str);
- bt_uuid16_create(&ext_uuid, GATT_CHARAC_EXT_PROPER_UUID);
-
- /* If it is CEP then it must contain the value */
- if (!bt_uuid_cmp(&uuid, &ext_uuid) && !val) {
- warn("cannot load CEP descriptor without value");
- return -EIO;
- }
-
- 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;
- }
-
- if (val) {
- if (!gatt_db_attribute_write(att, 0, (uint8_t *)&val,
- sizeof(val), 0, NULL,
- load_desc_value, NULL))
- 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;
- char val_str[33];
- uint8_t val[16];
- size_t val_len;
- bt_uuid_t uuid;
-
- if (sscanf(handle, "%04hx", &handle_int) != 1)
- return -EIO;
-
- /* Check if there is any value stored */
- if (sscanf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hx:%32s:%36s",
- &value_handle, &properties, val_str, uuid_str) != 4) {
- if (sscanf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hx:%36s",
- &value_handle, &properties, uuid_str) != 3)
- return -EIO;
- val_len = 0;
- } else
- val_len = str2val(val_str, val, sizeof(val));
-
- bt_string_to_uuid(&uuid, uuid_str);
-
- /* Log debug message. */
- DBG("loading characteristic handle: 0x%04x, value handle: 0x%04x,"
- " properties 0x%04x value: %s uuid: %s",
- handle_int, value_handle, properties,
- val_len ? val_str : "", 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("loading characteristic to db failed");
- return -EIO;
- }
-
- if (val_len) {
- if (!gatt_db_attribute_write(att, 0, val, val_len, 0, NULL,
- load_desc_value, NULL))
- 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:%36s", &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("loading included service to db failed - no such service");
- return -EIO;
- }
-
- att = gatt_db_service_add_included(service, att);
- if (!att) {
- warn("loading 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:%36s", 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) {
- error("Unable load service into 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) {
- gatt_db_clear(db);
- 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;
- GError *gerr = NULL;
+ char filename[PATH_MAX];
+ int err;
if (!gatt_cache_is_enabled(device))
return;
@@ -4087,25 +3621,14 @@ static void load_gatt_db(struct btd_device *device, const char *local,
create_filename(filename, PATH_MAX, "/%s/cache/%s", local, peer);
- key_file = g_key_file_new();
- if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) {
- error("Unable to load key file from %s: (%s)", filename,
- gerr->message);
- g_error_free(gerr);
+ err = btd_settings_gatt_db_load(device->db, filename);
+ if (err < 0) {
+ if (err == -ENOENT)
+ return;
+
+ warn("Error loading db from cache for %s: %s (%d)", peer,
+ strerror(-err), err);
}
- 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;
new file mode 100644
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2022 Intel Corporation.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+
+#include "log.h"
+#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
+#include "settings.h"
+
+#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 ssize_t str2val(const char *str, uint8_t *val, size_t len)
+{
+ const char *pos = str;
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (sscanf(pos, "%2hhx", &val[i]) != 1)
+ break;
+ pos += 2;
+ }
+
+ return i;
+}
+
+static void load_desc_value(struct gatt_db_attribute *attrib, int err,
+ void *user_data)
+{
+}
+
+static int load_desc(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 handle_int;
+ uint16_t val;
+ bt_uuid_t uuid, ext_uuid;
+
+ if (sscanf(handle, "%04hx", &handle_int) != 1)
+ return -EIO;
+
+ /* Check if there is any value stored, otherwise it is just the UUID */
+ if (sscanf(value, "%04hx:%36s", &val, uuid_str) != 2) {
+ if (sscanf(value, "%36s", uuid_str) != 1)
+ return -EIO;
+ val = 0;
+ }
+
+ DBG("loading descriptor handle: 0x%04x, value: 0x%04x, value uuid: %s",
+ handle_int, val, uuid_str);
+
+ bt_string_to_uuid(&uuid, uuid_str);
+ bt_uuid16_create(&ext_uuid, GATT_CHARAC_EXT_PROPER_UUID);
+
+ /* If it is CEP then it must contain the value */
+ if (!bt_uuid_cmp(&uuid, &ext_uuid) && !val)
+ return -EIO;
+
+ att = gatt_db_service_insert_descriptor(service, handle_int, &uuid,
+ 0, NULL, NULL, NULL);
+ if (!att || gatt_db_attribute_get_handle(att) != handle_int)
+ return -EIO;
+
+ if (val) {
+ if (!gatt_db_attribute_write(att, 0, (uint8_t *)&val,
+ sizeof(val), 0, NULL,
+ load_desc_value, NULL))
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int load_chrc(struct gatt_db *db, 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;
+ char val_str[33];
+ uint8_t val[16];
+ size_t val_len;
+ bt_uuid_t uuid;
+
+ if (sscanf(handle, "%04hx", &handle_int) != 1)
+ return -EIO;
+
+ /* Check if there is any value stored */
+ if (sscanf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hx:%32s:%36s",
+ &value_handle, &properties, val_str, uuid_str) != 4) {
+ if (sscanf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hx:%36s",
+ &value_handle, &properties, uuid_str) != 3)
+ return -EIO;
+ val_len = 0;
+ } else
+ val_len = str2val(val_str, val, sizeof(val));
+
+ bt_string_to_uuid(&uuid, uuid_str);
+
+ /* Log debug message. */
+ DBG("loading characteristic handle: 0x%04x, value handle: 0x%04x, "
+ "properties 0x%04x value: %s uuid: %s",
+ handle_int, value_handle,
+ properties, val_len ? val_str : "", 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)
+ return -EIO;
+
+ if (val_len) {
+ if (!gatt_db_attribute_write(att, 0, val, val_len, 0, NULL,
+ load_desc_value, NULL))
+ 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:%36s", &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)
+ return -EIO;
+
+ att = gatt_db_service_add_included(service, att);
+ if (!att)
+ 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:%36s", 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("Unable load service into db!");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int gatt_db_load(struct gatt_db *db, GKeyFile *key_file, char **keys)
+{
+ 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) {
+ 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) {
+ 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) {
+ 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(db, *handle, value, current_service);
+ } else {
+ ret = load_desc(db, *handle, value, current_service);
+ }
+
+ g_free(value);
+ if (ret) {
+ gatt_db_clear(db);
+ return ret;
+ }
+ }
+
+ if (current_service)
+ gatt_db_service_set_active(current_service, true);
+
+ return 0;
+}
+
+int btd_settings_gatt_db_load(struct gatt_db *db, const char *filename)
+{
+ char **keys;
+ GKeyFile *key_file;
+ GError *gerr = NULL;
+ int err;
+
+ key_file = g_key_file_new();
+ if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) {
+ DBG("Unable to load key file from %s: (%s)", filename,
+ gerr->message);
+ g_clear_error(&gerr);
+ }
+
+ keys = g_key_file_get_keys(key_file, "Attributes", NULL, NULL);
+
+ if (!keys) {
+ g_key_file_free(key_file);
+ return -ENOENT;
+ }
+
+ err = gatt_db_load(db, key_file, keys);
+
+ g_strfreev(keys);
+ g_key_file_free(key_file);
+
+ return err;
+}
+
+struct gatt_saver {
+ struct gatt_db *db;
+ uint16_t ext_props;
+ GKeyFile *key_file;
+};
+
+static void db_hash_read_value_cb(struct gatt_db_attribute *attrib,
+ int err, const uint8_t *value,
+ size_t length, void *user_data)
+{
+ const uint8_t **hash = user_data;
+
+ if (err || (length != 16))
+ return;
+
+ *hash = value;
+}
+
+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;
+ bt_uuid_t ext_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));
+
+ bt_uuid16_create(&ext_uuid, GATT_CHARAC_EXT_PROPER_UUID);
+ if (!bt_uuid_cmp(uuid, &ext_uuid) && saver->ext_props)
+ sprintf(value, "%04hx:%s", saver->ext_props, uuid_str);
+ else
+ 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, hash_uuid;
+
+ if (!gatt_db_attribute_get_char_data(attr, &handle_num, &value_handle,
+ &properties, &saver->ext_props,
+ &uuid)) {
+ DBG("Unable to locate Characteristic data");
+ return;
+ }
+
+ sprintf(handle, "%04hx", handle_num);
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+
+ /* Store Database Hash value if available */
+ bt_uuid16_create(&hash_uuid, GATT_CHARAC_DB_HASH);
+ if (!bt_uuid_cmp(&uuid, &hash_uuid)) {
+ const uint8_t *hash = NULL;
+
+ attr = gatt_db_get_attribute(saver->db, value_handle);
+
+ gatt_db_attribute_read(attr, 0, BT_ATT_OP_READ_REQ, NULL,
+ db_hash_read_value_cb, &hash);
+ if (hash)
+ sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hhx:"
+ "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
+ "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
+ "%02hhx%02hhx:%s", value_handle, properties,
+ hash[0], hash[1], hash[2], hash[3],
+ hash[4], hash[5], hash[6], hash[7],
+ hash[8], hash[9], hash[10], hash[11],
+ hash[12], hash[13], hash[14], hash[15],
+ uuid_str);
+ else
+ sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hhx:%s",
+ value_handle, properties, uuid_str);
+
+ } else
+ sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hhx:%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)) {
+ DBG("Unable to locate Included data");
+ return;
+ }
+
+ service = gatt_db_get_attribute(saver->db, start);
+ if (!service) {
+ DBG("Unable to locate Included Service");
+ return;
+ }
+
+ sprintf(handle, "%04hx", handle_num);
+
+ gatt_db_attribute_get_service_uuid(service, &uuid);
+ 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)) {
+ DBG("Unable to locate Service 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);
+}
+
+void btd_settings_gatt_db_store(struct gatt_db *db, const char *filename)
+{
+ GKeyFile *key_file;
+ GError *gerr = NULL;
+ char *data;
+ gsize length = 0;
+ struct gatt_saver saver;
+
+ key_file = g_key_file_new();
+ if (!g_key_file_load_from_file(key_file, filename, 0, &gerr)) {
+ DBG("Unable to load key file from %s: (%s)", filename,
+ gerr->message);
+ g_clear_error(&gerr);
+ }
+
+ /* Remove current attributes since it might have changed */
+ g_key_file_remove_group(key_file, "Attributes", NULL);
+
+ saver.key_file = key_file;
+ saver.db = db;
+
+ gatt_db_foreach_service(db, NULL, store_service, &saver);
+
+ data = g_key_file_to_data(key_file, &length, NULL);
+ if (!g_file_set_contents(filename, data, length, &gerr)) {
+ DBG("Unable set contents for %s: (%s)", filename,
+ gerr->message);
+ g_error_free(gerr);
+ }
+
+ g_free(data);
+ g_key_file_free(key_file);
+}
new file mode 100644
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2022 Intel Corporation.
+ *
+ */
+
+int btd_settings_gatt_db_load(struct gatt_db *db, const char *filename);
+void btd_settings_gatt_db_store(struct gatt_db *db, const char *filename);
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> This adds helper functions to store and load from/to file so they can get reused by the likes of gatt-database.c and btmon. --- Makefile.am | 3 +- src/device.c | 499 ++--------------------------------------------- src/settings.c | 510 +++++++++++++++++++++++++++++++++++++++++++++++++ src/settings.h | 11 ++ 4 files changed, 534 insertions(+), 489 deletions(-) create mode 100644 src/settings.c create mode 100644 src/settings.h