Fix wrong manufacturer data in release mode (#24)

* Fix wrong manufacturer data in release mode

* Improve code

* fix manufacturer data for little endian

* Minor fix

---------

Co-authored-by: Rohit Sangwan <rohitsangwan647@gmail.com>
This commit is contained in:
Foti Dim
2024-03-18 18:16:16 +01:00
committed by GitHub
parent fe0a654f36
commit 580fca6e4a
8 changed files with 147 additions and 47 deletions
+3
View File
@@ -4,6 +4,7 @@
"BTLE",
"bytevc",
"connnection",
"Cupertino",
"DSIMPLEBLE",
"hresult",
"hstring",
@@ -13,12 +14,14 @@
"LRESULT",
"microtask",
"modalias",
"msix",
"navideck",
"NSUUID",
"NTDDI",
"pairable",
"rssi",
"simpleble",
"sublist",
"unawaited",
"UNIVERSALBLE",
"Unpair",
+6 -6
View File
@@ -2,19 +2,19 @@ import 'package:flutter/foundation.dart';
class Capabilities {
static bool requiresRuntimePermission =
!_Platform.isWeb && !_Platform.isWindows && !_Platform.isLinux;
!Platform.isWeb && !Platform.isWindows && !Platform.isLinux;
static bool supportsBluetoothEnableApi =
!_Platform.isWeb && !_Platform.isCupertino;
!Platform.isWeb && !Platform.isCupertino;
static bool supportsConnectedDevicesApi = !_Platform.isWeb;
static bool supportsConnectedDevicesApi = !Platform.isWeb;
static bool supportsPairingApi = !_Platform.isWeb && !_Platform.isCupertino;
static bool supportsPairingApi = !Platform.isWeb && !Platform.isCupertino;
static bool supportsRequestMtuApi = !_Platform.isWeb;
static bool supportsRequestMtuApi = !Platform.isWeb;
}
class _Platform {
class Platform {
static bool isWeb = kIsWeb;
static bool isIOS = !isWeb && defaultTargetPlatform == TargetPlatform.iOS;
static bool isAndroid =
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:universal_ble/universal_ble.dart';
import 'package:universal_ble_example/data/capabilities.dart';
class ScannedItemWidget extends StatelessWidget {
final BleScanResult scanResult;
@@ -17,23 +18,33 @@ class ScannedItemWidget extends StatelessWidget {
title: Text(
'$name (${scanResult.rssi})',
),
subtitle: scanResult.isPaired != null
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("${scanResult.deviceId} "),
scanResult.isPaired == true
? const Text(
"Paired",
style: TextStyle(color: Colors.green),
)
: const Text(
"Not Paired",
style: TextStyle(color: Colors.red),
),
],
)
: Text(scanResult.deviceId),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(scanResult.deviceId),
// Show manufacturer data only on web and desktop
Visibility(
visible: (Platform.isWeb || Platform.isDesktop) &&
scanResult.manufacturerData?.isNotEmpty == true,
child: Text(
ManufacturerData.fromData(scanResult.manufacturerData!)
.toString(),
),
),
Visibility(
visible: scanResult.isPaired != null,
child: scanResult.isPaired == true
? const Text(
"Paired",
style: TextStyle(color: Colors.green),
)
: const Text(
"Not Paired",
style: TextStyle(color: Colors.red),
),
),
],
),
trailing: const Icon(Icons.arrow_forward_ios),
onTap: onTap,
),
+27
View File
@@ -22,3 +22,30 @@ class BleScanResult {
this.manufacturerData = manufacturerData ?? manufacturerDataHead;
}
}
/// Represents the manufacturer data of a BLE device.
/// Use [BleScanResult.manufacturerData] with [ManufacturerData.fromData] to create an instance of this class.
class ManufacturerData {
final int? companyId;
final Uint8List? data;
String? companyIdRadix16;
ManufacturerData(this.companyId, this.data) {
if (companyId != null) {
companyIdRadix16 = "0x0${companyId!.toRadixString(16)}";
}
}
factory ManufacturerData.fromData(Uint8List data) {
if (data.length < 2) return ManufacturerData(null, data);
int manufacturerIdInt = (data[0] + (data[1] << 8));
return ManufacturerData(
manufacturerIdInt,
data.sublist(2),
);
}
@override
String toString() {
return 'ManufacturerData: companyId: $companyIdRadix16, data: $data';
}
}
+34
View File
@@ -44,4 +44,38 @@ namespace universal_ble
poweredOn = 5,
};
enum class AdvertisementSectionType : uint8_t
{
Flags = 0x01,
IncompleteService16BitUuids = 0x02,
CompleteService16BitUuids = 0x03,
IncompleteService32BitUuids = 0x04,
CompleteService32BitUuids = 0x05,
IncompleteService128BitUuids = 0x06,
CompleteService128BitUuids = 0x07,
ShortenedLocalName = 0x08,
CompleteLocalName = 0x09,
TxPowerLevel = 0x0A,
ClassOfDevice = 0x0D,
SimplePairingHashC192 = 0x0E,
SecurityManagerTKValues = 0x10,
SecurityManagerOutOfBandFlags = 0x11,
SlaveConnectionIntervalRange = 0x12,
ServiceSolicitation16BitUuids = 0x14,
ServiceSolicitation32BitUuids = 0x1F,
ServiceSolicitation128BitUuids = 0x15,
ServiceData16BitUuids = 0x16,
ServiceData32BitUuids = 0x20,
ServiceData128BitUuids = 0x21,
PublicTargetAddress = 0x17,
RandomTargetAddress = 0x18,
Appearance = 0x19,
AdvertisingInterval = 0x1A,
LEBluetoothDeviceAddress = 0x1B,
LERole = 0x1C,
SimplePairingHashC256 = 0x1D,
ThreeDimensionInformationData = 0x3D,
ManufacturerSpecificData = 0xFF,
};
} // namespace universal_ble
+7
View File
@@ -119,4 +119,11 @@ namespace universal_ble
return std::string{chars};
}
bool isLittleEndian()
{
uint16_t number = 0x1;
char *numPtr = (char *)&number;
return (numPtr[0] == 1);
}
} // namespace SimpleBLE
+1
View File
@@ -25,6 +25,7 @@ namespace universal_ble
std::string to_hexstring(std::vector<uint8_t> bytes);
std::string to_uuidstr(winrt::guid guid);
bool isLittleEndian();
/// To call async functions synchronously
template <typename async_t>
+41 -24
View File
@@ -32,12 +32,6 @@ namespace universal_ble
std::unique_ptr<UniversalBleCallbackChannel> callbackChannel;
std::unordered_map<std::string, winrt::event_token> characteristicsTokens{}; // TODO: Remove the map and store the token inside the characteristic object object
union uint16_t_union
{
uint16_t uint16;
byte bytes[sizeof(uint16_t)];
};
void UniversalBlePlugin::RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar)
{
auto plugin = std::make_unique<UniversalBlePlugin>(registrar);
@@ -478,24 +472,46 @@ namespace universal_ble
{
try
{
if (advertisement == nullptr)
return {};
if (advertisement.ManufacturerData().Size() == 0)
{
return std::vector<uint8_t>();
}
return {};
auto manufacturerData = advertisement.ManufacturerData().GetAt(0);
// FIXME Compat with REG_DWORD_BIG_ENDIAN
uint8_t *prefix = uint16_t_union{manufacturerData.CompanyId()}.bytes;
auto result = std::vector<uint8_t>{prefix, prefix + sizeof(uint16_t_union)};
if (manufacturerData == nullptr)
return {};
uint16_t companyId = manufacturerData.CompanyId();
uint8_t prefix[2];
auto leastSignificantBit = static_cast<uint8_t>(companyId & 0xFF);
auto mostSignificantBit = static_cast<uint8_t>(companyId >> 8);
if (isLittleEndian())
{
prefix[0] = leastSignificantBit;
prefix[1] = mostSignificantBit;
}
else
{
prefix[0] = mostSignificantBit;
prefix[1] = leastSignificantBit;
}
std::vector<uint8_t> result = {prefix[0], prefix[1]};
auto data = to_bytevc(manufacturerData.Data());
result.insert(result.end(), data.begin(), data.end());
return result;
}
catch (const std::exception &e)
{
std::cerr << "Error in parsing manufacturer data for device " << deviceId << ": " << e.what() << std::endl;
}
catch (...)
{
std::cout << "Error in parsing manufacturer data: " << deviceId << std::endl;
return std::vector<uint8_t>();
std::cerr << "Unknown error occurred in parsing manufacturer data for device " << deviceId << std::endl;
}
return {};
}
winrt::fire_and_forget UniversalBlePlugin::InitializeAsync()
@@ -676,18 +692,19 @@ namespace universal_ble
auto universalScanResult = UniversalBleScanResult(deviceId);
std::string name = winrt::to_string(args.Advertisement().LocalName());
// Use CompleteName from dataType if localName is empty
if (name.empty())
auto dataSection = args.Advertisement().DataSections();
for (auto &&data : dataSection)
{
auto dataSection = args.Advertisement().DataSections();
for (auto &&data : dataSection)
auto dataBytes = to_bytevc(data.Data());
// Use CompleteName from dataType if localName is empty
if (name.empty() && data.DataType() == static_cast<uint8_t>(AdvertisementSectionType::CompleteLocalName))
{
auto dataBytes = to_bytevc(data.Data());
if (data.DataType() == 0x09)
{
name = std::string(dataBytes.begin(), dataBytes.end());
break;
}
name = std::string(dataBytes.begin(), dataBytes.end());
}
// Use ShortenedLocalName from dataType if localName is empty
else if (name.empty() && data.DataType() == static_cast<uint8_t>(AdvertisementSectionType::ShortenedLocalName))
{
name = std::string(dataBytes.begin(), dataBytes.end());
}
}