diff --git a/internal/platform/implementation/ios/BUILD b/internal/platform/implementation/ios/BUILD index a64b1011..8d2f4a5b 100644 --- a/internal/platform/implementation/ios/BUILD +++ b/internal/platform/implementation/ios/BUILD @@ -21,6 +21,8 @@ package(default_visibility = [ objc_library( name = "Platform", srcs = [ + "ble.mm", + "bluetooth_adapter.mm", "crypto.mm", "log_message.mm", "multi_thread_executor.mm", @@ -30,6 +32,8 @@ objc_library( "wifi_lan.mm", ], hdrs = [ + "ble.h", + "bluetooth_adapter.h", "log_message.h", "multi_thread_executor.h", "scheduled_executor.h", diff --git a/internal/platform/implementation/ios/Mediums/BUILD b/internal/platform/implementation/ios/Mediums/BUILD index bf70582a..435773a4 100644 --- a/internal/platform/implementation/ios/Mediums/BUILD +++ b/internal/platform/implementation/ios/Mediums/BUILD @@ -20,6 +20,8 @@ package(default_visibility = ["//internal/platform/implementation/ios:__subpacka objc_library( name = "Mediums", srcs = [ + "Ble/GNCMBleCentral.m", + "Ble/GNCMBlePeripheral.m", "GNCLeaks.m", "GNCMConnection.m", "WifiLan/GNCMBonjourBrowser.m", @@ -28,6 +30,8 @@ objc_library( "WifiLan/GNCMBonjourUtils.m", ], hdrs = [ + "Ble/GNCMBleCentral.h", + "Ble/GNCMBlePeripheral.h", "GNCLeaks.h", "GNCMConnection.h", "WifiLan/GNCMBonjourBrowser.h", diff --git a/internal/platform/implementation/ios/Mediums/Ble/GNCMBleCentral.h b/internal/platform/implementation/ios/Mediums/Ble/GNCMBleCentral.h new file mode 100644 index 00000000..8d87ec92 --- /dev/null +++ b/internal/platform/implementation/ios/Mediums/Ble/GNCMBleCentral.h @@ -0,0 +1,51 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "internal/platform/implementation/ios/Mediums/GNCMConnection.h" + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * This handler is called on a discover when a nearby advertising endpoint is discovered. + */ +typedef void (^GNCMScanResultHandler)(NSString *serviceUUID, NSData *serviceData); + +/** + * GNCMBleCentral discovers devices advertising the specified service UUID via BLE (using the + * GNCMBlePeripheral class) and calls the specififed scanning result handler when one is found. + * + * This class is thread-safe. Any calls made to it (and to the objects/closures it passes back via + * callbacks) can be made from any thread/queue. Callbacks made from this class are called on the + * specified queue. + */ +@interface GNCMBleCentral : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/** + * Initializes an `GNCMBleCentral` object. + * + * @param serviceUUID A string that uniquely identifies the scanning services to search for. + * @param scanResultHandler The handler that is called when an endpoint advertising the service + * UUID is discovered. + */ +- (instancetype)initWithServiceUUID:(NSString *)serviceUUID + scanResultHandler:(GNCMScanResultHandler)scanResultHandler + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/ios/Mediums/Ble/GNCMBleCentral.m b/internal/platform/implementation/ios/Mediums/Ble/GNCMBleCentral.m new file mode 100644 index 00000000..5feb3598 --- /dev/null +++ b/internal/platform/implementation/ios/Mediums/Ble/GNCMBleCentral.m @@ -0,0 +1,80 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "internal/platform/implementation/ios/Mediums/Ble/GNCMBleCentral.h" + +#include + +#import "internal/platform/implementation/ios/Mediums/GNCMConnection.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface GNCMBleCentral () +@end + +@implementation GNCMBleCentral { + /** Service UUID the central is scanning for. */ + CBUUID *_serviceUUID; + /** The scan result callback handler. */ + GNCMScanResultHandler _scanResultHandler; + /** Central manager used to scan or connect to peripherals. */ + CBCentralManager *_centralManager; + /** Serial background queue for |centralManager|. */ + dispatch_queue_t _selfQueue; +} + +- (instancetype)initWithServiceUUID:(NSString *)serviceUUID + scanResultHandler:(GNCMScanResultHandler)scanResultHandler { + self = [super init]; + if (self) { + _serviceUUID = [CBUUID UUIDWithString:serviceUUID]; + _scanResultHandler = scanResultHandler; + + // To make this class thread-safe, use a serial queue for all state changes, and have Core + // Bluetooth also use this queue. + _selfQueue = dispatch_queue_create("GNCCentralManagerQueue", DISPATCH_QUEUE_SERIAL); + _centralManager = [[CBCentralManager alloc] + initWithDelegate:self + queue:_selfQueue + options:@{CBCentralManagerOptionShowPowerAlertKey : @NO}]; + } + return self; +} + +- (void)dealloc { + // These calls must be made on |selfQueue|. Can't capture |self| in an async block, so must use + // dispatch_sync. This means dealloc must be called from an external queue, which means |self| + // must never be captured by any escaping block used in this class. + dispatch_sync(_selfQueue, ^{ + [_centralManager stopScan]; + }); +} + +#pragma mark CBCentralManagerDelegate + +- (void)centralManagerDidUpdateState:(CBCentralManager *)central { + if (central.state == CBManagerStatePoweredOn) { + NSLog(@"[NEARBY] CBCentralManager powered on; starting scan"); + [_centralManager + scanForPeripheralsWithServices:@[ _serviceUUID ] + options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES}]; + } else { + NSLog(@"[NEARBY] CBCentralManager not powered on; stopping scan"); + [_centralManager stopScan]; + } +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/ios/Mediums/Ble/GNCMBlePeripheral.h b/internal/platform/implementation/ios/Mediums/Ble/GNCMBlePeripheral.h new file mode 100644 index 00000000..1c515441 --- /dev/null +++ b/internal/platform/implementation/ios/Mediums/Ble/GNCMBlePeripheral.h @@ -0,0 +1,42 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +NS_ASSUME_NONNULL_BEGIN + +/** + * GNCMBlePeripheral advertises the specified service UUID via Ble for the purpose of being + * discovered by a central using the GNCMBleCentral class. + * + * This class is thread-safe. Any calls made to it (and to the objects/closures it passes back via + * callbacks) can be made from any thread/queue. Callbacks made from this class are called on the + * specified queue. + */ +@interface GNCMBlePeripheral : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/** + * Initializes an `GNCMBlePeripheral` object. + * + * @param serviceUUID A string that uniquely identifies the advertised service to search for. + * @param advertisementData The data to advertise. + */ +- (instancetype)initWithServiceUUID:(NSString *)serviceUUID + advertisementData:(NSData *)advertisementData NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/ios/Mediums/Ble/GNCMBlePeripheral.m b/internal/platform/implementation/ios/Mediums/Ble/GNCMBlePeripheral.m new file mode 100644 index 00000000..f3fcdc2a --- /dev/null +++ b/internal/platform/implementation/ios/Mediums/Ble/GNCMBlePeripheral.m @@ -0,0 +1,105 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "internal/platform/implementation/ios/Mediums/Ble/GNCMBlePeripheral.h" + +#include + +#import "internal/platform/implementation/ios/GNCUtils.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface GNCMBlePeripheral () +@end + +@implementation GNCMBlePeripheral { + /** GATT service for advertisement. */ + CBMutableService *_advertisementService; + /** Data to be advertised. */ + NSData *_advertisementData; + /** Peripheral manager used to advertise or connect to peripherals. */ + CBPeripheralManager *_peripheralManager; + /** Serial background queue for |peripheralManager|. */ + dispatch_queue_t _selfQueue; +} + +- (instancetype)initWithServiceUUID:(NSString *)serviceUUID + advertisementData:(NSData *)advertisementData { + self = [super init]; + if (self) { + _advertisementService = + [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:serviceUUID] primary:YES]; + _advertisementData = [advertisementData copy]; + + // To make this class thread-safe, use a serial queue for all state changes, and have Core + // Bluetooth also use this queue. + _selfQueue = dispatch_queue_create("GNCPeripheralManagerQueue", DISPATCH_QUEUE_SERIAL); + + // Set up the peripheral manager for the advertisement data. + _peripheralManager = [[CBPeripheralManager alloc] + initWithDelegate:self + queue:_selfQueue + options:@{CBPeripheralManagerOptionShowPowerAlertKey : @NO}]; + } + return self; +} + +- (void)dealloc { + // These calls must be made on |selfQueue|. Can't capture |self| in an async block, so must use + // dispatch_sync. This means delloc must be called from an external queue, which means |self| + // must never be captured by any escaping block used in this class. + dispatch_sync(_selfQueue, ^{ + [self stopAdvertising]; + }); +} + +#pragma mark CBPeripheralManagerDelegate + +- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral { + if (peripheral.state == CBManagerStatePoweredOn) { + NSLog(@"[NEARBY] CBPeripheralManager powered on; starting advertising"); + [_peripheralManager startAdvertising:@{ + CBAdvertisementDataServiceUUIDsKey : @[ _advertisementService.UUID ], + CBAdvertisementDataLocalNameKey : _advertisementData + }]; + } else { + NSLog(@"[NEARBY] CBPeripheralManager not powered on; stopping advertising"); + [self stopAdvertising]; + } +} + +- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral + error:(nullable NSError *)error { + if (error) { + NSLog(@"[NEARBY] Error starting advertising: %@,", [error localizedDescription]); + return; + } + if (_peripheralManager.state != CBPeripheralManagerStatePoweredOn) { + NSLog(@"[NEARBY] Error starting advertising: peripheral manager not on!"); + return; + } + + NSLog(@"[NEARBY] Peripheral manager started advertising"); +} + +#pragma mark Private + +/** Signals the peripheral manager to stop advertising. Must be called on _selfQueue */ +- (void)stopAdvertising { + [_peripheralManager stopAdvertising]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/ios/Tests/BUILD b/internal/platform/implementation/ios/Tests/BUILD index 4e048bcd..458ec0e8 100644 --- a/internal/platform/implementation/ios/Tests/BUILD +++ b/internal/platform/implementation/ios/Tests/BUILD @@ -21,6 +21,8 @@ objc_library( name = "PlatformTestslib", testonly = 1, srcs = [ + "GNCBleTest.mm", + "GNCBluetoothAdapterTest.mm", "GNCCryptoTest.mm", "GNCMultiThreadExecutorTest.mm", "GNCScheduledExecutorTest.mm", diff --git a/internal/platform/implementation/ios/Tests/GNCBLEATest.mm b/internal/platform/implementation/ios/Tests/GNCBLEATest.mm new file mode 100644 index 00000000..3818d9f6 --- /dev/null +++ b/internal/platform/implementation/ios/Tests/GNCBLEATest.mm @@ -0,0 +1,22 @@ +#import "internal/platform/implementation/ios/Tests/GNCBLEA.h" + +#import + +@interface GNCBLEATest : XCTestCase +@end + +@implementation GNCBLEATest +- (void)setUp { + [super setUp]; + // Remove if not used. +} + +- (void)tearDown { + // Remove if not used. + [super tearDown]; +} + +- (void)testFoo { + XCTAssertTrue(YES, @"A true test"); +} +@end diff --git a/internal/platform/implementation/ios/Tests/GNCBleTest.mm b/internal/platform/implementation/ios/Tests/GNCBleTest.mm new file mode 100644 index 00000000..27842e05 --- /dev/null +++ b/internal/platform/implementation/ios/Tests/GNCBleTest.mm @@ -0,0 +1,72 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#include +#include + +#include "internal/platform/byte_array.h" +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/implementation/bluetooth_adapter.h" +#include "internal/platform/implementation/platform.h" + +using ::location::nearby::ByteArray; +using ::location::nearby::Uuid; +using ::location::nearby::api::BluetoothAdapter; +using ::location::nearby::api::ImplementationPlatform; +using ::location::nearby::api::ble_v2::BleAdvertisementData; +using ::location::nearby::api::ble_v2::BleMedium; +using ::location::nearby::api::ble_v2::TxPowerLevel; + +static const char *const kAdvertisementString = "\x0a\x0b\x0c\x0d"; +static const TxPowerLevel kTxPowerLevel = TxPowerLevel::kHigh; + +@interface GNCBleTest : XCTestCase +@end + +@implementation GNCBleTest { + std::unique_ptr _adapter; + std::unique_ptr _ble; +} + +- (void)setUp { + [super setUp]; + _adapter = ImplementationPlatform::CreateBluetoothAdapter(); + _ble = ImplementationPlatform::CreateBleV2Medium(*_adapter); +} + +- (void)testStartandStopAdvertising { + Uuid service_uuid(1234, 5678); + ByteArray advertisement_bytes{std::string(kAdvertisementString)}; + + // Assemble regular advertisement data. + BleAdvertisementData advertising_data; + advertising_data.is_extended_advertisement = false; + advertising_data.service_data = {{service_uuid, advertisement_bytes}}; + + XCTAssertTrue(_ble->StartAdvertising(advertising_data, + {.tx_power_level = kTxPowerLevel, .is_connectable = true})); + XCTAssertTrue(_ble->StopAdvertising()); +} + +- (void)testStartandStopScanning { + Uuid service_uuid(1234, 5678); + + XCTAssertTrue(_ble->StartScanning(service_uuid, kTxPowerLevel, {})); + + XCTAssertTrue(_ble->StopScanning()); +} + +@end diff --git a/internal/platform/implementation/ios/Tests/GNCBluetoothAdapterTest.mm b/internal/platform/implementation/ios/Tests/GNCBluetoothAdapterTest.mm new file mode 100644 index 00000000..664a382a --- /dev/null +++ b/internal/platform/implementation/ios/Tests/GNCBluetoothAdapterTest.mm @@ -0,0 +1,87 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#include + +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/implementation/bluetooth_adapter.h" +#include "internal/platform/implementation/ios/bluetooth_adapter.h" + +using ::location::nearby::ios::BlePeripheral; +using ::location::nearby::ios::BluetoothAdapter; +using ScanMode = ::location::nearby::api::BluetoothAdapter::ScanMode; +using Status = ::location::nearby::api::BluetoothAdapter::Status; + +static const char kAdapterName[] = "MyBtAdapter"; +static const char kMacAddress[] = "4C:8B:1D:CE:BA:D1"; + +@interface GNCBluetoothAdapterTest : XCTestCase +@end + +@implementation GNCBluetoothAdapterTest + +- (void)testName { + BluetoothAdapter adapter; + + XCTAssertTrue(adapter.SetName(kAdapterName)); + + XCTAssertEqual(adapter.GetName(), std::string(kAdapterName)); +} + +- (void)testGetScanMode { + BluetoothAdapter adapter; + + // Always return kNone as ScanMode is not supported . + XCTAssertEqual(adapter.GetScanMode(), ScanMode::kNone); +} + +- (void)testSetScanMode_DefaultUnsupported { + BluetoothAdapter adapter; + + XCTAssertFalse(adapter.SetScanMode(ScanMode::kNone)); + XCTAssertFalse(adapter.SetScanMode(ScanMode::kConnectable)); + XCTAssertFalse(adapter.SetScanMode(ScanMode::kConnectableDiscoverable)); +} + +- (void)testSetStatus { + BluetoothAdapter adapter; + + XCTAssertTrue(adapter.SetStatus(Status::kDisabled)); + XCTAssertFalse(adapter.IsEnabled()); + + XCTAssertTrue(adapter.SetStatus(Status::kEnabled)); + XCTAssertTrue(adapter.IsEnabled()); +} + +- (void)testMacAddress { + BluetoothAdapter adapter; + + adapter.SetMacAddress(kMacAddress); + + XCTAssertEqual(adapter.GetMacAddress(), std::string(kMacAddress)); +} + +- (void)testGetPeripheral { + BluetoothAdapter adapter; + + adapter.SetMacAddress(kMacAddress); + + // The peripheral from adapter, the MAC address is the same. + BlePeripheral& peripheral = adapter.GetPeripheral(); + XCTAssertEqual(adapter.GetMacAddress(), peripheral.GetAddress()); +} + +@end diff --git a/internal/platform/implementation/ios/ble.h b/internal/platform/implementation/ios/ble.h new file mode 100644 index 00000000..ce0d0e28 --- /dev/null +++ b/internal/platform/implementation/ios/ble.h @@ -0,0 +1,69 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_IOS_BLE_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_IOS_BLE_H_ + +#import + +#include + +#include "internal/platform/cancellation_flag.h" +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/implementation/bluetooth_adapter.h" +#include "internal/platform/implementation/ios/bluetooth_adapter.h" + +@class GNCMBlePeripheral, GNCMBleCentral; + +namespace location { +namespace nearby { +namespace ios { + +/** Concrete BleMedium implementation. */ +class BleMedium : public api::ble_v2::BleMedium { + public: + explicit BleMedium(api::BluetoothAdapter& adapter); + + // api::BleMedium: + bool StartAdvertising(const api::ble_v2::BleAdvertisementData& advertising_data, + api::ble_v2::AdvertiseParameters advertise_set_parameters) override; + bool StopAdvertising() override; + bool StartScanning(const Uuid& service_uuid, api::ble_v2::TxPowerLevel tx_power_level, + api::ble_v2::BleMedium::ScanCallback scan_callback) override; + bool StopScanning() override; + std::unique_ptr StartGattServer( + api::ble_v2::ServerGattConnectionCallback callback) override; + std::unique_ptr ConnectToGattServer( + api::ble_v2::BlePeripheral& peripheral, api::ble_v2::TxPowerLevel tx_power_level, + api::ble_v2::ClientGattConnectionCallback callback) override; + std::unique_ptr OpenServerSocket( + const std::string& service_id) override; + std::unique_ptr Connect(const std::string& service_id, + api::ble_v2::TxPowerLevel tx_power_level, + api::ble_v2::BlePeripheral& peripheral, + CancellationFlag* cancellation_flag) override; + bool IsExtendedAdvertisementsAvailable() override; + + private: + BluetoothAdapter* adapter_; + GNCMBlePeripheral* peripheral_; + GNCMBleCentral* central_; + dispatch_queue_t callback_queue_; +}; + +} // namespace ios +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_IOS_BLE_H_ diff --git a/internal/platform/implementation/ios/ble.mm b/internal/platform/implementation/ios/ble.mm new file mode 100644 index 00000000..1c149138 --- /dev/null +++ b/internal/platform/implementation/ios/ble.mm @@ -0,0 +1,100 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "internal/platform/implementation/ios/ble.h" + +#include +#include + +#include "internal/platform/implementation/ble_v2.h" +#import "internal/platform/implementation/ios/Mediums/Ble/GNCMBleCentral.h" +#import "internal/platform/implementation/ios/Mediums/Ble/GNCMBlePeripheral.h" +#include "internal/platform/implementation/ios/bluetooth_adapter.h" +#include "internal/platform/implementation/ios/utils.h" + +namespace location { +namespace nearby { +namespace ios { + +using ::location::nearby::api::ble_v2::BleAdvertisementData; +using ::location::nearby::api::ble_v2::TxPowerLevel; +using ScanCallback = ::location::nearby::api::ble_v2::BleMedium::ScanCallback; + +BleMedium::BleMedium(::location::nearby::api::BluetoothAdapter& adapter) + : adapter_(static_cast(&adapter)) {} + +bool BleMedium::StartAdvertising( + const BleAdvertisementData& advertising_data, + ::location::nearby::api::ble_v2::AdvertiseParameters advertise_set_parameters) { + if (advertising_data.service_data.empty()) { + return false; + } + const std::string& service_uuid = advertising_data.service_data.begin()->first.Get16BitAsString(); + const ByteArray& service_data_bytes = advertising_data.service_data.begin()->second; + peripheral_ = + [[GNCMBlePeripheral alloc] initWithServiceUUID:ObjCStringFromCppString(service_uuid) + advertisementData:NSDataFromByteArray(service_data_bytes)]; + + return true; +} + +bool BleMedium::StopAdvertising() { + peripheral_ = nil; + return true; +} + +bool BleMedium::StartScanning(const Uuid& service_uuid, TxPowerLevel tx_power_level, + ScanCallback scan_callback) { + central_ = [[GNCMBleCentral alloc] + initWithServiceUUID:ObjCStringFromCppString(service_uuid.Get16BitAsString()) + scanResultHandler:^(NSString* serviceUUID, NSData* serviceData){ + // TODO(b/228751356): Add scan callback implementation. + }]; + + return true; +} + +bool BleMedium::StopScanning() { + central_ = nil; + return true; +} + +std::unique_ptr BleMedium::StartGattServer( + api::ble_v2::ServerGattConnectionCallback callback) { + return nullptr; +} + +std::unique_ptr BleMedium::ConnectToGattServer( + api::ble_v2::BlePeripheral& peripheral, TxPowerLevel tx_power_level, + api::ble_v2::ClientGattConnectionCallback callback) { + return nullptr; +} + +std::unique_ptr BleMedium::OpenServerSocket( + const std::string& service_id) { + return nullptr; +} + +std::unique_ptr BleMedium::Connect(const std::string& service_id, + TxPowerLevel tx_power_level, + api::ble_v2::BlePeripheral& peripheral, + CancellationFlag* cancellation_flag) { + return nullptr; +} + +bool BleMedium::IsExtendedAdvertisementsAvailable() { return false; } + +} // namespace ios +} // namespace nearby +} // namespace location diff --git a/internal/platform/implementation/ios/bluetooth_adapter.h b/internal/platform/implementation/ios/bluetooth_adapter.h new file mode 100644 index 00000000..82a08a83 --- /dev/null +++ b/internal/platform/implementation/ios/bluetooth_adapter.h @@ -0,0 +1,82 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_IOS_BLUETOOTH_ADAPTER_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_IOS_BLUETOOTH_ADAPTER_H_ + +#include + +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/implementation/bluetooth_adapter.h" + +namespace location { +namespace nearby { +namespace ios { + +class BluetoothAdapter; + +// Concrete BlePeripheral implementation. +class BlePeripheral : public api::ble_v2::BlePeripheral { + public: + std::string GetAddress() const override; + + private: + // Only BluetoothAdapter may instantiate BlePeripheral. + friend class BluetoothAdapter; + + explicit BlePeripheral(BluetoothAdapter* adapter) : adapter_(*adapter) {} + + BluetoothAdapter& adapter_; +}; + +// Concrete BluetoothAdapter implementation. +class BluetoothAdapter : public api::BluetoothAdapter { + public: + using Status = api::BluetoothAdapter::Status; + using ScanMode = api::BluetoothAdapter::ScanMode; + + ~BluetoothAdapter() override { SetStatus(Status::kDisabled); } + + bool SetStatus(Status status) override { + enabled_ = status == Status::kEnabled; + return true; + } + bool IsEnabled() const override { return enabled_; } + ScanMode GetScanMode() const override { return mode_; } + bool SetScanMode(ScanMode mode) override { return false; } + std::string GetName() const override { return name_; } + bool SetName(absl::string_view name) override { + name_ = std::string(name); + return true; + } + std::string GetMacAddress() const override { return mac_address_; } + void SetMacAddress(absl::string_view mac_address) { + mac_address_ = std::string(mac_address); + } + + BlePeripheral& GetPeripheral() { return peripheral_; } + + private: + BlePeripheral peripheral_{this}; + ScanMode mode_ = ScanMode::kNone; + std::string name_; + std::string mac_address_; + bool enabled_ = true; +}; + +} // namespace ios +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_IOS_BLUETOOTH_ADAPTER_H_ diff --git a/internal/platform/implementation/ios/bluetooth_adapter.mm b/internal/platform/implementation/ios/bluetooth_adapter.mm new file mode 100644 index 00000000..ee5fe93d --- /dev/null +++ b/internal/platform/implementation/ios/bluetooth_adapter.mm @@ -0,0 +1,30 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "internal/platform/implementation/ios/bluetooth_adapter.h" + +#include + +#include "absl/strings/string_view.h" +#include "internal/platform/implementation/bluetooth_adapter.h" + +namespace location { +namespace nearby { +namespace ios { + +std::string BlePeripheral::GetAddress() const { return adapter_.GetMacAddress(); } + +} // namespace ios +} // namespace nearby +} // namespace location diff --git a/internal/platform/implementation/ios/platform.mm b/internal/platform/implementation/ios/platform.mm index 2cf4007d..f1074c49 100644 --- a/internal/platform/implementation/ios/platform.mm +++ b/internal/platform/implementation/ios/platform.mm @@ -18,6 +18,7 @@ #include "internal/platform/implementation/ios/atomic_boolean.h" #include "internal/platform/implementation/ios/atomic_uint32.h" +#include "internal/platform/implementation/ios/ble.h" #include "internal/platform/implementation/ios/condition_variable.h" #include "internal/platform/implementation/ios/count_down_latch.h" #import "internal/platform/implementation/ios/log_message.h" @@ -118,7 +119,7 @@ std::unique_ptr ImplementationPlatform::CreateScheduledExecut // Mediums std::unique_ptr ImplementationPlatform::CreateBluetoothAdapter() { - return nullptr; + return std::make_unique(); } std::unique_ptr ImplementationPlatform::CreateBluetoothClassicMedium( @@ -132,7 +133,7 @@ std::unique_ptr ImplementationPlatform::CreateBleMedium(api::Bluetoot std::unique_ptr ImplementationPlatform::CreateBleV2Medium(api::BluetoothAdapter& adapter) { - return nullptr; + return std::make_unique(adapter); } std::unique_ptr ImplementationPlatform::CreateServerSyncMedium() {