#include "bluetoothradio.h" #include "util/stringUtils.h" #ifdef FF_HAVE_DBUS #include "common/dbus.h" #include "common/io/io.h" /* Example dbus reply: array [ dict entry( string "Address" variant string "XX:XX:XX:XX:XX:XX" ) dict entry( string "Name" variant string "xxxxxxxx" ) dict entry( string "Powered" variant boolean true ) dict entry( string "PowerState" variant string "on" ) dict entry( string "Manufacturer" variant uint16 2 ) dict entry( string "Version" variant byte 12 ) ] */ static const char* detectBluetoothProperty(FFBluetoothRadioResult* device, FFDBusData* dbus, DBusMessageIter* iter) { if(dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) return "Expected dict entry"; DBusMessageIter dictIter; dbus->lib->ffdbus_message_iter_recurse(iter, &dictIter); if(dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_STRING) return "Expected dict entry key to be a string"; 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, "Alias")) ffDBusGetString(dbus, &dictIter, &device->name); else if(ffStrEquals(deviceProperty, "Manufacturer")) { uint32_t vendorId; if (ffDBusGetUint(dbus, &dictIter, &vendorId)) ffStrbufSetStatic(&device->vendor, ffBluetoothRadioGetVendor(vendorId)); } else if(ffStrEquals(deviceProperty, "Version")) ffDBusGetUint(dbus, &dictIter, (uint32_t*) &device->lmpVersion); else if(ffStrEquals(deviceProperty, "Powered")) ffDBusGetBool(dbus, &dictIter, &device->enabled); else if(ffStrEquals(deviceProperty, "Discoverable")) ffDBusGetBool(dbus, &dictIter, &device->discoverable); else if(ffStrEquals(deviceProperty, "Pairable")) ffDBusGetBool(dbus, &dictIter, &device->connectable); return NULL; } static const char* detectBluetoothRoot(FFBluetoothRadioResult* device, const char* hciName, FFDBusData* dbus) { char objPath[300]; snprintf(objPath, sizeof(objPath), "/org/bluez/%s", hciName); DBusMessage* properties = ffDBusGetMethodReply(dbus, "org.bluez", objPath, "org.freedesktop.DBus.Properties", "GetAll", "org.bluez.Adapter1"); if(!properties) return "Failed to call org.freedesktop.DBus.Properties.GetAll"; DBusMessageIter rootIter; if(!dbus->lib->ffdbus_message_iter_init(properties, &rootIter)) { dbus->lib->ffdbus_message_unref(properties); return "Failed to get root iterator of org.freedesktop.DBus.Properties.GetAll"; } if(dbus->lib->ffdbus_message_iter_get_arg_type(&rootIter) != DBUS_TYPE_ARRAY) { dbus->lib->ffdbus_message_unref(properties); return "Expected array"; } DBusMessageIter arrayIter; dbus->lib->ffdbus_message_iter_recurse(&rootIter, &arrayIter); do { detectBluetoothProperty(device, dbus, &arrayIter); } while (dbus->lib->ffdbus_message_iter_next(&arrayIter)); dbus->lib->ffdbus_message_unref(properties); return NULL; } static const char* detectBluetooth(FFlist* devices) { FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/class/bluetooth"); if(dirp == NULL) return "Failed to open /sys/class/bluetooth"; FFDBusData dbus; const char* error = ffDBusLoadData(DBUS_BUS_SYSTEM, &dbus); if(error) return error; struct dirent* entry; while ((entry = readdir(dirp)) != NULL) { if (entry->d_name[0] == '.') continue; if (strchr(entry->d_name, ':') != NULL) // ignore connected devices continue; FFBluetoothRadioResult* device = ffListAdd(devices); ffStrbufInit(&device->name); ffStrbufInit(&device->address); ffStrbufInitStatic(&device->vendor, "Unknown"); device->lmpVersion = INT_MIN; device->lmpSubversion = INT_MIN; device->enabled = false; detectBluetoothRoot(device, entry->d_name, &dbus); } return NULL; } #endif const char* ffDetectBluetoothRadio(FFlist* devices /* FFBluetoothRadioResult */) { #ifdef FF_HAVE_DBUS return detectBluetooth(devices); #else FF_UNUSED(devices) return "Fastfetch was compiled without DBus support"; #endif }