Add stop advertising and stop scanning BLE implementation for Apple platforms

PiperOrigin-RevId: 559838443
This commit is contained in:
Nick Bourdakos
2023-08-24 12:45:23 -07:00
committed by Copybara-Service
parent 387393f1d6
commit 91fb4a9a3e
9 changed files with 218 additions and 8 deletions
@@ -19,11 +19,39 @@
NS_ASSUME_NONNULL_BEGIN
/**
* A block to be invoked when a call to
* @c createCharacteristicWithServiceID:characteristicUUID:permissions:properties:completionHandler:
* has completed.
*
* @param characteristic The created characteristic, or @c nil if an error occurred.
* @param error The cause of the failure, or @c nil if no error occurred.
*/
typedef void (^GNCCreateCharacteristicCompletionHandler)(
GNCBLEGATTCharacteristic *_Nullable characteristic, NSError *_Nullable error);
/**
* A block to be invoked when a call to @c updateCharacteristic:value:completionHandler: has
* completed.
*
* @param error The cause of the failure, or @c nil if no error occurred.
*/
typedef void (^GNCUpdateCharacteristicCompletionHandler)(NSError *_Nullable error);
/**
* A block to be invoked when a call to @c startAdvertisingData:completionHandler: has completed.
*
* @param error The cause of the failure, or @c nil if no error occurred.
*/
typedef void (^GNCStartAdvertisingCompletionHandler)(NSError *_Nullable error);
/**
* A block to be invoked when a call to @c stopAdvertisingWithcompletionHandler: has completed.
*
* @param error The cause of the failure, or @c nil if no error occurred.
*/
typedef void (^GNCStopAdvertisingCompletionHandler)(NSError *_Nullable error);
/**
* An object that manages and advertises GATT characteritics.
*
@@ -82,6 +110,15 @@ typedef void (^GNCStartAdvertisingCompletionHandler)(NSError *_Nullable error);
- (void)startAdvertisingData:(NSDictionary<CBUUID *, NSData *> *)serviceData
completionHandler:(nullable GNCStartAdvertisingCompletionHandler)completionHandler;
/**
* Stops advertising all service data.
*
* @param completionHandler Called on a private queue with @c nil if successfully stopped
* advertising or an error if one has occured.
*/
- (void)stopAdvertisingWithCompletionHandler:
(nullable GNCStopAdvertisingCompletionHandler)completionHandler;
@end
NS_ASSUME_NONNULL_END
@@ -223,6 +223,17 @@ static char *const kGNCBLEGATTServerQueueLabel = "com.nearby.GNCBLEGATTServer";
});
}
- (void)stopAdvertisingWithCompletionHandler:
(nullable GNCStopAdvertisingCompletionHandler)completionHandler {
dispatch_async(_queue, ^{
_advertisementData = nil;
[_peripheralManager stopAdvertising];
if (completionHandler) {
completionHandler(nil);
}
});
}
#pragma mark - Internal
- (void)internalAddPendingServicesIfPoweredOn {
@@ -30,6 +30,13 @@ NS_ASSUME_NONNULL_BEGIN
*/
typedef void (^GNCStartAdvertisingCompletionHandler)(NSError *_Nullable error);
/**
* A block to be invoked when a call to @c stopAdvertisingWithcompletionHandler: has completed.
*
* @param error The cause of the failure, or @c nil if no error occurred.
*/
typedef void (^GNCStopAdvertisingCompletionHandler)(NSError *_Nullable error);
/**
* A block to be invoked when a peripherals advertisement has been discovered.
*
@@ -50,6 +57,13 @@ typedef void (^GNCAdvertisementFoundHandler)(id<GNCPeripheral> peripheral,
*/
typedef void (^GNCStartScanningCompletionHandler)(NSError *_Nullable error);
/**
* A block to be invoked when a call to @c stopScanningWithCompletionHandler: has completed.
*
* @param error The cause of the failure, or @c nil if no error occurred.
*/
typedef void (^GNCStopScanningCompletionHandler)(NSError *_Nullable error);
/**
* A block to be invoked when a call to @c startGATTServerWithCompletionHandler: has completed.
*
@@ -100,6 +114,15 @@ typedef void (^GNCGATTConnectionCompletionHandler)(GNCBLEGATTClient *_Nullable c
- (void)startAdvertisingData:(NSDictionary<CBUUID *, NSData *> *)serviceData
completionHandler:(nullable GNCStartAdvertisingCompletionHandler)completionHandler;
/**
* Stops advertising all service data.
*
* @param completionHandler Called on a private queue with @c nil if successfully stopped
* advertising or an error if one has occured.
*/
- (void)stopAdvertisingWithCompletionHandler:
(nullable GNCStopAdvertisingCompletionHandler)completionHandler;
/**
* Scans for peripherals that are advertising the specified service.
*
@@ -112,6 +135,15 @@ typedef void (^GNCGATTConnectionCompletionHandler)(GNCBLEGATTClient *_Nullable c
advertisementFoundHandler:(GNCAdvertisementFoundHandler)advertisementFoundHandler
completionHandler:(nullable GNCStartScanningCompletionHandler)completionHandler;
/**
* Stops scanning for peripherals.
*
* @param completionHandler Called on a private queue with @c nil if successfully stopped
* scanning or an error if one has occured.
*/
- (void)stopScanningWithCompletionHandler:
(nullable GNCStopScanningCompletionHandler)completionHandler;
/**
* Starts a GATT server.
*
@@ -101,6 +101,19 @@ static NSError *AlreadyScanningError() {
});
}
- (void)stopAdvertisingWithCompletionHandler:
(nullable GNCStopAdvertisingCompletionHandler)completionHandler {
dispatch_async(_queue, ^{
if (!_server) {
if (completionHandler) {
completionHandler(nil);
}
return;
}
[_server stopAdvertisingWithCompletionHandler:completionHandler];
});
}
- (void)startScanningForService:(CBUUID *)serviceUUID
advertisementFoundHandler:(GNCAdvertisementFoundHandler)advertisementFoundHandler
completionHandler:(nullable GNCStartScanningCompletionHandler)completionHandler {
@@ -122,6 +135,18 @@ static NSError *AlreadyScanningError() {
});
}
- (void)stopScanningWithCompletionHandler:
(nullable GNCStopScanningCompletionHandler)completionHandler {
dispatch_async(_queue, ^{
_serviceUUID = nil;
_advertisementFoundHandler = nil;
[_centralManager stopScan];
if (completionHandler) {
completionHandler(nil);
}
});
}
- (void)startGATTServerWithCompletionHandler:
(nullable GNCGATTServerCompletionHandler)completionHandler {
dispatch_async(_queue, ^{
@@ -614,4 +614,44 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
[self waitForExpectations:@[ expectation ] timeout:3];
}
- (void)testStartStopStartAdvertising {
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
GNCBLEGATTServer *gattServer =
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
XCTestExpectation *expectation =
[[XCTestExpectation alloc] initWithDescription:@"Start advertising."];
[gattServer startAdvertisingData:@{[CBUUID UUIDWithString:@"FEF3"] : [NSData data]}
completionHandler:^(NSError *error) {
XCTAssertNil(error);
XCTAssertTrue(fakePeripheralManager.isAdvertising);
NSDictionary<NSString *, id> *data = fakePeripheralManager.advertisementData;
XCTAssertEqualObjects(data[CBAdvertisementDataLocalNameKey], @"");
XCTAssertEqualObjects(data[CBAdvertisementDataServiceUUIDsKey][0],
[CBUUID UUIDWithString:@"FEF3"]);
[gattServer stopAdvertisingWithCompletionHandler:^(NSError *error) {
XCTAssertNil(error);
XCTAssertFalse(fakePeripheralManager.isAdvertising);
[gattServer
startAdvertisingData:@{[CBUUID UUIDWithString:@"FEF4"] : [NSData data]}
completionHandler:^(NSError *error) {
XCTAssertNil(error);
XCTAssertTrue(fakePeripheralManager.isAdvertising);
NSDictionary<NSString *, id> *data =
fakePeripheralManager.advertisementData;
XCTAssertEqualObjects(data[CBAdvertisementDataLocalNameKey], @"");
XCTAssertEqualObjects(data[CBAdvertisementDataServiceUUIDsKey][0],
[CBUUID UUIDWithString:@"FEF4"]);
[expectation fulfill];
}];
}];
}];
[self waitForExpectations:@[ expectation ] timeout:3];
}
@end
@@ -106,6 +106,36 @@ static NSString *const kServiceUUID = @"0000FEF3-0000-1000-8000-00805F9B34FB";
[self waitForExpectations:@[ expectation ] timeout:3];
}
- (void)testStartStopStartScanning {
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
XCTestExpectation *expectation =
[[XCTestExpectation alloc] initWithDescription:@"Start scanning."];
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
[medium startScanningForService:serviceUUID
advertisementFoundHandler:^(id<GNCPeripheral> peripheral,
NSDictionary<CBUUID *, NSData *> *data) {
}
completionHandler:^(NSError *error) {
XCTAssertNil(error);
[medium stopScanningWithCompletionHandler:^(NSError *error) {
XCTAssertNil(error);
[medium startScanningForService:serviceUUID
advertisementFoundHandler:^(id<GNCPeripheral> peripheral,
NSDictionary<CBUUID *, NSData *> *data) {
}
completionHandler:^(NSError *error) {
XCTAssertNil(error);
[expectation fulfill];
}];
}];
}];
[self waitForExpectations:@[ expectation ] timeout:3];
}
#pragma mark - Decode Advertisement Data
- (void)testDecodeAndroidStyleAdvertisementData {
@@ -226,6 +256,22 @@ static NSString *const kServiceUUID = @"0000FEF3-0000-1000-8000-00805F9B34FB";
[self waitForExpectations:@[ expectation ] timeout:3];
}
- (void)testStopAdvertising {
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
XCTestExpectation *expectation =
[[XCTestExpectation alloc] initWithDescription:@"Stop advertising."];
// Stop advertising is fully covered with @c GNCBLEGATTServer tests. We are only testing stopping
// without having started which tests the code paths relevant to @c GNCBLEMedium.
[medium stopAdvertisingWithCompletionHandler:^(NSError *error) {
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:3];
}
#pragma mark - Connect
- (void)testSuccessfulConnect {
@@ -119,6 +119,7 @@
}
- (void)stopAdvertising {
_isAdvertising = false;
}
#pragma mark - Testing Helpers
@@ -67,8 +67,6 @@ class BleMedium : public api::ble_v2::BleMedium {
bool StartAdvertising(const api::ble_v2::BleAdvertisementData &advertising_data,
api::ble_v2::AdvertiseParameters advertise_set_parameters) override;
// TODO(b/290385712): Not yet implemented.
//
// Stops advertising.
//
// Returns whether or not advertising was successfully stopped.
@@ -92,8 +90,6 @@ class BleMedium : public api::ble_v2::BleMedium {
bool StartScanning(const Uuid &service_uuid, api::ble_v2::TxPowerLevel tx_power_level,
api::ble_v2::BleMedium::ScanCallback callback) override;
// TODO(b/290385712): Not yet implemented.
//
// Stops scanning.
//
// Returns whether or not scanning was successfully stopped.
@@ -87,8 +87,19 @@ bool BleMedium::StartAdvertising(const api::ble_v2::BleAdvertisementData &advert
return blockError == nil;
}
// TODO(b/290385712): Implement.
bool BleMedium::StopAdvertising() { return false; }
bool BleMedium::StopAdvertising() {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block NSError *blockError = nil;
[medium_ stopAdvertisingWithCompletionHandler:^(NSError *error) {
if (error != nil) {
GTMLoggerError(@"Failed to stop advertising: %@", error);
}
blockError = error;
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return blockError == nil;
}
// TODO(b/290385712): Implement.
std::unique_ptr<api::ble_v2::BleMedium::ScanningSession> BleMedium::StartScanning(
@@ -139,8 +150,19 @@ bool BleMedium::StartScanning(const Uuid &service_uuid, api::ble_v2::TxPowerLeve
return blockError == nil;
}
// TODO(b/290385712): Implement.
bool BleMedium::StopScanning() { return false; }
bool BleMedium::StopScanning() {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block NSError *blockError = nil;
[medium_ stopScanningWithCompletionHandler:^(NSError *error) {
if (error != nil) {
GTMLoggerError(@"Failed to stop scanning: %@", error);
}
blockError = error;
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return blockError == nil;
}
// TODO(b/290385712): Add implementation that calls ServerGattConnectionCallback methods.
std::unique_ptr<api::ble_v2::GattServer> BleMedium::StartGattServer(