From 75c94db1f87e418b63f4087c096756a4bb4d8bdd Mon Sep 17 00:00:00 2001 From: Mingshiou Wu Date: Tue, 10 Jun 2025 01:23:28 -0700 Subject: [PATCH] Refactor the logic of L2CAP packet handling. - Refactor the logic of L2CAP packet handling. - Add a way to response empty advertisement to fail the L2CAP request immediately. - Reduce unnessary logs. PiperOrigin-RevId: 769506649 --- .../Mediums/BLEv2/GNCBLEL2CAPConnection.m | 113 ++++++++++-------- .../apple/Mediums/Ble/GNCMBleUtils.mm | 2 - 2 files changed, 61 insertions(+), 54 deletions(-) diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEL2CAPConnection.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEL2CAPConnection.m index 85954cef..cc0430c4 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEL2CAPConnection.m +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEL2CAPConnection.m @@ -101,7 +101,7 @@ static NSData *PrefixLengthData(NSData *data) { // Prefix the service ID hash. packet = PrefixLengthData(PrefixDataWithServiceIDHash(_serviceIDHash, data)); if (_verboseLoggingEnabled) { - GNCLoggerDebug(@"[NEARBY] GNCBLEL2CAPConnection data to be sent: %@", [packet description]); + GNCLoggerDebug(@"GNCBLEL2CAPConnection data to be sent: %@", [packet description]); } [_stream sendData:packet completionBlock:^(BOOL result) { @@ -113,7 +113,7 @@ static NSData *PrefixLengthData(NSData *data) { } - (void)requestDataConnectionWithCompletion:(void (^)(BOOL))completion { - GNCLoggerInfo(@"[NEARBY] Sending l2cap packet request data connection"); + GNCLoggerInfo(@"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. @@ -141,7 +141,7 @@ static NSData *PrefixLengthData(NSData *data) { - (void)stream:(GNCBLEL2CAPStream *)stream didReceiveData:(NSData *)data { if (_verboseLoggingEnabled) { - GNCLoggerDebug(@"[NEARBY] BLEL2CAPConnection didReceiveData, data: %@, length: %lu", + GNCLoggerDebug(@"BLEL2CAPConnection didReceiveData, data: %@, length: %lu", [data debugDescription], data.length); } if (!data.length) { @@ -193,28 +193,27 @@ static NSData *PrefixLengthData(NSData *data) { } bytesProcessed = realData.length + kL2CAPPacketLength; - // Validate the L2CAP packet. // TODO: b/399815436 - Refactor the validation logic to connections layer. - if (_incomingConnection) { - if (!_handledIncomingConnectionL2CAPPacket) { - [self handleIncomingConnectionL2CAPPacketFromData:realData]; - return bytesProcessed; - } - if (!_handledReceivedBLEIntroPacket) { - [self handleBLEIntroPacketFromData:realData]; - return bytesProcessed; - } - } else { - if (!_handledOutgoingConnectionL2CAPPacket) { - [self handleOutgoingConnectionL2CAPPacketFromData:realData]; - return bytesProcessed; - } + 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; + } + + 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]) { - GNCLoggerError(@"[NEARBY]: Received wrong data packet and discarded"); return bytesProcessed; } @@ -254,40 +253,22 @@ static NSData *PrefixLengthData(NSData *data) { } } -- (void)handleIncomingConnectionL2CAPPacketFromData:(NSData *)data { +- (BOOL)handleL2CAPPacketFromData:(NSData *)data { GNCMBLEL2CAPPacket *l2capPacket = GNCMParseBLEL2CAPPacket(data); if (!l2capPacket) { - return; + return NO; } - switch (l2capPacket.command) { - case GNCMBLEL2CAPCommandRequestDataConnection: { - dispatch_async(_selfQueue, ^{ - [_stream sendData:PrefixLengthData(GNCMGenerateBLEL2CAPPacket( - GNCMBLEL2CAPCommandResponseDataConnectionReady, nil)) - completionBlock:^(BOOL result){ - }]; - }); - _handledReceivedL2CAPRequestDataConnectionPacket = YES; - } break; - case GNCMBLEL2CAPCommandRequestAdvertisement: - case GNCMBLEL2CAPCommandRequestAdvertisementFinish: - case GNCMBLEL2CAPCommandResponseAdvertisement: - case GNCMBLEL2CAPCommandResponseServiceIdNotFound: - case GNCMBLEL2CAPCommandResponseDataConnectionReady: - case GNCMBLEL2CAPCommandResponseDataConnectionFailure: - default: - break; // fall through - } - _handledIncomingConnectionL2CAPPacket = _handledReceivedL2CAPRequestDataConnectionPacket; -} -- (void)handleOutgoingConnectionL2CAPPacketFromData:(NSData *)data { - GNCMBLEL2CAPPacket *l2capPacket = GNCMParseBLEL2CAPPacket(data); - if (!l2capPacket) { - return; - } + GNCLoggerInfo(@"Received L2CAP packet with command: %lu", l2capPacket.command); + switch (l2capPacket.command) { case GNCMBLEL2CAPCommandResponseDataConnectionReady: { + if (_incomingConnection || _handledOutgoingConnectionL2CAPPacket) { + GNCLoggerError(@"Ignore L2CAP response data connection ready packet. " + @"incomingConnection: %d, handledOutgoingConnectionL2CAPPacket: %d", + _incomingConnection, _handledOutgoingConnectionL2CAPPacket); + break; + } [_requestDataConnectionCondition lock]; dispatch_async(_selfQueue, ^{ [_stream sendData:PrefixLengthData(GNCMGenerateBLEFramesIntroductionPacket(_serviceIDHash)) @@ -297,9 +278,37 @@ static NSData *PrefixLengthData(NSData *data) { }]; }); _handledReceivedL2CAPResponseDataConnectionReadyPacket = YES; + _handledOutgoingConnectionL2CAPPacket = + _handledReceivedL2CAPResponseDataConnectionReadyPacket; + + } break; + case GNCMBLEL2CAPCommandRequestDataConnection: { + if (!_incomingConnection || _handledIncomingConnectionL2CAPPacket) { + GNCLoggerError(@"Ignore L2CAP request data connection packet. " + @"incomingConnection: %d, handledIncomingConnectionL2CAPPacket: %d", + _incomingConnection, _handledIncomingConnectionL2CAPPacket); + break; + } + dispatch_async(_selfQueue, ^{ + [_stream sendData:PrefixLengthData(GNCMGenerateBLEL2CAPPacket( + GNCMBLEL2CAPCommandResponseDataConnectionReady, nil)) + completionBlock:^(BOOL result){ + }]; + }); + _handledReceivedL2CAPRequestDataConnectionPacket = YES; + _handledIncomingConnectionL2CAPPacket = _handledReceivedL2CAPRequestDataConnectionPacket; + } break; + case GNCMBLEL2CAPCommandRequestAdvertisement: { + // Return an empty advertisement response to fail the request immediately. + // TODO b/399815436 : Consider returning the real advertisement data in the future after + // refactoring the flow to mediums layer if we want to support it. + dispatch_async(_selfQueue, ^{ + [_stream sendData:PrefixLengthData(GNCMGenerateBLEL2CAPPacket( + GNCMBLEL2CAPCommandResponseAdvertisement, nil)) + completionBlock:^(BOOL result){ + }]; + }); } break; - case GNCMBLEL2CAPCommandRequestAdvertisement: - case GNCMBLEL2CAPCommandRequestDataConnection: case GNCMBLEL2CAPCommandRequestAdvertisementFinish: case GNCMBLEL2CAPCommandResponseAdvertisement: case GNCMBLEL2CAPCommandResponseServiceIdNotFound: @@ -307,16 +316,16 @@ static NSData *PrefixLengthData(NSData *data) { default: break; // fall through } - _handledOutgoingConnectionL2CAPPacket = _handledReceivedL2CAPResponseDataConnectionReadyPacket; + return YES; } - (void)handleBLEIntroPacketFromData:(NSData *)data { NSData *serviceIDHash = GNCMParseBLEFramesIntroductionPacket(data); if ([serviceIDHash isEqual:_serviceIDHash]) { _handledReceivedBLEIntroPacket = YES; - GNCLoggerInfo(@"[NEARBY]: Received BLE intro packet and handled."); + GNCLoggerInfo(@"Received BLE intro packet and handled."); } else { - GNCLoggerInfo(@"[NEARBY]: Received wrong BLE intro packet and discarded."); + GNCLoggerInfo(@"Received wrong BLE intro packet and discarded."); } } diff --git a/internal/platform/implementation/apple/Mediums/Ble/GNCMBleUtils.mm b/internal/platform/implementation/apple/Mediums/Ble/GNCMBleUtils.mm index 66051fc5..5e640839 100644 --- a/internal/platform/implementation/apple/Mediums/Ble/GNCMBleUtils.mm +++ b/internal/platform/implementation/apple/Mediums/Ble/GNCMBleUtils.mm @@ -116,7 +116,6 @@ NSData *GNCMGenerateBLEFramesDisconnectionPacket(NSData *serviceIDHash) { // TODO: b/399815436 - Add unit tests for this function. GNCMBLEL2CAPPacket *_Nullable GNCMParseBLEL2CAPPacket(NSData *data) { if (data.length < 1) { - GNCLoggerError(@"[NEARBY] Invalid packet length: %@", @(data.length)); return nil; } @@ -125,7 +124,6 @@ GNCMBLEL2CAPPacket *_Nullable GNCMParseBLEL2CAPPacket(NSData *data) { NSUInteger receivedDataLength = [data length]; GNCMBLEL2CAPCommand command = (GNCMBLEL2CAPCommand)bytes[0]; if (!IsSupportedCommand(command)) { - GNCLoggerError(@"[NEARBY] Invalid command: %lu", command); return nil; }