mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
Interface definition for Ble L2CAP Medium in Nearby Connections layer
PiperOrigin-RevId: 741054574
This commit is contained in:
committed by
Copybara-Service
parent
df2ddd0f20
commit
04b2f9cfc6
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user