Add request data connection before data transmission for L2CAP.

PiperOrigin-RevId: 753434675
This commit is contained in:
Edwin Wu
2025-04-30 21:55:40 -07:00
committed by Copybara-Service
parent fc0768f2c4
commit e9aef7153a
3 changed files with 61 additions and 6 deletions
@@ -47,6 +47,14 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)sendData:(NSData *)payload completion:(void (^)(BOOL))completion;
/**
* Requests data connection from the remote device.
*
* @param completion A block that is called when the data connection is ready. The block takes a
* BOOL parameter indicating whether the data connection is ready.
*/
- (void)requestDataConnectionWithCompletion:(void (^)(BOOL))completion;
@end
NS_ASSUME_NONNULL_END
@@ -23,6 +23,8 @@
#import "GoogleToolboxForMac/GTMLogger.h"
enum { kL2CAPPacketLength = 4 };
static const CGFloat kRequestDataConnectionDelayInSeconds = 0.0;
static const UInt8 kRequestDataConnectionTimeoutInSeconds = 5;
static char *const kGNCBLEL2CAPConnectionQueueLabel = "com.google.nearby.GNCBLEL2CAPConnection";
@@ -60,6 +62,7 @@ static NSData *PrefixLengthData(NSData *data) {
@property(nonatomic) NSUInteger expectedDataLength;
@property(nonatomic) NSMutableData *undeliveredData;
@property(nonatomic) BOOL verboseLoggingEnabled;
@property(nonatomic) NSCondition *requestDataConnectionCondition;
@end
@implementation GNCBLEL2CAPConnection
@@ -80,6 +83,7 @@ static NSData *PrefixLengthData(NSData *data) {
[connection setIncomingConnection:incomingConnection];
[connection setUndeliveredData:[NSMutableData data]];
[connection setExpectedDataLength:0];
[connection setRequestDataConnectionCondition:[[NSCondition alloc] init]];
return connection;
}
@@ -108,6 +112,31 @@ static NSData *PrefixLengthData(NSData *data) {
});
}
- (void)requestDataConnectionWithCompletion:(void (^)(BOOL))completion {
GTMLoggerInfo(@"[NEARBY] Sending l2cap packet request data connection");
// TODO b/399815436 - A bug is causing channel has written to the socket but the remote does not
// receive it. Add a delay to make sure the data is written to the socket. Remove the delay once
// the bug is fixed.
dispatch_time_t requestTime =
dispatch_time(DISPATCH_TIME_NOW, kRequestDataConnectionDelayInSeconds * NSEC_PER_SEC);
dispatch_after(requestTime, _selfQueue, ^(void) {
[_requestDataConnectionCondition lock];
NSData *requestDataConnectionPacket =
GNCMGenerateBLEL2CAPPacket(GNCMBLEL2CAPCommandRequestDataConnection, nil);
[_stream sendData:PrefixLengthData(requestDataConnectionPacket)
completionBlock:^(BOOL result){
}];
dispatch_async(_callbackQueue, ^{
NSDate *requestDataConnectionTimeout =
[NSDate dateWithTimeIntervalSinceNow:kRequestDataConnectionTimeoutInSeconds];
BOOL result = [_requestDataConnectionCondition waitUntilDate:requestDataConnectionTimeout];
completion(result);
[_requestDataConnectionCondition unlock];
});
});
}
#pragma mark GNCBLEL2CAPStreamDelegate
- (void)stream:(GNCBLEL2CAPStream *)stream didReceiveData:(NSData *)data {
@@ -263,12 +292,15 @@ static NSData *PrefixLengthData(NSData *data) {
}
switch (l2capPacket.command) {
case GNCMBLEL2CAPCommandResponseDataConnectionReady: {
_handledReceivedL2CAPResponseDataConnectionReadyPacket = YES;
[_requestDataConnectionCondition lock];
dispatch_async(_selfQueue, ^{
[_stream sendData:PrefixLengthData(GNCMGenerateBLEFramesIntroductionPacket(_serviceIDHash))
completionBlock:^(BOOL result){
completionBlock:^(BOOL result) {
[_requestDataConnectionCondition broadcast];
[_requestDataConnectionCondition unlock];
}];
});
_handledReceivedL2CAPResponseDataConnectionReadyPacket = YES;
} break;
case GNCMBLEL2CAPCommandRequestAdvertisement:
case GNCMBLEL2CAPCommandRequestDataConnection:
@@ -55,6 +55,7 @@
#import "GoogleToolboxForMac/GTMLogger.h"
static NSString *const kWeaveServiceUUID = @"FEF3";
static const UInt8 kRequestConnectionTimeoutInSeconds = 10;
namespace nearby {
namespace apple {
@@ -492,8 +493,10 @@ std::unique_ptr<api::ble_v2::BleL2capSocket> BleMedium::ConnectOverL2cap(
}
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_time_t timeout =
dispatch_time(DISPATCH_TIME_NOW, kRequestConnectionTimeoutInSeconds * NSEC_PER_SEC);
__block std::unique_ptr<BleL2capSocket> socket;
std::string service_id_str = service_id;
const std::string &service_id_str = service_id;
[medium_ openL2CAPChannelWithPSM:psm
peripheral:non_empty_peripheral->GetPeripheral()
completionHandler:^(GNCBLEL2CAPStream *stream, NSError *error) {
@@ -506,10 +509,22 @@ std::unique_ptr<api::ble_v2::BleL2capSocket> BleMedium::ConnectOverL2cap(
serviceID:@(service_id_str.c_str())
incomingConnection:NO
callbackQueue:dispatch_get_main_queue()];
socket = std::make_unique<BleL2capSocket>(connection, non_empty_peripheral);
dispatch_semaphore_signal(semaphore);
// Blocked call to wait for the packet validation result.
// TODO: b/399815436 - Remove this once the packet validation is moved to the
// Connections layer.
[connection requestDataConnectionWithCompletion:^(BOOL result) {
if (result) {
socket = std::make_unique<BleL2capSocket>(connection, non_empty_peripheral);
}
GTMLoggerInfo(result ? @"[NEARBY] Request data connection is ok"
: @"[NEARBY] Request data connection is not ok");
dispatch_semaphore_signal(semaphore);
}];
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (dispatch_semaphore_wait(semaphore, timeout) != 0) {
GTMLoggerError(@"[NEARBY] Failed to connect over L2CAP: timeout.");
return nullptr;
}
if (socket == nullptr) {
return nullptr;
}