diff mbox series

[v2,1/7] settings: Add btd_settings_gatt_db_{store,load}

Message ID 20220520041701.2572197-1-luiz.dentz@gmail.com
State Superseded
Headers show
Series [v2,1/7] settings: Add btd_settings_gatt_db_{store,load} | expand

Commit Message

Luiz Augusto von Dentz May 20, 2022, 4:16 a.m. UTC
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
diff mbox series

Patch

diff --git a/Makefile.am b/Makefile.am
index 82125c482..0074ea3ac 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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 \
diff --git a/src/device.c b/src/device.c
index 638bad061..0d7c62c9c 100644
--- a/src/device.c
+++ b/src/device.c
@@ -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;
diff --git a/src/settings.c b/src/settings.c
new file mode 100644
index 000000000..0f0530006
--- /dev/null
+++ b/src/settings.c
@@ -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
index 000000000..675f643a6
--- /dev/null
+++ b/src/settings.h
@@ -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);