Files
nearby/internal/platform/implementation/apple/Mediums/BLE/GNCMBleConnection.m
T
2025-08-28 20:26:08 -07:00

148 lines
5.3 KiB
Objective-C

// 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/GNCMBleConnection.h"
#import "internal/platform/implementation/apple/Log/GNCLogger.h"
#import "internal/platform/implementation/apple/Mediums/BLE/GNCLeaks.h"
#import "internal/platform/implementation/apple/Mediums/BLE/GNCMBleUtils.h"
#import "internal/platform/implementation/apple/Mediums/BLE/GNCMConnection.h"
#import "internal/platform/implementation/apple/Mediums/BLE/Sockets/Source/Shared/GNSSocket.h"
NS_ASSUME_NONNULL_BEGIN
@interface GNCMBleConnection () <GNSSocketDelegate>
@property(nonatomic) dispatch_queue_t selfQueue;
@property(nonatomic) GNSSocket *socket;
@property(nonatomic) NSData *serviceIDHash;
@property(nonatomic) dispatch_queue_t callbackQueue;
@property(nonatomic) BOOL expectedIntroPacket;
@property(nonatomic) BOOL receivedIntroPacket;
@end
@implementation GNCMBleConnection
+ (instancetype)connectionWithSocket:(GNSSocket *)socket
serviceID:(nullable NSString *)serviceID
expectedIntroPacket:(BOOL)expectedIntroPacket
callbackQueue:(dispatch_queue_t)callbackQueue {
GNCMBleConnection *connection = [[GNCMBleConnection alloc] init];
connection.socket = socket;
socket.delegate = connection;
connection.serviceIDHash = serviceID ? GNCMServiceIDHash(serviceID) : nil;
connection.callbackQueue = callbackQueue;
connection.selfQueue = dispatch_queue_create("GNCMBleConnectionQueue", DISPATCH_QUEUE_SERIAL);
connection.expectedIntroPacket = expectedIntroPacket;
return connection;
}
- (void)dealloc {
[_socket disconnect];
GNCVerifyDealloc(_socket, 2); // assert if not deallocated
}
#pragma mark GNCMConnection
- (void)sendData:(NSData *)data
progressHandler:(GNCMProgressHandler)progressHandler
completion:(GNCMPayloadResultHandler)completion {
dispatch_async(_selfQueue, ^{
NSMutableData *packet;
if (data.length == 0) {
// Get the Control introduction packet if data length is 0.
NSData *introData = GNCMGenerateBLEFramesIntroductionPacket(_serviceIDHash);
packet = [NSMutableData dataWithData:introData];
} else {
// Prefix the service ID hash.
packet = [NSMutableData dataWithData:_serviceIDHash];
[packet appendData:data];
}
[_socket sendData:packet
progressHandler:^(float progress) {
// Convert normalized progress value to number of bytes.
dispatch_async(_callbackQueue, ^{
progressHandler((size_t)(progress * packet.length));
});
}
completion:^(NSError *error) {
dispatch_async(_callbackQueue, ^{
completion(error ? GNCMPayloadResultFailure : GNCMPayloadResultSuccess);
});
}];
});
}
#pragma mark GNSSocketDelegate
- (void)socketDidConnect:(GNSSocket *)socket {
GNCLoggerError(@"Unexpected -socketDidConnect: call; should've already happened");
}
- (void)socket:(GNSSocket *)socket didDisconnectWithError:(NSError *)error {
dispatch_async(_selfQueue, ^{
if (_connectionHandlers.disconnectedHandler) {
dispatch_async(_callbackQueue, ^{
_connectionHandlers.disconnectedHandler();
});
}
});
}
- (void)socket:(GNSSocket *)socket didReceiveData:(NSData *)data {
// Extract the service ID prefix from each data packet.
NSMutableData *packet;
NSUInteger prefixLength = _serviceIDHash.length;
if (_expectedIntroPacket && !_receivedIntroPacket) {
// Check if the first packet is intro packet.
if (!_serviceIDHash) {
// If _serviceIdHash is nil, then we need to parse the first incoming packet if it conforms to
// introducion packet and extract the serviceIdHash for coming packets.
NSData *serviceIDHash = GNCMParseBLEFramesIntroductionPacket(data);
if (serviceIDHash) {
_serviceIDHash = serviceIDHash;
_receivedIntroPacket = YES;
} else {
GNCLoggerInfo(@"[NEARBY] Input stream: Received wrong intro packet and discarded");
}
} else {
NSData *introData = GNCMGenerateBLEFramesIntroductionPacket(_serviceIDHash);
if ([data isEqual:introData]) {
_receivedIntroPacket = YES;
} else {
GNCLoggerInfo(@"[NEARBY] Input stream: Received wrong intro packet and discarded");
}
}
return;
}
if (![[data subdataWithRange:NSMakeRange(0, prefixLength)] isEqual:_serviceIDHash]) {
GNCLoggerInfo(@"[NEARBY] Input stream: Received wrong data packet and discarded");
return;
}
packet = [NSMutableData
dataWithData:[data subdataWithRange:NSMakeRange(prefixLength, data.length - prefixLength)]];
dispatch_async(_selfQueue, ^{
if (_connectionHandlers.payloadHandler) {
dispatch_async(_callbackQueue, ^{
_connectionHandlers.payloadHandler(packet);
});
}
});
}
@end
NS_ASSUME_NONNULL_END