/* * * GattLib - GATT Library * * Copyright (C) 2016-2019 Olivier Martin * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "gattlib_internal.h" int gattlib_adapter_open(const char* adapter_name, void** adapter) { char object_path[20]; OrgBluezAdapter1 *adapter_proxy; GError *error = NULL; if (adapter == NULL) { return GATTLIB_INVALID_PARAMETER; } if (adapter_name) { snprintf(object_path, sizeof(object_path), "/org/bluez/%s", adapter_name); } else { strncpy(object_path, "/org/bluez/hci0", sizeof(object_path)); } adapter_proxy = org_bluez_adapter1_proxy_new_for_bus_sync( G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, "org.bluez", object_path, NULL, &error); if (adapter_proxy == NULL) { if (error) { fprintf(stderr, "Failed to get adapter %s: %s\n", object_path, error->message); g_error_free(error); } else { fprintf(stderr, "Failed to get adapter %s\n", object_path); } return GATTLIB_ERROR_DBUS; } // Ensure the adapter is powered on org_bluez_adapter1_set_powered(adapter_proxy, TRUE); *adapter = adapter_proxy; return GATTLIB_SUCCESS; } /* * Internal structure to pass to Device Manager signal handlers */ struct discovered_device_arg { void *adapter; gattlib_discovered_device_t callback; void *user_data; GSList** discovered_devices_ptr; }; static void device_manager_on_device1_signal(const char* device1_path, struct discovered_device_arg *arg) { GError *error = NULL; OrgBluezDevice1* device1 = org_bluez_device1_proxy_new_for_bus_sync( G_BUS_TYPE_SYSTEM, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, "org.bluez", device1_path, NULL, &error); if (error) { fprintf(stderr, "Failed to connection to new DBus Bluez Device: %s\n", error->message); g_error_free(error); } if (device1) { const gchar *address = org_bluez_device1_get_address(device1); // Check if the device is already part of the list GSList *item = g_slist_find_custom(*arg->discovered_devices_ptr, address, (GCompareFunc)g_ascii_strcasecmp); // First time this device is in the list if (item == NULL) { // Add the device to the list *arg->discovered_devices_ptr = g_slist_append(*arg->discovered_devices_ptr, g_strdup(address)); arg->callback( arg->adapter, org_bluez_device1_get_address(device1), org_bluez_device1_get_name(device1), arg->user_data); g_object_unref(device1); } } } static void on_dbus_object_added(GDBusObjectManager *device_manager, GDBusObject *object, gpointer user_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.Device1"); if (!interface) { return; } // It is a 'org.bluez.Device1' device_manager_on_device1_signal(object_path, user_data); } static void on_interface_proxy_properties_changed (GDBusObjectManagerClient *device_manager, GDBusObjectProxy *object_proxy, GDBusProxy *interface_proxy, GVariant *changed_properties, const gchar *const *invalidated_properties, gpointer user_data) { // Check if the object is a 'org.bluez.Device1' if (strcmp(g_dbus_proxy_get_interface_name(interface_proxy), "org.bluez.Device1") != 0) { return; } // It is a 'org.bluez.Device1' device_manager_on_device1_signal(g_dbus_proxy_get_object_path(interface_proxy), user_data); } int gattlib_adapter_scan_enable_with_filter(void *adapter, uuid_t **uuid_list, int16_t rssi_threshold, uint32_t enabled_filters, gattlib_discovered_device_t discovered_device_cb, int timeout, void *user_data) { GDBusObjectManager *device_manager; GError *error = NULL; int ret = GATTLIB_SUCCESS; int added_signal_id, changed_signal_id; GSList *discovered_devices = NULL; GVariantBuilder arg_properties_builder; g_variant_builder_init(&arg_properties_builder, G_VARIANT_TYPE("a{sv}")); if (enabled_filters & GATTLIB_DISCOVER_FILTER_USE_UUID) { char uuid_str[MAX_LEN_UUID_STR + 1]; GVariantBuilder list_uuid_builder; g_variant_builder_init(&list_uuid_builder, G_VARIANT_TYPE ("as")); for (uuid_t **uuid_ptr = uuid_list; *uuid_ptr != NULL; uuid_ptr++) { gattlib_uuid_to_string(*uuid_ptr, uuid_str, sizeof(uuid_str)); g_variant_builder_add(&list_uuid_builder, "s", uuid_str); } g_variant_builder_add(&arg_properties_builder, "{sv}", "UUIDs", g_variant_builder_end(&list_uuid_builder)); } if (enabled_filters & GATTLIB_DISCOVER_FILTER_USE_RSSI) { GVariant *rssi_variant = g_variant_new_int16(rssi_threshold); g_variant_builder_add(&arg_properties_builder, "{sv}", "RSSI", rssi_variant); g_variant_unref(rssi_variant); } org_bluez_adapter1_call_set_discovery_filter_sync((OrgBluezAdapter1*)adapter, g_variant_builder_end(&arg_properties_builder), NULL, &error); if (error) { printf("error: %d.%d\n", error->domain, error->code); fprintf(stderr, "Failed to set discovery filter: %s\n", error->message); g_error_free(error); return GATTLIB_ERROR_DBUS; } org_bluez_adapter1_call_start_discovery_sync((OrgBluezAdapter1*)adapter, NULL, &error); if (error) { fprintf(stderr, "Failed to start discovery: %s\n", error->message); g_error_free(error); return GATTLIB_ERROR_DBUS; } // // Get notification when objects are removed from the Bluez ObjectManager. // We should get notified when the connection is lost with the target to allow // us to advertise us again // device_manager = g_dbus_object_manager_client_new_for_bus_sync( G_BUS_TYPE_SYSTEM, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, "org.bluez", "/", NULL, NULL, NULL, NULL, &error); if (device_manager == NULL) { if (error) { fprintf(stderr, "Failed to get Bluez Device Manager: %s\n", error->message); g_error_free(error); } else { fprintf(stderr, "Failed to get Bluez Device Manager.\n"); } ret = GATTLIB_ERROR_DBUS; goto DISABLE_SCAN; } // Pass the user callback and the discovered device list pointer to the signal handlers struct discovered_device_arg discovered_device_arg = { .adapter = adapter, .callback = discovered_device_cb, .user_data = user_data, .discovered_devices_ptr = &discovered_devices, }; added_signal_id = g_signal_connect(G_DBUS_OBJECT_MANAGER(device_manager), "object-added", G_CALLBACK (on_dbus_object_added), &discovered_device_arg); // List for object changes to see if there are still devices around changed_signal_id = g_signal_connect(G_DBUS_OBJECT_MANAGER(device_manager), "interface-proxy-properties-changed", G_CALLBACK(on_interface_proxy_properties_changed), &discovered_device_arg); // Run Glib loop for 'timeout' seconds GMainLoop *loop = g_main_loop_new(NULL, 0); g_timeout_add_seconds (timeout, stop_scan_func, loop); g_main_loop_run(loop); g_main_loop_unref(loop); g_signal_handler_disconnect(G_DBUS_OBJECT_MANAGER(device_manager), added_signal_id); g_signal_handler_disconnect(G_DBUS_OBJECT_MANAGER(device_manager), changed_signal_id); g_object_unref(device_manager); DISABLE_SCAN: // Stop BLE device discovery gattlib_adapter_scan_disable(adapter); // Free discovered device list g_slist_foreach(discovered_devices, (GFunc)g_free, NULL); g_slist_free(discovered_devices); return ret; } int gattlib_adapter_scan_enable(void* adapter, gattlib_discovered_device_t discovered_device_cb, int timeout, void *user_data) { return gattlib_adapter_scan_enable_with_filter(adapter, NULL, 0 /* RSSI Threshold */, GATTLIB_DISCOVER_FILTER_USE_NONE, discovered_device_cb, timeout, user_data); } int gattlib_adapter_scan_disable(void* adapter) { GError *error = NULL; org_bluez_adapter1_call_stop_discovery_sync((OrgBluezAdapter1*)adapter, NULL, &error); // Ignore the error return GATTLIB_SUCCESS; } int gattlib_adapter_close(void* adapter) { g_object_unref(adapter); return GATTLIB_SUCCESS; } gboolean stop_scan_func(gpointer data) { g_main_loop_quit(data); return FALSE; }