From 0929b940c877c6a313e579bc5448ccef9b7aa212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 29 Jul 2024 16:32:36 +0800 Subject: [PATCH] BluetoothRadio (macOS): detect more info --- src/detection/bluetoothradio/bluetoothradio.h | 2 +- .../bluetoothradio/bluetoothradio_apple.m | 74 +++++++++++++++---- .../bluetoothradio/bluetoothradio_linux.c | 12 +-- .../bluetoothradio/bluetoothradio_windows.c | 2 +- src/modules/bluetoothradio/bluetoothradio.c | 22 +++--- 5 files changed, 78 insertions(+), 34 deletions(-) diff --git a/src/detection/bluetoothradio/bluetoothradio.h b/src/detection/bluetoothradio/bluetoothradio.h index ad68ca2fd..46ea60f58 100644 --- a/src/detection/bluetoothradio/bluetoothradio.h +++ b/src/detection/bluetoothradio/bluetoothradio.h @@ -6,9 +6,9 @@ typedef struct FFBluetoothRadioResult { FFstrbuf name; FFstrbuf address; + FFstrbuf vendor; int32_t lmpVersion; int32_t lmpSubversion; - const char* vendor; bool enabled; bool discoverable; bool connectable; diff --git a/src/detection/bluetoothradio/bluetoothradio_apple.m b/src/detection/bluetoothradio/bluetoothradio_apple.m index 77414a5f6..9c0426c71 100644 --- a/src/detection/bluetoothradio/bluetoothradio_apple.m +++ b/src/detection/bluetoothradio/bluetoothradio_apple.m @@ -1,27 +1,69 @@ #include "bluetoothradio.h" +#include "common/processing.h" #import +// For some reason the official declaration of IOBluetoothHostController don't include property `controllers` +@interface IOBluetoothHostController() ++ (id)controllers; +@end + const char* ffDetectBluetoothRadio(FFlist* devices /* FFBluetoothResult */) { - IOBluetoothHostController* ctrl = IOBluetoothHostController.defaultController; - if(!ctrl) - return "IOBluetoothHostController.defaultController failed"; + NSArray* ctrls = IOBluetoothHostController.controllers; + if(!ctrls) + return "IOBluetoothHostController.controllers returns nil"; - FFBluetoothRadioResult* device = ffListAdd(devices); - ffStrbufInitS(&device->name, ctrl.nameAsString.UTF8String); - ffStrbufInitS(&device->address, ctrl.addressAsString.UTF8String); - device->lmpVersion = -1; - device->lmpSubversion = -1; - device->enabled = ctrl.powerState == kBluetoothHCIPowerStateON; - device->discoverable = false; - device->connectable = true; + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + if (ffProcessAppendStdOut(&buffer, (char* const[]) { + "system_profiler", + "SPBluetoothDataType", + "-xml", + "-detailLevel", + "basic", + NULL + }) != NULL) + return "Starting `system_profiler SPBluetoothDataType -xml -detailLevel basic` failed"; - #ifdef __aarch64__ - device->vendor = "Apple"; - #else - device->vendor = "Unknown"; // Hackintosh? - #endif + NSArray* arr = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytes:buffer.chars length:buffer.length] + options:NSPropertyListImmutable + format:nil + error:nil]; + if (!arr || !arr.count) + return "system_profiler SPBluetoothDataType returned an empty array"; + + for (IOBluetoothHostController* ctrl in ctrls) + { + FFBluetoothRadioResult* device = ffListAdd(devices); + ffStrbufInitS(&device->name, ctrl.nameAsString.UTF8String); + ffStrbufInitS(&device->address, ctrl.addressAsString.UTF8String); + ffStrbufInitStatic(&device->vendor, "Apple"); + device->lmpVersion = INT_MIN; + device->lmpSubversion = INT_MIN; + device->enabled = ctrl.powerState == kBluetoothHCIPowerStateON; + device->discoverable = false; + device->connectable = true; + + for (NSDictionary* itemDict in arr[0][@"_items"]) + { + NSDictionary* props = itemDict[@"controller_properties"]; + if (!props) continue; + + if (![ctrl.addressAsString isEqualToString:props[@"controller_address"]]) continue; + + NSString* services = props[@"controller_supportedServices"]; + if ([services containsString:@" LEA "]) + device->lmpVersion = -11; + else if ([services containsString:@" GATT "]) + device->lmpVersion = -6; + + device->discoverable = ![props[@"controller_discoverable"] isEqualToString:@"attrib_off"]; + ffStrbufSetS(&device->vendor, ((NSString*) props[@"controller_vendorID"]).UTF8String); + ffStrbufSubstrAfterFirstC(&device->vendor, '('); + ffStrbufTrimRight(&device->vendor, ')'); + break; + } + } return NULL; } diff --git a/src/detection/bluetoothradio/bluetoothradio_linux.c b/src/detection/bluetoothradio/bluetoothradio_linux.c index 8a1ac0108..dca38cf03 100644 --- a/src/detection/bluetoothradio/bluetoothradio_linux.c +++ b/src/detection/bluetoothradio/bluetoothradio_linux.c @@ -65,9 +65,9 @@ static void detectBluetoothValue(FFDBusData* dbus, DBusMessageIter* iter, FFBlue ffDBusGetString(dbus, &dictIter, &device->name); else if(ffStrEquals(deviceProperty, "Manufacturer")) { - uint16_t detection; - if (ffDBusGetUint16(dbus, &dictIter, &detection)) - device->vendor = ffBluetoothRadioGetVendor(detection); + uint16_t vendorId; + if (ffDBusGetUint16(dbus, &dictIter, &vendorId)) + ffStrbufSetStatic(&device->vendor, ffBluetoothRadioGetVendor(vendorId)); } else if(ffStrEquals(deviceProperty, "Version")) { @@ -144,9 +144,9 @@ static void detectBluetoothObject(FFlist* devices, FFDBusData* dbus, DBusMessage FFBluetoothRadioResult* device = ffListAdd(devices); ffStrbufInit(&device->name); ffStrbufInit(&device->address); - device->lmpVersion = -1; - device->lmpSubversion = -1; - device->vendor = "Unknown"; + ffStrbufInitStatic(&device->vendor, "Unknown"); + device->lmpVersion = INT_MIN; + device->lmpSubversion = INT_MIN; device->enabled = false; while(true) diff --git a/src/detection/bluetoothradio/bluetoothradio_windows.c b/src/detection/bluetoothradio/bluetoothradio_windows.c index 20693c04a..772b3b1b5 100644 --- a/src/detection/bluetoothradio/bluetoothradio_windows.c +++ b/src/detection/bluetoothradio/bluetoothradio_windows.c @@ -93,7 +93,7 @@ const char* ffDetectBluetoothRadio(FFlist* devices /* FFBluetoothRadioResult */) device->lmpVersion = blri.radioInfo.lmpVersion; device->lmpSubversion = blri.radioInfo.lmpSubversion; - device->vendor = ffBluetoothRadioGetVendor(blri.radioInfo.mfg); + ffStrbufSetStatic(&device->vendor, ffBluetoothRadioGetVendor(blri.radioInfo.mfg)); device->enabled = true; device->connectable = ffBluetoothIsConnectable(hRadio); device->discoverable = ffBluetoothIsDiscoverable(hRadio); diff --git a/src/modules/bluetoothradio/bluetoothradio.c b/src/modules/bluetoothradio/bluetoothradio.c index 8adf49e9c..956da3ea1 100644 --- a/src/modules/bluetoothradio/bluetoothradio.c +++ b/src/modules/bluetoothradio/bluetoothradio.c @@ -26,14 +26,14 @@ static void printDevice(FFBluetoothRadioOptions* options, const FFBluetoothRadio const char* version = NULL; - switch (radio->lmpVersion) + switch (radio->lmpVersion < 0 ? -radio->lmpVersion : radio->lmpVersion) { case 0: version = "1.0b"; break; case 1: version = "1.1"; break; case 2: version = "1.2"; break; - case 3: version = "2.0 + EDR"; break; - case 4: version = "2.1 + EDR"; break; - case 5: version = "3.0 + HS"; break; + case 3: version = "2.0"; break; + case 4: version = "2.1"; break; + case 5: version = "3.0"; break; case 6: version = "4.0"; break; case 7: version = "4.1"; break; case 8: version = "4.2"; break; @@ -49,9 +49,9 @@ static void printDevice(FFBluetoothRadioOptions* options, const FFBluetoothRadio ffPrintLogoAndKey(key.chars, index, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY); if (version) - printf("Bluetooth %s (%s)\n", version, radio->vendor); + printf("Bluetooth %s%s (%s)\n", version, (radio->lmpVersion < 0 ? "+" : ""), radio->vendor.chars); else - fputs(radio->vendor, stdout); + ffStrbufPutTo(&radio->vendor, stdout); } else { @@ -61,7 +61,7 @@ static void printDevice(FFBluetoothRadioOptions* options, const FFBluetoothRadio {FF_FORMAT_ARG_TYPE_INT, &radio->lmpVersion, "lmp-version"}, {FF_FORMAT_ARG_TYPE_INT, &radio->lmpSubversion, "lmp-subversion"}, {FF_FORMAT_ARG_TYPE_STRING, version, "version"}, - {FF_FORMAT_ARG_TYPE_STRING, radio->vendor, "vendor"}, + {FF_FORMAT_ARG_TYPE_STRBUF, &radio->vendor, "vendor"}, {FF_FORMAT_ARG_TYPE_BOOL, &radio->discoverable, "discoverable"}, {FF_FORMAT_ARG_TYPE_BOOL, &radio->connectable, "connectable"}, })); @@ -102,6 +102,7 @@ void ffPrintBluetoothRadio(FFBluetoothRadioOptions* options) { ffStrbufDestroy(&radio->name); ffStrbufDestroy(&radio->address); + ffStrbufDestroy(&radio->vendor); } } @@ -158,15 +159,15 @@ void ffGenerateBluetoothRadioJsonResult(FF_MAYBE_UNUSED FFBluetoothRadioOptions* yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); yyjson_mut_obj_add_strbuf(doc, obj, "address", &item->address); - if (item->lmpVersion < 0) + if (item->lmpVersion == INT_MIN) yyjson_mut_obj_add_null(doc, obj, "lmpVersion"); else yyjson_mut_obj_add_int(doc, obj, "lmpVersion", item->lmpVersion); - if (item->lmpSubversion < 0) + if (item->lmpSubversion == INT_MIN) yyjson_mut_obj_add_null(doc, obj, "lmpSubversion"); else yyjson_mut_obj_add_int(doc, obj, "lmpSubversion", item->lmpSubversion); - yyjson_mut_obj_add_str(doc, obj, "vendor", item->vendor); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &item->vendor); yyjson_mut_obj_add_bool(doc, obj, "enabled", item->enabled); yyjson_mut_obj_add_bool(doc, obj, "discoverable", item->discoverable); yyjson_mut_obj_add_bool(doc, obj, "connectable", item->connectable); @@ -176,6 +177,7 @@ void ffGenerateBluetoothRadioJsonResult(FF_MAYBE_UNUSED FFBluetoothRadioOptions* { ffStrbufDestroy(&radio->name); ffStrbufDestroy(&radio->address); + ffStrbufDestroy(&radio->vendor); } }