mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-15 00:04:29 +08:00
settings: Add btd_settings_gatt_db_{store,load}
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.
This commit is contained in:
parent
c159d790e8
commit
02017e320b
@ -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 \
|
||||
|
499
src/device.c
499
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;
|
||||
|
510
src/settings.c
Normal file
510
src/settings.c
Normal file
@ -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);
|
||||
}
|
11
src/settings.h
Normal file
11
src/settings.h
Normal file
@ -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);
|
Loading…
Reference in New Issue
Block a user