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:
Vendored
+3
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user