diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.h b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.h index 9efacabc..893fd32f 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.h +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.h @@ -243,6 +243,14 @@ typedef void (^GNCGATTConnectionCompletionHandler)(GNCBLEGATTClient *_Nullable c */ - (void)stop; +/** + * Retrieves the peripheral with the specified identifier. + * + * @param identifier The identifier of the peripheral to retrieve. + * @return The peripheral with the specified identifier, or @c nil if not found. + */ +- (nullable CBPeripheral *)retrievePeripheralWithIdentifier:(NSUUID *)identifier; + @end NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.m b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.m index d0ddacd6..a6276c3c 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.m +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.m @@ -136,6 +136,22 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( } } +- (nullable CBPeripheral *)retrievePeripheralWithIdentifier:(NSUUID *)identifier { + NSAssert(_centralManager, @"CBCentralManager not created."); + NSAssert(identifier, @"Should have an identifier, self: %@", self); + + NSArray *peripherals = + [_centralManager retrievePeripheralsWithIdentifiers:@[ identifier ]]; + + for (CBPeripheral *peripheral in peripherals) { + if ([peripheral.identifier isEqual:identifier]) { + return peripheral; + } + } + + return nil; +} + - (BOOL)supportsExtendedAdvertisements { // TODO(b/294736083): CoreBluetooth doesn't support actually advertising any extensions, however // some devices can scan for them if the feature is available. If we return @c YES from this diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCCentralManager.h b/internal/platform/implementation/apple/Mediums/BLE/GNCCentralManager.h index 13057c39..0077c583 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCCentralManager.h +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCCentralManager.h @@ -97,6 +97,14 @@ NS_ASSUME_NONNULL_BEGIN /** Asks the central manager to stop scanning for peripherals. */ - (void)stopScan; +/** + * Retrieves the peripherals with the given identifiers. + * + * @param identifiers The identifiers of the peripherals to retrieve. + * @return An array of peripherals with the given identifiers. + */ +- (NSArray *)retrievePeripheralsWithIdentifiers:(NSArray *)identifiers; + @end /** diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMediumTest.m b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMediumTest.m index f329f92c..10b623d9 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMediumTest.m +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMediumTest.m @@ -385,4 +385,20 @@ static NSString *const kServiceUUID = @"0000FEF3-0000-1000-8000-00805F9B34FB"; [self waitForExpectations:@[ disconnectExpectation ] timeout:3]; } +- (void)testRetrievePeripheralWithIdentifier_exists { + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; + XCTAssertNotNil( + [medium retrievePeripheralWithIdentifier: + [[NSUUID alloc] initWithUUIDString:@"11111111-1111-1111-1111-111111111111"]]); +} + +- (void)testRetrievePeripheralWithIdentifier_doesNotExist { + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; + XCTAssertNil( + [medium retrievePeripheralWithIdentifier: + [[NSUUID alloc] initWithUUIDString:@"11111111-1111-1111-1111-111111111112"]]); +} + @end diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeCentralManager.m b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeCentralManager.m index c11de5d9..d941a2fb 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeCentralManager.m +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeCentralManager.m @@ -24,6 +24,7 @@ @implementation GNCFakeCentralManager { CBManagerState _state; NSArray *_serviceUUIDs; + NSDictionary *_peripherals; } @synthesize centralDelegate; @@ -32,6 +33,12 @@ self = [super init]; if (self) { _state = CBManagerStateUnknown; + // Add a fake peripheral + NSUUID *identifier = + [[NSUUID alloc] initWithUUIDString:@"11111111-1111-1111-1111-111111111111"]; + _peripherals = [NSMutableDictionary + dictionaryWithObject:[[GNCFakePeripheral alloc] initWithIdentifier:identifier] + forKey:identifier]; } return self; } @@ -63,6 +70,16 @@ - (void)stopScan { } +- (NSArray *)retrievePeripheralsWithIdentifiers:(NSArray *)identifiers { + NSMutableArray *peripherals = [NSMutableArray array]; + for (NSUUID *identifier in identifiers) { + if (_peripherals[identifier]) { + [peripherals addObject:(CBPeripheral *)_peripherals[identifier]]; + } + } + return peripherals; +} + #pragma mark - Testing Helpers - (NSArray *)serviceUUIDs { diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheral.h b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheral.h index adf449e6..e8b82cd5 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheral.h +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheral.h @@ -22,6 +22,9 @@ NS_ASSUME_NONNULL_BEGIN /** A fake implementation of @c GNCPeripheral to inject for testing. */ @interface GNCFakePeripheral : NSObject +/** Initializes the fake peripheral with the given identifier. */ +- (instancetype)initWithIdentifier:(NSUUID *)identifier; + /** The peripheral's delegate. */ @property(nonatomic, nullable, readwrite) id delegate; diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheral.m b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheral.m index 916b6495..96328859 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheral.m +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheral.m @@ -50,6 +50,15 @@ NS_ASSUME_NONNULL_BEGIN return self; } +- (instancetype)initWithIdentifier:(NSUUID *)identifier { + self = [super init]; + if (self) { + _services = [[NSMutableArray alloc] init]; + _identifier = identifier; + } + return self; +} + - (void)setPeripheralDelegate:(nullable id)peripheralDelegate { self.delegate = peripheralDelegate; } diff --git a/internal/platform/implementation/apple/Tests/ble_medium_test.mm b/internal/platform/implementation/apple/Tests/ble_medium_test.mm index b7fca780..b628c3f3 100644 --- a/internal/platform/implementation/apple/Tests/ble_medium_test.mm +++ b/internal/platform/implementation/apple/Tests/ble_medium_test.mm @@ -552,8 +552,7 @@ static const char *const kTestServiceID = "TestServiceID"; NSString *unknownUUIDString = [[NSUUID alloc] init].UUIDString; std::optional result2 = _medium->RetrieveBlePeripheralIdFromNativeId([unknownUUIDString UTF8String]); - XCTAssertTrue(result2.has_value()); // Valid UUID format should return an ID. - XCTAssertNotEqual(result2.value(), fakePeripheral.identifier.hash); + XCTAssertFalse(result2.has_value()); std::optional result3 = _medium->RetrieveBlePeripheralIdFromNativeId("invalid-uuid-string"); diff --git a/internal/platform/implementation/apple/ble_medium.mm b/internal/platform/implementation/apple/ble_medium.mm index c9d7ef35..f82df304 100644 --- a/internal/platform/implementation/apple/ble_medium.mm +++ b/internal/platform/implementation/apple/ble_medium.mm @@ -707,7 +707,7 @@ std::optional BleMedium::RetrieveBlePeriphera NSUUID *uuidFromString = [[NSUUID alloc] initWithUUIDString:uuidString]; if (uuidFromString == nil) { - GNCLoggerError(@"Native BLE peripheral ID is not a valid UUID."); + GNCLoggerError(@"Native BLE peripheral ID %@ is not a valid UUID.", uuidString); return std::nullopt; } @@ -718,7 +718,14 @@ std::optional BleMedium::RetrieveBlePeriphera } // Retrieve the BLE peripheral from CBCentralManager. - peripheral = [socketCentralManager_ retrievePeripheralWithIdentifier:uuidFromString]; + peripheral = [medium_ retrievePeripheralWithIdentifier:uuidFromString]; + + if (peripheral == nil) { + GNCLoggerError(@"Failed to retrieve BLE peripheral from identifier: %@", uuidFromString); + return std::nullopt; + } + + peripherals_.Add(peripheral); return peripheral.identifier.hash; } @@ -748,7 +755,7 @@ bool BleMedium::ShouldReportAdvertisement(NSDate *now, } if ([now timeIntervalSinceDate:it->second.last_timestamp] < kThresholdInterval && - it -> second.last_service_data.count == service_data.count) { + it->second.last_service_data.count == service_data.count) { bool is_same_advertisement = true; for (CBUUID *service_uuid in service_data.allKeys) { if (![service_data[service_uuid] isEqualToData:it->second.last_service_data[service_uuid]]) {