summaryrefslogtreecommitdiffstats
path: root/src/detection/bluetooth/bluetooth_linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/detection/bluetooth/bluetooth_linux.c')
-rw-r--r--src/detection/bluetooth/bluetooth_linux.c256
1 files changed, 256 insertions, 0 deletions
diff --git a/src/detection/bluetooth/bluetooth_linux.c b/src/detection/bluetooth/bluetooth_linux.c
new file mode 100644
index 0000000..fb4e422
--- /dev/null
+++ b/src/detection/bluetooth/bluetooth_linux.c
@@ -0,0 +1,256 @@
+#include "bluetooth.h"
+#include "common/strutil.h"
+
+#ifdef FF_HAVE_DBUS
+ #include "common/dbus.h"
+ #include "common/io.h"
+
+/* Example dbus reply, striped to only the relevant parts:
+array [ //root
+ dict entry( //object
+ object path "/org/bluez/hci0/dev_03_21_8B_91_16_4D"
+ array [
+ dict entry( //property
+ string "org.bluez.Device1"
+ array [
+ dict entry( //value
+ string "Address"
+ variant string "03:21:8B:91:16:4D"
+ )
+ dict entry( //value
+ string "Name"
+ variant string "JBL TUNE160BT"
+ )
+ dict entry( //value
+ string "Icon"
+ variant string "audio-headset"
+ )
+ dict entry( //value
+ string "Connected"
+ variant boolean true
+ )
+ ]
+ )
+ dict entry( //property
+ string "org.bluez.Battery1"
+ array [
+ dict entry( //value
+ string "Percentage"
+ variant byte 100
+ )
+ ]
+ )
+ ]
+ )
+]
+*/
+
+static bool detectBluetoothValue(FFDBusData* dbus, DBusMessageIter* iter, FFBluetoothResult* device) {
+ if (dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) {
+ return true;
+ }
+
+ DBusMessageIter dictIter;
+ dbus->lib->ffdbus_message_iter_recurse(iter, &dictIter);
+
+ if (dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_STRING) {
+ return true;
+ }
+
+ const char* deviceProperty;
+ dbus->lib->ffdbus_message_iter_get_basic(&dictIter, &deviceProperty);
+
+ dbus->lib->ffdbus_message_iter_next(&dictIter);
+
+ if (ffStrEquals(deviceProperty, "Address")) {
+ ffDBusGetString(dbus, &dictIter, &device->address);
+ } else if (ffStrEquals(deviceProperty, "Name")) {
+ ffDBusGetString(dbus, &dictIter, &device->name);
+ } else if (ffStrEquals(deviceProperty, "Icon")) {
+ ffDBusGetString(dbus, &dictIter, &device->type);
+ } else if (ffStrEquals(deviceProperty, "Percentage")) {
+ uint64_t percentage;
+ if (ffDBusGetUint(dbus, &dictIter, &percentage)) {
+ device->battery = (uint8_t) percentage;
+ }
+ } else if (ffStrEquals(deviceProperty, "Connected")) {
+ ffDBusGetBool(dbus, &dictIter, &device->connected);
+ } else if (ffStrEquals(deviceProperty, "Paired")) {
+ bool paired = true;
+ ffDBusGetBool(dbus, &dictIter, &paired);
+ if (!paired) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static void detectBluetoothProperty(FFDBusData* dbus, DBusMessageIter* iter, FFBluetoothResult* device) {
+ if (dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) {
+ return;
+ }
+
+ DBusMessageIter dictIter;
+ dbus->lib->ffdbus_message_iter_recurse(iter, &dictIter);
+
+ if (dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_STRING) {
+ return;
+ }
+
+ const char* propertyType;
+ dbus->lib->ffdbus_message_iter_get_basic(&dictIter, &propertyType);
+
+ if (!ffStrContains(propertyType, ".Device") && !ffStrContains(propertyType, ".Battery")) {
+ return; // We don't care about other properties
+ }
+
+ dbus->lib->ffdbus_message_iter_next(&dictIter);
+
+ if (dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_ARRAY) {
+ return;
+ }
+
+ DBusMessageIter arrayIter;
+ dbus->lib->ffdbus_message_iter_recurse(&dictIter, &arrayIter);
+
+ do {
+ bool shouldContinue = detectBluetoothValue(dbus, &arrayIter, device);
+ if (!shouldContinue) {
+ ffStrbufClear(&device->name);
+ break;
+ }
+ } while (dbus->lib->ffdbus_message_iter_next(&arrayIter));
+}
+
+static FFBluetoothResult* detectBluetoothObject(FFlist* devices, FFDBusData* dbus, DBusMessageIter* iter) {
+ if (dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) {
+ return NULL;
+ }
+
+ DBusMessageIter dictIter;
+ dbus->lib->ffdbus_message_iter_recurse(iter, &dictIter);
+
+ if (dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_OBJECT_PATH) {
+ return NULL;
+ }
+
+ const char* objectPath;
+ dbus->lib->ffdbus_message_iter_get_basic(&dictIter, &objectPath);
+
+ // We don't want adapter objects
+ if (!ffStrContains(objectPath, "/dev_")) {
+ return NULL;
+ }
+
+ dbus->lib->ffdbus_message_iter_next(&dictIter);
+
+ if (dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_ARRAY) {
+ return NULL;
+ }
+
+ DBusMessageIter arrayIter;
+ dbus->lib->ffdbus_message_iter_recurse(&dictIter, &arrayIter);
+
+ FFBluetoothResult* device = FF_LIST_ADD(FFBluetoothResult, *devices);
+ ffStrbufInit(&device->name);
+ ffStrbufInit(&device->address);
+ ffStrbufInit(&device->type);
+ device->battery = 0;
+ device->connected = false;
+
+ do {
+ detectBluetoothProperty(dbus, &arrayIter, device);
+ } while (dbus->lib->ffdbus_message_iter_next(&arrayIter));
+
+ return device;
+}
+
+static void detectBluetoothRoot(FFBluetoothOptions* options, FFlist* devices, FFDBusData* dbus, DBusMessageIter* iter, int32_t connectedCount) {
+ if (dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+ return;
+ }
+
+ DBusMessageIter arrayIter;
+ dbus->lib->ffdbus_message_iter_recurse(iter, &arrayIter);
+
+ do {
+ FFBluetoothResult* device = detectBluetoothObject(devices, dbus, &arrayIter);
+
+ if (device) {
+ if (!options->showDisconnected && !device->connected) {
+ ffStrbufDestroy(&device->name);
+ ffStrbufDestroy(&device->address);
+ ffStrbufDestroy(&device->type);
+ --devices->length;
+ }
+
+ if (device->name.length == 0) {
+ ffStrbufSetStatic(&device->name, "Unknown Device");
+ }
+
+ if (device->connected && --connectedCount == 0) {
+ break;
+ }
+ }
+ } while (dbus->lib->ffdbus_message_iter_next(&arrayIter));
+}
+
+static const char* detectBluetooth(FFBluetoothOptions* options, FFlist* devices, int32_t connectedCount) {
+ FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
+ const char* error = ffDBusLoadData(DBUS_BUS_SYSTEM, &dbus);
+ if (error) {
+ return error;
+ }
+
+ DBusMessage* managedObjects = ffDBusGetMethodReply(&dbus, "org.bluez", "/", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", NULL, NULL);
+ if (!managedObjects) {
+ return "Failed to call GetManagedObjects";
+ }
+
+ DBusMessageIter rootIter;
+ if (!dbus.lib->ffdbus_message_iter_init(managedObjects, &rootIter)) {
+ dbus.lib->ffdbus_message_unref(managedObjects);
+ return "Failed to get root iterator of GetManagedObjects";
+ }
+
+ detectBluetoothRoot(options, devices, &dbus, &rootIter, connectedCount);
+
+ dbus.lib->ffdbus_message_unref(managedObjects);
+ return NULL;
+}
+
+static uint32_t connectedDevices(void) {
+ FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/class/bluetooth");
+ if (dirp == NULL) {
+ return 0;
+ }
+
+ uint32_t result = 0;
+ struct dirent* entry;
+ while ((entry = readdir(dirp)) != NULL) {
+ if (strchr(entry->d_name, ':') != NULL) {
+ ++result;
+ }
+ }
+
+ return result;
+}
+
+#endif
+
+const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */) {
+#ifdef FF_HAVE_DBUS
+ int32_t connectedCount = -1;
+ if (!options->showDisconnected) {
+ connectedCount = (int32_t) connectedDevices();
+ if (connectedCount == 0) {
+ return NULL;
+ }
+ }
+
+ return detectBluetooth(options, devices, connectedCount);
+#else
+ FF_UNUSED(options, devices);
+ return "Fastfetch was compiled without DBus support";
+#endif
+}