Skip to content

Commit

Permalink
[Fix] iOS: support multiple scan filters. android still needs some work
Browse files Browse the repository at this point in the history
  • Loading branch information
chipweinberger committed Dec 22, 2023
1 parent 382769d commit 1ee6b5d
Showing 1 changed file with 86 additions and 17 deletions.
103 changes: 86 additions & 17 deletions ios/Classes/FlutterBluePlusPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,29 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
[scanOpts setObject:[NSNumber numberWithBool:YES] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
}

// services filter
// filters implemented by FBP, not the OS
BOOL hasCustomFilters =
[self hasFilter:@"with_remote_ids"] ||
[self hasFilter:@"with_names"] ||
[self hasFilter:@"with_keywords"] ||
[self hasFilter:@"with_msd"] ||
[self hasFilter:@"with_service_data"];

// filter services
NSArray *services = [NSArray array];
for (int i = 0; i < [withServices count]; i++) {
NSString *uuid = withServices[i];
services = [services arrayByAddingObject:[CBUUID UUIDWithString:uuid]];
}

// If any custom filter is set then we cannot filter by services.
// Why? An advertisement can match either the service filter *or*
// the custom filter. It does not have to match both. So we cannot have
// iOS & macOS filtering out any advertisements.
if (hasCustomFilters) {
services = [NSArray array];
}

// clear counts
[self.scanCounts removeAllObjects];

Expand Down Expand Up @@ -1028,32 +1044,63 @@ - (void)centralManager:(CBCentralManager *)central
[self.knownPeripherals setObject:peripheral forKey:remoteId];

// advertising data
NSArray *advServices = advertisementData[CBAdvertisementDataServiceUUIDsKey];
NSString *advName = advertisementData[CBAdvertisementDataLocalNameKey];
NSData *advMsd = advertisementData[CBAdvertisementDataManufacturerDataKey];
NSDictionary* advSd = advertisementData[CBAdvertisementDataServiceDataKey];

// filter remoteIds
if (![self filterRemoteIds:self.scanFilters[@"with_remote_ids"] target:remoteId]) {
return;
}
BOOL allow = NO;

// filter names
if (![self filterNames:self.scanFilters[@"with_names"] target:advName]) {
return;
}
// are any filters set?
BOOL isAnyFilterSet = [self hasFilter:@"with_services"] ||
[self hasFilter:@"with_remote_ids"] ||
[self hasFilter:@"with_names"] ||
[self hasFilter:@"with_keywords"] ||
[self hasFilter:@"with_msd"] ||
[self hasFilter:@"with_service_data"];

// filter keywords
if (![self filterKeywords:self.scanFilters[@"with_keywords"] target:advName]) {
return;
// no filters set? allow all
if (!isAnyFilterSet) {
allow = YES;
}

// filter msd
if (![self filterMsd:self.scanFilters[@"with_msd"] msd:advMsd]) {
return;
// apply filters only if filters are set
// Note: filters are additive. An advertisment can match *any* filter
if (isAnyFilterSet)
{
// filter services
if ([self filterServices:self.scanFilters[@"with_services"] target:advServices]) {
allow = YES;
}

// filter remoteIds
if ([self filterRemoteIds:self.scanFilters[@"with_remote_ids"] target:remoteId]) {
allow = YES;
}

// filter names
if (!allow && [self filterNames:self.scanFilters[@"with_names"] target:advName]) {
allow = YES;
}

// filter keywords
if (!allow && [self filterKeywords:self.scanFilters[@"with_keywords"] target:advName]) {
allow = YES;
}

// filter msd
if (!allow && [self filterMsd:self.scanFilters[@"with_msd"] msd:advMsd]) {
allow = YES;
}

// filter service data
if (!allow && [self filterServiceData:self.scanFilters[@"with_service_data"] sd:advSd]) {
allow = YES;
}
}

// filter service data
if (![self filterServiceData:self.scanFilters[@"with_service_data"] sd:advSd]) {
// If no filters are satisfied, return
if (!allow) {
return;
}

Expand Down Expand Up @@ -1795,6 +1842,28 @@ - (NSInteger)scanCountIncrement:(NSString *)remoteId {
return count;
}

- (BOOL)hasFilter:(NSString *)key {
NSArray *filterArray = self.scanFilters[key];
return (filterArray != nil && [filterArray count] > 0);
}

- (BOOL)filterServices:(NSArray<NSString *> *)services
target:(NSArray<CBUUID *> *)target
{
if (services.count == 0) {
return YES;
}
if (target == nil || target.count == 0) {
return NO;
}
for (CBUUID *s in target) {
if ([services containsObject:[s uuidStr]]) {
return YES;
}
}
return NO;
}

- (BOOL)filterKeywords:(NSArray<NSString *> *)keywords
target:(NSString *)target
{
Expand Down

0 comments on commit 1ee6b5d

Please sign in to comment.