settings: Add btd_settings_gatt_db_{store,load}
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 11 May 2022 22:33:27 +0000 (15:33 -0700)
committerAyush Garg <ayush.garg@samsung.com>
Mon, 15 May 2023 09:25:54 +0000 (14:55 +0530)
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.

Signed-off-by: Manika Shrivastava <manika.sh@samsung.com>
Signed-off-by: Ayush Garg <ayush.garg@samsung.com>
Makefile.am
src/device.c
src/settings.c [new file with mode: 0644]
src/settings.h [new file with mode: 0644]

index 7bb43d3..c9e7e5f 100755 (executable)
@@ -307,7 +307,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 \
index 0bb1e05..7c3686c 100644 (file)
@@ -63,6 +63,7 @@
 #include "storage.h"
 #include "attrib-server.h"
 #include "eir.h"
+#include "settings.h"
 
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 #include "sdp-xml.h"
 
 #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"
-
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 #define DEV_MAX_MANUFACTURER_DATA_LEN  248
 #endif
@@ -3183,171 +3179,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",
@@ -3365,33 +3200,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)
 {
@@ -6329,288 +6140,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;
@@ -6619,26 +6153,15 @@ 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);
-       }
-       keys = g_key_file_get_keys(key_file, "Attributes", NULL, NULL);
+       err = btd_settings_gatt_db_load(device->db, filename);
+       if (err < 0) {
+               if (err == -ENOENT)
+                       return;
 
-       if (!keys) {
-               warn("No cache for %s", peer);
-               g_key_file_free(key_file);
-               return;
+               warn("Error loading db from cache for %s: %s (%d)", peer,
+                                               strerror(-err), err);
        }
 
-       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,
diff --git a/src/settings.c b/src/settings.c
new file mode 100644 (file)
index 0000000..0f05300
--- /dev/null
@@ -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);
+}
diff --git a/src/settings.h b/src/settings.h
new file mode 100644 (file)
index 0000000..675f643
--- /dev/null
@@ -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);