Add implementation for async BLE start scan/advertise

PiperOrigin-RevId: 559863656
This commit is contained in:
Nick Bourdakos
2023-08-24 14:07:46 -07:00
committed by Copybara-Service
parent 91fb4a9a3e
commit 965cc0ecbc
5 changed files with 103 additions and 31 deletions
@@ -13,6 +13,7 @@
// limitations under the License.
#import <CoreBluetooth/CoreBluetooth.h>
#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>
#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<Uuid, ByteArray> service_data;
service_data[Uuid(0x0000FEF300001000, 0x800000805F9B34FB)] = ByteArray("one");
service_data[Uuid(0x0000FEF400001000, 0x800000805F9B34FB)] = ByteArray("two");
NSMutableDictionary<CBUUID *, NSData *> *actual =
::nearby::apple::ObjCServiceDataFromCPP(service_data);
XCTAssertEqualObjects(actual[serviceUUID1], data1);
XCTAssertEqualObjects(actual[serviceUUID2], data2);
}
#pragma mark - Objective-C to C++
- (void)testCPPUUIDFromObjC16Bit {
@@ -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<GNCPeripheral> peripheral, NSDictionary<CBUUID *, NSData *> *serviceData,
absl::AnyInvocable<void(api::ble_v2::BlePeripheral &peripheral,
api::ble_v2::BleAdvertisementData advertisement_data)>
callback);
GNCBLEMedium *medium_;
absl::Mutex peripherals_mutex_;
@@ -56,22 +56,31 @@ namespace apple {
BleMedium::BleMedium() : medium_([[GNCBLEMedium alloc] init]) {}
// TODO(b/290385712): Implement.
std::unique_ptr<api::ble_v2::BleMedium::AdvertisingSession> 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<CBUUID *, NSData *> *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>(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<CBUUID *, NSData *> *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<CBUUID *, NSData *> *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<GNCPeripheral> peripheral, NSDictionary<CBUUID *, NSData *> *serviceData,
absl::AnyInvocable<void(api::ble_v2::BlePeripheral &peripheral,
api::ble_v2::BleAdvertisementData advertisement_data)>
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<BlePeripheral>(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<api::ble_v2::BleMedium::ScanningSession> 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<GNCPeripheral> peripheral,
NSDictionary<CBUUID *, NSData *> *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>(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<GNCPeripheral> peripheral,
NSDictionary<CBUUID *, NSData *> *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<BlePeripheral>(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) {
@@ -56,6 +56,9 @@ CBCharacteristicProperties CBCharacteristicPropertiesFromCPP(
/** Converts a C++ characteristic to an Objective-C characteristic. */
GNCBLEGATTCharacteristic *ObjCGATTCharacteristicFromCPP(const api::ble_v2::GattCharacteristic &c);
NSMutableDictionary<CBUUID *, NSData *> *ObjCServiceDataFromCPP(
const absl::flat_hash_map<Uuid, nearby::ByteArray> &sd);
/** Converts a 16 or 128 bit CoreBluetooth UUID to a C++ UUID. */
Uuid CPPUUIDFromObjC(CBUUID *uuid);
@@ -20,6 +20,7 @@
#include <string>
#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<CBUUID *, NSData *> *ObjCServiceDataFromCPP(
const absl::flat_hash_map<Uuid, nearby::ByteArray> &sd) {
NSMutableDictionary<CBUUID *, NSData *> *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) {