Interface definition for Ble L2CAP Medium in Nearby Connections layer

PiperOrigin-RevId: 741054574
This commit is contained in:
Mingshiou Wu
2025-03-27 01:04:16 -07:00
committed by Copybara-Service
parent df2ddd0f20
commit 04b2f9cfc6
13 changed files with 552 additions and 50 deletions
+164 -41
View File
@@ -175,9 +175,7 @@ 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);
// Get psm value from L2CAP server if L2CAP is supported. Now just use the
// default value.
int psm = mediums::BleAdvertisementHeader::kDefaultPsmValue;
int psm = medium_.GetPSM();
mediums::BleAdvertisement medium_advertisement = {
mediums::BleAdvertisement::Version::kV2,
mediums::BleAdvertisement::SocketVersion::kV2,
@@ -570,13 +568,13 @@ ErrorOr<bool> BleV2::StartAcceptingConnections(
MutexLock lock(&mutex_);
if (service_id.empty()) {
LOG(INFO)
LOG(WARNING)
<< "Refusing to start accepting BLE connections with empty service id.";
return {Error(OperationResultCode::CLIENT_BLE_DUPLICATE_DISCOVERING)};
}
if (IsAcceptingConnectionsLocked(service_id)) {
LOG(INFO)
LOG(WARNING)
<< "Refusing to start accepting BLE connections for " << service_id
<< " because another BLE peripheral socket is already in-progress.";
return {Error(OperationResultCode::
@@ -596,47 +594,128 @@ ErrorOr<bool> BleV2::StartAcceptingConnections(
}
BleV2ServerSocket server_socket = medium_.OpenServerSocket(service_id);
if (!server_socket.IsValid()) {
LOG(INFO) << "Failed to start accepting Ble connections for service_id="
<< service_id;
return {Error(
OperationResultCode::CONNECTIVITY_BLE_SERVER_SOCKET_CREATION_FAILURE)};
if (server_socket.IsValid()) {
// Mark the fact that there's an in-progress Ble server accepting
// connections.
auto owned_server_socket =
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
// StopAcceptingL2capConnections() is invoked.
accept_loops_runner_.Execute(
"ble-accept",
[this, service_id, callback = std::move(callback),
server_socket = std::move(owned_server_socket)]() mutable {
while (true) {
BleV2Socket client_socket = server_socket.Accept();
if (!client_socket.IsValid()) {
LOG(WARNING) << "The client socket to accept is invalid.";
server_socket.Close();
break;
} else {
LOG(INFO) << "The client Ble GATT socket has been accepted.";
}
{
MutexLock lock(&mutex_);
client_socket.SetCloseNotifier([this, service_id]() {
MutexLock lock(&mutex_);
incoming_sockets_.erase(service_id);
});
incoming_sockets_.insert({service_id, client_socket});
}
if (callback) {
callback(std::move(client_socket), service_id);
}
}
});
} else {
LOG(INFO)
<< "Failed to start accepting Ble GATT connections for service_id="
<< service_id;
}
LOG(INFO) << "Start accepting Ble GATT connections for service_id="
<< service_id;
return {true};
}
// TODO(mingshiouwu): Add unit test for ble_l2cap flow
ErrorOr<bool> BleV2::StartAcceptingL2capConnections(
const std::string& service_id, AcceptedL2capConnectionCallback callback) {
MutexLock lock(&mutex_);
if (service_id.empty()) {
LOG(WARNING)
<< "Refusing to start accepting Ble L2CAP connections with empty "
"service id.";
return {Error(OperationResultCode::CLIENT_BLE_DUPLICATE_DISCOVERING)};
}
// Mark the fact that there's an in-progress Ble server accepting
// connections.
auto owned_server_socket =
server_sockets_.insert({service_id, std::move(server_socket)})
.first->second;
if (IsAcceptingL2capConnectionsLocked(service_id)) {
LOG(WARNING)
<< "Refusing to start accepting Ble L2CAP connections for "
<< service_id
<< " because another Ble peripheral socket is already in-progress.";
return {Error(OperationResultCode::
CLIENT_DUPLICATE_ACCEPTING_BLE_CONNECTION_REQUEST)};
}
// Start the accept loop on a dedicated thread - this stays alive and
// listening for new incoming connections until StopAcceptingConnections() is
// invoked.
accept_loops_runner_.Execute(
"ble-accept",
[this, service_id = service_id, callback = std::move(callback),
server_socket = std::move(owned_server_socket)]() mutable {
while (true) {
BleV2Socket client_socket = server_socket.Accept();
if (!client_socket.IsValid()) {
LOG(WARNING) << "The client socket to accept is invalid.";
server_socket.Close();
break;
}
{
MutexLock lock(&mutex_);
client_socket.SetCloseNotifier([this, service_id]() {
if (!radio_.IsEnabled()) {
LOG(INFO) << "Can't start accepting Ble L2CAP connections for "
<< service_id << " because Bluetooth isn't enabled.";
return {Error(OperationResultCode::MISCELLEANEOUS_BT_SYSTEM_SERVICE_NULL)};
}
if (!IsAvailableLocked()) {
LOG(INFO) << "Can't start accepting Ble L2CAP connections for "
<< service_id << " because Ble isn't available.";
return {Error(OperationResultCode::MEDIUM_UNAVAILABLE_BLE_NOT_AVAILABLE)};
}
BleL2capServerSocket server_socket =
medium_.OpenL2capServerSocket(service_id);
if (server_socket.IsValid()) {
// 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)})
.first->second;
// Start the accept loop on a dedicated thread - this stays alive and
// listening for new incoming connections until StopAcceptingConnections()
// is invoked.
accept_loops_runner_.Execute(
"ble-l2cap-accept",
[this, service_id, callback = std::move(callback),
server_socket = std::move(owned_server_socket)]() mutable {
while (true) {
BleL2capSocket client_socket = server_socket.Accept();
if (!client_socket.IsValid()) {
LOG(WARNING) << "The client L2CAP socket to accept is invalid.";
server_socket.Close();
break;
} else {
LOG(INFO) << "The client L2CAP socket has been accepted.";
}
{
MutexLock lock(&mutex_);
incoming_sockets_.erase(service_id);
});
incoming_sockets_.insert({service_id, client_socket});
client_socket.SetCloseNotifier([this, service_id]() {
MutexLock lock(&mutex_);
incoming_sockets_.erase(service_id);
});
l2cap_incoming_service_id_to_socket_map_.insert(
{service_id, client_socket});
}
if (callback) {
callback(std::move(client_socket), service_id);
}
}
if (callback) {
callback(std::move(client_socket), service_id);
}
}
});
});
} else {
LOG(INFO)
<< "Failed to start accepting Ble L2CAP connections for service_id="
<< service_id;
}
LOG(INFO) << "Start accepting Ble L2CAP connections for service_id="
<< service_id;
return {true};
}
@@ -675,11 +754,51 @@ bool BleV2::StopAcceptingConnections(const std::string& service_id) {
return true;
}
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()) {
LOG(INFO) << "Can't stop accepting Ble L2CAP connections because it was "
"never started.";
return false;
}
// Closing the BleL2capServerSocket will kick off the suicide of the thread
// 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);
// Store a handle to the BleL2capServerSocket, so we can use it after
// removing the entry from l2cap_server_socket_map_; 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
// frees up this service for another round.
// Finally, close the BleL2capServerSocket.
if (!listening_socket.Close().Ok()) {
LOG(INFO) << "Failed to close Ble L2CAP server socket for service_id="
<< service_id;
return false;
}
return true;
}
bool BleV2::IsAcceptingConnections(const std::string& service_id) {
MutexLock lock(&mutex_);
return IsAcceptingConnectionsLocked(service_id);
}
bool BleV2::IsAcceptingL2capConnections(const std::string& service_id) {
MutexLock lock(&mutex_);
return IsAcceptingL2capConnectionsLocked(service_id);
}
ErrorOr<BleV2Socket> BleV2::Connect(const std::string& service_id,
const BleV2Peripheral& peripheral,
CancellationFlag* cancellation_flag) {
@@ -741,6 +860,10 @@ bool BleV2::IsAcceptingConnectionsLocked(const std::string& service_id) {
return server_sockets_.contains(service_id);
}
bool BleV2::IsAcceptingL2capConnectionsLocked(const std::string& service_id) {
return l2cap_server_socket_map_.contains(service_id);
}
bool BleV2::IsAdvertisementGattServerRunningLocked() {
return gatt_server_ && gatt_server_->IsValid();
}
+33 -6
View File
@@ -62,6 +62,10 @@ class BleV2 final {
using AcceptedConnectionCallback = absl::AnyInvocable<void(
BleV2Socket socket, const std::string& service_id)>;
// Callback that is invoked when a new l2cap connection is accepted.
using AcceptedL2capConnectionCallback = absl::AnyInvocable<void(
BleL2capSocket socket, const std::string& service_id)>;
// The type of the BLE advertising. In current implementation, we don't
// support multiple advertising types on a Medium instance.
enum class AdvertisingType : int {
@@ -108,7 +112,7 @@ class BleV2 final {
ABSL_LOCKS_EXCLUDED(mutex_);
// (TODO:hais) update this after ble_v2 async api refactor.
// Stop Ble advertising with dummy bytes for legagy device.
// Stop Ble advertising with dummy bytes for legacy device.
bool StopLegacyAdvertising(const std::string& service_id)
ABSL_LOCKS_EXCLUDED(mutex_);
@@ -151,13 +155,26 @@ class BleV2 final {
AcceptedConnectionCallback callback)
ABSL_LOCKS_EXCLUDED(mutex_);
// Starts a worker thread, creates a Ble L2CAP socket, associates it with a
// service id.
ErrorOr<bool> StartAcceptingL2capConnections(
const std::string& service_id,
AcceptedL2capConnectionCallback l2cap_callback)
ABSL_LOCKS_EXCLUDED(mutex_);
// Closes socket corresponding to a service id.
bool StopAcceptingConnections(const std::string& service_id)
ABSL_LOCKS_EXCLUDED(mutex_);
bool StopAcceptingL2capConnections(const std::string& service_id)
ABSL_LOCKS_EXCLUDED(mutex_);
bool IsAcceptingConnections(const std::string& service_id)
ABSL_LOCKS_EXCLUDED(mutex_);
bool IsAcceptingL2capConnections(const std::string& service_id)
ABSL_LOCKS_EXCLUDED(mutex_);
// Establishes connection to Ble peripheral.
// Returns socket instance. On success, BleSocket.IsValid() return true.
ErrorOr<BleV2Socket> Connect(const std::string& service_id,
@@ -205,6 +222,10 @@ class BleV2 final {
// `mutex_` held.
bool IsAcceptingConnectionsLocked(const std::string& service_id)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Same as IsListeningForIncomingConnections(), but must be called with
// `mutex_` held.
bool IsAcceptingL2capConnectionsLocked(const std::string& service_id)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
bool IsAdvertisementGattServerRunningLocked()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
@@ -222,14 +243,11 @@ class BleV2 final {
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
bool StopAdvertisementGattServerLocked()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
ByteArray CreateAdvertisementHeader(int psm,
bool extended_advertisement_advertised)
ABSL_SHARED_LOCKS_REQUIRED(mutex_);
// For devices that don't have extended nor gatt adverting.
api::ble_v2::BleAdvertisementData CreateAdvertisingDataForLegacyDevice();
bool StartAdvertisingLocked(const std::string& service_id)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
bool StartFastAdvertisingLocked(
@@ -256,9 +274,7 @@ class BleV2 final {
// Called by StartScanning when using the async methods.
bool StopAsyncScanningLocked(absl::string_view service_id)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
api::ble_v2::TxPowerLevel PowerLevelToTxPowerLevel(PowerLevel power_level);
void RunOnBleThread(Runnable runnable);
static constexpr int kMaxConcurrentAcceptLoops = 5;
@@ -307,6 +323,17 @@ class BleV2 final {
ABSL_GUARDED_BY(mutex_);
mediums::InstantOnLostManager instant_on_lost_manager_;
// 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_);
// 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_);
};
} // namespace connections