diff --git a/internal/platform/implementation/apple/ble_medium.h b/internal/platform/implementation/apple/ble_medium.h index 06536992..3b464b6c 100644 --- a/internal/platform/implementation/apple/ble_medium.h +++ b/internal/platform/implementation/apple/ble_medium.h @@ -189,6 +189,17 @@ class BleMedium : public api::ble_v2::BleMedium { GNCBLEMedium *medium_; PeripheralsMap peripherals_; + struct BlockAdvertisementPacket { + bool block; + NSDate *last_timestamp; + nearby::ByteArray last_service_data; + }; + // A map for maintaining the set of advertisement packets that should be blocked or not. + absl::flat_hash_map + block_advertisement_packets_; + // The timestamp of the last time to start blocking advertisement packet process. We reset ths + // procedure every 10 minutes to avoid stale peripherals that are not advertising any more. + NSDate *last_block_advertisement_packet_timestamp_{nil}; GNSPeripheralServiceManager *socketPeripheralServiceManager_; GNSPeripheralManager *socketPeripheralManager_; diff --git a/internal/platform/implementation/apple/ble_medium.mm b/internal/platform/implementation/apple/ble_medium.mm index d2c810ff..052ee37c 100644 --- a/internal/platform/implementation/apple/ble_medium.mm +++ b/internal/platform/implementation/apple/ble_medium.mm @@ -55,6 +55,8 @@ static NSString *const kWeaveServiceUUID = @"FEF3"; static const UInt8 kRequestConnectionTimeoutInSeconds = 10; +static NSTimeInterval const kThresholdInterval = 2; // 2 seconds +static NSTimeInterval const kResetBlockAdvertisementPacketInterval = 600; // 10 minutes namespace nearby { namespace apple { @@ -124,18 +126,65 @@ bool BleMedium::StopAdvertising() { void BleMedium::HandleAdvertisementFound(id peripheral, NSDictionary *serviceData) { api::ble_v2::BleAdvertisementData data; + NSDate *currentTimestamp = [NSDate date]; + nearby::ByteArray *service_data_ptr = nullptr; + for (CBUUID *key in serviceData.allKeys) { data.service_data[CPPUUIDFromObjC(key)] = ByteArrayFromNSData(serviceData[key]); + service_data_ptr = &data.service_data[CPPUUIDFromObjC(key)]; + } + if (service_data_ptr == nullptr) { + GTMLoggerInfo(@"The service data for peripheral is empty: %@", peripheral); + return; } - // Add the peripheral to the map if we haven't discovered it yet. api::ble_v2::BlePeripheral::UniqueId unique_id = peripherals_.Add(peripheral); + if (block_advertisement_packets_.find(unique_id) == block_advertisement_packets_.end()) { + block_advertisement_packets_[unique_id].block = false; + } + + if (block_advertisement_packets_[unique_id].last_timestamp != nil) { + // If time pass more than 2 seconds or received different service data for same peripheral, we + // will unblock the advertisement packet. + NSTimeInterval interval = [currentTimestamp + timeIntervalSinceDate:block_advertisement_packets_[unique_id].last_timestamp]; + if ((interval > kThresholdInterval) || + (block_advertisement_packets_[unique_id].last_service_data != *service_data_ptr)) { + block_advertisement_packets_[unique_id].block = false; + block_advertisement_packets_[unique_id].last_timestamp = currentTimestamp; + } + } else { + // If there is no last timestamp, reset the block of block_advertising_packets_ to false. + block_advertisement_packets_[unique_id].block = false; + block_advertisement_packets_[unique_id].last_timestamp = currentTimestamp; + } + + if (block_advertisement_packets_[unique_id].block) { + // If the advertisement packet is blocked, skip to pass the packet to upper layer. + return; + } + + GTMLoggerInfo(@"Pass the advertisement packet to upper layer for unique_id: %llu, %@, data: %s", + unique_id, peripheral, service_data_ptr->data()); + block_advertisement_packets_[unique_id].last_service_data = *service_data_ptr; if (scanning_cb_.advertisement_found_cb) { scanning_cb_.advertisement_found_cb(unique_id, data); } if (scan_cb_.advertisement_found_cb) { scan_cb_.advertisement_found_cb(unique_id, data); } + block_advertisement_packets_[unique_id].block = true; + + // Reset the block advertisement packet map every 10 minutes. + if (last_block_advertisement_packet_timestamp_) { + if ([currentTimestamp timeIntervalSinceDate:last_block_advertisement_packet_timestamp_] > + kResetBlockAdvertisementPacketInterval) { + block_advertisement_packets_.clear(); + last_block_advertisement_packet_timestamp_ = currentTimestamp; + } + } else { + last_block_advertisement_packet_timestamp_ = currentTimestamp; + } } std::unique_ptr BleMedium::StartScanning( @@ -148,6 +197,7 @@ std::unique_ptr BleMedium::StartScannin // map every time we stopped a scan, we would not be able to connect to peripherals that we // discovered in that scan session. peripherals_.Clear(); + block_advertisement_packets_.clear(); socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUID]; [socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUID ]]; @@ -220,6 +270,7 @@ bool BleMedium::StartMultipleServicesScanning(const std::vector &service_u // map every time we stopped a scan, we would not be able to connect to peripherals that we // discovered in that scan session. peripherals_.Clear(); + block_advertisement_packets_.clear(); socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUIDs[0]]; [socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUIDs[0] ]];