From 387393f1d6b9e0cdf3387704df70970bf016d5e9 Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Wed, 23 Aug 2023 12:53:12 -0700 Subject: [PATCH] Delete old BLE code for Apple platforms PiperOrigin-RevId: 559515626 --- Package.swift | 8 - internal/platform/implementation/apple/BUILD | 46 +- .../implementation/apple/Mediums/BUILD | 6 - .../apple/Mediums/Ble/GNCMBleCentral.h | 114 ---- .../apple/Mediums/Ble/GNCMBleCentral.m | 510 --------------- .../apple/Mediums/Ble/GNCMBlePeripheral.h | 79 --- .../apple/Mediums/Ble/GNCMBlePeripheral.m | 293 --------- .../platform/implementation/apple/Tests/BUILD | 6 +- .../implementation/apple/Tests/GNCBleTest.mm | 112 ---- .../apple/Tests/GNCBluetoothAdapterTest.mm | 87 --- internal/platform/implementation/apple/ble.h | 219 +------ internal/platform/implementation/apple/ble.mm | 600 ------------------ .../implementation/apple/ble_gatt_server.h | 2 +- .../implementation/apple/bluetooth_adapter.h | 108 ---- .../implementation/apple/bluetooth_adapter.mm | 28 - .../platform/implementation/apple/platform.mm | 2 +- 16 files changed, 20 insertions(+), 2200 deletions(-) delete mode 100644 internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.h delete mode 100644 internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.m delete mode 100644 internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.h delete mode 100644 internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.m delete mode 100644 internal/platform/implementation/apple/Tests/GNCBleTest.mm delete mode 100644 internal/platform/implementation/apple/Tests/GNCBluetoothAdapterTest.mm delete mode 100644 internal/platform/implementation/apple/ble.mm delete mode 100644 internal/platform/implementation/apple/bluetooth_adapter.h delete mode 100644 internal/platform/implementation/apple/bluetooth_adapter.mm diff --git a/Package.swift b/Package.swift index ca4e39e6..e9ceacdc 100644 --- a/Package.swift +++ b/Package.swift @@ -570,14 +570,6 @@ let package = Package( "connections/implementation/mediums/webrtc", // This breaks the build, but seems to work fine without it? "internal/platform/medium_environment.cc", - // Temporarily ignore BLEv2 source files. - // TODO(b/293283024): Stop ignoring these files when BLEv2 migration is complete. - "internal/platform/implementation/apple/ble_gatt_server.mm", - "internal/platform/implementation/apple/ble_gatt_client.mm", - "internal/platform/implementation/apple/ble_medium.mm", - "internal/platform/implementation/apple/ble_peripheral.mm", - "internal/platform/implementation/apple/ble_server_socket.mm", - "internal/platform/implementation/apple/ble_socket.mm", ], sources: [ "compiled_proto", diff --git a/internal/platform/implementation/apple/BUILD b/internal/platform/implementation/apple/BUILD index 252b664b..0e2b044e 100644 --- a/internal/platform/implementation/apple/BUILD +++ b/internal/platform/implementation/apple/BUILD @@ -23,7 +23,6 @@ package(default_visibility = [ objc_library( name = "apple", srcs = [ - "ble_utils.mm", "crypto.mm", "device_info.mm", "log_message.mm", @@ -35,7 +34,6 @@ objc_library( "wifi_lan.mm", ], hdrs = [ - "ble_utils.h", "device_info.h", "log_message.h", "multi_thread_executor.h", @@ -51,7 +49,7 @@ objc_library( deps = [ ":Platform_cc", ":Shared", - ":ble", + ":ble_v2", "//internal/platform:base", "//internal/platform/implementation:comm", "//internal/platform/implementation:platform", @@ -77,55 +75,29 @@ objc_library( }), ) -objc_library( - name = "ble", - srcs = [ - "ble.mm", - "bluetooth_adapter.mm", - "utils.mm", - ], - hdrs = [ - "ble.h", - "bluetooth_adapter.h", - "utils.h", - ], - # Prevent Objective-C++ headers from being pulled into swift. - aspect_hints = ["//tools/build_defs/swift:no_module"], - deps = [ - "//internal/platform:base", - "//internal/platform:cancellation_flag", - "//internal/platform/implementation:comm", - "//internal/platform/implementation/apple/Mediums", - "//third_party/apple_frameworks:CoreBluetooth", - "//third_party/apple_frameworks:Foundation", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/types:optional", - ], -) - objc_library( name = "ble_v2", srcs = [ + "ble_gatt_client.h", "ble_gatt_client.mm", + "ble_gatt_server.h", "ble_gatt_server.mm", + "ble_medium.h", "ble_medium.mm", + "ble_peripheral.h", "ble_peripheral.mm", + "ble_server_socket.h", "ble_server_socket.mm", + "ble_socket.h", "ble_socket.mm", "ble_utils.mm", + "bluetooth_adapter_v2.h", "bluetooth_adapter_v2.mm", "utils.mm", ], hdrs = [ - "ble_gatt_client.h", - "ble_gatt_server.h", - "ble_medium.h", - "ble_peripheral.h", - "ble_server_socket.h", - "ble_socket.h", + "ble.h", "ble_utils.h", - "bluetooth_adapter_v2.h", "utils.h", ], # Prevent Objective-C++ headers from being pulled into swift. diff --git a/internal/platform/implementation/apple/Mediums/BUILD b/internal/platform/implementation/apple/Mediums/BUILD index d9f5917d..21f1c10d 100644 --- a/internal/platform/implementation/apple/Mediums/BUILD +++ b/internal/platform/implementation/apple/Mediums/BUILD @@ -27,9 +27,7 @@ objc_library( "BLEv2/GNCPeripheral.m", "BLEv2/GNCPeripheralManager.m", "BLEv2/NSData+GNCWebSafeBase64.m", - "Ble/GNCMBleCentral.m", "Ble/GNCMBleConnection.m", - "Ble/GNCMBlePeripheral.m", "Ble/GNCMBleUtils.mm", "GNCLeaks.h", "GNCLeaks.m", @@ -51,9 +49,7 @@ objc_library( "BLEv2/GNCPeripheral.h", "BLEv2/GNCPeripheralManager.h", "BLEv2/NSData+GNCWebSafeBase64.h", - "Ble/GNCMBleCentral.h", "Ble/GNCMBleConnection.h", - "Ble/GNCMBlePeripheral.h", "Ble/GNCMBleUtils.h", "GNCMConnection.h", "WiFiLAN/GNCIPv4Address.h", @@ -64,8 +60,6 @@ objc_library( ], deps = [ "//internal/platform/implementation/apple:Shared", - "//internal/platform/implementation/apple/Mediums/Ble/Sockets:Central", - "//internal/platform/implementation/apple/Mediums/Ble/Sockets:Peripheral", "//internal/platform/implementation/apple/Mediums/Ble/Sockets:Shared", "//proto/mediums:ble_frames_cc_proto", "//third_party/apple_frameworks:CoreBluetooth", diff --git a/internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.h b/internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.h deleted file mode 100644 index d617113f..00000000 --- a/internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.h +++ /dev/null @@ -1,114 +0,0 @@ -// 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 - -#import "internal/platform/implementation/apple/Mediums/GNCMConnection.h" - -@class CBUUID; - -NS_ASSUME_NONNULL_BEGIN - -/** - * This handler is called on a discover when a nearby advertising endpoint is discovered. - */ -typedef void (^GNCMScanResultHandler)(NSString *peripheralID, NSData *serviceData); - -/** - * This handler is called on a discovery when a nearby advertising endpoint is connected. - */ -typedef void (^GNCMGATTConnectionResultHandler)(NSError *_Nullable error); - -/** - * This handler is called on a discovery when a nearby advertising endpoint is connected. The input - * is a discovered set of characteristics. - */ -typedef void (^GNCMGATTDiscoverResultHandler)( - NSOrderedSet *_Nullable characteristicValues); - -/** - * This handler is called by a discovering endpoint to request a connection with an an advertising - * endpoint. - */ -typedef void (^GNCMBleConnectionRequester)(NSString *serviceID, - GNCMConnectionHandler connectionHandler); - -/** - * This handler is called on a discoverer when a nearby advertising endpoint is discovered. - * Call |requestConnection| to request a connection with the advertiser. - */ -typedef void (^GNCMBleRequestConnectionHandler)(GNCMBleConnectionRequester requestConnection); - -/** - * 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_DESIGNATED_INITIALIZER; - -/** - * Starts scanning with service UUID. - * - * @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. - * @param requestConnectionHandler The handler that is called when an endpoint is discovered. - * @param callbackQueue The queue on which all callbacks are made. - */ -- (BOOL)startScanningWithServiceUUID:(NSString *)serviceUUID - scanResultHandler:(GNCMScanResultHandler)scanResultHandler - requestConnectionHandler:(GNCMBleRequestConnectionHandler)requestConnectionHandler - callbackQueue:(nullable dispatch_queue_t)callbackQueue; - -/** - * Sets up a GATT connection. - * - * @param peripheralID A string that uniquely identifies the peripheral. - * @param gattConnectionResultHandler The handler that is called when an endpoint is - * connected. - */ -- (void)connectGattServerWithPeripheralID:(NSString *)peripheralID - gattConnectionResultHandler: - (GNCMGATTConnectionResultHandler)gattConnectionResultHandler; - -/** - * Discovers GATT service and its associated characteristics with values. - * - * @param serviceUUID A CBUUID for service to discover. - * @param gattCharacteristics Array of CBUUID for characteristic to discover. - * @param peripheralID A string that uniquely identifies the peripheral. - * @param gattDiscoverResultHandler This handler is called on a discovery for a discovered map of - * characteristic values when a nearby advertising endpoint is - * connected. - */ -- (void)discoverGattService:(CBUUID *)serviceUUID - gattCharacteristics:(NSArray *)characteristicUUIDs - peripheralID:(NSString *)peripheralID - gattDiscoverResultHandler:(GNCMGATTDiscoverResultHandler)gattDiscoverResultHandler; - -/** - * Disconnects GATT connection. - * - * @param peripheralID A string that uniquely identifies the peripheral. - */ -- (void)disconnectGattServiceWithPeripheralID:(NSString *)peripheralID; - -@end - -NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.m b/internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.m deleted file mode 100644 index bc5ea3aa..00000000 --- a/internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.m +++ /dev/null @@ -1,510 +0,0 @@ -// 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/apple/Mediums/Ble/GNCMBleCentral.h" - -#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleConnection.h" -#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleUtils.h" -#import "internal/platform/implementation/apple/Mediums/Ble/Sockets/Source/Central/GNSCentralManager.h" -#import "internal/platform/implementation/apple/Mediums/Ble/Sockets/Source/Central/GNSCentralPeerManager.h" -#import "internal/platform/implementation/apple/Mediums/GNCMConnection.h" - -NS_ASSUME_NONNULL_BEGIN - -typedef NS_ENUM(NSUInteger, GNCMCentralState) { - GNCMCentralStateStopped, - GNCMCentralStateScanning, -}; - -typedef void (^GNCMBleCharacteristicsHandler)(NSArray *characteristics, - NSError *error); -typedef void (^GNCMBleCharacteristicValueHandler)(CBCharacteristic *characteristic, NSError *error); -typedef void (^GNCIntHandler)(int); - -/** This lets a GNCIntHandler call itself. */ -GNCIntHandler GNCRecursiveIntHandler(void (^block)(GNCIntHandler blockSelf, int i)) { - return ^(int i) { - return block(GNCRecursiveIntHandler(block), i); - }; -} - -/** This represents a discovered peripheral. */ -@interface GNCMPeripheralInfo : NSObject -@property(nonatomic) CBPeripheral *peripheral; -@property(nonatomic, copy) NSDictionary *advertisementData; - -/** Called when characteristics are discovered. */ -@property(nonatomic, nullable) GNCMBleCharacteristicsHandler charsHandler; - -/** Called when a characteristic value is read. */ -@property(nonatomic, nullable) GNCMBleCharacteristicValueHandler charValueHandler; - -@end - -@implementation GNCMPeripheralInfo - -- (instancetype)initWithPeripheral:(CBPeripheral *)peripheral - advertisementData:(NSDictionary *)advertisementData { - self = [super init]; - if (self) { - _peripheral = peripheral; - _advertisementData = [advertisementData copy]; - } - return self; -} - -- (BOOL)isEqual:(id)object { - // There is always exactly one info object per peripheral, so compare by identity. This is needed - // for maintenance of the peripherals stored in multiple maps. - return self == object; -} - -- (NSUInteger)hash { - return (NSUInteger)self; -} - -@end - -@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; - /** Central state for stop or scanning. */ - GNCMCentralState _centralState; - /** The dictionary keyed by CBPeripheral identifier to value GNCMPeripheralInfo. */ - NSMutableDictionary *_nearbyPeripheralsByID; - /** Array of characteristic UUID used for discovering. */ - NSArray *_characteristicUUIDs; - /** GATT connection result handler. */ - GNCMGATTConnectionResultHandler _gattConnectionResultHandler; - /** GATT service and characteristic discovery result hanadler. */ - GNCMGATTDiscoverResultHandler _gattDiscoverResultHandler; - /** The discovered characteristic values map used to callback for `_gattDiscoverResultHandler`. */ - NSMutableOrderedSet *_gattCharacteristicValues; - /** Central manager used for socket connection based on weave protocol. */ - GNSCentralManager *_socketCentralManager; - /** A callback handler to reuqest connection on the discovered advertiser. */ - GNCMBleRequestConnectionHandler _requestConnectionHandler; - /** Client callback queue. If client doesn't assign it, then use main queue. */ - dispatch_queue_t _clientCallbackQueue; - /** Internal async priority queue. */ - dispatch_queue_t _internalCallbackQueue; - /** Flag to disable callback for dealloc. */ - BOOL _callbacksEnabled; -} - -- (instancetype)init { - if (self = [super init]) { - // 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); - - _nearbyPeripheralsByID = [NSMutableDictionary dictionary]; - _gattCharacteristicValues = [[NSMutableOrderedSet alloc] init]; - - _centralState = GNCMCentralStateStopped; - } - 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, ^{ - [self stopScanningInternal]; - [_socketCentralManager stopNoScanMode]; - - _callbacksEnabled = NO; - }); -} - -- (BOOL)startScanningWithServiceUUID:(NSString *)serviceUUID - scanResultHandler:(GNCMScanResultHandler)scanResultHandler - requestConnectionHandler:(GNCMBleRequestConnectionHandler)requestConnectionHandler - callbackQueue:(nullable dispatch_queue_t)callbackQueue { - NSLog(@"[NEARBY] Client rquests startScanning"); - _serviceUUID = [CBUUID UUIDWithString:serviceUUID]; - _scanResultHandler = scanResultHandler; - _requestConnectionHandler = requestConnectionHandler; - - // The client may be using the callback queue for other purposes, so wrap it with a private - // queue to know with certainty when all callbacks are done. - _clientCallbackQueue = callbackQueue ?: dispatch_get_main_queue(); - _internalCallbackQueue = - dispatch_queue_create("GNCMBleCentralCallbackQueue", DISPATCH_QUEUE_PRIORITY_DEFAULT); - _callbacksEnabled = YES; - - // Set up the central manager for scanning. - _centralManager = - [[CBCentralManager alloc] initWithDelegate:self - queue:_selfQueue - options:@{CBCentralManagerOptionShowPowerAlertKey : @NO}]; - - // Set up the central manager for the socket. - _socketCentralManager = [[GNSCentralManager alloc] initWithSocketServiceUUID:_serviceUUID - queue:_selfQueue]; - _socketCentralManager.delegate = self; - [_socketCentralManager startNoScanModeWithAdvertisedServiceUUIDs:@[ _serviceUUID ]]; - - _centralState = GNCMCentralStateScanning; - return YES; -} - -- (void)connectGattServerWithPeripheralID:(NSString *)peripheralID - gattConnectionResultHandler: - (GNCMGATTConnectionResultHandler)gattConnectionResultHandler { - _gattConnectionResultHandler = gattConnectionResultHandler; - dispatch_sync(_selfQueue, ^{ - GNCMPeripheralInfo *peripheralInfo = - _nearbyPeripheralsByID[[[NSUUID alloc] initWithUUIDString:peripheralID]]; - if (!peripheralInfo) return; - [_centralManager connectPeripheral:peripheralInfo.peripheral options:nil]; - }); -} - -- (void)discoverGattService:(CBUUID *)serviceUUID - gattCharacteristics:(NSArray *)characteristicUUIDs - peripheralID:(NSString *)peripheralID - gattDiscoverResultHandler:(GNCMGATTDiscoverResultHandler)gattDiscoverResultHandler { - _gattDiscoverResultHandler = gattDiscoverResultHandler; - [_gattCharacteristicValues removeAllObjects]; - dispatch_sync(_selfQueue, ^{ - GNCMPeripheralInfo *peripheralInfo = - _nearbyPeripheralsByID[[[NSUUID alloc] initWithUUIDString:peripheralID]]; - if (!peripheralInfo) return; - _characteristicUUIDs = [characteristicUUIDs copy]; - - // Start to discover service and the delegate will get its characteristics and read their values - // recursively - [peripheralInfo.peripheral discoverServices:@[ serviceUUID ]]; - }); -} - -- (void)disconnectGattServiceWithPeripheralID:(NSString *)peripheralID { - dispatch_sync(_selfQueue, ^{ - GNCMPeripheralInfo *peripheralInfo = - _nearbyPeripheralsByID[[[NSUUID alloc] initWithUUIDString:peripheralID]]; - if (!peripheralInfo) return; - _gattConnectionResultHandler = nil; - _gattDiscoverResultHandler = nil; - [_centralManager cancelPeripheralConnection:peripheralInfo.peripheral]; - }); -} - -#pragma mark CBCentralManagerDelegate - -- (void)centralManagerDidUpdateState:(CBCentralManager *)central { - if (central.state == CBManagerStatePoweredOn && !central.isScanning && - _centralState == GNCMCentralStateScanning) { - NSLog(@"[NEARBY] CBCentralManager powered on; starting scan"); - [self startScanningInternal]; - } else { - NSLog(@"[NEARBY] CBCentralManager not powered on; stopping scan"); - [self stopScanningInternal]; - } -} - -- (void)centralManager:(CBCentralManager *)central - didDiscoverPeripheral:(CBPeripheral *)peripheral - advertisementData:(NSDictionary *)advertisementData - RSSI:(NSNumber *)RSSI { - NSNumber *connectable = advertisementData[CBAdvertisementDataIsConnectable]; - if (![connectable boolValue]) return; - - // Look for the NC advertisement header in either the service data (from non-iOS) or the - // advertised name (from iOS). - NSData *serviceData = advertisementData[CBAdvertisementDataServiceDataKey][_serviceUUID] - ?: advertisementData[CBAdvertisementDataLocalNameKey]; - - // Try to look up the peripheral by ID. - GNCMPeripheralInfo *info = _nearbyPeripheralsByID[peripheral.identifier]; - if (!info) { - NSLog(@"[NEARBY] New peripheral: %@", peripheral); - // This is a new peripheral, so create a new peripheral info object. - info = [[GNCMPeripheralInfo alloc] initWithPeripheral:peripheral - advertisementData:advertisementData]; - } else { - info.peripheral = peripheral; - } - - _nearbyPeripheralsByID[peripheral.identifier] = info; - _scanResultHandler(peripheral.identifier.UUIDString, serviceData); -} - -- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { - NSLog(@"[NEARBY] Connected to peripheral: %@", peripheral); - peripheral.delegate = self; - // Tell the caller the connection is done. - _gattConnectionResultHandler(nil); -} - -- (void)centralManager:(CBCentralManager *)central - didFailToConnectPeripheral:(CBPeripheral *)peripheral - error:(nullable NSError *)error { - NSLog(@"[NEARBY] Failed to connect to peripheral: %@, error: %@", peripheral, error); - // Tell the caller the connection failed. - _gattConnectionResultHandler(error); -} - -- (void)centralManager:(CBCentralManager *)central - didDisconnectPeripheral:(CBPeripheral *)peripheral - error:(nullable NSError *)error { - NSLog(@"[NEARBY] Disconnected to peripheral: %@, error: %@", peripheral, error); -} - -#pragma mark CBPeripheralDelegate - -- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error { - GNCMPeripheralInfo *peripheralInfo = _nearbyPeripheralsByID[peripheral.identifier]; - if (!peripheralInfo) return; - if (error || (peripheral.services.count == 0)) { - NSLog(@"[NEARBY] Error reading advertisement: unable to discover services."); - _gattDiscoverResultHandler(nil); - } else { - NSLog(@"[NEARBY] Discovered services for %@: %@", peripheral.name, peripheral.services); - - // Helper functions for discovering characteristics and reading their values. - void (^discoverChars)(CBService *, GNCMBleCharacteristicsHandler) = - ^(CBService *service, GNCMBleCharacteristicsHandler handler) { - NSAssert(!peripheralInfo.charsHandler, @"Unexpected characteristic handler"); - peripheralInfo.charsHandler = handler; - - // Discover all characteristics that may contain the advertisement. - [peripheral discoverCharacteristics:_characteristicUUIDs forService:service]; - }; - void (^readCharValue)(CBCharacteristic *, GNCMBleCharacteristicValueHandler) = - ^(CBCharacteristic *characteristic, GNCMBleCharacteristicValueHandler handler) { - NSAssert(!peripheralInfo.charValueHandler, @"Unexpected characteristic value handler"); - peripheralInfo.charValueHandler = handler; - [peripheral readValueForCharacteristic:characteristic]; - }; - - // Multiple services may have the same UUID, so find the right service by searching for the - // characteristic containing the advertisement with a matching service ID hash. - __weak __typeof__(self) weakSelf = self; - void (^tryService)(int) = GNCRecursiveIntHandler(^(GNCIntHandler tryService, int serviceIndex) { - __strong __typeof__(self) strongSelf = weakSelf; - if (!strongSelf) return; - - // We've tried all the services, report the discovered characteristics and their values. - if (serviceIndex == peripheral.services.count) { - NSLog(@"[NEARBY] Done traversing all services or no services to traverse."); - _gattDiscoverResultHandler(_gattCharacteristicValues); - if (_gattCharacteristicValues.count > 0) { - CBCharacteristic *characteristic = [_gattCharacteristicValues objectAtIndex:0]; - [strongSelf completeGATTReadWithData:characteristic.value - forPeripheralInfo:peripheralInfo]; - } - return; - } - - NSLog(@"[NEARBY] Trying service: %@", peripheral.services[serviceIndex]); - discoverChars( - peripheral.services[serviceIndex], ^(NSArray *chars, NSError *error) { - void (^tryNextService)() = ^{ - tryService(serviceIndex + 1); - }; - - // If there was an error or there are no characteristics on this service, try next one. - if (error || (chars.count == 0)) { - tryNextService(); - return; - } - - // Read each characteristic. - void (^tryChar)(int) = GNCRecursiveIntHandler(^(GNCIntHandler tryChar, int charIndex) { - // We've tried all characteristics on this service without error, try next service. - if (charIndex == chars.count) { - NSLog(@"[NEARBY] No matching advertisement found"); - tryNextService(); - return; - } - - NSLog(@"[NEARBY] Trying characteristic: %@", chars[charIndex]); - readCharValue(chars[charIndex], ^(CBCharacteristic *characteristic, NSError *error) { - if (error) { - tryNextService(); - } else { - // We've found the characteristic and its non-nil value. Store it. - if (characteristic.value.length != 0) { - [_gattCharacteristicValues addObject:characteristic]; - } - tryChar(charIndex + 1); - } - }); - }); - - // Start searching the characteristics for the current service. - tryChar(0); - }); - }); - - // Start searching the services. - tryService(0); - } -} - -- (void)peripheral:(CBPeripheral *)peripheral - didDiscoverCharacteristicsForService:(CBService *)service - error:(nullable NSError *)error { - NSLog(@"[NEARBY] Discovered characteristics: %@ error: %@", service.characteristics, error); - GNCMPeripheralInfo *peripheralInfo = _nearbyPeripheralsByID[peripheral.identifier]; - if (!peripheralInfo) return; - GNCMBleCharacteristicsHandler charsHandler = peripheralInfo.charsHandler; - peripheralInfo.charsHandler = nil; - charsHandler(service.characteristics, error); -} - -- (void)peripheral:(CBPeripheral *)peripheral - didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic - error:(nullable NSError *)error { - NSLog(@"[NEARBY] Read characteristic value: %@ error: %@", characteristic, error); - GNCMPeripheralInfo *peripheralInfo = _nearbyPeripheralsByID[peripheral.identifier]; - if (!peripheralInfo) return; - GNCMBleCharacteristicValueHandler valueHandler = peripheralInfo.charValueHandler; - peripheralInfo.charValueHandler = nil; - valueHandler(characteristic, error); -} - -#pragma mark GNSCentralManagerDelegate - -- (void)centralManager:(GNSCentralManager *)centralManager - didDiscoverPeer:(GNSCentralPeerManager *)centralPeerManager - advertisementData:(nullable NSDictionary *)advertisementData { - if (!_callbacksEnabled) return; - - // Retrieve the cached peripheral info. - NSUUID *peerId = centralPeerManager.identifier; - GNCMPeripheralInfo *peripheralInfo = _nearbyPeripheralsByID[peerId]; - if (!peripheralInfo) return; - - __weak __typeof__(self) weakSelf = self; - [self callbackAsync:^{ - _requestConnectionHandler(^(NSString *serviceID, GNCMConnectionHandler connectionHandler) { - __strong __typeof__(self) strongSelf = weakSelf; - if (!strongSelf) return; - - void (^callConnectionHandler)(GNSSocket *__nullable) = ^(GNSSocket *__nullable socket) { - __strong __typeof__(self) strongSelf = weakSelf; - if (!strongSelf->_callbacksEnabled) return; - [strongSelf callbackAsync:^{ - if (!socket) { - NSLog(@"[NEARBY] Central failed to create BLE socket"); - connectionHandler(nil); - } else { - GNCMBleConnection *connection = - [GNCMBleConnection connectionWithSocket:socket - serviceID:serviceID - expectedIntroPacket:NO - callbackQueue:strongSelf->_clientCallbackQueue]; - connection.connectionHandlers = connectionHandler(connection); - } - }]; - }; - - dispatch_async(strongSelf->_selfQueue, ^{ - // A connection is being requested, so establish a BLE socket. Make sure to use the most - // up-to-date GNSCentralPeerManager in case the MAC address rotated. The cached - // peripheral info should be the single source of truth for which MAC address to use. - GNSCentralPeerManager *updatedCentralPeerManager = - [centralManager retrieveCentralPeerWithIdentifier:peripheralInfo.peripheral.identifier]; - if (!updatedCentralPeerManager) { - callConnectionHandler(nil); - } - - // Make a socket connection. - [updatedCentralPeerManager - socketWithPairingCharacteristic:NO - completion:^(GNSSocket *socket, NSError *error) { - __strong __typeof__(self) strongSelf = weakSelf; - if (!strongSelf) return; - dispatch_async(strongSelf->_selfQueue, ^{ - if (!error) { - // Call the connection handler when the socket has - // connected or fails to connect. - GNCMWaitForConnection(socket, ^(BOOL didConnect) { - callConnectionHandler(didConnect ? socket : nil); - }); - } else { - callConnectionHandler(nil); - } - }); - }]; - }); - }); - }]; -} - -- (void)centralManagerDidUpdateBleState:(GNSCentralManager *)centralManager { - // No op. -} - -#pragma mark Private - -/** This method assumes it's being called on selfQueue. */ -- (void)completeGATTReadWithData:(NSData *)advertisement - forPeripheralInfo:(GNCMPeripheralInfo *)peripheralInfo { - NSAssert(peripheralInfo, @"Nil peripheralInfo"); - if (peripheralInfo) { - CBPeripheral *peripheral = peripheralInfo.peripheral; - NSUUID *peripheralID = peripheral.identifier; - // Store the data for the socket callback, and report the peripheral to the socket library. - [_socketCentralManager retrievePeripheralWithIdentifier:peripheralID - advertisementData:peripheralInfo.advertisementData]; - } -} - -/** Signals the central manager to start scanning. Must be called on _selfQueue. */ -- (void)startScanningInternal { - if (![_centralManager isScanning]) { - NSLog(@"[NEARBY] startScanningInternal"); - - [_centralManager - scanForPeripheralsWithServices:@[ _serviceUUID ] - options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES}]; - } -} - -/** Signals the central manager to stop scanning. Must be called on _selfQueue */ -- (void)stopScanningInternal { - if ([_centralManager isScanning]) { - NSLog(@"[NEARBY] stopScanningInternal"); - _centralState = GNCMCentralStateStopped; - - [_centralManager stopScan]; - } -} - -/** Calls the specified block on the callback queue. */ -- (void)callbackAsync:(dispatch_block_t)block { - dispatch_queue_t clientCallbackQueue = _clientCallbackQueue; // don't capture |self| - dispatch_async(_internalCallbackQueue, ^{ - dispatch_sync(clientCallbackQueue, block); - }); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.h b/internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.h deleted file mode 100644 index 8511da36..00000000 --- a/internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.h +++ /dev/null @@ -1,79 +0,0 @@ -// 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 - -#import "internal/platform/implementation/apple/Mediums/GNCMConnection.h" - -@class CBCharacteristic; -@class CBUUID; - -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_DESIGNATED_INITIALIZER; - -/** - * Adds GATT CBService. - * - * @param serviceUUID A GATT service ID to advertise for. - */ -- (void)addCBServiceWithUUID:(CBUUID *)serviceUUID; - -/** - * Adds GATT CBCharacteristic. - * - * @param characteristic A characteristic CBUUID. - */ -- (void)addCharacteristic:(CBCharacteristic *)characteristic; - -/** - * Updates GATT CBCharacteristic with value. - * - * @param value The NSData to advertise. - * @param characteristicUUID A characteristic CBUUID. - */ -- (void)updateValue:(NSData *)value forCharacteristic:(CBUUID *)characteristicUUID; - -/** - * Stops GATT server service. - */ -- (void)stopGATTService; - -/** - * Starts advertising with service UUID and advertisement data. - * - * @param serviceUUID A string that uniquely identifies the advertised service to search for. - * @param advertisementData The data to advertise. - * @param endpointconnectedHandler The handler that is called when a discoverer connects. - * @param callbackQueue The queue on which all callbacks are made. If |callbackQueue| is not - * provided, then the main queue is used in the function. - */ -- (BOOL)startAdvertisingWithServiceUUID:(NSString *)serviceUUID - advertisementData:(NSData *)advertisementData - endpointConnectedHandler:(GNCMConnectionHandler)endpointConnectedHandler - callbackQueue:(nullable dispatch_queue_t)callbackQueue; - -@end - -NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.m b/internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.m deleted file mode 100644 index d915a3c3..00000000 --- a/internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.m +++ /dev/null @@ -1,293 +0,0 @@ -// 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/apple/Mediums/Ble/GNCMBlePeripheral.h" - -#import - -#import "internal/platform/implementation/apple/GNCUtils.h" -#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleConnection.h" -#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleUtils.h" -#import "internal/platform/implementation/apple/Mediums/Ble/Sockets/Source/Peripheral/GNSPeripheralManager.h" -#import "internal/platform/implementation/apple/Mediums/Ble/Sockets/Source/Peripheral/GNSPeripheralServiceManager.h" -#import "internal/platform/implementation/apple/Mediums/GNCMConnection.h" - -NS_ASSUME_NONNULL_BEGIN - -typedef NS_ENUM(NSUInteger, GNCMPeripheralState) { - GNCMPeripheralStateStopped, - GNCMPeripheralStateAdvertising, -}; - -@interface GNCMBlePeripheral () -@end - -@implementation GNCMBlePeripheral { - /** Service UUID for advertisement. */ - CBMutableService *_advertisementService; - /** GATT service for GATT connection. */ - CBMutableService *_GATTService; - /** GATT characteristics for GATT connection. */ - NSMutableArray *_gattCharacteristics; - /** CBUUID characteristic to NSData value dictionary for GATT connection. */ - NSMutableDictionary *_gattCharacteristicValues; - /** 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; - /** Peripheral state for stop or advertising. */ - GNCMPeripheralState _state; - /** Peripheral manager used for socket connection based on weave protocol. */ - GNSPeripheralManager *_socketPeripheralManager; - /** Peripheral service manager used to manage one BLE service. */ - GNSPeripheralServiceManager *_socketPeripheralServiceManager; - /** Client callback queue. If client doesn't assign it, then use main queue. */ - dispatch_queue_t _clientCallbackQueue; - /** Internal async priority queue. */ - dispatch_queue_t _internalCallbackQueue; - /** Flag to disable callback for dealloc. */ - BOOL _callbacksEnabled; -} - -- (instancetype)init { - if (self = [super init]) { - // 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); - - _state = GNCMPeripheralStateStopped; - } - 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 stopAdvertisingInternal]; - - _callbacksEnabled = NO; - }); -} - -- (void)addCBServiceWithUUID:(CBUUID *)serviceUUID { - if (!_GATTService) { - // If it has been called, then don't do it again. Initialize one time. - _GATTService = [[CBMutableService alloc] initWithType:serviceUUID primary:YES]; - _gattCharacteristics = [[NSMutableArray alloc] init]; - _gattCharacteristicValues = [[NSMutableDictionary alloc] init]; - } -} - -- (void)addCharacteristic:(CBCharacteristic *)characteristic { - if (_gattCharacteristics) { - [_gattCharacteristics addObject:characteristic]; - } - if (_gattCharacteristicValues) { - [_gattCharacteristicValues setObject:[[NSData alloc] init] forKey:characteristic.UUID]; - } -} - -- (void)updateValue:(NSData *)value forCharacteristic:(CBUUID *)characteristicUUID { - if ([_gattCharacteristicValues objectForKey:characteristicUUID]) { - [_gattCharacteristicValues setObject:value forKey:characteristicUUID]; - } -} - -- (void)stopGATTService { - if (!_GATTService) return; - dispatch_sync(_selfQueue, ^{ - [_peripheralManager removeService:_GATTService]; - }); -} - -- (BOOL)startAdvertisingWithServiceUUID:(NSString *)serviceUUID - advertisementData:(NSData *)advertisementData - endpointConnectedHandler:(GNCMConnectionHandler)endpointConnectedHandler - callbackQueue:(nullable dispatch_queue_t)callbackQueue { - NSLog(@"[NEARBY] Client rquests startAdvertising"); - // The client may be using the callback queue for other purposes, so wrap it with a private - // queue to know with certainty when all callbacks are done. - _clientCallbackQueue = callbackQueue ?: dispatch_get_main_queue(); - _internalCallbackQueue = - dispatch_queue_create("GNCMBlePeripheralCallbackQueue", DISPATCH_QUEUE_PRIORITY_DEFAULT); - _callbacksEnabled = YES; - - _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); - __weak __typeof__(self) weakSelf = self; - // Set up the peripheral manager for the socket. This must be done before creating the - // peripheral manager for the advertisement data because it's started/stopped in the - // -peripheralManagerDidUpdateState: callback. - _socketPeripheralServiceManager = [[GNSPeripheralServiceManager alloc] - initWithBleServiceUUID:_advertisementService.UUID - addPairingCharacteristic:NO - shouldAcceptSocketHandler:^BOOL(GNSSocket *socket) { - // Call the connection handler when the socket has connected or fails to connect. - GNCMWaitForConnection(socket, ^(BOOL didConnect) { - [weakSelf establishConnectionWithSocket:socket - didConnect:didConnect - endpointConnectedHandler:endpointConnectedHandler]; - }); - return YES; - } - queue:_selfQueue]; - _socketPeripheralManager = [[GNSPeripheralManager alloc] initWithAdvertisedName:nil - restoreIdentifier:nil - queue:_selfQueue]; - [_socketPeripheralManager addPeripheralServiceManager:_socketPeripheralServiceManager - bleServiceAddedCompletion:^(NSError *error) { - NSLog(@"Failed to add service"); - }]; - - // Set up the peripheral manager for the advertisement data. - _peripheralManager = [[CBPeripheralManager alloc] - initWithDelegate:self - queue:_selfQueue - options:@{CBPeripheralManagerOptionShowPowerAlertKey : @NO}]; - - if (_GATTService) { - if (_gattCharacteristics && _gattCharacteristics.count > 0) { - _GATTService.characteristics = _gattCharacteristics; - } - } - _state = GNCMPeripheralStateAdvertising; - return YES; -} - -#pragma mark CBPeripheralManagerDelegate - -- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral { - NSLog(@"[NEARBY] peripheralManagerDidUpdateState %li", (long)peripheral.state); - if (peripheral.state == CBManagerStatePoweredOn && !peripheral.isAdvertising && - _state == GNCMPeripheralStateAdvertising) { - NSLog(@"[NEARBY] CBPeripheralManager powered on; starting advertising"); - [_socketPeripheralManager start]; - [self startAdvertisingInternal]; - } else { - NSLog(@"[NEARBY] CBPeripheralManager not powered on; stopping advertising"); - [self stopAdvertisingInternal]; - } -} - -- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral - error:(nullable NSError *)error { - if (error) { - NSLog(@"[NEARBY] Error starting advertising: %@,", [error localizedDescription]); - return; - } - if (_peripheralManager.state != CBManagerStatePoweredOn) { - NSLog(@"[NEARBY] Error starting advertising: peripheral manager not on!"); - return; - } - - NSLog(@"[NEARBY] Peripheral manager started advertising"); -} - -- (void)peripheralManager:(CBPeripheralManager *)peripheral - didReceiveReadRequest:(CBATTRequest *)request { - NSLog(@"[NEARBY] peripheralManager:didReceiveReadRequest"); - // This is called when a central asks to read a characteristic's value. - CBATTError error = CBATTErrorAttributeNotFound; - NSData *value = _gattCharacteristicValues[request.characteristic.UUID]; - if (value != nil && value.length > 0) { - if (request.offset > value.length) { - error = CBATTErrorInvalidOffset; - } else { - // Reply with the advertisement data. - NSRange rangeFromOffset = NSMakeRange(request.offset, value.length - request.offset); - request.value = [value subdataWithRange:rangeFromOffset]; - error = CBATTErrorSuccess; - } - } - [_peripheralManager respondToRequest:request withResult:error]; -} - -#pragma mark Private - -/** Signals the peripheral manager to start advertising. Must be called on _selfQueue */ -- (void)startAdvertisingInternal { - if (![_peripheralManager isAdvertising]) { - NSLog(@"[NEARBY] startAdvertisingInternal"); - - if (_GATTService) { - [_peripheralManager addService:_GATTService]; - } - [_peripheralManager startAdvertising:@{ - CBAdvertisementDataServiceUUIDsKey : @[ _advertisementService.UUID ], - CBAdvertisementDataLocalNameKey : _advertisementData - }]; - } -} - -/** Signals the peripheral manager to stop advertising. Must be called on _selfQueue */ -- (void)stopAdvertisingInternal { - if ([_peripheralManager isAdvertising]) { - NSLog(@"[NEARBY] stopAdvertisingInternal"); - _state = GNCMPeripheralStateStopped; - - if (_GATTService) { - [_peripheralManager removeService:_GATTService]; - } - [_peripheralManager stopAdvertising]; - } -} - -/** - * Connects with socket and callback the |GNCMBleConnection| is established or nil if it is not. - */ -- (void)establishConnectionWithSocket:(GNSSocket *)socket - didConnect:(BOOL)didConnect - endpointConnectedHandler:(GNCMConnectionHandler)endpointConnectedHandler { - if (!_callbacksEnabled) { - return; - } - - [self callbackAsync:^{ - if (!didConnect) { - NSLog(@"[NEARBY] Peripheral failed to create BLE socket"); - endpointConnectedHandler(nil); - } else { - GNCMBleConnection *connection = [GNCMBleConnection connectionWithSocket:socket - serviceID:nil - expectedIntroPacket:YES - callbackQueue:_clientCallbackQueue]; - connection.connectionHandlers = endpointConnectedHandler(connection); - } - }]; -} - -/** - * Calls the specified block on the callback queue, preventing it from being dispatched to the - * client callback queue when callbacks are disabled. And without capturing |self|, since - * callbacks are disabled in dealloc. - */ -- (void)callbackAsync:(dispatch_block_t)block { - dispatch_queue_t clientCallbackQueue = _clientCallbackQueue; // don't capture |self| - dispatch_async(_internalCallbackQueue, ^{ - dispatch_sync(clientCallbackQueue, block); - }); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Tests/BUILD b/internal/platform/implementation/apple/Tests/BUILD index 9cc10150..d10e8cc3 100644 --- a/internal/platform/implementation/apple/Tests/BUILD +++ b/internal/platform/implementation/apple/Tests/BUILD @@ -31,8 +31,6 @@ objc_library( "GNCBLEMedium+Testing.h", "GNCBLEMediumTest.m", "GNCBLEUtilsTest.mm", - "GNCBleTest.mm", - "GNCBluetoothAdapterTest.mm", "GNCCryptoTest.mm", "GNCFakeCentralManager.h", "GNCFakeCentralManager.m", @@ -53,8 +51,8 @@ objc_library( "//internal/platform/implementation:comm", "//internal/platform/implementation:platform", "//internal/platform/implementation:types", - "//internal/platform/implementation/apple", - "//internal/platform/implementation/apple:ble", + "//internal/platform/implementation/apple", # buildcleaner: keep + "//internal/platform/implementation/apple:ble_v2", "//internal/platform/implementation/apple/Mediums", "//third_party/apple_frameworks:CoreBluetooth", "//third_party/apple_frameworks:Foundation", diff --git a/internal/platform/implementation/apple/Tests/GNCBleTest.mm b/internal/platform/implementation/apple/Tests/GNCBleTest.mm deleted file mode 100644 index d8679575..00000000 --- a/internal/platform/implementation/apple/Tests/GNCBleTest.mm +++ /dev/null @@ -1,112 +0,0 @@ -// 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 "internal/platform/implementation/apple/bluetooth_adapter.h" - -#include -#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 ::nearby::ByteArray; -using ::nearby::Uuid; -using ::nearby::api::BluetoothAdapter; -using ::nearby::api::ImplementationPlatform; -using ::nearby::api::ble_v2::BleAdvertisementData; -using ::nearby::api::ble_v2::BleMedium; -using ::nearby::api::ble_v2::GattCharacteristic; -using ::nearby::api::ble_v2::TxPowerLevel; -using IOSBluetoothAdapter = ::nearby::apple::BluetoothAdapter; - -static const char *const kAdvertisementString = "\x0a\x0b\x0c\x0d"; -static const TxPowerLevel kTxPowerLevel = TxPowerLevel::kHigh; - -@interface GNCBleTest : XCTestCase -@end - -// TODO(b/222392304): More tests on GNCBleTest. -@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})); - - [NSThread sleepForTimeInterval:0.1]; - - XCTAssertTrue(_ble->StopAdvertising()); -} - -- (void)testStartandStopScanning { - Uuid service_uuid(1234, 5678); - - XCTAssertTrue(_ble->StartScanning(service_uuid, kTxPowerLevel, BleMedium::ScanCallback{})); - - [NSThread sleepForTimeInterval:0.1]; - - XCTAssertTrue(_ble->StopScanning()); -} - -- (void)testGattServerWorking { - // Test creating gatt_server. - auto gatt_server = _ble->StartGattServer(/*ServerGattConnectionCallback=*/{}); - XCTAssert(gatt_server != nullptr); - - // Test creating characteristic. - Uuid service_uuid(1234, 5678); - Uuid characteristic_uuid(5678, 1234); - GattCharacteristic::Permission permission = GattCharacteristic::Permission::kRead; - GattCharacteristic::Property property = GattCharacteristic::Property::kRead; - - // NOLINTNEXTLINE - absl::optional gatt_characteristic = - gatt_server->CreateCharacteristic(service_uuid, characteristic_uuid, permission, property); - XCTAssertTrue(gatt_characteristic.has_value()); - - // Test updating characteristic. - ByteArray any_byte("any"); - XCTAssertTrue(gatt_server->UpdateCharacteristic(gatt_characteristic.value(), any_byte)); - - gatt_server->Stop(); -} - -- (void)testCreateGattClient { - IOSBluetoothAdapter *adapter = static_cast(_adapter.get()); - auto gatt_client = _ble->ConnectToGattServer(adapter->GetPeripheral(), kTxPowerLevel, {}); - - XCTAssert(gatt_client != nullptr); -} - -@end diff --git a/internal/platform/implementation/apple/Tests/GNCBluetoothAdapterTest.mm b/internal/platform/implementation/apple/Tests/GNCBluetoothAdapterTest.mm deleted file mode 100644 index bb4955c8..00000000 --- a/internal/platform/implementation/apple/Tests/GNCBluetoothAdapterTest.mm +++ /dev/null @@ -1,87 +0,0 @@ -// 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/apple/bluetooth_adapter.h" -#include "internal/platform/implementation/ble_v2.h" -#include "internal/platform/implementation/bluetooth_adapter.h" - -using ::nearby::apple::BlePeripheral; -using ::nearby::apple::BluetoothAdapter; -using ScanMode = ::nearby::api::BluetoothAdapter::ScanMode; -using Status = ::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/apple/ble.h b/internal/platform/implementation/apple/ble.h index 6d41a16d..fd540fe7 100644 --- a/internal/platform/implementation/apple/ble.h +++ b/internal/platform/implementation/apple/ble.h @@ -12,218 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_BLE_H_ -#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_BLE_H_ +// Note: File language is detected using heuristics. Many Objective-C++ headers are incorrectly +// classified as C++ resulting in invalid linter errors. The use of "NSArray" and other Foundation +// classes like "NSData", "NSDictionary" and "NSUUID" are highly weighted for Objective-C and +// Objective-C++ scores. Oddly, "#import " does not contribute any points. +// This comment alone should be enough to trick the IDE in to believing this is actually some sort +// of Objective-C file. See: cs/google3/devtools/search/lang/recognize_language_classifiers_data -#import #import -#include -#include -#include - -#include "absl/types/optional.h" -#include "internal/platform/cancellation_flag.h" -#include "internal/platform/implementation/apple/bluetooth_adapter.h" -#include "internal/platform/implementation/ble_v2.h" -#include "internal/platform/implementation/bluetooth_adapter.h" - -#import "internal/platform/implementation/apple/Mediums/GNCMConnection.h" - -@class GNCMBlePeripheral, GNCMBleCentral; - -namespace nearby { -namespace apple { - -/** InputStream that reads from GNCMConnection. */ -class BleInputStream : public InputStream { - public: - BleInputStream(); - ~BleInputStream() override; - - ExceptionOr Read(std::int64_t size) override; - Exception Close() override; - - GNCMConnectionHandlers *GetConnectionHandlers() { return connectionHandlers_; } - - private: - GNCMConnectionHandlers *connectionHandlers_; - NSMutableArray *newDataPackets_; - NSMutableData *accumulatedData_; - NSCondition *condition_; -}; - -/** OutputStream that writes to GNCMConnection. */ -class BleOutputStream : public OutputStream { - public: - explicit BleOutputStream(id connection) - : connection_(connection), condition_([[NSCondition alloc] init]) {} - ~BleOutputStream() override; - - Exception Write(const ByteArray &data) override; - Exception Flush() override; - Exception Close() override; - - private: - id connection_; - NSCondition *condition_; -}; - -/** Concrete BleSocket implementation. */ -class BleSocket : public api::ble_v2::BleSocket { - public: - BleSocket(id connection, BlePeripheral *peripheral); - ~BleSocket() override; - - InputStream &GetInputStream() override { return *input_stream_; } - OutputStream &GetOutputStream() override { return *output_stream_; } - Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); - BlePeripheral *GetRemotePeripheral() override { return peripheral_; } - - bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_); - - private: - void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - mutable absl::Mutex mutex_; - bool closed_ ABSL_GUARDED_BY(mutex_) = false; - std::unique_ptr input_stream_; - std::unique_ptr output_stream_; - BlePeripheral *peripheral_; -}; - -/** Concrete BleServerSocket implementation. */ -class BleServerSocket : public api::ble_v2::BleServerSocket { - public: - ~BleServerSocket() override; - - std::unique_ptr Accept() override ABSL_LOCKS_EXCLUDED(mutex_); - Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); - - bool Connect(std::unique_ptr socket) ABSL_LOCKS_EXCLUDED(mutex_); - void SetCloseNotifier(absl::AnyInvocable notifier) ABSL_LOCKS_EXCLUDED(mutex_); - - private: - Exception DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - mutable absl::Mutex mutex_; - absl::CondVar cond_; - absl::flat_hash_set> pending_sockets_ ABSL_GUARDED_BY(mutex_); - absl::AnyInvocable close_notifier_ ABSL_GUARDED_BY(mutex_); - bool closed_ ABSL_GUARDED_BY(mutex_) = false; -}; - -/** 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; - std::unique_ptr StartAdvertising( - const api::ble_v2::BleAdvertisementData &advertising_data, - api::ble_v2::AdvertiseParameters advertise_parameters, AdvertisingCallback callback) 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 StartScanning(const Uuid &service_uuid, - api::ble_v2::TxPowerLevel tx_power_level, - ScanningCallback callback) 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; - - bool GetRemotePeripheral(const std::string& mac_address, - GetRemotePeripheralCallback callback) override { - return false; - } - - bool GetRemotePeripheral(api::ble_v2::BlePeripheral::UniqueId id, - GetRemotePeripheralCallback callback) override { - return false; - } - - private: - // A concrete implemenation for GattServer. - class GattServer : public api::ble_v2::GattServer { - public: - GattServer() = default; - GattServer(BluetoothAdapter *adapter, GNCMBlePeripheral *peripheral) - : adapter_(adapter), peripheral_(peripheral) {} - - absl::optional CreateCharacteristic( - const Uuid &service_uuid, const Uuid &characteristic_uuid, - api::ble_v2::GattCharacteristic::Permission permission, - api::ble_v2::GattCharacteristic::Property property) override; - - bool UpdateCharacteristic(const api::ble_v2::GattCharacteristic &characteristic, - const nearby::ByteArray &value) override; - absl::Status NotifyCharacteristicChanged(const api::ble_v2::GattCharacteristic &characteristic, - bool confirm, const ByteArray &new_value) override; - void Stop() override; - BlePeripheral &GetBlePeripheral() override { return adapter_->GetPeripheral(); } - - private: - BluetoothAdapter *adapter_ = nullptr; - GNCMBlePeripheral *peripheral_; - }; - - // A concrete implemenation for GattClient. - class GattClient : public api::ble_v2::GattClient { - public: - GattClient() = default; - explicit GattClient(GNCMBleCentral *central, const std::string &peripheral_id) - : central_(central), peripheral_id_(peripheral_id) {} - - bool DiscoverServiceAndCharacteristics(const Uuid &service_uuid, - const std::vector &characteristic_uuids) override; - - // NOLINTNEXTLINE - absl::optional GetCharacteristic( - const Uuid &service_uuid, const Uuid &characteristic_uuid) override; - - // NOLINTNEXTLINE - absl::optional ReadCharacteristic( - const api::ble_v2::GattCharacteristic &characteristic) override; - - bool WriteCharacteristic(const api::ble_v2::GattCharacteristic &characteristic, - absl::string_view value, - api::ble_v2::GattClient::WriteType write_type) override; - - bool SetCharacteristicSubscription( - const api::ble_v2::GattCharacteristic &characteristic, bool enable, - absl::AnyInvocable on_characteristic_changed_cb) override; - - void Disconnect() override; - - private: - GNCMBleCentral *central_; - std::string peripheral_id_; - absl::flat_hash_map - gatt_characteristic_values_; - }; - - absl::Mutex mutex_; - BluetoothAdapter *adapter_; - GNCMBlePeripheral *peripheral_; - GNCMBleCentral *central_; - absl::flat_hash_map server_sockets_ ABSL_GUARDED_BY(mutex_); - dispatch_queue_t callback_queue_; -}; - -} // namespace apple -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_BLE_H_ +#import "internal/platform/implementation/apple/ble_medium.h" diff --git a/internal/platform/implementation/apple/ble.mm b/internal/platform/implementation/apple/ble.mm deleted file mode 100644 index a6fdfd77..00000000 --- a/internal/platform/implementation/apple/ble.mm +++ /dev/null @@ -1,600 +0,0 @@ -// 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/apple/ble.h" - -#include - -#include -#include -#include -#include - -#include "internal/platform/implementation/apple/bluetooth_adapter.h" -#include "internal/platform/implementation/apple/utils.h" -#include "internal/platform/implementation/ble_v2.h" - -#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.h" -#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.h" - -namespace nearby { -namespace apple { - -using Permission = api::ble_v2::GattCharacteristic::Permission; -using Property = api::ble_v2::GattCharacteristic::Property; - -namespace { - -CBAttributePermissions PermissionToCBPermissions(Permission permission) { - CBAttributePermissions characteristPermissions = 0; - if ((permission & Permission::kRead) != Permission::kNone) { - characteristPermissions |= CBAttributePermissionsReadable; - } - if ((permission & Permission::kWrite) != Permission::kNone) { - characteristPermissions |= CBAttributePermissionsWriteable; - } - return characteristPermissions; -} - -CBCharacteristicProperties PropertiesToCBProperties(Property property) { - CBCharacteristicProperties characteristicProperties = 0; - if ((property & Property::kRead) != Property::kNone) { - characteristicProperties |= CBCharacteristicPropertyRead; - } - if ((property & Property::kWrite) != Property::kNone) { - characteristicProperties |= CBCharacteristicPropertyWrite; - } - if ((property & Property::kIndicate) != Property::kNone) { - characteristicProperties |= CBCharacteristicPropertyIndicate; - } - if ((property & Property::kNotify) != Property::kNone) { - characteristicProperties |= CBCharacteristicPropertyNotify; - } - return characteristicProperties; -} - -} // namespace - -using ::nearby::api::ble_v2::BleAdvertisementData; -using ::nearby::api::ble_v2::TxPowerLevel; -using ScanCallback = ::nearby::api::ble_v2::BleMedium::ScanCallback; - -/** InputStream that reads from GNCMConnection. */ -BleInputStream::BleInputStream() - : newDataPackets_([NSMutableArray array]), - accumulatedData_([NSMutableData data]), - condition_([[NSCondition alloc] init]) { - // Create the handlers of incoming data from the remote endpoint. - connectionHandlers_ = [GNCMConnectionHandlers - payloadHandler:^(NSData* data) { - [condition_ lock]; - // Add the incoming data to the data packet array to be processed in read() below. - [newDataPackets_ addObject:data]; - [condition_ signal]; - [condition_ unlock]; - } - disconnectedHandler:^{ - [condition_ lock]; - // Release the data packet array, meaning the stream has been closed or severed. - newDataPackets_ = nil; - [condition_ signal]; - [condition_ unlock]; - }]; -} - -BleInputStream::~BleInputStream() { - NSCAssert(!newDataPackets_, @"BleInputStream not closed before destruction"); -} - -ExceptionOr BleInputStream::Read(std::int64_t size) { - // Block until either (a) the connection has been closed, (b) we have enough data to return. - NSData* dataToReturn; - [condition_ lock]; - while (true) { - // Check if the stream has been closed or severed. - if (!newDataPackets_) break; - - if (newDataPackets_.count > 0) { - // Add the packet data to the accumulated data. - for (NSData* data in newDataPackets_) { - if (data.length > 0) { - [accumulatedData_ appendData:data]; - } - } - [newDataPackets_ removeAllObjects]; - } - - if ((size == -1) && (accumulatedData_.length > 0)) { - // Return all of the data. - dataToReturn = accumulatedData_; - accumulatedData_ = [NSMutableData data]; - break; - } else if (accumulatedData_.length > 0) { - // Return up to |size| bytes of the data. - std::int64_t sizeToReturn = (accumulatedData_.length < size) ? accumulatedData_.length : size; - NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn); - dataToReturn = [accumulatedData_ subdataWithRange:range]; - [accumulatedData_ replaceBytesInRange:range withBytes:nil length:0]; - break; - } - - [condition_ wait]; - } - [condition_ unlock]; - - if (dataToReturn) { - NSLog(@"[NEARBY] Input stream: Received data of size: %lu", (unsigned long)dataToReturn.length); - return ExceptionOr(ByteArrayFromNSData(dataToReturn)); - } else { - return ExceptionOr{Exception::kIo}; - } -} - -Exception BleInputStream::Close() { - // Unblock pending read operation. - [condition_ lock]; - newDataPackets_ = nil; - [condition_ signal]; - [condition_ unlock]; - return {Exception::kSuccess}; -} - -/** OutputStream that writes to GNCMConnection. */ -BleOutputStream::~BleOutputStream() { - NSCAssert(!connection_, @"BleOutputStream not closed before destruction"); -} - -Exception BleOutputStream::Write(const ByteArray& data) { - [condition_ lock]; - NSLog(@"[NEARBY] Sending data of size: %lu", (unsigned long)NSDataFromByteArray(data).length); - - NSMutableData* packet = [NSMutableData dataWithData:NSDataFromByteArray(data)]; - - // Send the data, blocking until the completion handler is called. - __block GNCMPayloadResult sendResult = GNCMPayloadResultFailure; - __block bool isComplete = NO; - NSCondition* condition = condition_; // don't capture |this| in completion - - // Check if connection_ is nil, then just don't wait and return as failure. - if (connection_ != nil) { - [connection_ sendData:packet - progressHandler:^(size_t count) { - } - completion:^(GNCMPayloadResult result) { - // Make sure we haven't already reported completion before. This prevents a crash - // where we try leaving a dispatch group more times than we entered it. - // b/79095653. - if (isComplete) { - return; - } - isComplete = YES; - sendResult = result; - [condition lock]; - [condition signal]; - [condition unlock]; - }]; - [condition_ wait]; - [condition_ unlock]; - } else { - sendResult = GNCMPayloadResultFailure; - [condition_ unlock]; - } - - if (sendResult == GNCMPayloadResultSuccess) { - return {Exception::kSuccess}; - } else { - return {Exception::kIo}; - } -} - -Exception BleOutputStream::Flush() { - // The write() function blocks until the data is received by the remote endpoint, so there's - // nothing to do here. - return {Exception::kSuccess}; -} - -Exception BleOutputStream::Close() { - // Unblock pending write operation. - [condition_ lock]; - connection_ = nil; - [condition_ signal]; - [condition_ unlock]; - return {Exception::kSuccess}; -} - -/** BleSocket implementation.*/ -BleSocket::BleSocket(id connection, BlePeripheral* peripheral) - : input_stream_(new BleInputStream()), - output_stream_(new BleOutputStream(connection)), - peripheral_(peripheral) {} - -BleSocket::~BleSocket() { - absl::MutexLock lock(&mutex_); - DoClose(); -} - -bool BleSocket::IsClosed() const { - absl::MutexLock lock(&mutex_); - return closed_; -} - -Exception BleSocket::Close() { - absl::MutexLock lock(&mutex_); - DoClose(); - return {Exception::kSuccess}; -} - -void BleSocket::DoClose() { - if (!closed_) { - input_stream_->Close(); - output_stream_->Close(); - closed_ = true; - } -} - -/** WifiLanServerSocket implementation. */ -BleServerSocket::~BleServerSocket() { - absl::MutexLock lock(&mutex_); - DoClose(); -} - -std::unique_ptr BleServerSocket::Accept() { - absl::MutexLock lock(&mutex_); - while (!closed_ && pending_sockets_.empty()) { - cond_.Wait(&mutex_); - } - // Return early if closed. - if (closed_) return {}; - - auto remote_socket = std::move(pending_sockets_.extract(pending_sockets_.begin()).value()); - return std::move(remote_socket); -} - -bool BleServerSocket::Connect(std::unique_ptr socket) { - absl::MutexLock lock(&mutex_); - if (closed_) { - return false; - } - // add client socket to the pending list - pending_sockets_.insert(std::move(socket)); - cond_.SignalAll(); - if (closed_) { - return false; - } - return true; -} - -void BleServerSocket::SetCloseNotifier(absl::AnyInvocable notifier) { - absl::MutexLock lock(&mutex_); - close_notifier_ = std::move(notifier); -} - -Exception BleServerSocket::Close() { - absl::MutexLock lock(&mutex_); - return DoClose(); -} - -Exception BleServerSocket::DoClose() { - bool should_notify = !closed_; - closed_ = true; - if (should_notify) { - cond_.SignalAll(); - if (close_notifier_) { - auto notifier = std::move(close_notifier_); - mutex_.Unlock(); - // Notifier may contain calls to public API, and may cause deadlock, if - // mutex_ is held during the call. - notifier(); - mutex_.Lock(); - } - } - return {Exception::kSuccess}; -} - -/** BleMedium implementation. */ -BleMedium::BleMedium(::nearby::api::BluetoothAdapter& adapter) - : adapter_(static_cast(&adapter)) {} - -bool BleMedium::StartAdvertising( - const BleAdvertisementData& advertising_data, - ::nearby::api::ble_v2::AdvertiseParameters advertise_set_parameters) { - if (advertising_data.service_data.empty()) { - return false; - } - const auto& service_uuid = advertising_data.service_data.begin()->first.Get16BitAsString(); - const ByteArray& service_data_bytes = advertising_data.service_data.begin()->second; - - if (!peripheral_) { - peripheral_ = [[GNCMBlePeripheral alloc] init]; - } - - auto& peripheral = adapter_->GetPeripheral(); - [peripheral_ - startAdvertisingWithServiceUUID:ObjCStringFromCppString(service_uuid) - advertisementData:NSDataFromByteArray(service_data_bytes) - endpointConnectedHandler:^GNCMConnectionHandlers*(id connection) { - // TODO(edwinwu): This server_socket is supposed to be gotten from the map by key of - // servcie_id. We now always get the first iteration since we don't know the key now. - // Try the way to move the Ble socket frame verification up to one layer. - std::string service_id; - BleServerSocket* server_socket; - if (!server_sockets_.empty()) { - service_id = server_sockets_.begin()->first; - server_socket = server_sockets_.begin()->second; - } else { - return nil; - } - auto socket = std::make_unique(connection, &peripheral); - GNCMConnectionHandlers* connectionHandlers = - static_cast(socket->GetInputStream()).GetConnectionHandlers(); - server_socket->Connect(std::move(socket)); - return connectionHandlers; - } - callbackQueue:callback_queue_]; - return true; -} - -bool BleMedium::StopAdvertising() { - peripheral_ = nil; - return true; -} -std::unique_ptr BleMedium::StartAdvertising( - const api::ble_v2::BleAdvertisementData& advertising_data, - api::ble_v2::AdvertiseParameters advertise_parameters, - BleMedium::AdvertisingCallback callback) { - // TODO(hais): add real impl for iOs StartAdvertising - return std::make_unique(AdvertisingSession{}); -} -bool BleMedium::StartScanning(const Uuid& service_uuid, TxPowerLevel tx_power_level, - ScanCallback scan_callback) { - if (!central_) { - central_ = [[GNCMBleCentral alloc] init]; - } - - __block ScanCallback callback = std::move(scan_callback); - [central_ startScanningWithServiceUUID:ObjCStringFromCppString(service_uuid.Get16BitAsString()) - scanResultHandler:^(NSString* peripheralID, NSData* serviceData) { - BleAdvertisementData advertisement_data; - advertisement_data.service_data = {{service_uuid, ByteArrayFromNSData(serviceData)}}; - BlePeripheral& peripheral = adapter_->GetPeripheral(); - peripheral.SetPeripheralId(CppStringFromObjCString(peripheralID)); - callback.advertisement_found_cb(peripheral, advertisement_data); - } - requestConnectionHandler:^(GNCMBleConnectionRequester connectionRequester) { - BlePeripheral& peripheral = adapter_->GetPeripheral(); - peripheral.SetConnectionRequester(connectionRequester); - } - callbackQueue:callback_queue_]; - - return true; -} - -bool BleMedium::StopScanning() { - central_ = nil; - return true; -} - -std::unique_ptr BleMedium::StartScanning( - const Uuid& service_uuid, TxPowerLevel tx_power_level, BleMedium::ScanningCallback callback) { - // TODO(hais): add real impl for windows StartScanning. - return std::make_unique(ScanningSession{}); -} - -std::unique_ptr BleMedium::StartGattServer( - api::ble_v2::ServerGattConnectionCallback callback) { - if (!peripheral_) { - peripheral_ = [[GNCMBlePeripheral alloc] init]; - } - return std::make_unique(adapter_, peripheral_); -} - -std::unique_ptr BleMedium::ConnectToGattServer( - api::ble_v2::BlePeripheral& peripheral, TxPowerLevel tx_power_level, - api::ble_v2::ClientGattConnectionCallback callback) { - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - __block NSError* connectedError; - BlePeripheral iosPeripheral = static_cast(peripheral); - std::string peripheral_id = iosPeripheral.GetPeripheralId(); - [central_ connectGattServerWithPeripheralID:ObjCStringFromCppString(peripheral_id) - gattConnectionResultHandler:^(NSError* _Nullable error) { - connectedError = error; - dispatch_semaphore_signal(semaphore); - }]; - dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC)); - - if (connectedError) { - return nullptr; - } - return std::make_unique(central_, peripheral_id); -} - -std::unique_ptr BleMedium::OpenServerSocket( - const std::string& service_id) { - auto server_socket = std::make_unique(); - server_socket->SetCloseNotifier([this, service_id]() { - absl::MutexLock lock(&mutex_); - server_sockets_.erase(service_id); - }); - absl::MutexLock lock(&mutex_); - server_sockets_.insert({service_id, server_socket.get()}); - return server_socket; -} - -std::unique_ptr BleMedium::Connect(const std::string& service_id, - TxPowerLevel tx_power_level, - api::ble_v2::BlePeripheral& peripheral, - CancellationFlag* cancellation_flag) { - NSString* serviceID = ObjCStringFromCppString(service_id); - __block std::unique_ptr socket; - __block BlePeripheral ios_peripheral = static_cast(peripheral); - GNCMBleConnectionRequester connection_requester = ios_peripheral.GetConnectionRequester(); - if (!connection_requester) return {}; - - dispatch_group_t group = dispatch_group_create(); - dispatch_group_enter(group); - if (cancellation_flag->Cancelled()) { - NSLog(@"[NEARBY] BLE Connect: Has been cancelled: service_id=%@", serviceID); - dispatch_group_leave(group); // unblock - return {}; - } - - connection_requester(serviceID, ^(id connection) { - // If the connection wasn't successfully established, return a NULL socket. - if (connection) { - socket = std::make_unique(connection, &ios_peripheral); - } - - dispatch_group_leave(group); // unblock - return socket != nullptr - ? static_cast(socket->GetInputStream()).GetConnectionHandlers() - : nullptr; - }); - dispatch_group_wait(group, DISPATCH_TIME_FOREVER); - - // Send the (empty) intro packet, which the BLE advertiser is expecting. - if (socket != nullptr) { - socket->GetOutputStream().Write(ByteArray()); - } - - return std::move(socket); -} - -bool BleMedium::IsExtendedAdvertisementsAvailable() { return false; } - -// NOLINTNEXTLINE -absl::optional BleMedium::GattServer::CreateCharacteristic( - const Uuid& service_uuid, const Uuid& characteristic_uuid, - api::ble_v2::GattCharacteristic::Permission permission, - api::ble_v2::GattCharacteristic::Property property) { - api::ble_v2::GattCharacteristic characteristic = {.uuid = characteristic_uuid, - .service_uuid = service_uuid, - .permission = permission, - .property = property}; - [peripheral_ - addCBServiceWithUUID:[CBUUID - UUIDWithString:ObjCStringFromCppString( - characteristic.service_uuid.Get16BitAsString())]]; - [peripheral_ - addCharacteristic:[[CBMutableCharacteristic alloc] - initWithType:[CBUUID UUIDWithString:ObjCStringFromCppString(std::string( - characteristic.uuid))] - properties:PropertiesToCBProperties(characteristic.property) - value:nil - permissions:PermissionToCBPermissions(characteristic.permission)]]; - return characteristic; -} - -bool BleMedium::GattServer::UpdateCharacteristic( - const api::ble_v2::GattCharacteristic& characteristic, const nearby::ByteArray& value) { - [peripheral_ updateValue:NSDataFromByteArray(value) - forCharacteristic:[CBUUID UUIDWithString:ObjCStringFromCppString( - std::string(characteristic.uuid))]]; - return true; -} - -absl::Status BleMedium::GattServer::NotifyCharacteristicChanged( - const api::ble_v2::GattCharacteristic& characteristic, bool confirm, - const ByteArray& new_value) { - // no-op because client cannot request notifications. - return absl::UnimplementedError("Unimplemented!"); -} - -void BleMedium::GattServer::Stop() { [peripheral_ stopGATTService]; } - -bool BleMedium::GattClient::DiscoverServiceAndCharacteristics( - const Uuid& service_uuid, const std::vector& characteristic_uuids) { - // Discover all characteristics that may contain the advertisement. - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - gatt_characteristic_values_.clear(); - CBUUID* serviceUUID = [CBUUID UUIDWithString:ObjCStringFromCppString(std::string(service_uuid))]; - - absl::flat_hash_map gatt_characteristics; - NSMutableArray* characteristicUUIDs = - [NSMutableArray arrayWithCapacity:characteristic_uuids.size()]; - for (const auto& characteristic_uuid : characteristic_uuids) { - [characteristicUUIDs addObject:[CBUUID UUIDWithString:ObjCStringFromCppString( - std::string(characteristic_uuid))]]; - gatt_characteristics.insert({std::string(characteristic_uuid), characteristic_uuid}); - } - - [central_ discoverGattService:serviceUUID - gattCharacteristics:characteristicUUIDs - peripheralID:ObjCStringFromCppString(peripheral_id_) - gattDiscoverResultHandler:^(NSOrderedSet* _Nullable cb_characteristics) { - if (cb_characteristics != nil) { - for (CBCharacteristic* cb_characteristic in cb_characteristics) { - auto const& it = gatt_characteristics.find( - CppStringFromObjCString(cb_characteristic.UUID.UUIDString)); - if (it == gatt_characteristics.end()) continue; - - api::ble_v2::GattCharacteristic characteristic = {.uuid = it->second, - .service_uuid = service_uuid}; - gatt_characteristic_values_.insert( - {characteristic, ByteArrayFromNSData(cb_characteristic.value).string_data()}); - } - } - - dispatch_semaphore_signal(semaphore); - }]; - - dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC)); - - if (gatt_characteristic_values_.empty()) { - return false; - } - return true; -} - -// NOLINTNEXTLINE -absl::optional BleMedium::GattClient::GetCharacteristic( - const Uuid& service_uuid, const Uuid& characteristic_uuid) { - api::ble_v2::GattCharacteristic characteristic = {.uuid = characteristic_uuid, - .service_uuid = service_uuid}; - auto const it = gatt_characteristic_values_.find(characteristic); - if (it == gatt_characteristic_values_.end()) { - return absl::nullopt; // NOLINT - } - return it->first; -} - -// NOLINTNEXTLINE -absl::optional BleMedium::GattClient::ReadCharacteristic( - const api::ble_v2::GattCharacteristic& characteristic) { - auto const it = gatt_characteristic_values_.find(characteristic); - if (it == gatt_characteristic_values_.end()) { - return absl::nullopt; // NOLINT - } - return it->second; -} - -bool BleMedium::GattClient::WriteCharacteristic( - const api::ble_v2::GattCharacteristic& characteristic, absl::string_view value, - api::ble_v2::GattClient::WriteType write_type) { - // No op. - return false; -} - -bool BleMedium::GattClient::SetCharacteristicSubscription( - const api::ble_v2::GattCharacteristic& characteristic, bool enable, - absl::AnyInvocable on_characteristic_changed_cb) { - // No op since we can't write characteristics. - return false; -} - -void BleMedium::GattClient::Disconnect() { - [central_ disconnectGattServiceWithPeripheralID:ObjCStringFromCppString(peripheral_id_)]; -} - -} // namespace apple -} // namespace nearby diff --git a/internal/platform/implementation/apple/ble_gatt_server.h b/internal/platform/implementation/apple/ble_gatt_server.h index e42d4998..abf5e8ea 100644 --- a/internal/platform/implementation/apple/ble_gatt_server.h +++ b/internal/platform/implementation/apple/ble_gatt_server.h @@ -71,7 +71,7 @@ class GattServer : public api::ble_v2::GattServer { private: GNCBLEGATTServer *gatt_server_; - BlePeripheral peripheral_; + EmptyBlePeripheral peripheral_; }; } // namespace apple diff --git a/internal/platform/implementation/apple/bluetooth_adapter.h b/internal/platform/implementation/apple/bluetooth_adapter.h deleted file mode 100644 index ce08753f..00000000 --- a/internal/platform/implementation/apple/bluetooth_adapter.h +++ /dev/null @@ -1,108 +0,0 @@ -// 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_APPLE_BLUETOOTH_ADAPTER_H_ -#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_BLUETOOTH_ADAPTER_H_ - -#include - -#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.h" -#include "internal/platform/implementation/ble_v2.h" -#include "internal/platform/implementation/bluetooth_adapter.h" -#include "internal/platform/prng.h" - -namespace nearby { -namespace apple { - -class BluetoothAdapter; - -// Concrete BlePeripheral implementation. -class BlePeripheral : public api::ble_v2::BlePeripheral { - public: - std::string GetAddress() const override; - - api::ble_v2::BlePeripheral::UniqueId GetUniqueId() const override { - return unique_id_; - } - - std::string GetPeripheralId() const { return peripheral_id_; } - - void SetPeripheralId(const std::string& peripheral_id) { - peripheral_id_ = peripheral_id; - } - - void SetConnectionRequester(GNCMBleConnectionRequester connection_requester) { - connection_requester_ = connection_requester; - } - - GNCMBleConnectionRequester GetConnectionRequester() { - return connection_requester_; - } - - private: - // Only BluetoothAdapter may instantiate BlePeripheral. - friend class BluetoothAdapter; - - explicit BlePeripheral(BluetoothAdapter* adapter) : adapter_(*adapter) { - unique_id_ = Prng().NextInt64(); - } - - BluetoothAdapter& adapter_; - std::string peripheral_id_; - GNCMBleConnectionRequester connection_requester_; - api::ble_v2::BlePeripheral::UniqueId unique_id_; -}; - -// 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) { - return SetName(name, /* persist= */ true); - } - bool SetName(absl::string_view name, bool persist) 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 apple -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_BLUETOOTH_ADAPTER_H_ diff --git a/internal/platform/implementation/apple/bluetooth_adapter.mm b/internal/platform/implementation/apple/bluetooth_adapter.mm deleted file mode 100644 index 7369e63c..00000000 --- a/internal/platform/implementation/apple/bluetooth_adapter.mm +++ /dev/null @@ -1,28 +0,0 @@ -// 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/apple/bluetooth_adapter.h" - -#include - -#include "absl/strings/string_view.h" -#include "internal/platform/implementation/bluetooth_adapter.h" - -namespace nearby { -namespace apple { - -std::string BlePeripheral::GetAddress() const { return adapter_.GetMacAddress(); } - -} // namespace apple -} // namespace nearby diff --git a/internal/platform/implementation/apple/platform.mm b/internal/platform/implementation/apple/platform.mm index b70fc53a..781a50ea 100644 --- a/internal/platform/implementation/apple/platform.mm +++ b/internal/platform/implementation/apple/platform.mm @@ -170,7 +170,7 @@ std::unique_ptr ImplementationPlatform::CreateBleMedium(api::Bluetoot std::unique_ptr ImplementationPlatform::CreateBleV2Medium( api::BluetoothAdapter& adapter) { - return std::make_unique(adapter); + return std::make_unique(); } std::unique_ptr ImplementationPlatform::CreateServerSyncMedium() {