gattlib/dbus/gattlib.c

923 lines
30 KiB
C
Raw Normal View History

2017-03-14 13:38:45 +01:00
/*
2021-09-01 10:42:20 +02:00
* SPDX-License-Identifier: BSD-3-Clause
2017-03-14 13:38:45 +01:00
*
2021-09-01 10:42:20 +02:00
* Copyright (c) 2016-2021, Olivier Martin <olivier@labapart.org>
2017-03-14 13:38:45 +01:00
*/
#include <glib.h>
#include <stdbool.h>
2019-06-03 12:03:56 +02:00
#include <stdio.h>
2017-03-14 13:38:45 +01:00
#include <stdlib.h>
#include "gattlib_internal.h"
#define CONNECT_TIMEOUT 4
static const char *m_dbus_error_unknown_object = "GDBus.Error:org.freedesktop.DBus.Error.UnknownObject";
static void* glib_event_thread(void* main_loop_p) {
GMainLoop** main_loop = (GMainLoop**) main_loop_p;
g_main_loop_run(*main_loop);
return NULL;
}
2017-03-14 13:38:45 +01:00
gboolean on_handle_device_property_change(
OrgBluezGattCharacteristic1 *object,
GVariant *arg_changed_properties,
const gchar *const *arg_invalidated_properties,
gpointer user_data)
{
gatt_connection_t* connection = user_data;
gattlib_context_t* conn_context = connection->context;
2017-03-14 13:38:45 +01:00
// Retrieve 'Value' from 'arg_changed_properties'
if (g_variant_n_children (arg_changed_properties) > 0) {
GVariantIter *iter;
const gchar *key;
GVariant *value;
g_variant_get (arg_changed_properties, "a{sv}", &iter);
while (g_variant_iter_loop (iter, "{&sv}", &key, &value)) {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_DEBUG, "DBUS: device_property_change: %s: %s", key, g_variant_print(value, TRUE));
if (strcmp(key, "Connected") == 0) {
if (!g_variant_get_boolean(value)) {
// Disconnection case
2019-05-19 12:47:21 +02:00
if (gattlib_has_valid_handler(&connection->disconnection)) {
gattlib_call_disconnection_handler(&connection->disconnection);
}
}
} else if (strcmp(key, "ServicesResolved") == 0) {
if (g_variant_get_boolean(value)) {
// Stop the timeout for connection
g_source_remove(conn_context->connection_timeout);
// Tell we are now connected
g_main_loop_quit(conn_context->connection_loop);
}
2017-03-14 13:38:45 +01:00
}
}
2019-07-18 20:26:16 +02:00
g_variant_iter_free(iter);
2017-03-14 13:38:45 +01:00
}
return TRUE;
}
void get_device_path_from_mac_with_adapter(OrgBluezAdapter1* adapter, const char *mac_address, char *object_path, size_t object_path_len)
{
char device_address_str[20 + 1];
const char* adapter_path = g_dbus_proxy_get_object_path((GDBusProxy *)ORG_BLUEZ_ADAPTER1_PROXY(adapter));
// Transform string from 'DA:94:40:95:E0:87' to 'dev_DA_94_40_95_E0_87'
strncpy(device_address_str, mac_address, sizeof(device_address_str));
for (int i = 0; i < strlen(device_address_str); i++) {
if (device_address_str[i] == ':') {
device_address_str[i] = '_';
}
}
// Force a null-terminated character
device_address_str[20] = '\0';
// Generate object path like: /org/bluez/hci0/dev_DA_94_40_95_E0_87
snprintf(object_path, object_path_len, "%s/dev_%s", adapter_path, device_address_str);
}
void get_device_path_from_mac(const char *adapter_name, const char *mac_address, char *object_path, size_t object_path_len)
2017-03-14 13:38:45 +01:00
{
char device_address_str[20 + 1];
2019-07-09 18:26:22 +02:00
const char* adapter;
2017-03-14 13:38:45 +01:00
2019-07-09 18:26:22 +02:00
if (adapter_name) {
adapter = adapter_name;
2017-03-14 13:38:45 +01:00
} else {
2019-07-09 18:26:22 +02:00
adapter = "hci0";
2017-03-14 13:38:45 +01:00
}
// Transform string from 'DA:94:40:95:E0:87' to 'dev_DA_94_40_95_E0_87'
2019-07-09 18:26:22 +02:00
strncpy(device_address_str, mac_address, sizeof(device_address_str));
for (int i = 0; i < strlen(device_address_str); i++) {
2017-03-14 13:38:45 +01:00
if (device_address_str[i] == ':') {
device_address_str[i] = '_';
}
}
// Force a null-terminated character
device_address_str[20] = '\0';
2017-03-14 13:38:45 +01:00
// Generate object path like: /org/bluez/hci0/dev_DA_94_40_95_E0_87
2019-07-09 18:26:22 +02:00
snprintf(object_path, object_path_len, "/org/bluez/%s/dev_%s", adapter, device_address_str);
}
/**
* @param src Local Adaptater interface
* @param dst Remote Bluetooth address
* @param dst_type Set LE address type (either BDADDR_LE_PUBLIC or BDADDR_LE_RANDOM)
* @param sec_level Set security level (either BT_IO_SEC_LOW, BT_IO_SEC_MEDIUM, BT_IO_SEC_HIGH)
* @param psm Specify the PSM for GATT/ATT over BR/EDR
* @param mtu Specify the MTU size
*/
gatt_connection_t *gattlib_connect(void* adapter, const char *dst, unsigned long options)
2019-07-09 18:26:22 +02:00
{
struct gattlib_adapter *gattlib_adapter = adapter;
const char* adapter_name = NULL;
GDBusObjectManager *device_manager;
2019-07-09 18:26:22 +02:00
GError *error = NULL;
char object_path[100];
// In case NULL is passed, we initialized default adapter
if (gattlib_adapter == NULL) {
gattlib_adapter = init_default_adapter();
} else {
adapter_name = gattlib_adapter->adapter_name;
}
// even after init_default_adapter() - the adapter can be NULL
if (gattlib_adapter == NULL) {
return NULL;
}
get_device_path_from_mac(adapter_name, dst, object_path, sizeof(object_path));
2017-03-14 13:38:45 +01:00
gattlib_context_t* conn_context = calloc(sizeof(gattlib_context_t), 1);
if (conn_context == NULL) {
return NULL;
}
2020-04-08 15:45:09 +02:00
conn_context->adapter = gattlib_adapter;
2017-03-14 13:38:45 +01:00
gatt_connection_t* connection = calloc(sizeof(gatt_connection_t), 1);
if (connection == NULL) {
2019-05-21 21:25:04 +02:00
goto FREE_CONN_CONTEXT;
} else {
connection->context = conn_context;
2017-03-14 13:38:45 +01:00
}
OrgBluezDevice1* device = org_bluez_device1_proxy_new_for_bus_sync(
G_BUS_TYPE_SYSTEM,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
"org.bluez",
object_path,
NULL,
&error);
if (device == NULL) {
2019-05-03 09:47:02 +02:00
if (error) {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to connect to DBus Bluez Device: %s", error->message);
2019-05-03 09:47:02 +02:00
g_error_free(error);
}
2017-03-14 13:38:45 +01:00
goto FREE_CONNECTION;
} else {
conn_context->device = device;
conn_context->device_object_path = strdup(object_path);
2017-03-14 13:38:45 +01:00
}
// Register a handle for notification
g_signal_connect(device,
"g-properties-changed",
G_CALLBACK (on_handle_device_property_change),
connection);
2017-03-14 13:38:45 +01:00
error = NULL;
org_bluez_device1_call_connect_sync(device, NULL, &error);
if (error) {
if (strncmp(error->message, m_dbus_error_unknown_object, strlen(m_dbus_error_unknown_object)) == 0) {
// You might have this error if the computer has not scanned or has not already had
// pairing information about the targetted device.
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Device '%s' cannot be found", dst);
} else {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Device connected error (device:%s): %s",
conn_context->device_object_path,
error->message);
}
2019-05-03 09:47:02 +02:00
g_error_free(error);
2017-03-14 13:38:45 +01:00
goto FREE_DEVICE;
}
// Wait for the property 'UUIDs' to be changed. We assume 'org.bluez.GattService1
// and 'org.bluez.GattCharacteristic1' to be advertised at that moment.
conn_context->connection_loop = g_main_loop_new(NULL, 0);
2017-03-14 13:38:45 +01:00
conn_context->connection_timeout = g_timeout_add_seconds(CONNECT_TIMEOUT, stop_scan_func,
conn_context->connection_loop);
g_main_loop_run(conn_context->connection_loop);
g_main_loop_unref(conn_context->connection_loop);
// Set the attribute to NULL even if not required
conn_context->connection_loop = NULL;
2017-03-14 13:38:45 +01:00
// Get list of objects belonging to Device Manager
device_manager = get_device_manager_from_adapter(conn_context->adapter);
if (device_manager == NULL) {
goto FREE_DEVICE;
}
conn_context->dbus_objects = g_dbus_object_manager_get_objects(device_manager);
// Set up a new GMainLoop to handle notification/indication events.
conn_context->connection_loop = g_main_loop_new(NULL, 0);
pthread_create(&conn_context->event_thread, NULL, glib_event_thread, &conn_context->connection_loop);
2017-03-14 13:38:45 +01:00
return connection;
FREE_DEVICE:
free(conn_context->device_object_path);
g_object_unref(conn_context->device);
2017-03-14 13:38:45 +01:00
FREE_CONNECTION:
free(connection);
2019-05-21 21:25:04 +02:00
FREE_CONN_CONTEXT:
free(conn_context);
2022-03-28 09:03:13 +02:00
// destroy default adapter
if(adapter == NULL)
{
gattlib_adapter_close(gattlib_adapter);
}
2017-03-14 13:38:45 +01:00
return NULL;
}
gatt_connection_t *gattlib_connect_async(void *adapter, const char *dst,
unsigned long options,
gatt_connect_cb_t connect_cb, void* data)
2017-03-14 13:38:45 +01:00
{
gatt_connection_t *connection;
connection = gattlib_connect(adapter, dst, options);
if ((connection != NULL) && (connect_cb != NULL)) {
connect_cb(connection, data);
}
return connection;
2017-03-14 13:38:45 +01:00
}
int gattlib_disconnect(gatt_connection_t* connection) {
gattlib_context_t* conn_context = connection->context;
GError *error = NULL;
org_bluez_device1_call_disconnect_sync(conn_context->device, NULL, &error);
2019-05-03 09:47:02 +02:00
if (error) {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to disconnect DBus Bluez Device: %s", error->message);
2019-05-03 09:47:02 +02:00
g_error_free(error);
}
free(conn_context->device_object_path);
g_object_unref(conn_context->device);
g_list_free_full(conn_context->dbus_objects, g_object_unref);
g_main_loop_quit(conn_context->connection_loop);
pthread_join(conn_context->event_thread, NULL);
g_main_loop_unref(conn_context->connection_loop);
disconnect_all_notifications(conn_context);
2021-04-13 15:52:49 +02:00
free(conn_context->adapter->adapter_name);
2021-07-27 07:08:15 +02:00
free(conn_context->adapter);
free(connection->context);
2017-03-14 13:38:45 +01:00
free(connection);
2019-05-21 21:25:04 +02:00
return GATTLIB_SUCCESS;
2017-03-14 13:38:45 +01:00
}
// Bluez was using org.bluez.Device1.GattServices until 5.37 to expose the list of available GATT Services
#if BLUEZ_VERSION < BLUEZ_VERSIONS(5, 38)
int gattlib_discover_primary(gatt_connection_t* connection, gattlib_primary_service_t** services, int* services_count) {
if (connection == NULL) {
GATTLIB_LOG(GATTLIB_ERROR, "Gattlib connection not initialized.");
return GATTLIB_INVALID_PARAMETER;
}
gattlib_context_t* conn_context = connection->context;
2017-03-14 13:38:45 +01:00
OrgBluezDevice1* device = conn_context->device;
const gchar* const* service_str;
GError *error = NULL;
const gchar* const* service_strs = org_bluez_device1_get_gatt_services(device);
if (service_strs == NULL) {
if (services != NULL) {
*services = NULL;
}
if (services_count != NULL) {
*services_count = 0;
}
2019-05-21 21:25:04 +02:00
return GATTLIB_SUCCESS;
2017-03-14 13:38:45 +01:00
}
// Maximum number of primary services
int count_max = 0, count = 0;
for (service_str = service_strs; *service_str != NULL; service_str++) {
count_max++;
}
gattlib_primary_service_t* primary_services = malloc(count_max * sizeof(gattlib_primary_service_t));
if (primary_services == NULL) {
2019-05-21 21:25:04 +02:00
return GATTLIB_OUT_OF_MEMORY;
2017-03-14 13:38:45 +01:00
}
for (service_str = service_strs; *service_str != NULL; service_str++) {
error = NULL;
OrgBluezGattService1* service_proxy = org_bluez_gatt_service1_proxy_new_for_bus_sync(
G_BUS_TYPE_SYSTEM,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
"org.bluez",
*service_str,
NULL,
&error);
if (service_proxy == NULL) {
2019-05-03 09:47:02 +02:00
if (error) {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open service '%s': %s", *service_str, error->message);
2019-05-03 09:47:02 +02:00
g_error_free(error);
} else {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open service '%s'.", *service_str);
2019-05-03 09:47:02 +02:00
}
2017-03-14 13:38:45 +01:00
continue;
}
if (org_bluez_gatt_service1_get_primary(service_proxy)) {
primary_services[count].attr_handle_start = 0;
primary_services[count].attr_handle_end = 0;
gattlib_string_to_uuid(
org_bluez_gatt_service1_get_uuid(service_proxy),
MAX_LEN_UUID_STR + 1,
&primary_services[count].uuid);
count++;
}
g_object_unref(service_proxy);
}
if (services != NULL) {
*services = primary_services;
}
if (services_count != NULL) {
*services_count = count;
}
2019-05-21 21:25:04 +02:00
return GATTLIB_SUCCESS;
2017-03-14 13:38:45 +01:00
}
#else
int gattlib_discover_primary(gatt_connection_t* connection, gattlib_primary_service_t** services, int* services_count) {
if (connection == NULL) {
GATTLIB_LOG(GATTLIB_ERROR, "Gattlib connection not initialized.");
return GATTLIB_INVALID_PARAMETER;
}
2017-03-14 13:38:45 +01:00
gattlib_context_t* conn_context = connection->context;
GDBusObjectManager *device_manager = get_device_manager_from_adapter(conn_context->adapter);
2017-03-14 13:38:45 +01:00
OrgBluezDevice1* device = conn_context->device;
const gchar* const* service_str;
GError *error = NULL;
2019-05-21 21:25:04 +02:00
int ret = GATTLIB_SUCCESS;
2017-03-14 13:38:45 +01:00
const gchar* const* service_strs = org_bluez_device1_get_uuids(device);
if (device_manager == NULL) {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Gattlib context not initialized.");
return GATTLIB_INVALID_PARAMETER;
}
2017-03-14 13:38:45 +01:00
if (service_strs == NULL) {
if (services != NULL) {
*services = NULL;
}
if (services_count != NULL) {
*services_count = 0;
}
2019-05-21 21:25:04 +02:00
return GATTLIB_SUCCESS;
2017-03-14 13:38:45 +01:00
}
// Maximum number of primary services
int count_max = 0, count = 0;
for (service_str = service_strs; *service_str != NULL; service_str++) {
count_max++;
}
gattlib_primary_service_t* primary_services = malloc(count_max * sizeof(gattlib_primary_service_t));
if (primary_services == NULL) {
2019-05-21 21:25:04 +02:00
return GATTLIB_OUT_OF_MEMORY;
2017-03-14 13:38:45 +01:00
}
GList *l;
for (l = conn_context->dbus_objects; l != NULL; l = l->next) {
2017-03-14 13:38:45 +01:00
GDBusObject *object = l->data;
const char* object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object));
GDBusInterface *interface = g_dbus_object_manager_get_interface(device_manager, object_path, "org.bluez.GattService1");
if (!interface) {
continue;
}
2019-07-18 20:26:16 +02:00
g_object_unref(interface);
2017-03-14 13:38:45 +01:00
error = NULL;
OrgBluezGattService1* service_proxy = org_bluez_gatt_service1_proxy_new_for_bus_sync(
G_BUS_TYPE_SYSTEM,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
"org.bluez",
object_path,
NULL,
&error);
if (service_proxy == NULL) {
2019-05-03 09:47:02 +02:00
if (error) {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open service '%s': %s", object_path, error->message);
2019-05-03 09:47:02 +02:00
g_error_free(error);
} else {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open service '%s'.", object_path);
2019-05-03 09:47:02 +02:00
}
2017-03-14 13:38:45 +01:00
continue;
}
// Ensure the service is attached to this device
const gchar * service_property = org_bluez_gatt_service1_get_device(service_proxy);
if (service_property == NULL) {
if (error) {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to get service property '%s': %s", object_path, error->message);
g_error_free(error);
} else {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to get service property '%s'.", object_path);
}
continue;
}
if (strcmp(conn_context->device_object_path, service_property)) {
2019-05-21 21:27:35 +02:00
g_object_unref(service_proxy);
2017-03-14 13:38:45 +01:00
continue;
}
if (org_bluez_gatt_service1_get_primary(service_proxy)) {
// Object path is in the form '/org/bluez/hci0/dev_DE_79_A2_A1_E9_FA/service0024
// We convert the last 4 hex characters into the handle
int service_handle = 0xFFFF; // Initialize with an invalid value.
sscanf(object_path + strlen(object_path) - 4, "%x", &service_handle);
primary_services[count].attr_handle_start = service_handle;
primary_services[count].attr_handle_end = service_handle;
// Loop through all objects, as ordering is not guaranteed.
for (GList *m = conn_context->dbus_objects; m != NULL; m = m->next) {
2019-06-03 12:03:56 +02:00
GDBusObject *characteristic_object = m->data;
const char* characteristic_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(characteristic_object));
interface = g_dbus_object_manager_get_interface(device_manager, characteristic_path, "org.bluez.GattCharacteristic1");
2019-06-03 12:03:56 +02:00
if (!interface) {
continue;
} else if (strncmp(object_path, characteristic_path, strlen(object_path)) != 0) {
// The selected characteristic does not belong to the object, ignore.
2019-07-18 20:26:16 +02:00
g_object_unref(interface);
2019-06-03 12:03:56 +02:00
continue;
} else {
// Release the interface object to prevent memory leak.
2019-07-18 20:26:16 +02:00
g_object_unref(interface);
2019-06-03 12:03:56 +02:00
// Object path is in the form '/org/bluez/hci0/dev_DE_79_A2_A1_E9_FA/service0024/char0029'.
// We convert the last 4 hex characters into the handle
int char_handle = primary_services[count].attr_handle_end; // Initialize with existing good value for safety.
2019-06-03 12:03:56 +02:00
sscanf(characteristic_path + strlen(characteristic_path) - 4, "%x", &char_handle);
// Once here, update the end handle of the service
primary_services[count].attr_handle_end = MAX(primary_services[count].attr_handle_end, char_handle);
2019-06-03 12:03:56 +02:00
}
}
2017-03-14 13:38:45 +01:00
gattlib_string_to_uuid(
org_bluez_gatt_service1_get_uuid(service_proxy),
MAX_LEN_UUID_STR + 1,
&primary_services[count].uuid);
count++;
}
2019-05-21 21:27:35 +02:00
g_object_unref(service_proxy);
2017-03-14 13:38:45 +01:00
}
if (services != NULL) {
*services = primary_services;
}
if (services_count != NULL) {
*services_count = count;
}
2019-05-21 21:25:32 +02:00
if (ret != GATTLIB_SUCCESS) {
free(primary_services);
}
return ret;
2017-03-14 13:38:45 +01:00
}
#endif
// Bluez was using org.bluez.Device1.GattServices until 5.37 to expose the list of available GATT Services
#if BLUEZ_VERSION < BLUEZ_VERSIONS(5, 38)
2019-06-03 12:03:56 +02:00
int gattlib_discover_char_range(gatt_connection_t* connection, int start, int end, gattlib_characteristic_t** characteristics, int* characteristics_count) {
gattlib_context_t* conn_context = connection->context;
2017-03-14 13:38:45 +01:00
OrgBluezDevice1* device = conn_context->device;
GError *error = NULL;
2019-06-03 12:03:56 +02:00
int handle;
2017-03-14 13:38:45 +01:00
const gchar* const* service_strs = org_bluez_device1_get_gatt_services(device);
const gchar* const* service_str;
const gchar* const* characteristic_strs;
2019-06-03 12:03:56 +02:00
const gchar* characteristic_str;
2017-03-14 13:38:45 +01:00
if (service_strs == NULL) {
2019-05-21 21:25:04 +02:00
return GATTLIB_INVALID_PARAMETER;
2017-03-14 13:38:45 +01:00
}
// Maximum number of primary services
int count_max = 0, count = 0;
for (service_str = service_strs; *service_str != NULL; service_str++) {
error = NULL;
OrgBluezGattService1* service_proxy = org_bluez_gatt_service1_proxy_new_for_bus_sync(
G_BUS_TYPE_SYSTEM,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
"org.bluez",
*service_str,
NULL,
&error);
if (service_proxy == NULL) {
2019-05-03 09:47:02 +02:00
if (error) {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open services '%s': %s", *service_str, error->message);
2019-05-03 09:47:02 +02:00
g_error_free(error);
} else {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open services '%s'.", *service_str);
2019-05-03 09:47:02 +02:00
}
2017-03-14 13:38:45 +01:00
continue;
}
characteristic_strs = org_bluez_gatt_service1_get_characteristics(service_proxy);
if (characteristic_strs == NULL) {
2019-05-21 21:27:35 +02:00
g_object_unref(service_proxy);
2017-03-14 13:38:45 +01:00
continue;
}
2019-07-07 20:40:12 +02:00
for (characteristic_str = *characteristic_strs; characteristic_str != NULL; characteristic_str++) {
2019-06-03 12:03:56 +02:00
// Object path is in the form '/org/bluez/hci0/dev_DE_79_A2_A1_E9_FA/service0024/char0029'.
// We convert the last 4 hex characters into the handle
sscanf(characteristic_str + strlen(characteristic_str) - 4, "%x", &handle);
// Check if handle is in range
if ((handle < start) || (handle > end)) {
continue;
}
2017-03-14 13:38:45 +01:00
count_max++;
}
2019-05-21 21:27:35 +02:00
2017-03-14 13:38:45 +01:00
g_object_unref(service_proxy);
}
gattlib_characteristic_t* characteristic_list = malloc(count_max * sizeof(gattlib_characteristic_t));
if (characteristic_list == NULL) {
2019-05-21 21:25:04 +02:00
return GATTLIB_OUT_OF_MEMORY;
2017-03-14 13:38:45 +01:00
}
for (service_str = service_strs; *service_str != NULL; service_str++) {
error = NULL;
OrgBluezGattService1* service_proxy = org_bluez_gatt_service1_proxy_new_for_bus_sync(
G_BUS_TYPE_SYSTEM,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
"org.bluez",
*service_str,
NULL,
&error);
if (service_proxy == NULL) {
2019-05-03 09:47:02 +02:00
if (error) {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open service '%s': %s", *service_str, error->message);
2019-05-03 09:47:02 +02:00
g_error_free(error);
} else {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open service '%s'.", *service_str);
2019-05-03 09:47:02 +02:00
}
2017-03-14 13:38:45 +01:00
continue;
}
characteristic_strs = org_bluez_gatt_service1_get_characteristics(service_proxy);
if (characteristic_strs == NULL) {
2019-05-21 21:27:35 +02:00
g_object_unref(service_proxy);
2017-03-14 13:38:45 +01:00
continue;
}
2019-07-07 20:40:12 +02:00
for (characteristic_str = *characteristic_strs; characteristic_str != NULL; characteristic_str++) {
2019-06-03 12:03:56 +02:00
// Object path is in the form '/org/bluez/hci0/dev_DE_79_A2_A1_E9_FA/service0024/char0029'.
// We convert the last 4 hex characters into the handle
sscanf(characteristic_str + strlen(characteristic_str) - 4, "%x", &handle);
// Check if handle is in range
if ((handle < start) || (handle > end)) {
continue;
}
2017-03-14 13:38:45 +01:00
OrgBluezGattCharacteristic1 *characteristic_proxy = org_bluez_gatt_characteristic1_proxy_new_for_bus_sync(
G_BUS_TYPE_SYSTEM,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
"org.bluez",
2019-07-07 20:40:12 +02:00
characteristic_str,
2017-03-14 13:38:45 +01:00
NULL,
&error);
if (characteristic_proxy == NULL) {
2019-05-03 09:47:02 +02:00
if (error) {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open characteristic '%s': %s", characteristic_str, error->message);
2019-05-03 09:47:02 +02:00
g_error_free(error);
} else {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open characteristic '%s'.", characteristic_str);
2019-05-03 09:47:02 +02:00
}
2017-03-14 13:38:45 +01:00
continue;
} else {
characteristic_list[count].handle = 0;
characteristic_list[count].value_handle = 0;
2021-07-27 07:08:15 +02:00
characteristic_list[count].properties = 0;
2017-03-14 13:38:45 +01:00
const gchar *const * flags = org_bluez_gatt_characteristic1_get_flags(characteristic_proxy);
for (; *flags != NULL; flags++) {
if (strcmp(*flags,"broadcast") == 0) {
characteristic_list[count].properties |= GATTLIB_CHARACTERISTIC_BROADCAST;
} else if (strcmp(*flags,"read") == 0) {
characteristic_list[count].properties |= GATTLIB_CHARACTERISTIC_READ;
} else if (strcmp(*flags,"write") == 0) {
characteristic_list[count].properties |= GATTLIB_CHARACTERISTIC_WRITE;
} else if (strcmp(*flags,"write-without-response") == 0) {
characteristic_list[count].properties |= GATTLIB_CHARACTERISTIC_WRITE_WITHOUT_RESP;
} else if (strcmp(*flags,"notify") == 0) {
characteristic_list[count].properties |= GATTLIB_CHARACTERISTIC_NOTIFY;
} else if (strcmp(*flags,"indicate") == 0) {
characteristic_list[count].properties |= GATTLIB_CHARACTERISTIC_INDICATE;
}
}
gattlib_string_to_uuid(
org_bluez_gatt_characteristic1_get_uuid(characteristic_proxy),
MAX_LEN_UUID_STR + 1,
&characteristic_list[count].uuid);
count++;
}
g_object_unref(characteristic_proxy);
}
g_object_unref(service_proxy);
}
2019-06-03 12:03:56 +02:00
*characteristics = characteristic_list;
*characteristics_count = count;
2019-05-21 21:25:04 +02:00
return GATTLIB_SUCCESS;
2017-03-14 13:38:45 +01:00
}
#else
static void add_characteristics_from_service(gattlib_context_t* conn_context, GDBusObjectManager *device_manager,
const char* service_object_path,
int start, int end,
gattlib_characteristic_t* characteristic_list, int* count)
2019-06-03 12:03:56 +02:00
{
2017-03-14 13:38:45 +01:00
GError *error = NULL;
for (GList *l = conn_context->dbus_objects; l != NULL; l = l->next) {
2017-03-14 13:38:45 +01:00
GDBusObject *object = l->data;
const char* object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object));
GDBusInterface *interface = g_dbus_object_manager_get_interface(device_manager, object_path, "org.bluez.GattCharacteristic1");
if (!interface) {
continue;
}
2019-07-18 20:26:16 +02:00
g_object_unref(interface);
2017-03-14 13:38:45 +01:00
OrgBluezGattCharacteristic1* characteristic = org_bluez_gatt_characteristic1_proxy_new_for_bus_sync(
G_BUS_TYPE_SYSTEM,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
"org.bluez",
object_path,
NULL,
&error);
if (characteristic == NULL) {
2019-05-03 09:47:02 +02:00
if (error) {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open characteristic '%s': %s", object_path, error->message);
2019-05-03 09:47:02 +02:00
g_error_free(error);
} else {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open characteristic '%s'.", object_path);
2019-05-03 09:47:02 +02:00
}
2017-03-14 13:38:45 +01:00
continue;
}
2022-03-04 10:21:16 +01:00
const gchar * property_value = org_bluez_gatt_characteristic1_get_service(characteristic);
if (property_value == NULL){
if (error) {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to get service '%s': %s", object_path, error->message);
g_error_free(error);
} else {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to get service '%s'.", object_path);
}
continue;
}
if (strcmp(property_value, service_object_path)) {
2019-05-21 21:27:35 +02:00
g_object_unref(characteristic);
2017-03-14 13:38:45 +01:00
continue;
} else {
2019-06-03 12:03:56 +02:00
int handle;
// Object path is in the form '/org/bluez/hci0/dev_DE_79_A2_A1_E9_FA/service0024/char0029'.
// We convert the last 4 hex characters into the handle
sscanf(object_path + strlen(object_path) - 4, "%x", &handle);
// Check if handle is in range
if ((handle < start) || (handle > end)) {
continue;
}
characteristic_list[*count].handle = handle;
characteristic_list[*count].value_handle = handle;
2021-07-27 07:08:15 +02:00
characteristic_list[*count].properties = 0;
2017-03-14 13:38:45 +01:00
const gchar *const * flags = org_bluez_gatt_characteristic1_get_flags(characteristic);
for (; *flags != NULL; flags++) {
if (strcmp(*flags,"broadcast") == 0) {
characteristic_list[*count].properties |= GATTLIB_CHARACTERISTIC_BROADCAST;
} else if (strcmp(*flags,"read") == 0) {
characteristic_list[*count].properties |= GATTLIB_CHARACTERISTIC_READ;
} else if (strcmp(*flags,"write") == 0) {
characteristic_list[*count].properties |= GATTLIB_CHARACTERISTIC_WRITE;
} else if (strcmp(*flags,"write-without-response") == 0) {
characteristic_list[*count].properties |= GATTLIB_CHARACTERISTIC_WRITE_WITHOUT_RESP;
} else if (strcmp(*flags,"notify") == 0) {
characteristic_list[*count].properties |= GATTLIB_CHARACTERISTIC_NOTIFY;
} else if (strcmp(*flags,"indicate") == 0) {
characteristic_list[*count].properties |= GATTLIB_CHARACTERISTIC_INDICATE;
}
}
gattlib_string_to_uuid(
org_bluez_gatt_characteristic1_get_uuid(characteristic),
MAX_LEN_UUID_STR + 1,
&characteristic_list[*count].uuid);
*count = *count + 1;
}
2019-05-21 21:27:35 +02:00
g_object_unref(characteristic);
2017-03-14 13:38:45 +01:00
}
}
2019-06-03 12:03:56 +02:00
int gattlib_discover_char_range(gatt_connection_t* connection, int start, int end, gattlib_characteristic_t** characteristics, int* characteristics_count) {
2017-03-14 13:38:45 +01:00
gattlib_context_t* conn_context = connection->context;
GDBusObjectManager *device_manager = get_device_manager_from_adapter(conn_context->adapter);
2017-03-14 13:38:45 +01:00
GError *error = NULL;
GList *l;
if (device_manager == NULL) {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Gattlib context not initialized.");
return GATTLIB_INVALID_PARAMETER;
2017-03-14 13:38:45 +01:00
}
// Count the maximum number of characteristic to allocate the array (we count all the characterstic for all devices)
int count_max = 0, count = 0;
for (l = conn_context->dbus_objects; l != NULL; l = l->next) {
2017-03-14 13:38:45 +01:00
GDBusObject *object = l->data;
const char* object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object));
GDBusInterface *interface = g_dbus_object_manager_get_interface(device_manager, object_path, "org.bluez.GattCharacteristic1");
if (!interface) {
// Check if this DBUS Path is actually the Battery interface
interface = g_dbus_object_manager_get_interface(device_manager, object_path, "org.bluez.Battery1");
if (!interface) {
continue;
}
2017-03-14 13:38:45 +01:00
}
2019-07-18 20:26:16 +02:00
g_object_unref(interface);
2017-03-14 13:38:45 +01:00
count_max++;
}
gattlib_characteristic_t* characteristic_list = malloc(count_max * sizeof(gattlib_characteristic_t));
if (characteristic_list == NULL) {
2019-05-21 21:25:04 +02:00
return GATTLIB_OUT_OF_MEMORY;
2017-03-14 13:38:45 +01:00
}
// List all services for this device
for (l = conn_context->dbus_objects; l != NULL; l = l->next) {
2017-03-14 13:38:45 +01:00
GDBusObject *object = l->data;
const char* object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object));
GDBusInterface *interface = g_dbus_object_manager_get_interface(device_manager, object_path, "org.bluez.GattService1");
if (!interface) {
// Check if this DBUS Path is actually the Battery interface. In this case,
// we add a fake characteristic for the battery.
interface = g_dbus_object_manager_get_interface(device_manager, object_path, "org.bluez.Battery1");
if (interface) {
g_object_unref(interface);
characteristic_list[count].handle = 0;
characteristic_list[count].value_handle = 0;
characteristic_list[count].properties = GATTLIB_CHARACTERISTIC_READ | GATTLIB_CHARACTERISTIC_NOTIFY;
gattlib_string_to_uuid(
"00002a19-0000-1000-8000-00805f9b34fb",
MAX_LEN_UUID_STR + 1,
&characteristic_list[count].uuid);
count++;
}
2017-03-14 13:38:45 +01:00
continue;
}
2019-07-18 20:26:16 +02:00
g_object_unref(interface);
2017-03-14 13:38:45 +01:00
error = NULL;
OrgBluezGattService1* service_proxy = org_bluez_gatt_service1_proxy_new_for_bus_sync(
G_BUS_TYPE_SYSTEM,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
"org.bluez",
object_path,
NULL,
&error);
if (service_proxy == NULL) {
2019-05-03 09:47:02 +02:00
if (error) {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open service '%s': %s", object_path, error->message);
2019-05-03 09:47:02 +02:00
g_error_free(error);
} else {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open service '%s'.", object_path);
2019-05-03 09:47:02 +02:00
}
2017-03-14 13:38:45 +01:00
continue;
}
// Ensure the service is attached to this device
const char* service_object_path = org_bluez_gatt_service1_get_device(service_proxy);
if (strcmp(conn_context->device_object_path, service_object_path)) {
2019-05-21 21:27:35 +02:00
g_object_unref(service_proxy);
2017-03-14 13:38:45 +01:00
continue;
}
// Add all characteristics attached to this service
add_characteristics_from_service(conn_context, device_manager, object_path, start, end, characteristic_list, &count);
2019-05-21 21:27:35 +02:00
g_object_unref(service_proxy);
2017-03-14 13:38:45 +01:00
}
2019-06-03 12:03:56 +02:00
*characteristics = characteristic_list;
*characteristics_count = count;
2019-05-21 21:25:04 +02:00
return GATTLIB_SUCCESS;
2017-03-14 13:38:45 +01:00
}
#endif
2019-06-03 12:03:56 +02:00
int gattlib_discover_char(gatt_connection_t* connection, gattlib_characteristic_t** characteristics, int* characteristics_count)
{
return gattlib_discover_char_range(connection, 0x00, 0xFF, characteristics, characteristics_count);
}
2017-03-14 13:38:45 +01:00
int gattlib_discover_desc_range(gatt_connection_t* connection, int start, int end, gattlib_descriptor_t** descriptors, int* descriptor_count) {
2019-05-21 21:25:04 +02:00
return GATTLIB_NOT_SUPPORTED;
2017-03-14 13:38:45 +01:00
}
int gattlib_discover_desc(gatt_connection_t* connection, gattlib_descriptor_t** descriptors, int* descriptor_count) {
2019-05-21 21:25:04 +02:00
return GATTLIB_NOT_SUPPORTED;
2017-03-14 13:38:45 +01:00
}
int get_bluez_device_from_mac(struct gattlib_adapter *adapter, const char *mac_address, OrgBluezDevice1 **bluez_device1)
{
GError *error = NULL;
char object_path[100];
if (adapter != NULL) {
get_device_path_from_mac_with_adapter(adapter->adapter_proxy, mac_address, object_path, sizeof(object_path));
} else {
get_device_path_from_mac(NULL, mac_address, object_path, sizeof(object_path));
}
*bluez_device1 = org_bluez_device1_proxy_new_for_bus_sync(
G_BUS_TYPE_SYSTEM,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
"org.bluez",
object_path,
NULL,
&error);
if (error) {
2021-10-18 01:25:29 +02:00
GATTLIB_LOG(GATTLIB_ERROR, "Failed to connection to new DBus Bluez Device: %s", error->message);
g_error_free(error);
return GATTLIB_ERROR_DBUS;
}
return GATTLIB_SUCCESS;
}
int gattlib_get_rssi(gatt_connection_t *connection, int16_t *rssi)
{
2022-05-13 22:38:06 +02:00
if (connection == NULL) {
return GATTLIB_INVALID_PARAMETER;
}
gattlib_context_t* conn_context = connection->context;
if (rssi == NULL) {
return GATTLIB_INVALID_PARAMETER;
}
*rssi = org_bluez_device1_get_rssi(conn_context->device);
return GATTLIB_SUCCESS;
}
int gattlib_get_rssi_from_mac(void *adapter, const char *mac_address, int16_t *rssi)
{
OrgBluezDevice1 *bluez_device1;
int ret;
2021-03-18 23:49:58 +01:00
if (rssi == NULL || mac_address == NULL) {
return GATTLIB_INVALID_PARAMETER;
}
ret = get_bluez_device_from_mac(adapter, mac_address, &bluez_device1);
if (ret != GATTLIB_SUCCESS) {
2019-07-18 20:26:16 +02:00
g_object_unref(bluez_device1);
return ret;
}
*rssi = org_bluez_device1_get_rssi(bluez_device1);
2019-07-18 20:26:16 +02:00
g_object_unref(bluez_device1);
return GATTLIB_SUCCESS;
}