Add manufacturer data filtering (#23)

* Add manufacturer data filtering

* Implement manufacturer data filter in windows

* Fix manufacturerData filtering on windows

* Fix linux

* Clean comments

---------

Co-authored-by: Foti Dim <fdimanidis@gmail.com>
This commit is contained in:
Rohit Sangwan
2024-03-18 23:14:44 +05:30
committed by GitHub
parent 580fca6e4a
commit 1a0e48ba1a
22 changed files with 770 additions and 356 deletions
+1
View File
@@ -21,6 +21,7 @@
"pairable",
"rssi",
"simpleble",
"subdata",
"sublist",
"unawaited",
"UNIVERSALBLE",
@@ -127,21 +127,53 @@ data class UniversalBleCharacteristic (
}
}
/** Generated class from Pigeon that represents data sent in messages. */
/**
* Scan Filters
*
* Generated class from Pigeon that represents data sent in messages.
*/
data class UniversalScanFilter (
val withServices: List<String?>
val withServices: List<String?>,
val withManufacturerData: List<UniversalManufacturerDataFilter?>
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): UniversalScanFilter {
val withServices = list[0] as List<String?>
return UniversalScanFilter(withServices)
val withManufacturerData = list[1] as List<UniversalManufacturerDataFilter?>
return UniversalScanFilter(withServices, withManufacturerData)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
withServices,
withManufacturerData,
)
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class UniversalManufacturerDataFilter (
val companyIdentifier: Long? = null,
val data: ByteArray? = null,
val mask: ByteArray? = null
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): UniversalManufacturerDataFilter {
val companyIdentifier = list[0].let { if (it is Int) it.toLong() else it as Long? }
val data = list[1] as ByteArray?
val mask = list[2] as ByteArray?
return UniversalManufacturerDataFilter(companyIdentifier, data, mask)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
companyIdentifier,
data,
mask,
)
}
}
@@ -166,6 +198,11 @@ private object UniversalBlePlatformChannelCodec : StandardMessageCodec() {
}
}
131.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalManufacturerDataFilter.fromList(it)
}
}
132.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalScanFilter.fromList(it)
}
@@ -187,10 +224,14 @@ private object UniversalBlePlatformChannelCodec : StandardMessageCodec() {
stream.write(130)
writeValue(stream, value.toList())
}
is UniversalScanFilter -> {
is UniversalManufacturerDataFilter -> {
stream.write(131)
writeValue(stream, value.toList())
}
is UniversalScanFilter -> {
stream.write(132)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
@@ -144,7 +144,7 @@ fun Int.parseGattErrorCode(): String? {
}
fun UniversalScanFilter.toScanFilters(): List<ScanFilter> {
var scanFilters: List<ScanFilter> = emptyList()
var scanFilters: ArrayList<ScanFilter> = arrayListOf()
// Add withServices Filter
for (service in this.withServices) {
@@ -152,7 +152,7 @@ fun UniversalScanFilter.toScanFilters(): List<ScanFilter> {
val serviceUUID = service?.validFullUUID()
serviceUUID?.let {
val parcelUUId = ParcelUuid.fromString(it)
scanFilters = scanFilters.plus(
scanFilters.add(
ScanFilter.Builder().setServiceUuid(parcelUUId).build()
)
Log.e(TAG, "scanFilters: $parcelUUId")
@@ -167,7 +167,37 @@ fun UniversalScanFilter.toScanFilters(): List<ScanFilter> {
}
}
return scanFilters
// Add ManufacturerData Filter
for (manufacturerData in this.withManufacturerData) {
try {
manufacturerData?.companyIdentifier?.let {
val data: ByteArray = manufacturerData.data ?: ByteArray(0)
val mask: ByteArray? = manufacturerData.mask
if (mask == null) {
scanFilters.add(
ScanFilter.Builder().setManufacturerData(
it.toInt(), data
).build()
)
} else {
scanFilters.add(
ScanFilter.Builder().setManufacturerData(
it.toInt(), data, mask
).build()
)
}
}
} catch (e: Exception) {
Log.e(TAG, e.toString())
throw FlutterError(
"illegalIllegalArgument",
"Invalid manufacturerData: ${manufacturerData?.companyIdentifier} ${manufacturerData?.data} ${manufacturerData?.mask}",
e.toString()
)
}
}
return scanFilters.toList()
}
val ScanResult.manufacturerDataHead: ByteArray?
+40 -3
View File
@@ -129,20 +129,52 @@ struct UniversalBleCharacteristic {
}
}
/// Scan Filters
///
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalScanFilter {
var withServices: [String?]
var withManufacturerData: [UniversalManufacturerDataFilter?]
static func fromList(_ list: [Any?]) -> UniversalScanFilter? {
let withServices = list[0] as! [String?]
let withManufacturerData = list[1] as! [UniversalManufacturerDataFilter?]
return UniversalScanFilter(
withServices: withServices
withServices: withServices,
withManufacturerData: withManufacturerData
)
}
func toList() -> [Any?] {
return [
withServices
withServices,
withManufacturerData,
]
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalManufacturerDataFilter {
var companyIdentifier: Int64? = nil
var data: FlutterStandardTypedData? = nil
var mask: FlutterStandardTypedData? = nil
static func fromList(_ list: [Any?]) -> UniversalManufacturerDataFilter? {
let companyIdentifier: Int64? = isNullish(list[0]) ? nil : (list[0] is Int64? ? list[0] as! Int64? : Int64(list[0] as! Int32))
let data: FlutterStandardTypedData? = nilOrValue(list[1])
let mask: FlutterStandardTypedData? = nilOrValue(list[2])
return UniversalManufacturerDataFilter(
companyIdentifier: companyIdentifier,
data: data,
mask: mask
)
}
func toList() -> [Any?] {
return [
companyIdentifier,
data,
mask,
]
}
}
@@ -157,6 +189,8 @@ private class UniversalBlePlatformChannelCodecReader: FlutterStandardReader {
case 130:
return UniversalBleService.fromList(self.readValue() as! [Any?])
case 131:
return UniversalManufacturerDataFilter.fromList(self.readValue() as! [Any?])
case 132:
return UniversalScanFilter.fromList(self.readValue() as! [Any?])
default:
return super.readValue(ofType: type)
@@ -175,9 +209,12 @@ private class UniversalBlePlatformChannelCodecWriter: FlutterStandardWriter {
} else if let value = value as? UniversalBleService {
super.writeByte(130)
super.writeValue(value.toList())
} else if let value = value as? UniversalScanFilter {
} else if let value = value as? UniversalManufacturerDataFilter {
super.writeByte(131)
super.writeValue(value.toList())
} else if let value = value as? UniversalScanFilter {
super.writeByte(132)
super.writeValue(value.toList())
} else {
super.writeValue(value)
}
+45 -2
View File
@@ -143,11 +143,11 @@ public extension CBPeripheral {
extension String {
var validFullUUID: String {
let uuidLength = self.count
let uuidLength = count
if uuidLength == 4 || uuidLength == 8 {
let baseUuid = "00000000-0000-1000-8000-00805F9B34FB"
let start = baseUuid.startIndex
let range = baseUuid.index(start, offsetBy: 4-uuidLength)..<baseUuid.index(start, offsetBy: 4)
let range = baseUuid.index(start, offsetBy: 4 - uuidLength) ..< baseUuid.index(start, offsetBy: 4)
return baseUuid.replacingCharacters(in: range, with: self)
} else {
return self
@@ -155,6 +155,49 @@ extension String {
}
}
extension FlutterStandardTypedData {
func toData() -> Data {
return Data(data)
}
}
func isManufacturerDataMatchingFilters(filters: [UniversalManufacturerDataFilter], msd: Data?) -> Bool {
guard let msd = msd, !msd.isEmpty else {
return false
}
for filter in filters {
guard let companyIdentifier: Int64 = filter.companyIdentifier else {
continue
}
let manufacturerId = msd.subdata(in: 0 ..< 2).withUnsafeBytes { $0.load(as: UInt16.self) }
let manufacturerData = msd.subdata(in: 2 ..< msd.count)
if manufacturerId == companyIdentifier && findData(find: filter.data?.toData(), inData: manufacturerData, usingMask: filter.mask?.toData()) {
return true
}
}
return false
}
func findData(find: Data?, inData data: Data, usingMask mask: Data?) -> Bool {
if let find = find {
// If mask is null, use a default mask of all 1s
let mask = mask ?? Data(repeating: 0xFF, count: find.count)
// Ensure find & mask are same length
guard find.count == mask.count else {
return false
}
for i in 0 ..< find.count {
// Perform bitwise AND with mask and then compare
if (find[i] & mask[i]) != (data[i] & mask[i]) {
return false
}
}
}
return true
}
// Future classes
class CharacteristicReadFuture {
let deviceId: String
+15 -2
View File
@@ -27,6 +27,7 @@ private var discoveredPeripherals = [String: CBPeripheral]()
private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentralManagerDelegate, CBPeripheralDelegate {
var callbackChannel: UniversalBleCallbackChannel
private lazy var manager: CBCentralManager = .init(delegate: self, queue: nil)
private var scanFilter: UniversalScanFilter? = nil
private var discoveredServicesProgressMap: [String: [UniversalBleService]] = [:]
private var characteristicReadFutures = [CharacteristicReadFuture]()
private var characteristicWriteFutures = [CharacteristicWriteFuture]()
@@ -57,18 +58,20 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
}
func startScan(filter: UniversalScanFilter?) throws {
// Apply services filter
var withServices: [CBUUID] = []
for service in filter?.withServices ?? [] {
if let service = service {
if UUID(uuidString: service.validFullUUID) == nil {
if UUID(uuidString: service.validFullUUID) == nil {
throw FlutterError(code: "IllegalArgument", message: "Invalid service UUID:\(service)", details: nil)
}
withServices.append(CBUUID(string: service))
}
}
// Save scanFilter for later user
scanFilter = filter
manager.scanForPeripherals(withServices: withServices)
}
@@ -241,6 +244,16 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
discoveredPeripherals[peripheral.uuid.uuidString] = peripheral
let manufacturerData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data
let services = advertisementData[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID]
// Handle ScanFilters
if let filter = scanFilter {
let manufacturerFilter = filter.withManufacturerData.compactMap { $0 }
// If scan filters are not empty, check if manufacturer data matches filters
if !manufacturerFilter.isEmpty, !isManufacturerDataMatchingFilters(filters: manufacturerFilter, msd: manufacturerData) {
return
}
}
callbackChannel.onScanResult(result: UniversalBleScanResult(
deviceId: peripheral.uuid.uuidString,
name: peripheral.name,
-1
View File
@@ -24,7 +24,6 @@ class MockUniversalBle extends UniversalBlePlatform {
@override
Future<void> startScan({
WebRequestOptionsBuilder? webRequestOptions,
ScanFilter? scanFilter,
}) async {
onScanResult?.call(_mockBleScanResult);
+24 -12
View File
@@ -25,9 +25,9 @@ class _MyAppState extends State<MyApp> {
bool _isQueueEnabled = true;
AvailabilityState? bleAvailabilityState;
late WebRequestOptionsBuilder _requestOptions;
final List<String> _services = [
"00001800-0000-1000-8000-00805f9b34fb",
"0000180f-0000-1000-8000-00805f9b34fb",
"00002a00-0000-1000-8000-00805f9b34fb",
"00002a01-0000-1000-8000-00805f9b34fb",
"00002a19-0000-1000-8000-00805f9b34fb",
@@ -48,13 +48,6 @@ class _MyAppState extends State<MyApp> {
UniversalBle.queuesCommands = _isQueueEnabled;
UniversalBle.timeout = const Duration(seconds: 10);
/// Add common services for web
if (kIsWeb) {
_services.addAll(WebRequestOptionsBuilder.defaultServices);
}
_requestOptions =
WebRequestOptionsBuilder.acceptAllDevices(optionalServices: _services);
UniversalBle.onAvailabilityChange = (state) {
setState(() {
bleAvailabilityState = state;
@@ -77,6 +70,28 @@ class _MyAppState extends State<MyApp> {
};
}
Future<void> startScan() async {
await UniversalBle.startScan(
scanFilter: ScanFilter(
withServices: kIsWeb ? _services : [],
withManufacturerData: [
ManufacturerDataFilter(
companyIdentifier: 0x012D,
data: Uint8List.fromList(
[0x03, 0x00, 0x64, 0x00],
),
),
ManufacturerDataFilter(
companyIdentifier: 0x012D,
data: Uint8List.fromList(
[0x03, 0x00, 0x65, 0x00],
),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -110,10 +125,7 @@ class _MyAppState extends State<MyApp> {
_isScanning = true;
});
try {
await UniversalBle.startScan(
webRequestOptions: _requestOptions,
// scanFilter: ScanFilter(withServices: ['180f']),
);
await startScan();
} catch (e) {
setState(() {
_isScanning = false;
-1
View File
@@ -1,6 +1,5 @@
export 'package:universal_ble/src/models/uuid.dart';
export 'package:universal_ble/src/models/scan_filter.dart';
export 'package:universal_ble/src/models/web_request_options_builder.dart';
export 'package:universal_ble/src/models/ble_property.dart';
export 'package:universal_ble/src/models/ble_service.dart';
export 'package:universal_ble/src/models/availability_state.dart';
+22
View File
@@ -1,7 +1,29 @@
import 'dart:typed_data';
class ScanFilter {
final List<String> withServices;
final List<ManufacturerDataFilter> withManufacturerData;
ScanFilter({
this.withServices = const [],
this.withManufacturerData = const [],
});
}
class ManufacturerDataFilter {
int? companyIdentifier;
// Mask and data must be of same length
Uint8List? data;
/// For any bit in the mask, set it the 1 if it needs to match
/// the one in manufacturer data, otherwise set it to 0.
/// The 'mask' must have the same length as 'data'.
Uint8List? mask;
ManufacturerDataFilter({
this.companyIdentifier,
this.data,
this.mask,
});
}
@@ -1,124 +0,0 @@
import 'dart:typed_data';
import 'package:flutter_web_bluetooth/flutter_web_bluetooth.dart';
import 'package:universal_ble/universal_ble.dart';
class WebRequestOptionsBuilder {
final bool _acceptAllDevices;
final List<WebRequestFilterBuilder> _requestFilters;
final List<WebRequestFilterBuilder>? _exclusionFilters;
List<String>? _optionalServices;
WebRequestOptionsBuilder(
List<WebRequestFilterBuilder> requestFilters, {
List<WebRequestFilterBuilder>? exclusionFilters,
List<String>? optionalServices,
}) : _optionalServices = optionalServices,
_requestFilters = requestFilters,
_exclusionFilters = exclusionFilters,
_acceptAllDevices = false {
if (_requestFilters.isEmpty) {
throw StateError(
'No filters have been set, consider using '
'RequestOptionsBuilder.acceptAllDevices() instead.',
);
}
}
/// To accept all devices
WebRequestOptionsBuilder.acceptAllDevices({
List<String>? optionalServices,
}) : _optionalServices = optionalServices,
_acceptAllDevices = true,
_requestFilters = [],
_exclusionFilters = null;
/// For internal use
/// To convert UniversalBleRequestOptions to FlutterWebBluetoothRequestOptions
RequestOptionsBuilder toRequestOptionsBuilder({ScanFilter? scanFilter}) {
if (scanFilter != null) _applyScanFilter(scanFilter);
if (_acceptAllDevices) {
return RequestOptionsBuilder.acceptAllDevices(
optionalServices: _optionalServices,
);
}
return RequestOptionsBuilder(
_requestFilters.map((e) => e.getRequestFilterBuilder()).toList(),
exclusionFilters:
_exclusionFilters?.map((e) => e.getRequestFilterBuilder()).toList(),
optionalServices: _optionalServices,
);
}
/// To apply scan filter
void _applyScanFilter(ScanFilter scanFilter) {
List<String> filterServices = scanFilter.withServices;
_optionalServices ??= filterServices.toValidUUIDList();
for (var requestFilter in _requestFilters) {
// Apply services filter
requestFilter.services ??= filterServices.toValidUUIDList();
}
}
/// List of common services
static List<String> get defaultServices {
return BluetoothDefaultServiceUUIDS.values
.map((e) => e.uuid.toString())
.toList();
}
/// List of common services in 16 bit
static List<String> get defaultServices16Bit {
return BluetoothDefaultServiceUUIDS.values
.map((e) => e.uuid16.toString())
.toList();
}
}
/// To filter by name, namePrefix, services, manufacturerData
class WebRequestFilterBuilder {
String? name;
String? namePrefix;
List<String>? services;
List<WebManufacturerDataFilterBuilder>? manufacturerData;
WebRequestFilterBuilder({
this.name,
this.namePrefix,
this.services,
this.manufacturerData,
});
RequestFilterBuilder getRequestFilterBuilder() {
return RequestFilterBuilder(
name: name,
namePrefix: namePrefix,
services: services,
manufacturerData: manufacturerData
?.map((e) => e.getManufacturerDataFilterBuilder())
.toList(),
);
}
}
/// To filter by manufacturer data
class WebManufacturerDataFilterBuilder {
int? companyIdentifier;
Uint8List? dataPrefix;
Uint8List? mask;
WebManufacturerDataFilterBuilder({
this.companyIdentifier,
this.dataPrefix,
this.mask,
});
ManufacturerDataFilterBuilder getManufacturerDataFilterBuilder() {
return ManufacturerDataFilterBuilder(
companyIdentifier: companyIdentifier,
dataPrefix: dataPrefix,
mask: mask,
);
}
}
+1 -5
View File
@@ -44,14 +44,10 @@ class UniversalBle {
/// might throw errors if Bluetooth is not available
/// `webRequestOptions` supported on Web only
static Future<void> startScan({
WebRequestOptionsBuilder? webRequestOptions,
ScanFilter? scanFilter,
}) async {
return await _executeCommand(
() => _platform.startScan(
webRequestOptions: webRequestOptions,
scanFilter: scanFilter,
),
() => _platform.startScan(scanFilter: scanFilter),
timeout: null,
);
}
@@ -17,6 +17,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
final BlueZClient _client = BlueZClient();
BlueZAdapter? _activeAdapter;
ScanFilter? _scanFilter;
Completer<void>? _initializationCompleter;
final Map<String, BlueZDevice> _devices = {};
final Map<String, StreamSubscription> _deviceStreamSubscriptions = {};
@@ -54,7 +55,6 @@ class UniversalBleLinux extends UniversalBlePlatform {
@override
Future<void> startScan({
WebRequestOptionsBuilder? webRequestOptions,
ScanFilter? scanFilter,
}) async {
await _ensureInitialized();
@@ -63,6 +63,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
_activeAdapter?.setDiscoveryFilter(
uuids: scanFilter?.withServices.toValidUUIDList(),
);
_scanFilter = scanFilter;
await _activeAdapter?.startDiscovery();
_client.devices.forEach(_onDeviceAdd);
}
@@ -371,6 +372,16 @@ class UniversalBleLinux extends UniversalBlePlatform {
}
void _onDeviceAdd(BlueZDevice device) {
if (!_isValidDevice(device)) return;
// Apply Filters
if (_scanFilter != null) {
List<ManufacturerDataFilter>? manufacturerDataFilter =
_scanFilter?.withManufacturerData;
if (manufacturerDataFilter != null &&
manufacturerDataFilter.isNotEmpty) {}
}
// Update scan results only if rssi is available
if (device.rssi != 0) onScanResult?.call(device.toBleScanResult());
@@ -421,6 +432,79 @@ class UniversalBleLinux extends UniversalBlePlatform {
});
}
bool _isValidDevice(BlueZDevice device) {
ScanFilter? scanFilter = _scanFilter;
if (scanFilter == null) return true;
// Check manufacturerData filter
if (!_isValidManufacturerData(
scanFilter.withManufacturerData,
device.manufacturerDataFilter,
)) {
return false;
}
return true;
}
bool _isValidManufacturerData(
List<ManufacturerDataFilter> filterMfdList,
List<ManufacturerDataFilter> deviceMfdList,
) {
if (filterMfdList.isEmpty) return true;
if (deviceMfdList.isEmpty) return false;
// Check all filters
for (var filterMfd in filterMfdList) {
// Check if device have manufacturerData for this filter
for (var deviceMfd in deviceMfdList) {
// Check companyIdentifier
if (filterMfd.companyIdentifier != deviceMfd.companyIdentifier) {
continue;
}
// Check data
Uint8List? filterData = filterMfd.data;
Uint8List? deviceData = deviceMfd.data;
// If filter data is null and device data is not, continue to next deviceMfd
if (filterData != null && deviceData == null) continue;
if (filterData == null || deviceData == null) {
return true;
}
if (filterData.length > deviceData.length) continue;
// Apply mask
Uint8List? filterMask = filterMfd.mask;
bool dataMatched = true;
if (filterMask != null && (filterMask.length == filterData.length)) {
for (int i = 0; i < filterData.length; i++) {
if ((filterData[i] & filterMask[i]) !=
(deviceData[i] & filterMask[i])) {
dataMatched = false;
break;
}
}
}
// Compare data directly
else {
for (int i = 0; i < filterData.length; i++) {
if (filterData[i] != deviceData[i]) {
dataMatched = false;
break;
}
}
}
if (dataMatched) return true;
}
}
return false;
}
void _onDeviceRemoved(BlueZDevice device) {
_devices.remove(device.address);
// Stop listener
@@ -451,6 +535,24 @@ class BluezProperty {
}
extension BlueZDeviceExtension on BlueZDevice {
List<ManufacturerDataFilter> get manufacturerDataFilter {
try {
if (manufacturerData.isEmpty) return [];
return manufacturerData.entries
.map((e) => ManufacturerDataFilter(
companyIdentifier: e.key.id,
data: Uint8List.fromList(e.value),
))
.toList();
} catch (e) {
UniversalBlePlatform.logInfo(
'Error parsing manufacturerData: $e',
isError: true,
);
return [];
}
}
Uint8List get manufacturerDataHead {
try {
if (manufacturerData.isEmpty) return Uint8List(0);
+125 -149
View File
@@ -15,8 +15,7 @@ PlatformException _createConnectionError(String channelName) {
);
}
List<Object?> wrapResponse(
{Object? result, PlatformException? error, bool empty = false}) {
List<Object?> wrapResponse({Object? result, PlatformException? error, bool empty = false}) {
if (empty) {
return <Object?>[];
}
@@ -98,8 +97,7 @@ class UniversalBleService {
result as List<Object?>;
return UniversalBleService(
uuid: result[0]! as String,
characteristics:
(result[1] as List<Object?>?)?.cast<UniversalBleCharacteristic?>(),
characteristics: (result[1] as List<Object?>?)?.cast<UniversalBleCharacteristic?>(),
);
}
}
@@ -130,16 +128,21 @@ class UniversalBleCharacteristic {
}
}
/// Scan Filters
class UniversalScanFilter {
UniversalScanFilter({
required this.withServices,
required this.withManufacturerData,
});
List<String?> withServices;
List<UniversalManufacturerDataFilter?> withManufacturerData;
Object encode() {
return <Object?>[
withServices,
withManufacturerData,
];
}
@@ -147,6 +150,38 @@ class UniversalScanFilter {
result as List<Object?>;
return UniversalScanFilter(
withServices: (result[0] as List<Object?>?)!.cast<String?>(),
withManufacturerData: (result[1] as List<Object?>?)!.cast<UniversalManufacturerDataFilter?>(),
);
}
}
class UniversalManufacturerDataFilter {
UniversalManufacturerDataFilter({
this.companyIdentifier,
this.data,
this.mask,
});
int? companyIdentifier;
Uint8List? data;
Uint8List? mask;
Object encode() {
return <Object?>[
companyIdentifier,
data,
mask,
];
}
static UniversalManufacturerDataFilter decode(Object result) {
result as List<Object?>;
return UniversalManufacturerDataFilter(
companyIdentifier: result[0] as int?,
data: result[1] as Uint8List?,
mask: result[2] as Uint8List?,
);
}
}
@@ -164,9 +199,12 @@ class _UniversalBlePlatformChannelCodec extends StandardMessageCodec {
} else if (value is UniversalBleService) {
buffer.putUint8(130);
writeValue(buffer, value.encode());
} else if (value is UniversalScanFilter) {
} else if (value is UniversalManufacturerDataFilter) {
buffer.putUint8(131);
writeValue(buffer, value.encode());
} else if (value is UniversalScanFilter) {
buffer.putUint8(132);
writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
@@ -175,13 +213,15 @@ class _UniversalBlePlatformChannelCodec extends StandardMessageCodec {
@override
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
case 128:
case 128:
return UniversalBleCharacteristic.decode(readValue(buffer)!);
case 129:
case 129:
return UniversalBleScanResult.decode(readValue(buffer)!);
case 130:
case 130:
return UniversalBleService.decode(readValue(buffer)!);
case 131:
case 131:
return UniversalManufacturerDataFilter.decode(readValue(buffer)!);
case 132:
return UniversalScanFilter.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
@@ -198,14 +238,11 @@ class UniversalBlePlatformChannel {
: __pigeon_binaryMessenger = binaryMessenger;
final BinaryMessenger? __pigeon_binaryMessenger;
static const MessageCodec<Object?> pigeonChannelCodec =
_UniversalBlePlatformChannelCodec();
static const MessageCodec<Object?> pigeonChannelCodec = _UniversalBlePlatformChannelCodec();
Future<int> getBluetoothAvailabilityState() async {
const String __pigeon_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getBluetoothAvailabilityState';
final BasicMessageChannel<Object?> __pigeon_channel =
BasicMessageChannel<Object?>(
const String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getBluetoothAvailabilityState';
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
__pigeon_channelName,
pigeonChannelCodec,
binaryMessenger: __pigeon_binaryMessenger,
@@ -231,10 +268,8 @@ class UniversalBlePlatformChannel {
}
Future<bool> enableBluetooth() async {
const String __pigeon_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.enableBluetooth';
final BasicMessageChannel<Object?> __pigeon_channel =
BasicMessageChannel<Object?>(
const String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.enableBluetooth';
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
__pigeon_channelName,
pigeonChannelCodec,
binaryMessenger: __pigeon_binaryMessenger,
@@ -260,10 +295,8 @@ class UniversalBlePlatformChannel {
}
Future<void> startScan(UniversalScanFilter? filter) async {
const String __pigeon_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.startScan';
final BasicMessageChannel<Object?> __pigeon_channel =
BasicMessageChannel<Object?>(
const String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.startScan';
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
__pigeon_channelName,
pigeonChannelCodec,
binaryMessenger: __pigeon_binaryMessenger,
@@ -284,10 +317,8 @@ class UniversalBlePlatformChannel {
}
Future<void> stopScan() async {
const String __pigeon_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.stopScan';
final BasicMessageChannel<Object?> __pigeon_channel =
BasicMessageChannel<Object?>(
const String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.stopScan';
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
__pigeon_channelName,
pigeonChannelCodec,
binaryMessenger: __pigeon_binaryMessenger,
@@ -308,10 +339,8 @@ class UniversalBlePlatformChannel {
}
Future<void> connect(String deviceId) async {
const String __pigeon_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.connect';
final BasicMessageChannel<Object?> __pigeon_channel =
BasicMessageChannel<Object?>(
const String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.connect';
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
__pigeon_channelName,
pigeonChannelCodec,
binaryMessenger: __pigeon_binaryMessenger,
@@ -332,10 +361,8 @@ class UniversalBlePlatformChannel {
}
Future<void> disconnect(String deviceId) async {
const String __pigeon_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.disconnect';
final BasicMessageChannel<Object?> __pigeon_channel =
BasicMessageChannel<Object?>(
const String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.disconnect';
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
__pigeon_channelName,
pigeonChannelCodec,
binaryMessenger: __pigeon_binaryMessenger,
@@ -355,19 +382,15 @@ class UniversalBlePlatformChannel {
}
}
Future<void> setNotifiable(String deviceId, String service,
String characteristic, int bleInputProperty) async {
const String __pigeon_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setNotifiable';
final BasicMessageChannel<Object?> __pigeon_channel =
BasicMessageChannel<Object?>(
Future<void> setNotifiable(String deviceId, String service, String characteristic, int bleInputProperty) async {
const String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setNotifiable';
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
__pigeon_channelName,
pigeonChannelCodec,
binaryMessenger: __pigeon_binaryMessenger,
);
final List<Object?>? __pigeon_replyList = await __pigeon_channel.send(
<Object?>[deviceId, service, characteristic, bleInputProperty])
as List<Object?>?;
final List<Object?>? __pigeon_replyList =
await __pigeon_channel.send(<Object?>[deviceId, service, characteristic, bleInputProperty]) as List<Object?>?;
if (__pigeon_replyList == null) {
throw _createConnectionError(__pigeon_channelName);
} else if (__pigeon_replyList.length > 1) {
@@ -382,10 +405,8 @@ class UniversalBlePlatformChannel {
}
Future<List<UniversalBleService?>> discoverServices(String deviceId) async {
const String __pigeon_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.discoverServices';
final BasicMessageChannel<Object?> __pigeon_channel =
BasicMessageChannel<Object?>(
const String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.discoverServices';
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
__pigeon_channelName,
pigeonChannelCodec,
binaryMessenger: __pigeon_binaryMessenger,
@@ -406,23 +427,19 @@ class UniversalBlePlatformChannel {
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (__pigeon_replyList[0] as List<Object?>?)!
.cast<UniversalBleService?>();
return (__pigeon_replyList[0] as List<Object?>?)!.cast<UniversalBleService?>();
}
}
Future<Uint8List> readValue(
String deviceId, String service, String characteristic) async {
const String __pigeon_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.readValue';
final BasicMessageChannel<Object?> __pigeon_channel =
BasicMessageChannel<Object?>(
Future<Uint8List> readValue(String deviceId, String service, String characteristic) async {
const String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.readValue';
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
__pigeon_channelName,
pigeonChannelCodec,
binaryMessenger: __pigeon_binaryMessenger,
);
final List<Object?>? __pigeon_replyList = await __pigeon_channel
.send(<Object?>[deviceId, service, characteristic]) as List<Object?>?;
final List<Object?>? __pigeon_replyList =
await __pigeon_channel.send(<Object?>[deviceId, service, characteristic]) as List<Object?>?;
if (__pigeon_replyList == null) {
throw _createConnectionError(__pigeon_channelName);
} else if (__pigeon_replyList.length > 1) {
@@ -442,16 +459,14 @@ class UniversalBlePlatformChannel {
}
Future<int> requestMtu(String deviceId, int expectedMtu) async {
const String __pigeon_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestMtu';
final BasicMessageChannel<Object?> __pigeon_channel =
BasicMessageChannel<Object?>(
const String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestMtu';
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
__pigeon_channelName,
pigeonChannelCodec,
binaryMessenger: __pigeon_binaryMessenger,
);
final List<Object?>? __pigeon_replyList = await __pigeon_channel
.send(<Object?>[deviceId, expectedMtu]) as List<Object?>?;
final List<Object?>? __pigeon_replyList =
await __pigeon_channel.send(<Object?>[deviceId, expectedMtu]) as List<Object?>?;
if (__pigeon_replyList == null) {
throw _createConnectionError(__pigeon_channelName);
} else if (__pigeon_replyList.length > 1) {
@@ -470,24 +485,15 @@ class UniversalBlePlatformChannel {
}
}
Future<void> writeValue(String deviceId, String service,
String characteristic, Uint8List value, int bleOutputProperty) async {
const String __pigeon_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.writeValue';
final BasicMessageChannel<Object?> __pigeon_channel =
BasicMessageChannel<Object?>(
Future<void> writeValue(String deviceId, String service, String characteristic, Uint8List value, int bleOutputProperty) async {
const String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.writeValue';
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
__pigeon_channelName,
pigeonChannelCodec,
binaryMessenger: __pigeon_binaryMessenger,
);
final List<Object?>? __pigeon_replyList = await __pigeon_channel
.send(<Object?>[
deviceId,
service,
characteristic,
value,
bleOutputProperty
]) as List<Object?>?;
final List<Object?>? __pigeon_replyList =
await __pigeon_channel.send(<Object?>[deviceId, service, characteristic, value, bleOutputProperty]) as List<Object?>?;
if (__pigeon_replyList == null) {
throw _createConnectionError(__pigeon_channelName);
} else if (__pigeon_replyList.length > 1) {
@@ -502,10 +508,8 @@ class UniversalBlePlatformChannel {
}
Future<bool> isPaired(String deviceId) async {
const String __pigeon_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.isPaired';
final BasicMessageChannel<Object?> __pigeon_channel =
BasicMessageChannel<Object?>(
const String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.isPaired';
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
__pigeon_channelName,
pigeonChannelCodec,
binaryMessenger: __pigeon_binaryMessenger,
@@ -531,10 +535,8 @@ class UniversalBlePlatformChannel {
}
Future<void> pair(String deviceId) async {
const String __pigeon_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.pair';
final BasicMessageChannel<Object?> __pigeon_channel =
BasicMessageChannel<Object?>(
const String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.pair';
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
__pigeon_channelName,
pigeonChannelCodec,
binaryMessenger: __pigeon_binaryMessenger,
@@ -555,10 +557,8 @@ class UniversalBlePlatformChannel {
}
Future<void> unPair(String deviceId) async {
const String __pigeon_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.unPair';
final BasicMessageChannel<Object?> __pigeon_channel =
BasicMessageChannel<Object?>(
const String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.unPair';
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
__pigeon_channelName,
pigeonChannelCodec,
binaryMessenger: __pigeon_binaryMessenger,
@@ -578,12 +578,9 @@ class UniversalBlePlatformChannel {
}
}
Future<List<UniversalBleScanResult?>> getConnectedDevices(
List<String?> withServices) async {
const String __pigeon_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getConnectedDevices';
final BasicMessageChannel<Object?> __pigeon_channel =
BasicMessageChannel<Object?>(
Future<List<UniversalBleScanResult?>> getConnectedDevices(List<String?> withServices) async {
const String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getConnectedDevices';
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
__pigeon_channelName,
pigeonChannelCodec,
binaryMessenger: __pigeon_binaryMessenger,
@@ -604,8 +601,7 @@ class UniversalBlePlatformChannel {
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (__pigeon_replyList[0] as List<Object?>?)!
.cast<UniversalBleScanResult?>();
return (__pigeon_replyList[0] as List<Object?>?)!.cast<UniversalBleScanResult?>();
}
}
}
@@ -625,7 +621,7 @@ class _UniversalBleCallbackChannelCodec extends StandardMessageCodec {
@override
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
case 128:
case 128:
return UniversalBleScanResult.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
@@ -635,8 +631,7 @@ class _UniversalBleCallbackChannelCodec extends StandardMessageCodec {
/// Native -> Flutter
abstract class UniversalBleCallbackChannel {
static const MessageCodec<Object?> pigeonChannelCodec =
_UniversalBleCallbackChannelCodec();
static const MessageCodec<Object?> pigeonChannelCodec = _UniversalBleCallbackChannelCodec();
void onAvailabilityChanged(int state);
@@ -644,25 +639,21 @@ abstract class UniversalBleCallbackChannel {
void onScanResult(UniversalBleScanResult result);
void onValueChanged(
String deviceId, String characteristicId, Uint8List value);
void onValueChanged(String deviceId, String characteristicId, Uint8List value);
void onConnectionChanged(String deviceId, int state);
static void setup(UniversalBleCallbackChannel? api,
{BinaryMessenger? binaryMessenger}) {
static void setup(UniversalBleCallbackChannel? api, {BinaryMessenger? binaryMessenger}) {
{
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<
Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onAvailabilityChanged',
pigeonChannelCodec,
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onAvailabilityChanged', pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
__pigeon_channel.setMessageHandler(null);
} else {
__pigeon_channel.setMessageHandler((Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onAvailabilityChanged was null.');
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onAvailabilityChanged was null.');
final List<Object?> args = (message as List<Object?>?)!;
final int? arg_state = (args[0] as int?);
assert(arg_state != null,
@@ -672,25 +663,22 @@ abstract class UniversalBleCallbackChannel {
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(
error: PlatformException(code: 'error', message: e.toString()));
} catch (e) {
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
}
});
}
}
{
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<
Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPairStateChange',
pigeonChannelCodec,
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPairStateChange', pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
__pigeon_channel.setMessageHandler(null);
} else {
__pigeon_channel.setMessageHandler((Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPairStateChange was null.');
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPairStateChange was null.');
final List<Object?> args = (message as List<Object?>?)!;
final String? arg_deviceId = (args[0] as String?);
assert(arg_deviceId != null,
@@ -704,28 +692,24 @@ abstract class UniversalBleCallbackChannel {
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(
error: PlatformException(code: 'error', message: e.toString()));
} catch (e) {
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
}
});
}
}
{
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<
Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult',
pigeonChannelCodec,
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult', pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
__pigeon_channel.setMessageHandler(null);
} else {
__pigeon_channel.setMessageHandler((Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult was null.');
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult was null.');
final List<Object?> args = (message as List<Object?>?)!;
final UniversalBleScanResult? arg_result =
(args[0] as UniversalBleScanResult?);
final UniversalBleScanResult? arg_result = (args[0] as UniversalBleScanResult?);
assert(arg_result != null,
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult was null, expected non-null UniversalBleScanResult.');
try {
@@ -733,25 +717,22 @@ abstract class UniversalBleCallbackChannel {
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(
error: PlatformException(code: 'error', message: e.toString()));
} catch (e) {
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
}
});
}
}
{
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<
Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged',
pigeonChannelCodec,
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged', pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
__pigeon_channel.setMessageHandler(null);
} else {
__pigeon_channel.setMessageHandler((Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged was null.');
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged was null.');
final List<Object?> args = (message as List<Object?>?)!;
final String? arg_deviceId = (args[0] as String?);
assert(arg_deviceId != null,
@@ -763,30 +744,26 @@ abstract class UniversalBleCallbackChannel {
assert(arg_value != null,
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged was null, expected non-null Uint8List.');
try {
api.onValueChanged(
arg_deviceId!, arg_characteristicId!, arg_value!);
api.onValueChanged(arg_deviceId!, arg_characteristicId!, arg_value!);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(
error: PlatformException(code: 'error', message: e.toString()));
} catch (e) {
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
}
});
}
}
{
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<
Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged',
pigeonChannelCodec,
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged', pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
__pigeon_channel.setMessageHandler(null);
} else {
__pigeon_channel.setMessageHandler((Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged was null.');
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged was null.');
final List<Object?> args = (message as List<Object?>?)!;
final String? arg_deviceId = (args[0] as String?);
assert(arg_deviceId != null,
@@ -799,9 +776,8 @@ abstract class UniversalBleCallbackChannel {
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(
error: PlatformException(code: 'error', message: e.toString()));
} catch (e) {
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
}
});
}
@@ -29,7 +29,6 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
@override
Future<void> startScan({
WebRequestOptionsBuilder? webRequestOptions,
ScanFilter? scanFilter,
}) async {
await _channel.startScan(
@@ -207,12 +206,19 @@ extension _UniversalBleScanResultExtension on UniversalBleScanResult {
extension _ScanFilterExtension on ScanFilter? {
UniversalScanFilter? toUniversalScanFilter() {
List<UniversalManufacturerDataFilter>? manufacturerDataFilters = this
?.withManufacturerData
.map((e) => UniversalManufacturerDataFilter(
companyIdentifier: e.companyIdentifier,
data: e.data,
mask: e.mask,
))
.toList();
// Windows crashes if it's null, so we need to pass empty scan filter in this case
if (this == null && Platform.isWindows) {
return UniversalScanFilter(withServices: []);
}
return UniversalScanFilter(
withServices: this?.withServices.toValidUUIDList() ?? [],
withManufacturerData: manufacturerDataFilters ?? [],
);
}
}
@@ -8,7 +8,6 @@ abstract class UniversalBlePlatform {
Future<bool> enableBluetooth();
Future<void> startScan({
WebRequestOptionsBuilder? webRequestOptions,
ScanFilter? scanFilter,
});
@@ -109,22 +109,11 @@ class UniversalBleWeb extends UniversalBlePlatform {
@override
Future<void> startScan({
WebRequestOptionsBuilder? webRequestOptions,
ScanFilter? scanFilter,
}) async {
RequestOptionsBuilder requestFilterBuilder;
if (webRequestOptions != null) {
requestFilterBuilder = webRequestOptions.toRequestOptionsBuilder(
scanFilter: scanFilter,
);
} else if (scanFilter != null) {
requestFilterBuilder = scanFilter.toRequestOptionsBuilder();
} else {
requestFilterBuilder = RequestOptionsBuilder.acceptAllDevices();
}
BluetoothDevice device = await FlutterWebBluetooth.instance.requestDevice(
requestFilterBuilder,
scanFilter?.toRequestOptionsBuilder() ??
RequestOptionsBuilder.acceptAllDevices(),
);
// Update local device list
@@ -389,8 +378,37 @@ extension _UnmodifiableMapViewExtension on UnmodifiableMapView<int, ByteData> {
extension ScanFilterExtension on ScanFilter {
RequestOptionsBuilder toRequestOptionsBuilder() {
List<RequestFilterBuilder> filters = [];
// Add services filter
for (var service in withServices.toValidUUIDList()) {
filters.add(
RequestFilterBuilder(
services: [service],
),
);
}
// Add manufacturer data filter
for (var manufacturerData in withManufacturerData) {
filters.add(
RequestFilterBuilder(
manufacturerData: [
ManufacturerDataFilterBuilder(
companyIdentifier: manufacturerData.companyIdentifier,
dataPrefix: manufacturerData.data,
mask: manufacturerData.mask,
),
],
),
);
}
if (filters.isEmpty) {
return RequestOptionsBuilder.acceptAllDevices();
}
return RequestOptionsBuilder(
[RequestFilterBuilder(services: withServices.toValidUUIDList())],
filters,
optionalServices: withServices.toValidUUIDList(),
);
}
+17 -1
View File
@@ -130,8 +130,24 @@ class UniversalBleCharacteristic {
UniversalBleCharacteristic(this.uuid, this.properties);
}
/// Scan Filters
class UniversalScanFilter {
final List<String?> withServices;
final List<UniversalManufacturerDataFilter?> withManufacturerData;
UniversalScanFilter(this.withServices);
UniversalScanFilter(
this.withServices,
this.withManufacturerData,
);
}
class UniversalManufacturerDataFilter {
int? companyIdentifier;
Uint8List? data;
Uint8List? mask;
UniversalManufacturerDataFilter({
this.companyIdentifier,
this.data,
this.mask,
});
}
+103 -5
View File
@@ -273,8 +273,11 @@ UniversalBleCharacteristic UniversalBleCharacteristic::FromEncodableList(const E
// UniversalScanFilter
UniversalScanFilter::UniversalScanFilter(const EncodableList& with_services)
: with_services_(with_services) {}
UniversalScanFilter::UniversalScanFilter(
const EncodableList& with_services,
const EncodableList& with_manufacturer_data)
: with_services_(with_services),
with_manufacturer_data_(with_manufacturer_data) {}
const EncodableList& UniversalScanFilter::with_services() const {
return with_services_;
@@ -285,16 +288,104 @@ void UniversalScanFilter::set_with_services(const EncodableList& value_arg) {
}
const EncodableList& UniversalScanFilter::with_manufacturer_data() const {
return with_manufacturer_data_;
}
void UniversalScanFilter::set_with_manufacturer_data(const EncodableList& value_arg) {
with_manufacturer_data_ = value_arg;
}
EncodableList UniversalScanFilter::ToEncodableList() const {
EncodableList list;
list.reserve(1);
list.reserve(2);
list.push_back(EncodableValue(with_services_));
list.push_back(EncodableValue(with_manufacturer_data_));
return list;
}
UniversalScanFilter UniversalScanFilter::FromEncodableList(const EncodableList& list) {
UniversalScanFilter decoded(
std::get<EncodableList>(list[0]));
std::get<EncodableList>(list[0]),
std::get<EncodableList>(list[1]));
return decoded;
}
// UniversalManufacturerDataFilter
UniversalManufacturerDataFilter::UniversalManufacturerDataFilter() {}
UniversalManufacturerDataFilter::UniversalManufacturerDataFilter(
const int64_t* company_identifier,
const std::vector<uint8_t>* data,
const std::vector<uint8_t>* mask)
: company_identifier_(company_identifier ? std::optional<int64_t>(*company_identifier) : std::nullopt),
data_(data ? std::optional<std::vector<uint8_t>>(*data) : std::nullopt),
mask_(mask ? std::optional<std::vector<uint8_t>>(*mask) : std::nullopt) {}
const int64_t* UniversalManufacturerDataFilter::company_identifier() const {
return company_identifier_ ? &(*company_identifier_) : nullptr;
}
void UniversalManufacturerDataFilter::set_company_identifier(const int64_t* value_arg) {
company_identifier_ = value_arg ? std::optional<int64_t>(*value_arg) : std::nullopt;
}
void UniversalManufacturerDataFilter::set_company_identifier(int64_t value_arg) {
company_identifier_ = value_arg;
}
const std::vector<uint8_t>* UniversalManufacturerDataFilter::data() const {
return data_ ? &(*data_) : nullptr;
}
void UniversalManufacturerDataFilter::set_data(const std::vector<uint8_t>* value_arg) {
data_ = value_arg ? std::optional<std::vector<uint8_t>>(*value_arg) : std::nullopt;
}
void UniversalManufacturerDataFilter::set_data(const std::vector<uint8_t>& value_arg) {
data_ = value_arg;
}
const std::vector<uint8_t>* UniversalManufacturerDataFilter::mask() const {
return mask_ ? &(*mask_) : nullptr;
}
void UniversalManufacturerDataFilter::set_mask(const std::vector<uint8_t>* value_arg) {
mask_ = value_arg ? std::optional<std::vector<uint8_t>>(*value_arg) : std::nullopt;
}
void UniversalManufacturerDataFilter::set_mask(const std::vector<uint8_t>& value_arg) {
mask_ = value_arg;
}
EncodableList UniversalManufacturerDataFilter::ToEncodableList() const {
EncodableList list;
list.reserve(3);
list.push_back(company_identifier_ ? EncodableValue(*company_identifier_) : EncodableValue());
list.push_back(data_ ? EncodableValue(*data_) : EncodableValue());
list.push_back(mask_ ? EncodableValue(*mask_) : EncodableValue());
return list;
}
UniversalManufacturerDataFilter UniversalManufacturerDataFilter::FromEncodableList(const EncodableList& list) {
UniversalManufacturerDataFilter decoded;
auto& encodable_company_identifier = list[0];
if (!encodable_company_identifier.IsNull()) {
decoded.set_company_identifier(encodable_company_identifier.LongValue());
}
auto& encodable_data = list[1];
if (!encodable_data.IsNull()) {
decoded.set_data(std::get<std::vector<uint8_t>>(encodable_data));
}
auto& encodable_mask = list[2];
if (!encodable_mask.IsNull()) {
decoded.set_mask(std::get<std::vector<uint8_t>>(encodable_mask));
}
return decoded;
}
@@ -312,6 +403,8 @@ EncodableValue UniversalBlePlatformChannelCodecSerializer::ReadValueOfType(
case 130:
return CustomEncodableValue(UniversalBleService::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
case 131:
return CustomEncodableValue(UniversalManufacturerDataFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
case 132:
return CustomEncodableValue(UniversalScanFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
default:
return flutter::StandardCodecSerializer::ReadValueOfType(type, stream);
@@ -337,8 +430,13 @@ void UniversalBlePlatformChannelCodecSerializer::WriteValue(
WriteValue(EncodableValue(std::any_cast<UniversalBleService>(*custom_value).ToEncodableList()), stream);
return;
}
if (custom_value->type() == typeid(UniversalScanFilter)) {
if (custom_value->type() == typeid(UniversalManufacturerDataFilter)) {
stream->WriteByte(131);
WriteValue(EncodableValue(std::any_cast<UniversalManufacturerDataFilter>(*custom_value).ToEncodableList()), stream);
return;
}
if (custom_value->type() == typeid(UniversalScanFilter)) {
stream->WriteByte(132);
WriteValue(EncodableValue(std::any_cast<UniversalScanFilter>(*custom_value).ToEncodableList()), stream);
return;
}
+48 -1
View File
@@ -179,15 +179,22 @@ class UniversalBleCharacteristic {
};
// Scan Filters
//
// Generated class from Pigeon that represents data sent in messages.
class UniversalScanFilter {
public:
// Constructs an object setting all fields.
explicit UniversalScanFilter(const flutter::EncodableList& with_services);
explicit UniversalScanFilter(
const flutter::EncodableList& with_services,
const flutter::EncodableList& with_manufacturer_data);
const flutter::EncodableList& with_services() const;
void set_with_services(const flutter::EncodableList& value_arg);
const flutter::EncodableList& with_manufacturer_data() const;
void set_with_manufacturer_data(const flutter::EncodableList& value_arg);
private:
static UniversalScanFilter FromEncodableList(const flutter::EncodableList& list);
@@ -197,6 +204,46 @@ class UniversalScanFilter {
friend class UniversalBleCallbackChannel;
friend class UniversalBleCallbackChannelCodecSerializer;
flutter::EncodableList with_services_;
flutter::EncodableList with_manufacturer_data_;
};
// Generated class from Pigeon that represents data sent in messages.
class UniversalManufacturerDataFilter {
public:
// Constructs an object setting all non-nullable fields.
UniversalManufacturerDataFilter();
// Constructs an object setting all fields.
explicit UniversalManufacturerDataFilter(
const int64_t* company_identifier,
const std::vector<uint8_t>* data,
const std::vector<uint8_t>* mask);
const int64_t* company_identifier() const;
void set_company_identifier(const int64_t* value_arg);
void set_company_identifier(int64_t value_arg);
const std::vector<uint8_t>* data() const;
void set_data(const std::vector<uint8_t>* value_arg);
void set_data(const std::vector<uint8_t>& value_arg);
const std::vector<uint8_t>* mask() const;
void set_mask(const std::vector<uint8_t>* value_arg);
void set_mask(const std::vector<uint8_t>& value_arg);
private:
static UniversalManufacturerDataFilter FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBlePlatformChannelCodecSerializer;
friend class UniversalBleCallbackChannel;
friend class UniversalBleCallbackChannelCodecSerializer;
std::optional<int64_t> company_identifier_;
std::optional<std::vector<uint8_t>> data_;
std::optional<std::vector<uint8_t>> mask_;
};
+105 -22
View File
@@ -30,7 +30,8 @@ namespace universal_ble
const auto signalStrengthKey = L"System.Devices.Aep.SignalStrength";
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
std::unordered_map<std::string, winrt::event_token> characteristicsTokens{}; // TODO: Remove the map and store the token inside the characteristic object
std::vector<UniversalManufacturerDataFilter> manufacturerScanFilter = std::vector<UniversalManufacturerDataFilter>();
void UniversalBlePlugin::RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar)
{
@@ -89,21 +90,49 @@ namespace universal_ble
{
if (bluetoothRadio && bluetoothRadio.State() == RadioState::On)
{
setupDeviceWatcher();
DeviceWatcherStatus status = deviceWatcher.Status();
if (status != DeviceWatcherStatus::Started)
{
deviceWatcher.Start();
}
else
{
return FlutterError("Already scanning");
}
if (!bluetoothLEWatcher)
{
bluetoothLEWatcher = BluetoothLEAdvertisementWatcher();
bluetoothLEWatcher.ScanningMode(BluetoothLEScanningMode::Active);
// reset scan filters
manufacturerScanFilter.clear();
if (filter != nullptr)
ApplyScanFilter(filter, bluetoothLEWatcher);
{
// Apply Services filter
const auto &services = filter->with_services();
if (!services.empty())
{
for (const auto &uuid : services)
{
std::string uuid_str = std::get<std::string>(uuid);
bluetoothLEWatcher.AdvertisementFilter().Advertisement().ServiceUuids().Append(uuid_to_guid(uuid_str));
}
}
// Set ManufacturerData filter
const auto &manufacturerData = filter->with_manufacturer_data();
for (const flutter::EncodableValue &data : manufacturerData)
{
UniversalManufacturerDataFilter manufacturerDataFilter = std::any_cast<UniversalManufacturerDataFilter>(std::get<flutter::CustomEncodableValue>(data));
manufacturerScanFilter.push_back(manufacturerDataFilter);
}
}
bluetoothLEWatcherReceivedToken = bluetoothLEWatcher.Received({this, &UniversalBlePlugin::BluetoothLEWatcher_Received});
}
bluetoothLEWatcher.Start();
setupDeviceWatcher();
DeviceWatcherStatus status = deviceWatcher.Status();
if (status != DeviceWatcherStatus::Started)
deviceWatcher.Start();
else
return FlutterError("Already scanning");
return std::nullopt;
}
else
@@ -112,20 +141,6 @@ namespace universal_ble
}
};
void UniversalBlePlugin::ApplyScanFilter(const UniversalScanFilter *filter, BluetoothLEAdvertisementWatcher &bluetoothWatcher)
{
// Apply Services filter
const auto &services = filter->with_services();
if (!services.empty())
{
for (const auto &uuid : services)
{
std::string uuid_str = std::get<std::string>(uuid);
bluetoothWatcher.AdvertisementFilter().Advertisement().ServiceUuids().Append(uuid_to_guid(uuid_str));
}
}
}
std::optional<FlutterError> UniversalBlePlugin::StopScan()
{
@@ -688,6 +703,17 @@ namespace universal_ble
if (!args.IsConnectable())
return;
// Apply ManufacturerData filter
if (!manufacturerScanFilter.empty())
{
// Avoid devices if ManufacturerData is not present
if (args.Advertisement().ManufacturerData().Size() == 0)
return;
// Avoid devices if ManufacturerData does not match the filter
if (!filterByManufacturerData(args.Advertisement().ManufacturerData()))
return;
}
auto deviceId = _mac_address_to_str(args.BluetoothAddress());
auto universalScanResult = UniversalBleScanResult(deviceId);
std::string name = winrt::to_string(args.Advertisement().LocalName());
@@ -747,6 +773,63 @@ namespace universal_ble
}
}
bool UniversalBlePlugin::filterByManufacturerData(IVector<BluetoothLEManufacturerData> deviceManufactureData)
{
if (manufacturerScanFilter.empty())
return true;
for (auto &&filter : manufacturerScanFilter)
{
const int64_t *company_identifier = filter.company_identifier();
const std::vector<uint8_t> *data_filter = filter.data();
const std::vector<uint8_t> *mask = filter.mask();
if (company_identifier == nullptr)
continue;
uint16_t companyId = static_cast<uint16_t>(*company_identifier);
for (auto &&deviceMfData : deviceManufactureData)
{
if (deviceMfData.CompanyId() == companyId)
{
// If data filter is not present then return true
if (data_filter == nullptr)
return true;
auto deviceData = to_bytevc(deviceMfData.Data());
if (deviceData.size() < data_filter->size())
continue;
bool isMatch = true;
for (size_t i = 0; i < data_filter->size(); i++)
{
if (mask != nullptr && mask->size() > i)
{
if (((*mask)[i] & (*data_filter)[i]) != ((*mask)[i] & deviceData[i]))
{
isMatch = false;
break;
}
}
else
{
if ((*data_filter)[i] != deviceData[i])
{
isMatch = false;
break;
}
}
}
if (isMatch)
return true;
}
}
}
return false;
}
AvailabilityState UniversalBlePlugin::getAvailabilityStateFromRadio(RadioState radioState)
{
auto state = [=]() -> AvailabilityState
+1 -1
View File
@@ -123,7 +123,7 @@ namespace universal_ble
winrt::fire_and_forget WriteAsync(GattCharacteristic characteristic, GattWriteOption writeOption,
const std::vector<uint8_t> &value,
std::function<void(std::optional<FlutterError> reply)> result);
void ApplyScanFilter(const UniversalScanFilter *filter, BluetoothLEAdvertisementWatcher &bluetoothWatcher);
bool filterByManufacturerData(IVector<BluetoothLEManufacturerData> deviceManufactureData);
// UniversalBlePlatformChannel implementation.
void GetBluetoothAvailabilityState(std::function<void(ErrorOr<int64_t> reply)> result) override;