From 7f488271f166326c94fee540a30049c9a7181b6b Mon Sep 17 00:00:00 2001 From: Edwin Wu Date: Wed, 9 Apr 2025 22:57:18 -0700 Subject: [PATCH] Implement openL2CAPServerWithCompletionHandler in GNCBLEMedium class. PiperOrigin-RevId: 745877478 --- .../apple/Mediums/BLEv2/GNCBLEMedium.h | 21 ++++++++++++ .../apple/Mediums/BLEv2/GNCBLEMedium.m | 32 +++++++++++++++++++ .../apple/Tests/GNCBLEMedium+Testing.h | 1 + .../apple/Tests/GNCBLEMediumTest.m | 23 +++++++++++++ 4 files changed, 77 insertions(+) diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.h index 9f81b4e3..8094f07a 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.h +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.h @@ -18,7 +18,9 @@ @class GNCBLEGATTServer; @class GNCBLEGATTClient; @class GNCBLEGATTCharacteristic; +@class GNCBLEL2CAPServer; +@protocol GNCPeripheralManager; @protocol GNCPeripheral; NS_ASSUME_NONNULL_BEGIN @@ -87,6 +89,15 @@ typedef void (^GNCGATTDisconnectionHandler)(); typedef void (^GNCGATTConnectionCompletionHandler)(GNCBLEGATTClient *_Nullable client, NSError *_Nullable error); +/** + * A block to be invoked when a call to @c openL2CAPServerWithCompletionHandler: has completed. + * + * @param server The successfully started L2CAP server, or @c nil if an error occurred. + * @param error The cause of the failure, or @c nil if no error occurred. + */ +typedef void (^GNCOpenL2CAPServerCompletionHandler)(GNCBLEL2CAPServer *_Nullable server, + NSError *_Nullable error); + /** * The main BLE medium used inside of Nearby. This serves as the entry point for all BLE and GATT * related operations. @@ -181,6 +192,16 @@ typedef void (^GNCGATTConnectionCompletionHandler)(GNCBLEGATTClient *_Nullable c completionHandler: (nullable GNCGATTConnectionCompletionHandler)completionHandler; +/** + * Opens a L2CAP server. + * + * @param completionHandler Called on a private queue with the L2CAP server if successfully started + * or an error if one has occured. + * @param peripheralManager The peripheral manager instance. + */ +- (void)openL2CAPServerWithCompletionHandler:(GNCOpenL2CAPServerCompletionHandler)completionHandler + peripheralManager:(nullable id)peripheralManager; + @end NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.m index 0f4b91df..bf92971d 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.m +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.m @@ -20,6 +20,7 @@ #import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h" #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/GNCBLEL2CAPServer.h" #import "internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.h" #import "internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h" #import "internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCBase85.h" @@ -33,6 +34,16 @@ static NSError *AlreadyScanningError() { return [NSError errorWithDomain:GNCBLEErrorDomain code:GNCBLEErrorAlreadyScanning userInfo:nil]; } +static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( + id _Nullable peripheralManager) { + if (!peripheralManager) { + return [[GNCBLEL2CAPServer alloc] init]; + } else { + return [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:peripheralManager + queue:dispatch_get_main_queue()]; + } +} + @interface GNCBLEMedium () @end @@ -42,6 +53,7 @@ static NSError *AlreadyScanningError() { // The active GATT server, or @nil if one hasn't been started yet. GNCBLEGATTServer *_server; + GNCBLEL2CAPServer *_l2capServer; // The services that is being actively scanned for. NSMutableArray *_scanningServiceUUIDs; @@ -181,6 +193,26 @@ static NSError *AlreadyScanningError() { }); } +- (void)openL2CAPServerWithCompletionHandler:(GNCOpenL2CAPServerCompletionHandler)completionHandler + peripheralManager:(nullable id)peripheralManager { + // Capture the completion handler. + GNCOpenL2CAPServerCompletionHandler localCompletionHandler = completionHandler; + dispatch_async(_queue, ^{ + if (!_l2capServer) { + _l2capServer = CreateL2CapServer(peripheralManager); + } + __weak __typeof__(GNCBLEL2CAPServer *) weakL2capServer = _l2capServer; + [_l2capServer startListeningChannelWithCompletionHandler:^(NSError *error) { + __typeof__(GNCBLEL2CAPServer *) strongL2capServer = weakL2capServer; + if (error) { + localCompletionHandler(nil, error); + return; + } + localCompletionHandler(strongL2capServer, nil); + }]; + }); +} + #pragma mark - Internal - (void)internalStartScanningIfPoweredOn { diff --git a/internal/platform/implementation/apple/Tests/GNCBLEMedium+Testing.h b/internal/platform/implementation/apple/Tests/GNCBLEMedium+Testing.h index 3431613a..f0867c2b 100644 --- a/internal/platform/implementation/apple/Tests/GNCBLEMedium+Testing.h +++ b/internal/platform/implementation/apple/Tests/GNCBLEMedium+Testing.h @@ -17,6 +17,7 @@ #import @protocol GNCCentralManager; +@protocol GNCPeripheralManager; NS_ASSUME_NONNULL_BEGIN diff --git a/internal/platform/implementation/apple/Tests/GNCBLEMediumTest.m b/internal/platform/implementation/apple/Tests/GNCBLEMediumTest.m index 33cea03b..21c1fee2 100644 --- a/internal/platform/implementation/apple/Tests/GNCBLEMediumTest.m +++ b/internal/platform/implementation/apple/Tests/GNCBLEMediumTest.m @@ -24,6 +24,7 @@ #import "internal/platform/implementation/apple/Tests/GNCBLEMedium+Testing.h" #import "internal/platform/implementation/apple/Tests/GNCFakeCentralManager.h" #import "internal/platform/implementation/apple/Tests/GNCFakePeripheral.h" +#import "internal/platform/implementation/apple/Tests/GNCFakePeripheralManager.h" static NSString *const kServiceUUID = @"0000FEF3-0000-1000-8000-00805F9B34FB"; @@ -273,6 +274,28 @@ static NSString *const kServiceUUID = @"0000FEF3-0000-1000-8000-00805F9B34FB"; [self waitForExpectations:@[ expectation ] timeout:3]; } +#pragma mark - Open L2CAP Channel + +- (void)testOpenL2CAPServerSocket { + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Open L2CAP server."]; + + // Open L2CAP server is fully covered with @c GNCBLEL2CAPServer tests. + [medium + openL2CAPServerWithCompletionHandler:^(GNCBLEL2CAPServer *l2capServer, NSError *error) { + XCTAssertNotNil(l2capServer); + XCTAssertNil(error); + [expectation fulfill]; + } + peripheralManager:fakePeripheralManager]; + + [self waitForExpectations:@[ expectation ] timeout:3]; +} + #pragma mark - Connect - (void)testSuccessfulConnect {