mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46:12 -04:00
Add OpenL2capServerSocket and skeleton BleL2capServerSocket/BleL2capSocket
PiperOrigin-RevId: 746334743
This commit is contained in:
committed by
Copybara-Service
parent
d999507674
commit
b10957d712
@@ -97,6 +97,9 @@ BleV2::~BleV2() {
|
||||
while (!server_sockets_.empty()) {
|
||||
StopAcceptingConnections(server_sockets_.begin()->first);
|
||||
}
|
||||
while (!l2cap_server_sockets_.empty()) {
|
||||
StopAcceptingL2capConnections(l2cap_server_sockets_.begin()->first);
|
||||
}
|
||||
|
||||
serial_executor_.Shutdown();
|
||||
alarm_executor_.Shutdown();
|
||||
@@ -175,7 +178,11 @@ ErrorOr<bool> BleV2::StartAdvertising(const std::string& service_id,
|
||||
// Wrap the connections advertisement to the medium advertisement.
|
||||
ByteArray service_id_hash = mediums::bleutils::GenerateHash(
|
||||
service_id, mediums::BleAdvertisement::kServiceIdHashLength);
|
||||
int psm = medium_.GetPSM();
|
||||
int psm = mediums::BleAdvertisementHeader::kDefaultPsmValue;
|
||||
const auto it = l2cap_server_sockets_.find(service_id);
|
||||
if (it != l2cap_server_sockets_.end()) {
|
||||
psm = it->second.GetPSM();
|
||||
}
|
||||
mediums::BleAdvertisement medium_advertisement = {
|
||||
mediums::BleAdvertisement::Version::kV2,
|
||||
mediums::BleAdvertisement::SocketVersion::kV2,
|
||||
@@ -677,7 +684,7 @@ ErrorOr<bool> BleV2::StartAcceptingL2capConnections(
|
||||
// Mark the fact that there's an in-progress Ble server accepting
|
||||
// connections.
|
||||
auto owned_server_socket =
|
||||
l2cap_server_socket_map_.insert({service_id, std::move(server_socket)})
|
||||
l2cap_server_sockets_.insert({service_id, std::move(server_socket)})
|
||||
.first->second;
|
||||
// Start the accept loop on a dedicated thread - this stays alive and
|
||||
// listening for new incoming connections until StopAcceptingConnections()
|
||||
@@ -701,7 +708,7 @@ ErrorOr<bool> BleV2::StartAcceptingL2capConnections(
|
||||
MutexLock lock(&mutex_);
|
||||
incoming_sockets_.erase(service_id);
|
||||
});
|
||||
l2cap_incoming_service_id_to_socket_map_.insert(
|
||||
l2cap_incoming_service_id_to_sockets_.insert(
|
||||
{service_id, client_socket});
|
||||
}
|
||||
if (callback) {
|
||||
@@ -757,8 +764,8 @@ bool BleV2::StopAcceptingConnections(const std::string& service_id) {
|
||||
bool BleV2::StopAcceptingL2capConnections(const std::string& service_id) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
const auto it = l2cap_server_socket_map_.find(service_id);
|
||||
if (it == l2cap_server_socket_map_.end()) {
|
||||
const auto it = l2cap_server_sockets_.find(service_id);
|
||||
if (it == l2cap_server_sockets_.end()) {
|
||||
LOG(INFO) << "Can't stop accepting Ble L2CAP connections because it was "
|
||||
"never started.";
|
||||
return false;
|
||||
@@ -768,15 +775,15 @@ bool BleV2::StopAcceptingL2capConnections(const std::string& service_id) {
|
||||
// in accept_loops_thread_pool_ that blocks on BleL2capServerSocket.accept().
|
||||
// That may take some time to complete, but there's no particular reason to
|
||||
// wait around for it.
|
||||
auto item = l2cap_server_socket_map_.extract(it);
|
||||
auto item = l2cap_server_sockets_.extract(it);
|
||||
|
||||
// Store a handle to the BleL2capServerSocket, so we can use it after
|
||||
// removing the entry from l2cap_server_socket_map_; making it scoped
|
||||
// removing the entry from l2cap_server_sockets_; making it scoped
|
||||
// is a bonus that takes care of deallocation before we leave this method.
|
||||
BleL2capServerSocket& listening_socket = item.mapped();
|
||||
|
||||
// Regardless of whether or not we fail to close the existing
|
||||
// BleL2capServerSocket, remove it from l2cap_server_socket_map_ so that it
|
||||
// BleL2capServerSocket, remove it from l2cap_server_sockets_ so that it
|
||||
// frees up this service for another round.
|
||||
|
||||
// Finally, close the BleL2capServerSocket.
|
||||
@@ -898,7 +905,7 @@ bool BleV2::IsAcceptingConnectionsLocked(const std::string& service_id) {
|
||||
}
|
||||
|
||||
bool BleV2::IsAcceptingL2capConnectionsLocked(const std::string& service_id) {
|
||||
return l2cap_server_socket_map_.contains(service_id);
|
||||
return l2cap_server_sockets_.contains(service_id);
|
||||
}
|
||||
|
||||
bool BleV2::IsAdvertisementGattServerRunningLocked() {
|
||||
|
||||
@@ -333,14 +333,14 @@ class BleV2 final {
|
||||
|
||||
// A map of service_id -> L2capServerSocket. If map is non-empty, we
|
||||
// are currently listening for incoming connections.
|
||||
absl::flat_hash_map<std::string, BleL2capServerSocket>
|
||||
l2cap_server_socket_map_ ABSL_GUARDED_BY(mutex_);
|
||||
absl::flat_hash_map<std::string, BleL2capServerSocket> l2cap_server_sockets_
|
||||
ABSL_GUARDED_BY(mutex_);
|
||||
|
||||
// A map of service_id -> BleL2capSocket.
|
||||
// Tracks currently connected incoming sockets. This lets the device know when
|
||||
// it's okay to restart L2CAP server related operations.
|
||||
absl::flat_hash_map<std::string, BleL2capSocket>
|
||||
l2cap_incoming_service_id_to_socket_map_ ABSL_GUARDED_BY(mutex_);
|
||||
l2cap_incoming_service_id_to_sockets_ ABSL_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
|
||||
@@ -369,6 +369,12 @@ Status P2pClusterPcpHandler::StopAdvertisingImpl(ClientProxy* client) {
|
||||
config_package_nearby::nearby_connections_feature::kEnableBleV2)) {
|
||||
ble_v2_medium_.StopAdvertising(client->GetAdvertisingServiceId());
|
||||
ble_v2_medium_.StopAcceptingConnections(client->GetAdvertisingServiceId());
|
||||
if (NearbyFlags::GetInstance().GetBoolFlag(
|
||||
config_package_nearby::nearby_connections_feature::
|
||||
kEnableBleL2cap)) {
|
||||
ble_v2_medium_.StopAcceptingL2capConnections(
|
||||
client->GetAdvertisingServiceId());
|
||||
}
|
||||
} else {
|
||||
ble_medium_.StopAdvertising(client->GetAdvertisingServiceId());
|
||||
ble_medium_.StopAcceptingConnections(client->GetAdvertisingServiceId());
|
||||
@@ -1606,6 +1612,7 @@ P2pClusterPcpHandler::StartListeningForIncomingConnectionsImpl(
|
||||
}
|
||||
}
|
||||
}
|
||||
// wifi lan
|
||||
if (options.enable_wlan_listening &&
|
||||
!wifi_lan_medium_.IsAcceptingConnections(std::string(service_id))) {
|
||||
ErrorOr<bool> wifi_lan_result = wifi_lan_medium_.StartAcceptingConnections(
|
||||
|
||||
@@ -252,8 +252,7 @@ BleV2ServerSocket BleV2Medium::OpenServerSocket(const std::string& service_id) {
|
||||
|
||||
BleL2capServerSocket BleV2Medium::OpenL2capServerSocket(
|
||||
const std::string& service_id) {
|
||||
// TODO(mingshiouwu): Replace with a real implementation listening flow.
|
||||
return BleL2capServerSocket(*this, nullptr);
|
||||
return BleL2capServerSocket(*this, impl_->OpenL2capServerSocket(service_id));
|
||||
}
|
||||
|
||||
BleV2Socket BleV2Medium::Connect(const std::string& service_id,
|
||||
|
||||
@@ -191,8 +191,7 @@ class BleV2ServerSocket final {
|
||||
std::unique_ptr<api::ble_v2::BleSocket> socket = impl_->Accept();
|
||||
BleV2Peripheral peripheral;
|
||||
if (!socket) {
|
||||
NEARBY_LOGS(INFO) << "BleServerSocket Accept() failed on server socket: "
|
||||
<< this;
|
||||
LOG(INFO) << "BleServerSocket Accept() failed on server socket: " << this;
|
||||
} else {
|
||||
auto* platform_peripheral = socket->GetRemotePeripheral();
|
||||
if (platform_peripheral != nullptr) {
|
||||
@@ -204,7 +203,7 @@ class BleV2ServerSocket final {
|
||||
|
||||
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
|
||||
Exception Close() {
|
||||
NEARBY_LOGS(INFO) << "BleServerSocket Closing:: " << this;
|
||||
LOG(INFO) << "BleServerSocket Closing:: " << this;
|
||||
return impl_->Close();
|
||||
}
|
||||
|
||||
@@ -381,6 +380,9 @@ class BleL2capServerSocket final {
|
||||
std::unique_ptr<api::ble_v2::BleL2capServerSocket> socket)
|
||||
: impl_(std::move(socket)) {}
|
||||
|
||||
// Gets PSM value has been published by the server.
|
||||
int GetPSM() { return impl_->GetPSM(); }
|
||||
|
||||
// Accepts an incoming connection.
|
||||
BleL2capSocket Accept() {
|
||||
std::unique_ptr<api::ble_v2::BleL2capSocket> socket = impl_->Accept();
|
||||
@@ -545,12 +547,6 @@ class BleV2Medium final {
|
||||
impl_->AddAlternateUuidForService(uuid, service_id);
|
||||
}
|
||||
|
||||
// Returns PSM value.
|
||||
int GetPSM() {
|
||||
// TODO(mingshiouwu): Replace with real implementation.
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex mutex_;
|
||||
std::unique_ptr<api::ble_v2::BleMedium> impl_;
|
||||
|
||||
@@ -87,6 +87,10 @@ objc_library(
|
||||
"ble_gatt_client.mm",
|
||||
"ble_gatt_server.h",
|
||||
"ble_gatt_server.mm",
|
||||
"ble_l2cap_server_socket.h",
|
||||
"ble_l2cap_server_socket.mm",
|
||||
"ble_l2cap_socket.h",
|
||||
"ble_l2cap_socket.mm",
|
||||
"ble_medium.h",
|
||||
"ble_medium.mm",
|
||||
"ble_peripheral.h",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#import <CoreBluetooth/CoreBluetooth.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class GNCBLEL2CAPStream;
|
||||
@protocol GNCPeripheralManager;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEL2CAPStream.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheralManager.h"
|
||||
#import "GoogleToolboxForMac/GTMLogger.h"
|
||||
|
||||
@@ -36,7 +37,7 @@ static char *const kGNCBLEL2CAPServerQueueLabel = "com.google.nearby.GNCBLEL2CAP
|
||||
GNCStartListeningL2CAPChannelCompletionHandler _startListeningL2CAPChannelcompletionHandler;
|
||||
|
||||
CBL2CAPChannel *_l2CAPChannel;
|
||||
|
||||
GNCBLEL2CAPStream *_l2CAPStream;
|
||||
/// Whether start call has been performed when the peripheral was off.
|
||||
BOOL _alreadyStartedWhenPeripheralPoweredOff;
|
||||
}
|
||||
@@ -141,6 +142,8 @@ static char *const kGNCBLEL2CAPServerQueueLabel = "com.google.nearby.GNCBLEL2CAP
|
||||
if (error) {
|
||||
GTMLoggerError(@"[NEARBY] Failed to unpublish L2CAP channel: %@", error);
|
||||
}
|
||||
[_l2CAPStream tearDown];
|
||||
_l2CAPStream = nil;
|
||||
_PSM = 0;
|
||||
}
|
||||
|
||||
@@ -155,7 +158,7 @@ static char *const kGNCBLEL2CAPServerQueueLabel = "com.google.nearby.GNCBLEL2CAP
|
||||
}
|
||||
|
||||
// Cleanup older references.
|
||||
if (_l2CAPChannel) {
|
||||
if (_l2CAPStream || _l2CAPChannel) {
|
||||
// The device may establish a new L2CAP socket connection while the old socket is still
|
||||
// connected if sysproxy stopped for a reason other than Bluetooth disconnection. Closing the
|
||||
// channel here ensures the server and the client state is reset between the two
|
||||
@@ -202,6 +205,8 @@ static char *const kGNCBLEL2CAPServerQueueLabel = "com.google.nearby.GNCBLEL2CAP
|
||||
}
|
||||
|
||||
- (void)closeL2CAPChannel {
|
||||
[_l2CAPStream tearDown];
|
||||
_l2CAPStream = nil;
|
||||
_l2CAPChannel = nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class GNCBLEL2CAPStream;
|
||||
|
||||
/// Block invoked when the stream is closed.
|
||||
typedef void (^GNCBLEL2CAPStreamClosedBlock)(void);
|
||||
|
||||
@@ -52,6 +50,9 @@ typedef void (^GNCBLEL2CAPControllerReceivedDataBlock)(NSData *data);
|
||||
/// stream is torn down.
|
||||
- (void)sendData:(NSData *)data completionBlock:(void (^)(BOOL))completionBlock;
|
||||
|
||||
/// Closes the stream.
|
||||
- (void)close;
|
||||
|
||||
/// Tears down the stream.
|
||||
- (void)tearDown;
|
||||
|
||||
|
||||
@@ -39,12 +39,12 @@
|
||||
|
||||
@interface GNCBLEL2CAPStream () <NSStreamDelegate>
|
||||
|
||||
/// Input stream from the watch. Operations to this stream are synchronized by |_streamQueue|
|
||||
/// Input stream from the device. Operations to this stream are synchronized by |_streamQueue|
|
||||
/// dispatch queue.
|
||||
@property(nonatomic, nullable) NSInputStream *inputStream;
|
||||
|
||||
/// Output stream to the watch. Operations to this stream are synchronized by synchronized access on
|
||||
/// |_writeBufferArray|.
|
||||
/// Output stream to the device. Operations to this stream are synchronized by synchronized access
|
||||
/// on |_writeBufferArray|.
|
||||
@property(nonatomic, nullable) NSOutputStream *outputStream;
|
||||
|
||||
@end
|
||||
@@ -56,7 +56,7 @@
|
||||
/// Serial queue used when invoking |_receivedDataBlock|.
|
||||
dispatch_queue_t _receivedDataQueue;
|
||||
|
||||
/// Queue used exclusively from events on |toWatchStream| and |fromWatchStream|.
|
||||
/// Queue used exclusively from events on |inputStream| and |outputStream|.
|
||||
dispatch_queue_t _streamQueue;
|
||||
|
||||
/// Pending data to be written to the remote device, synchronized access on itself.
|
||||
@@ -99,6 +99,10 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
_closedBlock();
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
dispatch_async(_streamQueue, ^{
|
||||
GTMLoggerDebug(@"[NEARBY] Closing inputStream %@ by tearDown", self.inputStream);
|
||||
@@ -300,7 +304,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
/// Receives data from watch and invokes |_receivedDataBlock|.
|
||||
/// Receives data from device and invokes |_receivedDataBlock|.
|
||||
- (void)receiveStreamData {
|
||||
dispatch_assert_queue_debug(_streamQueue);
|
||||
|
||||
@@ -312,7 +316,7 @@
|
||||
[data appendBytes:readBuffer length:(NSUInteger)bytesRead];
|
||||
|
||||
if (_verboseLoggingEnabled) {
|
||||
GTMLoggerDebug(@"[NEARBY] Stream data from watch of length %@", @(data.length));
|
||||
GTMLoggerDebug(@"[NEARBY] Stream data from device of length %@", @(data.length));
|
||||
}
|
||||
|
||||
dispatch_async(_receivedDataQueue, ^{
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2025 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.
|
||||
|
||||
// Note: File language is detected using heuristics. Many Objective-C++ headers
|
||||
// are incorrectly classified as C++ resulting in invalid linter errors. The use
|
||||
// of "NSArray" and other Foundation classes like "NSData", "NSDictionary" and
|
||||
// "NSUUID" are highly weighted for Objective-C and Objective-C++ scores. Oddly,
|
||||
// "#import <Foundation/Foundation.h>" does not contribute any points. This
|
||||
// comment alone should be enough to trick the IDE in to believing this is
|
||||
// actually some sort of Objective-C file. See:
|
||||
// cs/google3/devtools/search/lang/recognize_language_classifiers_data
|
||||
|
||||
#include <memory>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEL2CAPServer.h"
|
||||
#import "internal/platform/implementation/apple/ble_l2cap_socket.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace apple {
|
||||
|
||||
// A BLE L2CAP server socket for listening incoming L2CAP socket.
|
||||
class BleL2capServerSocket : public api::ble_v2::BleL2capServerSocket {
|
||||
public:
|
||||
// Creates a BLE L2CAP server socket.
|
||||
//
|
||||
// @param l2cap_server The L2CAP server to use.
|
||||
explicit BleL2capServerSocket(GNCBLEL2CAPServer* l2cap_server);
|
||||
~BleL2capServerSocket() override = default;
|
||||
|
||||
// Gets PSM value has been published by the server.
|
||||
int GetPSM() const override;
|
||||
|
||||
// Blocks until either:
|
||||
// - at least one incoming connection request is available, or
|
||||
// - ServerSocket is closed.
|
||||
// On success, returns connected socket, ready to exchange data.
|
||||
// Returns nullptr on error.
|
||||
// Once error is reported, it is permanent, and L2CAP ServerSocket has to be
|
||||
// closed.
|
||||
std::unique_ptr<api::ble_v2::BleL2capSocket> Accept() override;
|
||||
|
||||
// Closes the L2CAP server socket.
|
||||
Exception Close() override;
|
||||
|
||||
private:
|
||||
// The L2CAP server to use for listening incoming L2CAP socket and publishing
|
||||
// PSM value.
|
||||
GNCBLEL2CAPServer* l2cap_server_;
|
||||
};
|
||||
|
||||
} // namespace apple
|
||||
} // namespace nearby
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2025 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/ble_l2cap_server_socket.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEL2CAPServer.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEL2CAPStream.h"
|
||||
#import "internal/platform/implementation/apple/ble_l2cap_socket.h"
|
||||
#import "GoogleToolboxForMac/GTMLogger.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace apple {
|
||||
|
||||
BleL2capServerSocket::BleL2capServerSocket(GNCBLEL2CAPServer* l2cap_server)
|
||||
: l2cap_server_(l2cap_server) {}
|
||||
|
||||
int BleL2capServerSocket::GetPSM() const { return [l2cap_server_ PSM]; }
|
||||
|
||||
std::unique_ptr<api::ble_v2::BleL2capSocket> BleL2capServerSocket::Accept() {
|
||||
// TODO: edwinwu - Implement to wrap up l2cap channel with |GNCBLEL2CAPStream|.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Exception BleL2capServerSocket::Close() {
|
||||
[l2cap_server_ close];
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
} // namespace apple
|
||||
} // namespace nearby
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright 2025 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.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEL2CAPStream.h"
|
||||
#import "internal/platform/implementation/apple/ble_peripheral.h"
|
||||
#include "internal/platform/implementation/ble_v2.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace apple {
|
||||
|
||||
/** A readable stream of bytes. */
|
||||
class BleL2capInputStream : public InputStream {
|
||||
public:
|
||||
// Creates a BleL2capInputStream.
|
||||
//
|
||||
// @param stream The underlying stream to use for reading and writing.
|
||||
explicit BleL2capInputStream(GNCBLEL2CAPStream* stream);
|
||||
~BleL2capInputStream() override = default;
|
||||
|
||||
// Reads at most `size` bytes from the input stream.
|
||||
//
|
||||
// Returns an empty byte array on end of file, or Exception::kIo on error.
|
||||
ExceptionOr<ByteArray> Read(std::int64_t size) override;
|
||||
|
||||
// Closes the stream preventing further reads.
|
||||
//
|
||||
// Returns Exception::kIo on error, otherwise Exception::kSuccess.
|
||||
Exception Close() override;
|
||||
|
||||
private:
|
||||
GNCBLEL2CAPStream* stream_;
|
||||
};
|
||||
|
||||
/** A writable stream of bytes. */
|
||||
class BleL2capOutputStream : public OutputStream {
|
||||
public:
|
||||
// Creates a BleL2capOutputStream.
|
||||
//
|
||||
// @param stream The underlying stream to use for reading and writing.
|
||||
explicit BleL2capOutputStream(GNCBLEL2CAPStream* stream);
|
||||
~BleL2capOutputStream() override = default;
|
||||
|
||||
// Write the provided bytes to the output stream.
|
||||
//
|
||||
// Returns Exception::kIo on error, otherwise Exception::kSuccess.
|
||||
Exception Write(const ByteArray& data) override;
|
||||
|
||||
// no-op
|
||||
//
|
||||
// Always returns Exception::kSuccess.
|
||||
Exception Flush() override;
|
||||
|
||||
// Closes the stream preventing further writes.
|
||||
//
|
||||
// Returns Exception::kIo on error, otherwise Exception::kSuccess.
|
||||
Exception Close() override;
|
||||
|
||||
private:
|
||||
GNCBLEL2CAPStream* stream_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Concrete BleL2capSocket implementation.
|
||||
*/
|
||||
class BleL2capSocket : public api::ble_v2::BleL2capSocket {
|
||||
public:
|
||||
explicit BleL2capSocket(GNCBLEL2CAPStream* stream);
|
||||
|
||||
// The peripheral used to create the socket must outlive the socket or
|
||||
// undefined behavior will occur.
|
||||
BleL2capSocket(GNCBLEL2CAPStream* stream,
|
||||
api::ble_v2::BlePeripheral* peripheral);
|
||||
~BleL2capSocket() override = default;
|
||||
|
||||
// Returns the InputStream of the BleL2capSocket.
|
||||
// On error, returned stream will report Exception::kIo on any operation.
|
||||
//
|
||||
// The returned object is not owned by the caller, and can be invalidated once
|
||||
// the BleL2capSocket object is destroyed.
|
||||
InputStream& GetInputStream() override;
|
||||
|
||||
// Returns the OutputStream of the BleL2capSocket.
|
||||
// On error, returned stream will report Exception::kIo on any operation.
|
||||
//
|
||||
// The returned object is not owned by the caller, and can be invalidated once
|
||||
// the BleL2capSocket object is destroyed.
|
||||
OutputStream& GetOutputStream() override;
|
||||
|
||||
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
|
||||
Exception Close() override;
|
||||
|
||||
// Sets the close notifier by client side.
|
||||
void SetCloseNotifier(absl::AnyInvocable<void()> notifier) override;
|
||||
|
||||
// Returns valid BlePeripheral pointer if there is a connection, and
|
||||
// nullptr otherwise.
|
||||
api::ble_v2::BlePeripheral* GetRemotePeripheral() override {
|
||||
return peripheral_;
|
||||
}
|
||||
|
||||
private:
|
||||
GNCBLEL2CAPStream* stream_;
|
||||
std::unique_ptr<BleL2capInputStream> input_stream_;
|
||||
std::unique_ptr<BleL2capOutputStream> output_stream_;
|
||||
api::ble_v2::BlePeripheral* peripheral_;
|
||||
};
|
||||
|
||||
} // namespace apple
|
||||
} // namespace nearby
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2025 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/ble_l2cap_socket.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEL2CAPStream.h"
|
||||
#import "GoogleToolboxForMac/GTMLogger.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace apple {
|
||||
|
||||
#pragma mark - BleL2capInputStream
|
||||
|
||||
BleL2capInputStream::BleL2capInputStream(GNCBLEL2CAPStream* stream) : stream_(stream) {
|
||||
GTMLoggerInfo(@"BleL2capInputStream::BleL2capInputStream");
|
||||
}
|
||||
|
||||
ExceptionOr<ByteArray> BleL2capInputStream::Read(std::int64_t size) {
|
||||
// TODO: edwinwu - Implement to read data from l2cap channel.
|
||||
return {Exception::kIo};
|
||||
}
|
||||
|
||||
Exception BleL2capInputStream::Close() {
|
||||
// The input stream reads directly from the connection. It can not be closed without closing the
|
||||
// connection itself. A call to `BleL2capSocket::Close` will close the connection.
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
#pragma mark - BleL2capOutputStream
|
||||
|
||||
BleL2capOutputStream::BleL2capOutputStream(GNCBLEL2CAPStream* stream) : stream_(stream) {}
|
||||
|
||||
Exception BleL2capOutputStream::Write(const ByteArray& data) {
|
||||
// TODO: edwinwu - Implement to write data to l2cap channel.
|
||||
return {Exception::kIo};
|
||||
}
|
||||
|
||||
Exception BleL2capOutputStream::Flush() { return {Exception::kSuccess}; }
|
||||
|
||||
Exception BleL2capOutputStream::Close() { return {Exception::kSuccess}; }
|
||||
|
||||
#pragma mark - BleL2capSocket
|
||||
|
||||
BleL2capSocket::BleL2capSocket(GNCBLEL2CAPStream* stream)
|
||||
: BleL2capSocket(stream, new EmptyBlePeripheral()) {}
|
||||
|
||||
BleL2capSocket::BleL2capSocket(GNCBLEL2CAPStream* stream, api::ble_v2::BlePeripheral* peripheral)
|
||||
: stream_(stream),
|
||||
input_stream_(std::make_unique<BleL2capInputStream>(stream)),
|
||||
output_stream_(std::make_unique<BleL2capOutputStream>(stream)),
|
||||
peripheral_(peripheral) {}
|
||||
|
||||
InputStream& BleL2capSocket::GetInputStream() { return *input_stream_; }
|
||||
|
||||
OutputStream& BleL2capSocket::GetOutputStream() { return *output_stream_; }
|
||||
|
||||
Exception BleL2capSocket::Close() {
|
||||
[stream_ close];
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
void BleL2capSocket::SetCloseNotifier(absl::AnyInvocable<void()> notifier) {}
|
||||
|
||||
} // namespace apple
|
||||
} // namespace nearby
|
||||
@@ -118,6 +118,12 @@ class BleMedium : public api::ble_v2::BleMedium {
|
||||
std::unique_ptr<api::ble_v2::BleServerSocket> OpenServerSocket(
|
||||
const std::string &service_id) override;
|
||||
|
||||
// Opens a L2CAP server socket based on service ID.
|
||||
//
|
||||
// On success, returns a new BleL2capServerSocket. On error, returns nullptr.
|
||||
std::unique_ptr<api::ble_v2::BleL2capServerSocket> OpenL2capServerSocket(
|
||||
const std::string &service_id) override;
|
||||
|
||||
// TODO(b/290385712): cancellation_flag support is not yet implemented.
|
||||
//
|
||||
// Connects to a BLE peripheral.
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.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"
|
||||
#import "internal/platform/implementation/apple/ble_peripheral.h"
|
||||
#import "internal/platform/implementation/apple/ble_server_socket.h"
|
||||
#import "internal/platform/implementation/apple/ble_socket.h"
|
||||
@@ -42,6 +43,7 @@
|
||||
#import "GoogleToolboxForMac/GTMLogger.h"
|
||||
|
||||
// TODO(b/293336684): Old Weave imports that need to be deleted once shared Weave is complete.
|
||||
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEL2CAPServer.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleConnection.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleUtils.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/Ble/Sockets/Source/Central/GNSCentralManager.h"
|
||||
@@ -362,6 +364,29 @@ std::unique_ptr<api::ble_v2::BleServerSocket> BleMedium::OpenServerSocket(
|
||||
return std::move(server_socket);
|
||||
}
|
||||
|
||||
std::unique_ptr<api::ble_v2::BleL2capServerSocket> BleMedium::OpenL2capServerSocket(
|
||||
const std::string &service_id) {
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
__block GNCBLEL2CAPServer *block_l2cap_server = nil;
|
||||
[medium_
|
||||
openL2CAPServerWithCompletionHandler:^(GNCBLEL2CAPServer *server, NSError *error) {
|
||||
if (error != nil) {
|
||||
GTMLoggerError(@"Error opening L2CAP server: %@", error);
|
||||
}
|
||||
block_l2cap_server = server;
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}
|
||||
peripheralManager:nil];
|
||||
if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC)) != 0) {
|
||||
GTMLoggerError(@"Opening L2CAP server timed out.");
|
||||
return nullptr;
|
||||
}
|
||||
if (!block_l2cap_server) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_unique<BleL2capServerSocket>(block_l2cap_server);
|
||||
}
|
||||
|
||||
// TODO(b/290385712): Add support for @c cancellation_flag.
|
||||
// TODO(b/293336684): Old Weave code that need to be deleted once shared Weave is complete.
|
||||
std::unique_ptr<api::ble_v2::BleSocket> BleMedium::Connect(const std::string &service_id,
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -411,6 +408,9 @@ class BleL2capServerSocket {
|
||||
public:
|
||||
virtual ~BleL2capServerSocket() = default;
|
||||
|
||||
// Gets PSM value has been published by the server.
|
||||
virtual int GetPSM() const = 0;
|
||||
|
||||
// Blocks until either:
|
||||
// - at least one incoming connection request is available, or
|
||||
// - ServerSocket is closed.
|
||||
@@ -567,6 +567,7 @@ class BleMedium {
|
||||
//
|
||||
// On success, returns a new BleL2capServerSocket.
|
||||
// On error, returns nullptr.
|
||||
// Platform implementation should override this method if it supports L2CAP.
|
||||
virtual std::unique_ptr<BleL2capServerSocket> OpenL2capServerSocket(
|
||||
const std::string& service_id) {
|
||||
return nullptr;
|
||||
|
||||
Reference in New Issue
Block a user