Implementation of BLE L2CAP connecting flow.

PiperOrigin-RevId: 749092197
This commit is contained in:
Edwin Wu
2025-04-18 11:26:54 -07:00
committed by Copybara-Service
parent 24b8cff1a7
commit 4911d49f82
12 changed files with 112 additions and 5 deletions
+8 -2
View File
@@ -284,8 +284,14 @@ BleV2Socket BleV2Medium::Connect(const std::string& service_id,
BleL2capSocket BleV2Medium::ConnectOverL2cap(
const std::string& service_id, TxPowerLevel tx_power_level,
const BleV2Peripheral& peripheral, CancellationFlag* cancellation_flag) {
// TODO(mingshiouwu): Replace with a real implementation connecting flow.
return BleL2capSocket(peripheral, nullptr);
BleL2capSocket socket;
peripheral.GetImpl([&](api::ble_v2::BlePeripheral& device) {
socket = BleL2capSocket(
peripheral,
impl_->ConnectOverL2cap(peripheral.GetPsm(), service_id, tx_power_level,
device, cancellation_flag));
});
return socket;
}
bool BleV2Medium::IsExtendedAdvertisementsAvailable() {
+1
View File
@@ -336,6 +336,7 @@ class GattClient final {
class BleL2capSocket final {
public:
BleL2capSocket() = default;
BleL2capSocket(BleV2Peripheral peripheral,
std::unique_ptr<api::ble_v2::BleL2capSocket> socket)
: peripheral_(peripheral) {}
@@ -45,6 +45,14 @@ typedef void (^GNCRequestDisconnectionHandler)(id<GNCPeripheral> peripheral);
- (instancetype)initWithPeripheral:(id<GNCPeripheral>)peripheral
requestDisconnectionHandler:(GNCRequestDisconnectionHandler)requestDisconnectionHandler;
/**
* Opens a L2CAP channel with the @c PSM.
*
* @param PSM The PSM to use for opening the L2CAP channel.
*/
// TODO: b/399815436 - Add CompletionHandler for this method.
- (void)openL2CAPChannelWithPSM:(uint16_t)PSM;
/** Cancels an active or pending local connection to a peripheral. */
- (void)disconnect;
@@ -32,7 +32,7 @@ static char *const kGNCBLEL2CAPClientQueueLabel = "com.googlenearby.GNCBLEL2CAPC
GNCRequestDisconnectionHandler _requestDisconnectionHandler;
// The L2CAP channel that is used to send and receive data.
CBL2CAPChannel *_channel;
CBL2CAPChannel *_l2CAPChannel;
}
- (instancetype)initWithPeripheral:(id<GNCPeripheral>)peripheral
@@ -58,6 +58,10 @@ static char *const kGNCBLEL2CAPClientQueueLabel = "com.googlenearby.GNCBLEL2CAPC
return self;
};
- (void)openL2CAPChannelWithPSM:(uint16_t)PSM {
[_peripheral openL2CAPChannelWithPSM:PSM];
}
- (void)disconnect {
dispatch_async(_queue, ^{
_requestDisconnectionHandler(_peripheral);
@@ -74,7 +78,7 @@ static char *const kGNCBLEL2CAPClientQueueLabel = "com.googlenearby.GNCBLEL2CAPC
return;
}
GTMLoggerInfo(@"[NEARBY] Opened L2CAP channel: %@", channel);
_channel = channel;
_l2CAPChannel = channel;
// TODO: b/399815436 - Implement to wrap up l2cap channel.
}
@@ -211,6 +211,15 @@ typedef void (^GNCOpenL2CAPServerCompletionHandler)(GNCBLEL2CAPServer *_Nullable
- (void)openL2CAPServerWithCompletionHandler:(GNCOpenL2CAPServerCompletionHandler)completionHandler
peripheralManager:(nullable id<GNCPeripheralManager>)peripheralManager;
/**
* Opens a L2CAP channel with the @c PSM on the remote peripheral.
*
* @param PSM The PSM to use for opening the L2CAP channel.
* @param remotePeripheral The peripheral to which the L2CAP channel is being opened.
*/
// TODO: b/399815436 - Add CompletionHandler for this method.
- (void)openL2CAPChannelWithPSM:(uint16_t)PSM peripheral:(id<GNCPeripheral>)remotePeripheral;
@end
NS_ASSUME_NONNULL_END
@@ -21,6 +21,7 @@
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h"
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h"
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEL2CAPServer.h"
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEL2CAPClient.h"
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.h"
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h"
#import "internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCBase85.h"
@@ -54,6 +55,7 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer(
// The active GATT server, or @nil if one hasn't been started yet.
GNCBLEGATTServer *_server;
GNCBLEL2CAPServer *_l2capServer;
GNCBLEL2CAPClient *_l2capClient;
// The services that is being actively scanned for.
NSMutableArray<CBUUID *> *_scanningServiceUUIDs;
@@ -223,6 +225,18 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer(
});
}
- (void)openL2CAPChannelWithPSM:(uint16_t)PSM peripheral:(id<GNCPeripheral>)remotePeripheral {
dispatch_async(_queue, ^{
if (!_l2capClient) {
_l2capClient =
[[GNCBLEL2CAPClient alloc] initWithPeripheral:remotePeripheral
requestDisconnectionHandler:^(id<GNCPeripheral> _Nonnull peripheral){
}];
}
[_l2capClient openL2CAPChannelWithPSM:PSM];
});
}
#pragma mark - Internal
- (void)internalStartScanningIfPoweredOn {
@@ -112,6 +112,18 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)readValueForCharacteristic:(CBCharacteristic *)characteristic;
/**
* Opens an L2CAP channel with the specified PSM.
*
* When you call this method to open an L2CAP channel, the peripheral calls the
* @c peripheral:didOpenL2CAPChannel:error: method of its delegate object. If the peripheral
* successfully opens the L2CAP channel, you can access it through the peripherals @c l2capChannels
* property.
*
* @param PSM The PSM value to use for the L2CAP channel.
*/
- (void)openL2CAPChannelWithPSM:(uint16_t)PSM;
@end
/**
@@ -55,6 +55,15 @@ NS_ASSUME_NONNULL_BEGIN
/** Similates a delay in all delegate calls by the specified amount. */
@property(nonatomic, readwrite) NSTimeInterval delegateDelay;
/**
* Similates a @c openL2CAPChannelWithPSM: error.
*
* Setting this error to a value other than @c nil will simulate a failure when calling
* @c openL2CAPChannelWithPSM: and will call the
* @c gnc_peripheral:didOpenL2CAPChannel:error: delegate method with the provided error.
*/
@property(nonatomic, nullable, readwrite) NSError *openL2CAPChannelError;
@end
NS_ASSUME_NONNULL_END
@@ -119,6 +119,18 @@ NS_ASSUME_NONNULL_BEGIN
}
}
- (void)openL2CAPChannelWithPSM:(uint16_t)PSM {
[self delayDelegateUsingBlock:^() {
if (_openL2CAPChannelError) {
CBL2CAPChannel *channel = [[CBL2CAPChannel alloc] init];
[peripheralDelegate gnc_peripheral:self
didOpenL2CAPChannel:channel
error:_openL2CAPChannelError];
}
// TODO: b/399815436 - Add testing for L2CAP channels if error is nil.
}];
}
@end
NS_ASSUME_NONNULL_END
@@ -147,6 +147,18 @@ class BleMedium : public api::ble_v2::BleMedium {
api::ble_v2::BlePeripheral &peripheral,
CancellationFlag *cancellation_flag) override;
// TODO(b/290385712): cancellation_flag support is not yet implemented.
//
// Connects to a BLE peripheral over L2CAP.
//
// The peripheral must outlive the socket or undefined behavior will occur. The peripheral
// should not be modified by this method.
//
// On success, returns a new BleL2capSocket. On error, returns nullptr.
std::unique_ptr<api::ble_v2::BleL2capSocket> ConnectOverL2cap(
int psm, const std::string &service_id, api::ble_v2::TxPowerLevel tx_power_level,
api::ble_v2::BlePeripheral &peripheral, CancellationFlag *cancellation_flag) override;
// Returns whether the hardware supports BOTH advertising extensions and extended scans.
//
// This is currently always false for all Apple hardware.
@@ -33,6 +33,7 @@
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h"
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.h"
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h"
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEL2CAPClient.h"
#import "internal/platform/implementation/apple/ble_gatt_client.h"
#import "internal/platform/implementation/apple/ble_gatt_server.h"
#import "internal/platform/implementation/apple/ble_l2cap_server_socket.h"
@@ -461,6 +462,24 @@ std::unique_ptr<api::ble_v2::BleSocket> BleMedium::Connect(const std::string &se
return std::move(socket);
}
std::unique_ptr<api::ble_v2::BleL2capSocket> BleMedium::ConnectOverL2cap(
int psm, const std::string &service_id, api::ble_v2::TxPowerLevel tx_power_level,
api::ble_v2::BlePeripheral &peripheral, CancellationFlag *cancellation_flag) {
// Check that the @c api::ble_v2::BlePeripheral is a @c nearby::apple::BlePeripheral and not a
// @c nearby::apple::EmptyBlePeripheral instance, so we can retreive the CBPeripheral object.
BlePeripheral *non_empty_peripheral = dynamic_cast<BlePeripheral *>(&peripheral);
if (non_empty_peripheral == nullptr) {
GTMLoggerError(@"[NEARBY] Failed to connect over L2CAP: peripheral is empty.");
return nullptr;
}
// TODO: b/399815436 - Continue to add implementation for this method when BleL2capSocket is
// ready.
[medium_ openL2CAPChannelWithPSM:psm peripheral:non_empty_peripheral->GetPeripheral()];
return nullptr;
}
bool BleMedium::IsExtendedAdvertisementsAvailable() {
return [medium_ supportsExtendedAdvertisements];
}
+2 -1
View File
@@ -589,8 +589,9 @@ class BleMedium {
//
// On success, returns a new BleL2capSocket.
// On error, returns nullptr.
// Platform implementation should override this method if it supports L2CAP.
virtual std::unique_ptr<BleL2capSocket> ConnectOverL2cap(
const std::string& service_id, TxPowerLevel tx_power_level,
int psm, const std::string& service_id, TxPowerLevel tx_power_level,
BlePeripheral& peripheral, CancellationFlag* cancellation_flag) {
return nullptr;
}