From 1a87241ffddcc015f3985aeeea3c704742a19c81 Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Thu, 26 Sep 2024 11:08:00 -0700 Subject: [PATCH] Implement `GattClient#Disconnect` for Apple implementation of BLEv2 PiperOrigin-RevId: 679218551 --- .../apple/Mediums/BLEv2/GNCBLEGATTClient.h | 16 +- .../apple/Mediums/BLEv2/GNCBLEGATTClient.m | 21 ++- .../apple/Mediums/BLEv2/GNCBLEMedium.m | 8 +- .../apple/Mediums/BLEv2/GNCCentralManager.h | 15 ++ .../apple/Tests/GNCBLEGATTClient+Testing.h | 5 +- .../apple/Tests/GNCBLEGATTClientTest.m | 151 +++++++++++++----- .../apple/Tests/GNCBLEMediumTest.m | 9 +- .../apple/Tests/GNCFakeCentralManager.m | 4 + .../implementation/apple/ble_gatt_client.mm | 5 +- .../implementation/apple/ble_medium.mm | 12 ++ 10 files changed, 191 insertions(+), 55 deletions(-) diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h index f41f8f00..fd35919e 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h @@ -49,6 +49,14 @@ typedef void (^GNCGetCharacteristicCompletionHandler)( typedef void (^GNCReadCharacteristicValueCompletionHandler)(NSData *_Nullable value, NSError *_Nullable error); +/** + * A block to be invoked after a call to @c disconnect, requesting that the local connection to the + * remote peripheral be cancelled. + * + * @param peripheral The remote peripheral to disconnect from. + */ +typedef void (^GNCRequestDisconnectionHandler)(id peripheral); + /** * An object that can be used to discover, explore, and interact with GATT services and * characteristics available on a remote peripheral. @@ -64,8 +72,11 @@ typedef void (^GNCReadCharacteristicValueCompletionHandler)(NSData *_Nullable va * Initializes the GATT client with a specified peripheral. * * @param peripheral The peripheral instance. + * @param requestDisconnectionHandler Called on a private queue with @c peripheral when the + * connection to the peripheral should be cancelled. */ -- (instancetype)initWithPeripheral:(id)peripheral; +- (instancetype)initWithPeripheral:(id)peripheral + requestDisconnectionHandler:(GNCRequestDisconnectionHandler)requestDisconnectionHandler; /** * Discovers the specified characteristics of a service. @@ -112,6 +123,9 @@ typedef void (^GNCReadCharacteristicValueCompletionHandler)(NSData *_Nullable va completionHandler: (nullable GNCReadCharacteristicValueCompletionHandler)completionHandler; +/** Cancels an active or pending local connection to a peripheral. */ +- (void)disconnect; + @end NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.m index 8b91c879..8f23477a 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.m +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.m @@ -49,6 +49,7 @@ static NSError *AlreadyReadingCharacteristicError() { @implementation GNCBLEGATTClient { dispatch_queue_t _queue; id _peripheral; + GNCRequestDisconnectionHandler _requestDisconnectionHandler; /** * A map of service UUIDs with each service holding a map of a list of characterisitcs to a @@ -70,15 +71,18 @@ static NSError *AlreadyReadingCharacteristicError() { *_readCharacteristicValueCompletionHandlers; } -- (instancetype)initWithPeripheral:(id)peripheral { - return [self - initWithPeripheral:peripheral - queue:dispatch_queue_create(kGNCBLEGATTClientQueueLabel, DISPATCH_QUEUE_SERIAL)]; +- (instancetype)initWithPeripheral:(id)peripheral + requestDisconnectionHandler:(GNCRequestDisconnectionHandler)requestDisconnectionHandler { + return [self initWithPeripheral:peripheral + queue:dispatch_queue_create(kGNCBLEGATTClientQueueLabel, + DISPATCH_QUEUE_SERIAL) + requestDisconnectionHandler:requestDisconnectionHandler]; }; // Private. - (instancetype)initWithPeripheral:(id)peripheral - queue:(nullable dispatch_queue_t)queue { + queue:(nullable dispatch_queue_t)queue + requestDisconnectionHandler:(GNCRequestDisconnectionHandler)requestDisconnectionHandler { self = [super init]; if (self) { _queue = queue ?: dispatch_get_main_queue(); @@ -86,6 +90,7 @@ static NSError *AlreadyReadingCharacteristicError() { _peripheral.peripheralDelegate = self; _discoverCharacteristicsCompletionHandlers = [[NSMutableDictionary alloc] init]; _readCharacteristicValueCompletionHandlers = [[NSMutableDictionary alloc] init]; + _requestDisconnectionHandler = requestDisconnectionHandler; } return self; }; @@ -173,6 +178,12 @@ static NSError *AlreadyReadingCharacteristicError() { }); } +- (void)disconnect { + dispatch_async(_queue, ^{ + _requestDisconnectionHandler(_peripheral); + }); +} + #pragma mark - Internal - (CBCharacteristic *)synchronousCharacteristicWithUUID:(CBUUID *)characteristicUUID diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.m index 139b5169..7c7d96ef 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.m +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.m @@ -245,7 +245,13 @@ static NSError *AlreadyScanningError() { GNCGATTConnectionCompletionHandler handler = _connectionCompletionHandlers[peripheral.identifier]; _connectionCompletionHandlers[peripheral.identifier] = nil; if (handler) { - GNCBLEGATTClient *client = [[GNCBLEGATTClient alloc] initWithPeripheral:peripheral]; + GNCBLEGATTClient *client = + [[GNCBLEGATTClient alloc] initWithPeripheral:peripheral + requestDisconnectionHandler:^(id peripheral) { + dispatch_async(_queue, ^{ + [_centralManager cancelPeripheralConnection:peripheral]; + }); + }]; handler(client, nil); } } diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.h index 770377ea..13057c39 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.h +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.h @@ -79,6 +79,21 @@ NS_ASSUME_NONNULL_BEGIN - (void)connectPeripheral:(id)peripheral options:(nullable NSDictionary *)options; +/** + * Cancels an active or pending local connection to a peripheral. + * + * This method is nonblocking, and any @c CBPeripheral class commands that are still pending to + * @c peripheral may not complete. Because other apps may still have a connection to the peripheral, + * canceling a local connection doesn’t guarantee that the underlying physical link is immediately + * disconnected. From the app’s perspective, however, the peripheral is effectively disconnected, + * and the central manager object calls the @c centralManager:didDisconnectPeripheral:error: method + * of its delegate object. + * + * @param peripheral The peripheral to which the central manager is either trying to connect or has + * already connected. + */ +- (void)cancelPeripheralConnection:(id)peripheral; + /** Asks the central manager to stop scanning for peripherals. */ - (void)stopScan; diff --git a/internal/platform/implementation/apple/Tests/GNCBLEGATTClient+Testing.h b/internal/platform/implementation/apple/Tests/GNCBLEGATTClient+Testing.h index a3a52f65..035138fd 100644 --- a/internal/platform/implementation/apple/Tests/GNCBLEGATTClient+Testing.h +++ b/internal/platform/implementation/apple/Tests/GNCBLEGATTClient+Testing.h @@ -31,9 +31,12 @@ NS_ASSUME_NONNULL_BEGIN * @param peripheral The peripheral instance. * @param queue The queue to run on, this must match the queue that the peripheral's delegate is * running on. Defaults to the main queue when @c nil. + * @param requestDisconnectionHandler Called on a private queue with @c peripheral when the + * connection to the peripheral should be cancelled. */ - (instancetype)initWithPeripheral:(id)peripheral - queue:(nullable dispatch_queue_t)queue; + queue:(nullable dispatch_queue_t)queue + requestDisconnectionHandler:(GNCRequestDisconnectionHandler)requestDisconnectionHandler; @end diff --git a/internal/platform/implementation/apple/Tests/GNCBLEGATTClientTest.m b/internal/platform/implementation/apple/Tests/GNCBLEGATTClientTest.m index b53cdef7..4fca9a78 100644 --- a/internal/platform/implementation/apple/Tests/GNCBLEGATTClientTest.m +++ b/internal/platform/implementation/apple/Tests/GNCBLEGATTClientTest.m @@ -19,6 +19,7 @@ #import #import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h" +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h" #import "internal/platform/implementation/apple/Tests/GNCBLEGATTClient+Testing.h" #import "internal/platform/implementation/apple/Tests/GNCFakePeripheral.h" @@ -37,8 +38,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 - (void)testDiscoverCharacteristics { GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -64,8 +68,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 fakePeripheral.discoverServicesError = [NSError errorWithDomain:@"fake" code:0 userInfo:nil]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -97,8 +104,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 code:0 userInfo:nil]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -123,8 +133,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 - (void)testDuplicateDiscoverCharacteristics { GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -163,8 +176,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 - (void)testDiscoverCharacteristicsMultipleCallsWithDifferentServices { GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID1 = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *serviceUUID2 = [CBUUID UUIDWithString:kServiceUUID2]; @@ -209,8 +225,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 - (void)testDiscoverCharacteristicsMultipleCallsWithDifferentCharacteristics { GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID1 = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -256,8 +275,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 - (void)testGetCharacteristic { GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -287,8 +309,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 fakePeripheral.discoverServicesError = [NSError errorWithDomain:@"fake" code:0 userInfo:nil]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -324,8 +349,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 code:0 userInfo:nil]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -353,8 +381,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 - (void)testDuplicateGetCharacteristic { GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -400,8 +431,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 - (void)testGetNonExistentCharacteristic { GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -425,8 +459,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 - (void)testReadValueForCharacteristic { GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -461,8 +498,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 fakePeripheral.discoverServicesError = [NSError errorWithDomain:@"fake" code:0 userInfo:nil]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -503,8 +543,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 code:0 userInfo:nil]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -541,8 +584,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 code:0 userInfo:nil]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -576,8 +622,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 - (void)testDuplicateReadValueForCharacteristic { GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -630,8 +679,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 - (void)testReadValueForMultipleCharacteristics { GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID1 = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -691,8 +743,11 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 - (void)testReadValueForUndiscoveredCharacteristic { GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; @@ -714,13 +769,33 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [self waitForExpectations:@[ expectation ] timeout:3]; } +- (void)testDisconnect { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"Disconnect."]; + + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id peripheral) { + XCTAssertNotNil(peripheral); + [expectation fulfill]; + }]; + + [gattClient disconnect]; + + [self waitForExpectations:@[ expectation ] timeout:3]; +} + #pragma mark - Delegate Calls - (void)testUnexpectedDelegateCalls { GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; - GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral - queue:nil]; + GNCBLEGATTClient *gattClient = + [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil + requestDisconnectionHandler:^(id __unused peripheral){ + }]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; diff --git a/internal/platform/implementation/apple/Tests/GNCBLEMediumTest.m b/internal/platform/implementation/apple/Tests/GNCBLEMediumTest.m index 47121133..33cea03b 100644 --- a/internal/platform/implementation/apple/Tests/GNCBLEMediumTest.m +++ b/internal/platform/implementation/apple/Tests/GNCBLEMediumTest.m @@ -18,6 +18,7 @@ #import #import +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h" #import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h" #import "internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h" #import "internal/platform/implementation/apple/Tests/GNCBLEMedium+Testing.h" @@ -313,8 +314,6 @@ static NSString *const kServiceUUID = @"0000FEF3-0000-1000-8000-00805F9B34FB"; - (void)testDisconnect { GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - XCTestExpectation *connectExpectation = - [[XCTestExpectation alloc] initWithDescription:@"Connect."]; XCTestExpectation *disconnectExpectation = [[XCTestExpectation alloc] initWithDescription:@"Disconnect."]; @@ -327,13 +326,9 @@ static NSString *const kServiceUUID = @"0000FEF3-0000-1000-8000-00805F9B34FB"; completionHandler:^(GNCBLEGATTClient *client, NSError *error) { XCTAssertNotNil(client); XCTAssertNil(error); - [connectExpectation fulfill]; + [client disconnect]; }]; - [self waitForExpectations:@[ connectExpectation ] timeout:3]; - - [fakeCentralManager simulateCentralManagerDidDisconnectPeripheral:peripheral]; - [self waitForExpectations:@[ disconnectExpectation ] timeout:3]; } diff --git a/internal/platform/implementation/apple/Tests/GNCFakeCentralManager.m b/internal/platform/implementation/apple/Tests/GNCFakeCentralManager.m index dc080f7e..30867e1b 100644 --- a/internal/platform/implementation/apple/Tests/GNCFakeCentralManager.m +++ b/internal/platform/implementation/apple/Tests/GNCFakeCentralManager.m @@ -56,6 +56,10 @@ [centralDelegate gnc_centralManager:self didConnectPeripheral:peripheral]; } +- (void)cancelPeripheralConnection:(id)peripheral { + [centralDelegate gnc_centralManager:self didDisconnectPeripheral:peripheral error:nil]; +} + - (void)stopScan { } diff --git a/internal/platform/implementation/apple/ble_gatt_client.mm b/internal/platform/implementation/apple/ble_gatt_client.mm index 13cbb684..a5dc5055 100644 --- a/internal/platform/implementation/apple/ble_gatt_client.mm +++ b/internal/platform/implementation/apple/ble_gatt_client.mm @@ -127,8 +127,9 @@ bool GattClient::SetCharacteristicSubscription( return false; } -// TODO(b/290385712): Implement. -void GattClient::Disconnect() {} +void GattClient::Disconnect() { + [gatt_client_ disconnect]; +} } // namespace apple } // namespace nearby diff --git a/internal/platform/implementation/apple/ble_medium.mm b/internal/platform/implementation/apple/ble_medium.mm index 9c1f6a55..f7bb3b0a 100644 --- a/internal/platform/implementation/apple/ble_medium.mm +++ b/internal/platform/implementation/apple/ble_medium.mm @@ -145,9 +145,15 @@ void BleMedium::HandleAdvertisementFound(id peripheral, std::unique_ptr BleMedium::StartScanning( const Uuid &service_uuid, api::ble_v2::TxPowerLevel tx_power_level, api::ble_v2::BleMedium::ScanningCallback callback) { + absl::MutexLock lock(&peripherals_mutex_); CBUUID *serviceUUID = CBUUID128FromCPP(service_uuid); scanning_cb_ = std::move(callback); + // Clear the map of discovered peripherals only when we are starting a new scan. If we cleared the + // 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(); + socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUID]; [socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUID ]]; @@ -171,9 +177,15 @@ std::unique_ptr BleMedium::StartScannin bool BleMedium::StartScanning(const Uuid &service_uuid, api::ble_v2::TxPowerLevel tx_power_level, api::ble_v2::BleMedium::ScanCallback callback) { + absl::MutexLock lock(&peripherals_mutex_); CBUUID *serviceUUID = CBUUID128FromCPP(service_uuid); scan_cb_ = std::move(callback); + // Clear the map of discovered peripherals only when we are starting a new scan. If we cleared the + // 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(); + socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUID]; [socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUID ]];