mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 22:56:12 -04:00
[BLEREFACTOR]: Add refactor flow for Ble/L2CAP in Apple platform layer.
PiperOrigin-RevId: 829179079
This commit is contained in:
committed by
Copybara-Service
parent
5caa639fcf
commit
d7a3d13fe2
@@ -52,6 +52,9 @@ constexpr auto kEnableBleL2cap =
|
||||
// Disable/Enable BLE v2 in Nearby Connections SDK.
|
||||
constexpr auto kEnableBleV2 =
|
||||
flags::Flag<bool>(kConfigPackage, "45401515", true);
|
||||
// When true, refactor the BLE/L2CAP logic in Nearby Connections SDK.
|
||||
constexpr auto kRefactorBleL2cap =
|
||||
flags::Flag<bool>(kConfigPackage, "45713654", false);
|
||||
// Enable/Disable DCT advertising/scanning specification.
|
||||
constexpr auto kEnableDct =
|
||||
flags::Flag<bool>(kConfigPackage, "45697202", false);
|
||||
|
||||
@@ -226,8 +226,7 @@ BasePcpHandler::StartOperationResult P2pClusterPcpHandler::StartAdvertisingImpl(
|
||||
if (ble_medium_.StartLegacyAdvertising(
|
||||
service_id, local_endpoint_id,
|
||||
advertising_options.fast_advertisement_service_uuid)) {
|
||||
LOG(INFO) << __func__
|
||||
<< "Ble started advertising for legacy device.";
|
||||
LOG(INFO) << __func__ << "Ble started advertising for legacy device.";
|
||||
mediums_started_successfully.push_back(bluetooth_medium);
|
||||
VLOG(1) << __func__ << "After Ble, BT added";
|
||||
bluetooth_classic_advertiser_client_id_ = client->GetClientId();
|
||||
@@ -2379,49 +2378,68 @@ BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::BleConnectImpl(
|
||||
config_package_nearby::nearby_connections_feature::kEnableBleL2cap) &&
|
||||
peripheral.GetPsm() !=
|
||||
mediums::BleAdvertisementHeader::kDefaultPsmValue) {
|
||||
ErrorOr<BleL2capSocket> ble_l2cap_socket_result =
|
||||
ble_medium_.ConnectOverL2cap(
|
||||
endpoint->service_id, peripheral,
|
||||
client->GetCancellationFlag(endpoint->endpoint_id));
|
||||
if (!ble_l2cap_socket_result.has_error()) {
|
||||
LOG(INFO) << "In BleConnectImpl(), connected to Ble L2CAP device "
|
||||
<< absl::BytesToHexString(peripheral.GetId().data())
|
||||
<< " for endpoint(id=" << endpoint->endpoint_id << ").";
|
||||
auto channel = std::make_unique<BleL2capEndpointChannel>(
|
||||
endpoint->service_id, /*channel_name=*/endpoint->endpoint_id,
|
||||
ble_l2cap_socket_result.value());
|
||||
return BasePcpHandler::ConnectImplResult{
|
||||
.medium = BLE,
|
||||
.status = {Status::kSuccess},
|
||||
.operation_result_code = OperationResultCode::DETAIL_SUCCESS,
|
||||
.endpoint_channel = std::move(channel),
|
||||
};
|
||||
if (NearbyFlags::GetInstance().GetBoolFlag(
|
||||
config_package_nearby::nearby_connections_feature::
|
||||
kRefactorBleL2cap)) {
|
||||
// TODO(edwinwu): Implement refactored Ble L2CAP flow in next cl.
|
||||
LOG(WARNING)
|
||||
<< "In BleConnectImpl(), failed to connect to Ble L2CAP "
|
||||
"device due to refactor Ble L2CAP flow is not implemented yet.";
|
||||
} else {
|
||||
LOG(WARNING) << "In BleConnectImpl(), failed to connect to Ble L2CAP "
|
||||
"device "
|
||||
<< absl::BytesToHexString(peripheral.GetId().data())
|
||||
<< " for endpoint(id=" << endpoint->endpoint_id << ").";
|
||||
ErrorOr<BleL2capSocket> ble_l2cap_socket_result =
|
||||
ble_medium_.ConnectOverL2cap(
|
||||
endpoint->service_id, peripheral,
|
||||
client->GetCancellationFlag(endpoint->endpoint_id));
|
||||
if (!ble_l2cap_socket_result.has_error()) {
|
||||
LOG(INFO) << "In BleConnectImpl(), connected to Ble L2CAP device "
|
||||
<< absl::BytesToHexString(peripheral.GetId().data())
|
||||
<< " for endpoint(id=" << endpoint->endpoint_id << ").";
|
||||
auto channel = std::make_unique<BleL2capEndpointChannel>(
|
||||
endpoint->service_id, /*channel_name=*/endpoint->endpoint_id,
|
||||
ble_l2cap_socket_result.value());
|
||||
return BasePcpHandler::ConnectImplResult{
|
||||
.medium = BLE,
|
||||
.status = {Status::kSuccess},
|
||||
.operation_result_code = OperationResultCode::DETAIL_SUCCESS,
|
||||
.endpoint_channel = std::move(channel),
|
||||
};
|
||||
} else {
|
||||
LOG(WARNING) << "In BleConnectImpl(), failed to connect to Ble L2CAP "
|
||||
"device "
|
||||
<< absl::BytesToHexString(peripheral.GetId().data())
|
||||
<< " for endpoint(id=" << endpoint->endpoint_id << ").";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ErrorOr<BleSocket> ble_socket_result =
|
||||
ble_medium_.Connect(endpoint->service_id, peripheral,
|
||||
client->GetCancellationFlag(endpoint->endpoint_id));
|
||||
if (ble_socket_result.has_error()) {
|
||||
LOG(ERROR) << "In BleConnectImpl(), failed to connect to BLE device "
|
||||
<< absl::BytesToHexString(peripheral.GetId().data())
|
||||
<< " for endpoint(id=" << endpoint->endpoint_id << ").";
|
||||
std::unique_ptr<BleEndpointChannel> channel = nullptr;
|
||||
if (NearbyFlags::GetInstance().GetBoolFlag(
|
||||
config_package_nearby::nearby_connections_feature::
|
||||
kRefactorBleL2cap)) {
|
||||
// TODO(edwinwu): Implement refactored Ble L2CAP flow in next cl.
|
||||
return BasePcpHandler::ConnectImplResult{
|
||||
.status = {Status::kBleError},
|
||||
.operation_result_code =
|
||||
ble_socket_result.error().operation_result_code().value(),
|
||||
.operation_result_code = OperationResultCode::DETAIL_UNKNOWN,
|
||||
};
|
||||
} else {
|
||||
ErrorOr<BleSocket> ble_socket_result =
|
||||
ble_medium_.Connect(endpoint->service_id, peripheral,
|
||||
client->GetCancellationFlag(endpoint->endpoint_id));
|
||||
if (ble_socket_result.has_error()) {
|
||||
LOG(ERROR) << "In BleConnectImpl(), failed to connect to BLE device "
|
||||
<< absl::BytesToHexString(peripheral.GetId().data())
|
||||
<< " for endpoint(id=" << endpoint->endpoint_id << ").";
|
||||
return BasePcpHandler::ConnectImplResult{
|
||||
.status = {Status::kBleError},
|
||||
.operation_result_code =
|
||||
ble_socket_result.error().operation_result_code().value(),
|
||||
};
|
||||
}
|
||||
channel = std::make_unique<BleEndpointChannel>(
|
||||
endpoint->service_id, /*channel_name=*/endpoint->endpoint_id,
|
||||
ble_socket_result.value());
|
||||
}
|
||||
|
||||
auto channel = std::make_unique<BleEndpointChannel>(
|
||||
endpoint->service_id, /*channel_name=*/endpoint->endpoint_id,
|
||||
ble_socket_result.value());
|
||||
|
||||
return BasePcpHandler::ConnectImplResult{
|
||||
.medium = BLE,
|
||||
.status = {Status::kSuccess},
|
||||
|
||||
@@ -26,4 +26,7 @@
|
||||
/** Checks whether BLE L2CAP is enabled in the Nearby Connections SDK. */
|
||||
@property(nonatomic, class, readonly) BOOL bleL2capEnabled;
|
||||
|
||||
/** Checks whether BLE L2CAP refactor is enabled in the Nearby Connections SDK. */
|
||||
@property(nonatomic, class, readonly) BOOL refactorBleL2capEnabled;
|
||||
|
||||
@end
|
||||
|
||||
@@ -36,4 +36,10 @@
|
||||
kEnableBleL2cap);
|
||||
}
|
||||
|
||||
+ (BOOL)refactorBleL2capEnabled {
|
||||
return nearby::NearbyFlags::GetInstance().GetBoolFlag(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kRefactorBleL2cap);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h"
|
||||
#import "internal/platform/implementation/apple/Log/GNCLogger.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPStream.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCLeaks.h"
|
||||
@@ -98,8 +99,12 @@ static NSData *PrefixLengthData(NSData *data) {
|
||||
dispatch_async(_selfQueue, ^{
|
||||
NSData *packet;
|
||||
|
||||
// Prefix the service ID hash.
|
||||
packet = PrefixLengthData(PrefixDataWithServiceIDHash(_serviceIDHash, data));
|
||||
if (GNCFeatureFlags.refactorBleL2capEnabled) {
|
||||
packet = PrefixLengthData(data);
|
||||
} else {
|
||||
// Prefix the service ID hash.
|
||||
packet = PrefixLengthData(PrefixDataWithServiceIDHash(_serviceIDHash, data));
|
||||
}
|
||||
if (_verboseLoggingEnabled) {
|
||||
GNCLoggerDebug(@"GNCBLEL2CAPConnection data to be sent: %@", [packet description]);
|
||||
}
|
||||
@@ -202,42 +207,49 @@ static NSData *PrefixLengthData(NSData *data) {
|
||||
}
|
||||
bytesProcessed = realData.length + kL2CAPPacketLength;
|
||||
|
||||
// TODO: b/399815436 - Refactor the validation logic to connections layer.
|
||||
if ([self handleL2CAPPacketFromData:realData]) {
|
||||
return bytesProcessed;
|
||||
}
|
||||
if (GNCFeatureFlags.refactorBleL2capEnabled) {
|
||||
if (_connectionHandlers.payloadHandler) {
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
_connectionHandlers.payloadHandler([realData copy]);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// TODO: b/399815436 - Refactor the validation logic to connections layer.
|
||||
if ([self handleL2CAPPacketFromData:realData]) {
|
||||
return bytesProcessed;
|
||||
}
|
||||
|
||||
// TODO: b/399815436 - All BLE control packets should be handled here and not passed to
|
||||
// upper layer. Need to refine the flow after refactoring.
|
||||
if (_incomingConnection && !_handledReceivedBLEIntroPacket) {
|
||||
[self handleBLEIntroPacketFromData:realData];
|
||||
return bytesProcessed;
|
||||
}
|
||||
// TODO: b/399815436 - All BLE control packets should be handled here and not passed to
|
||||
// upper layer. Need to refine the flow after refactoring.
|
||||
if (_incomingConnection && !_handledReceivedBLEIntroPacket) {
|
||||
[self handleBLEIntroPacketFromData:realData];
|
||||
return bytesProcessed;
|
||||
}
|
||||
|
||||
if (realData.length < _serviceIDHash.length) {
|
||||
GNCLoggerError(@"Data length mismatch. Expected size: > %lu, Data: %@", _serviceIDHash.length,
|
||||
realData);
|
||||
return bytesProcessed;
|
||||
}
|
||||
if (realData.length < _serviceIDHash.length) {
|
||||
GNCLoggerError(@"Data length mismatch. Expected size: > %lu, Data: %@", _serviceIDHash.length,
|
||||
realData);
|
||||
return bytesProcessed;
|
||||
}
|
||||
|
||||
// Extract the service ID prefix from each data packet and validate it.
|
||||
NSUInteger prefixLength = _serviceIDHash.length;
|
||||
if (![[realData subdataWithRange:NSMakeRange(0, prefixLength)] isEqual:_serviceIDHash]) {
|
||||
return bytesProcessed;
|
||||
}
|
||||
// Extract the service ID prefix from each data packet and validate it.
|
||||
NSUInteger prefixLength = _serviceIDHash.length;
|
||||
if (![[realData subdataWithRange:NSMakeRange(0, prefixLength)] isEqual:_serviceIDHash]) {
|
||||
return bytesProcessed;
|
||||
}
|
||||
|
||||
dispatch_async(_selfQueue, ^{
|
||||
[_stream sendData:PrefixLengthData(GNCMGenerateBLEFramesPacketAcknowledgementPacket(
|
||||
_serviceIDHash, realData.length))
|
||||
completionBlock:^(BOOL result){
|
||||
}];
|
||||
});
|
||||
if (_connectionHandlers.payloadHandler) {
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
_connectionHandlers.payloadHandler([NSData
|
||||
dataWithData:[realData subdataWithRange:NSMakeRange(prefixLength,
|
||||
realData.length - prefixLength)]]);
|
||||
dispatch_async(_selfQueue, ^{
|
||||
[_stream sendData:PrefixLengthData(GNCMGenerateBLEFramesPacketAcknowledgementPacket(
|
||||
_serviceIDHash, realData.length))
|
||||
completionBlock:^(BOOL result){
|
||||
}];
|
||||
});
|
||||
if (_connectionHandlers.payloadHandler) {
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
_connectionHandlers.payloadHandler(
|
||||
[realData subdataWithRange:NSMakeRange(prefixLength, realData.length - prefixLength)]);
|
||||
});
|
||||
}
|
||||
}
|
||||
return bytesProcessed;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCMBleConnection.h"
|
||||
|
||||
#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.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"
|
||||
@@ -58,14 +59,18 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
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];
|
||||
if (GNCFeatureFlags.refactorBleL2capEnabled) {
|
||||
packet = [NSMutableData dataWithData:data];
|
||||
} else {
|
||||
// Prefix the service ID hash.
|
||||
packet = [NSMutableData dataWithData:_serviceIDHash];
|
||||
[packet appendData:data];
|
||||
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
|
||||
@@ -100,46 +105,55 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}
|
||||
|
||||
- (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");
|
||||
if (GNCFeatureFlags.refactorBleL2capEnabled) {
|
||||
dispatch_async(_selfQueue, ^{
|
||||
if (_connectionHandlers.payloadHandler) {
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
_connectionHandlers.payloadHandler([data copy]);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
NSData *introData = GNCMGenerateBLEFramesIntroductionPacket(_serviceIDHash);
|
||||
if ([data isEqual:introData]) {
|
||||
_receivedIntroPacket = YES;
|
||||
});
|
||||
} else {
|
||||
// Extract the service ID prefix from each data packet.
|
||||
NSData *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 {
|
||||
GNCLoggerInfo(@"[NEARBY] Input stream: Received wrong intro packet and discarded");
|
||||
NSData *introData = GNCMGenerateBLEFramesIntroductionPacket(_serviceIDHash);
|
||||
if ([data isEqual:introData]) {
|
||||
_receivedIntroPacket = YES;
|
||||
} else {
|
||||
GNCLoggerInfo(@"[NEARBY] Input stream: Received wrong intro packet and discarded");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
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);
|
||||
});
|
||||
if (![[data subdataWithRange:NSMakeRange(0, prefixLength)] isEqual:_serviceIDHash]) {
|
||||
GNCLoggerInfo(@"[NEARBY] Input stream: Received wrong data packet and discarded");
|
||||
return;
|
||||
}
|
||||
});
|
||||
packet = [data subdataWithRange:NSMakeRange(prefixLength, data.length - prefixLength)];
|
||||
|
||||
dispatch_async(_selfQueue, ^{
|
||||
if (_connectionHandlers.payloadHandler) {
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
_connectionHandlers.payloadHandler(packet);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "internal/platform/implementation/ble.h"
|
||||
#include "internal/platform/implementation/bluetooth_adapter.h"
|
||||
|
||||
#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTCharacteristic.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTClient.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h"
|
||||
@@ -628,8 +629,10 @@ std::unique_ptr<api::ble::BleSocket> BleMedium::Connect(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Send the (empty) intro packet, which the BLE advertiser is expecting.
|
||||
socket->GetOutputStream().Write(ByteArray());
|
||||
if (!GNCFeatureFlags.refactorBleL2capEnabled) {
|
||||
// Send the (empty) intro packet, which the BLE advertiser is expecting.
|
||||
socket->GetOutputStream().Write(ByteArray());
|
||||
}
|
||||
return std::move(socket);
|
||||
}
|
||||
|
||||
@@ -658,17 +661,22 @@ std::unique_ptr<api::ble::BleL2capSocket> BleMedium::ConnectOverL2cap(
|
||||
serviceID:@(service_id_str.c_str())
|
||||
incomingConnection:NO
|
||||
callbackQueue:connection_callback_queue_];
|
||||
// Blocked call to wait for the packet validation result.
|
||||
// TODO: b/419654808 - Remove this once the packet validation is moved to the
|
||||
// Connections layer.
|
||||
[connection requestDataConnectionWithCompletion:^(BOOL result) {
|
||||
if (result) {
|
||||
socket = std::make_unique<BleL2capSocket>(connection, peripheral_id);
|
||||
}
|
||||
GNCLoggerInfo(result ? @"[NEARBY] Request data connection is ok"
|
||||
: @"[NEARBY] Request data connection is not ok");
|
||||
if (GNCFeatureFlags.refactorBleL2capEnabled) {
|
||||
socket = std::make_unique<BleL2capSocket>(connection, peripheral_id);
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
} else {
|
||||
// Blocked call to wait for the packet validation result.
|
||||
// TODO: b/419654808 - Remove this once the packet validation is moved to the
|
||||
// Connections layer.
|
||||
[connection requestDataConnectionWithCompletion:^(BOOL result) {
|
||||
if (result) {
|
||||
socket = std::make_unique<BleL2capSocket>(connection, peripheral_id);
|
||||
}
|
||||
GNCLoggerInfo(result ? @"[NEARBY] Request data connection is ok"
|
||||
: @"[NEARBY] Request data connection is not ok");
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
}
|
||||
}];
|
||||
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC);
|
||||
if (dispatch_semaphore_wait(semaphore, timeout) != 0) {
|
||||
|
||||
Reference in New Issue
Block a user