diff --git a/internal/platform/implementation/apple/Tests/GNCBLEUtilsTest.mm b/internal/platform/implementation/apple/Tests/GNCBLEUtilsTest.mm index 4434a2b2..1cc73ecb 100644 --- a/internal/platform/implementation/apple/Tests/GNCBLEUtilsTest.mm +++ b/internal/platform/implementation/apple/Tests/GNCBLEUtilsTest.mm @@ -13,6 +13,7 @@ // limitations under the License. #import +#import #import #import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h" @@ -25,6 +26,7 @@ using Property = ::nearby::api::ble_v2::GattCharacteristic::Property; using Permission = ::nearby::api::ble_v2::GattCharacteristic::Permission; using WriteType = ::nearby::api::ble_v2::GattClient::WriteType; using Uuid = ::nearby::Uuid; +using ByteArray = ::nearby::ByteArray; @interface GNCBLEUtilsTest : XCTestCase @end @@ -120,6 +122,22 @@ using Uuid = ::nearby::Uuid; XCTAssertEqual(actual.properties, properties); } +- (void)testObjCServiceDataFromCpp { + CBUUID *serviceUUID1 = [CBUUID UUIDWithString:@"0000FEF3-0000-1000-8000-00805F9B34FB"]; + CBUUID *serviceUUID2 = [CBUUID UUIDWithString:@"0000FEF4-0000-1000-8000-00805F9B34FB"]; + NSData *data1 = [@"one" dataUsingEncoding:NSUTF8StringEncoding]; + NSData *data2 = [@"two" dataUsingEncoding:NSUTF8StringEncoding]; + + absl::flat_hash_map service_data; + service_data[Uuid(0x0000FEF300001000, 0x800000805F9B34FB)] = ByteArray("one"); + service_data[Uuid(0x0000FEF400001000, 0x800000805F9B34FB)] = ByteArray("two"); + + NSMutableDictionary *actual = + ::nearby::apple::ObjCServiceDataFromCPP(service_data); + XCTAssertEqualObjects(actual[serviceUUID1], data1); + XCTAssertEqualObjects(actual[serviceUUID2], data2); +} + #pragma mark - Objective-C to C++ - (void)testCPPUUIDFromObjC16Bit { diff --git a/internal/platform/implementation/apple/ble_medium.h b/internal/platform/implementation/apple/ble_medium.h index 049e0a59..7b00fed3 100644 --- a/internal/platform/implementation/apple/ble_medium.h +++ b/internal/platform/implementation/apple/ble_medium.h @@ -48,8 +48,6 @@ class BleMedium : public api::ble_v2::BleMedium { BleMedium(); ~BleMedium() override = default; - // TODO(b/290385712): Not yet implemented. - // // Async interface for StartAdvertising. // // Result status will be passed to start_advertising_result callback. To stop advertising, invoke @@ -72,8 +70,6 @@ class BleMedium : public api::ble_v2::BleMedium { // Returns whether or not advertising was successfully stopped. bool StopAdvertising() override; - // TODO(b/290385712): Not yet implemented. - // // Async interface for StartScanning. // // Result status will be passed to start_scanning_result callback on a private queue. To stop @@ -147,6 +143,12 @@ class BleMedium : public api::ble_v2::BleMedium { api::ble_v2::BleMedium::GetRemotePeripheralCallback callback) override; private: + void HandleAdvertisementFound( + id peripheral, NSDictionary *serviceData, + absl::AnyInvocable + callback); + GNCBLEMedium *medium_; absl::Mutex peripherals_mutex_; diff --git a/internal/platform/implementation/apple/ble_medium.mm b/internal/platform/implementation/apple/ble_medium.mm index 35d555aa..ae64d334 100644 --- a/internal/platform/implementation/apple/ble_medium.mm +++ b/internal/platform/implementation/apple/ble_medium.mm @@ -56,22 +56,31 @@ namespace apple { BleMedium::BleMedium() : medium_([[GNCBLEMedium alloc] init]) {} -// TODO(b/290385712): Implement. std::unique_ptr BleMedium::StartAdvertising( const api::ble_v2::BleAdvertisementData &advertising_data, api::ble_v2::AdvertiseParameters advertise_set_parameters, api::ble_v2::BleMedium::AdvertisingCallback callback) { - return nullptr; + NSMutableDictionary *serviceData = + ObjCServiceDataFromCPP(advertising_data.service_data); + + __block api::ble_v2::BleMedium::AdvertisingCallback blockCallback = std::move(callback); + + [medium_ startAdvertisingData:serviceData + completionHandler:^(NSError *error) { + blockCallback.start_advertising_result( + error == nil ? absl::OkStatus() + : absl::InternalError(error.localizedDescription.UTF8String)); + }]; + + return std::make_unique(AdvertisingSession{.stop_advertising = [this] { + return StopAdvertising() ? absl::OkStatus() : absl::InternalError("Failed to stop advertising"); + }}); } bool BleMedium::StartAdvertising(const api::ble_v2::BleAdvertisementData &advertising_data, api::ble_v2::AdvertiseParameters advertise_set_parameters) { - NSMutableDictionary *serviceData = [[NSMutableDictionary alloc] init]; - for (const auto &pair : advertising_data.service_data) { - CBUUID *key = CBUUID16FromCPP(pair.first); - NSData *data = NSDataFromByteArray(pair.second); - [serviceData setObject:data forKey:key]; - } + NSMutableDictionary *serviceData = + ObjCServiceDataFromCPP(advertising_data.service_data); dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); __block NSError *blockError = nil; @@ -101,11 +110,54 @@ bool BleMedium::StopAdvertising() { return blockError == nil; } -// TODO(b/290385712): Implement. +void BleMedium::HandleAdvertisementFound( + id peripheral, NSDictionary *serviceData, + absl::AnyInvocable + callback) { + absl::MutexLock lock(&peripherals_mutex_); + [socketCentralManager_ retrievePeripheralWithIdentifier:peripheral.identifier + advertisementData:@{}]; + + api::ble_v2::BleAdvertisementData data; + for (CBUUID *key in serviceData.allKeys) { + data.service_data[CPPUUIDFromObjC(key)] = ByteArrayFromNSData(serviceData[key]); + } + + // Add the peripheral to the map if we haven't discovered it yet. + auto ble_peripheral = std::make_unique(peripheral); + auto unique_id = ble_peripheral->GetUniqueId(); + auto it = peripherals_.find(unique_id); + if (it == peripherals_.end()) { + peripherals_[unique_id] = std::move(ble_peripheral); + } + callback(*peripherals_[unique_id], data); +} + std::unique_ptr BleMedium::StartScanning( const Uuid &service_uuid, api::ble_v2::TxPowerLevel tx_power_level, api::ble_v2::BleMedium::ScanningCallback callback) { - return nullptr; + CBUUID *serviceUUID = CBUUID128FromCPP(service_uuid); + __block api::ble_v2::BleMedium::ScanningCallback blockCallback = std::move(callback); + + socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUID]; + [socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUID ]]; + + [medium_ startScanningForService:serviceUUID + advertisementFoundHandler:^(id peripheral, + NSDictionary *serviceData) { + HandleAdvertisementFound(peripheral, serviceData, + std::move(blockCallback.advertisement_found_cb)); + } + completionHandler:^(NSError *error) { + blockCallback.start_scanning_result( + error == nil ? absl::OkStatus() + : absl::InternalError(error.localizedDescription.UTF8String)); + }]; + + return std::make_unique(ScanningSession{.stop_scanning = [this] { + return StopScanning() ? absl::OkStatus() : absl::InternalError("Failed to stop scanning"); + }}); } bool BleMedium::StartScanning(const Uuid &service_uuid, api::ble_v2::TxPowerLevel tx_power_level, @@ -121,23 +173,8 @@ bool BleMedium::StartScanning(const Uuid &service_uuid, api::ble_v2::TxPowerLeve [medium_ startScanningForService:serviceUUID advertisementFoundHandler:^(id peripheral, NSDictionary *serviceData) { - absl::MutexLock lock(&peripherals_mutex_); - [socketCentralManager_ retrievePeripheralWithIdentifier:peripheral.identifier - advertisementData:@{}]; - - api::ble_v2::BleAdvertisementData data; - for (CBUUID *key in serviceData.allKeys) { - data.service_data[CPPUUIDFromObjC(key)] = ByteArrayFromNSData(serviceData[key]); - } - - // Add the peripheral to the map if we haven't discovered it yet. - auto ble_peripheral = std::make_unique(peripheral); - auto unique_id = ble_peripheral->GetUniqueId(); - auto it = peripherals_.find(unique_id); - if (it == peripherals_.end()) { - peripherals_[unique_id] = std::move(ble_peripheral); - } - blockCallback.advertisement_found_cb(*peripherals_[unique_id], data); + HandleAdvertisementFound(peripheral, serviceData, + std::move(blockCallback.advertisement_found_cb)); } completionHandler:^(NSError *error) { if (error != nil) { diff --git a/internal/platform/implementation/apple/ble_utils.h b/internal/platform/implementation/apple/ble_utils.h index 209298fb..4477c403 100644 --- a/internal/platform/implementation/apple/ble_utils.h +++ b/internal/platform/implementation/apple/ble_utils.h @@ -56,6 +56,9 @@ CBCharacteristicProperties CBCharacteristicPropertiesFromCPP( /** Converts a C++ characteristic to an Objective-C characteristic. */ GNCBLEGATTCharacteristic *ObjCGATTCharacteristicFromCPP(const api::ble_v2::GattCharacteristic &c); +NSMutableDictionary *ObjCServiceDataFromCPP( + const absl::flat_hash_map &sd); + /** Converts a 16 or 128 bit CoreBluetooth UUID to a C++ UUID. */ Uuid CPPUUIDFromObjC(CBUUID *uuid); diff --git a/internal/platform/implementation/apple/ble_utils.mm b/internal/platform/implementation/apple/ble_utils.mm index cf08649e..cd9f3cdb 100644 --- a/internal/platform/implementation/apple/ble_utils.mm +++ b/internal/platform/implementation/apple/ble_utils.mm @@ -20,6 +20,7 @@ #include #import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h" +#import "internal/platform/implementation/apple/utils.h" namespace nearby { namespace apple { @@ -98,6 +99,17 @@ GNCBLEGATTCharacteristic *ObjCGATTCharacteristicFromCPP(const GattCharacteristic properties:properties]; } +NSMutableDictionary *ObjCServiceDataFromCPP( + const absl::flat_hash_map &sd) { + NSMutableDictionary *serviceData = [NSMutableDictionary dictionary]; + for (const auto &pair : sd) { + CBUUID *key = CBUUID16FromCPP(pair.first); + NSData *data = NSDataFromByteArray(pair.second); + [serviceData setObject:data forKey:key]; + } + return serviceData; +} + Uuid CPPUUIDFromObjC(CBUUID *uuid) { NSString *uuidString = uuid.UUIDString; if (uuidString.length == 4) {