Implement Bluetooth Multiplex.

PiperOrigin-RevId: 651921709
This commit is contained in:
hai007
2024-07-12 16:52:46 -07:00
committed by Copybara-Service
parent c9dbaad286
commit 25deb0962f
28 changed files with 853 additions and 306 deletions
@@ -28,6 +28,7 @@
#include "internal/platform/input_stream.h"
#include "internal/platform/mutex.h"
#include "internal/platform/output_stream.h"
#include "internal/platform/socket.h"
namespace nearby {
namespace connections {
+21 -5
View File
@@ -71,6 +71,7 @@
#include "internal/platform/exception.h"
#include "internal/platform/feature_flags.h"
#include "internal/platform/future.h"
#include "internal/platform/implementation/system_clock.h"
#include "internal/platform/logging.h"
#include "internal/platform/mutex_lock.h"
#include "internal/platform/prng.h"
@@ -1513,6 +1514,11 @@ void BasePcpHandler::OnIncomingFrame(
client->SetRemoteOsInfo(endpoint_id, connection_response.os_info());
}
if (connection_response.has_multiplex_socket_bitmask()) {
client->SetRemoteMultiplexSocketBitmask(
endpoint_id, connection_response.multiplex_socket_bitmask());
}
if (connection_response.has_safe_to_disconnect_version()) {
NEARBY_LOGS(INFO)
<< "[safe-to-disconnect]: endpoint_id=" << endpoint_id
@@ -2125,8 +2131,8 @@ void BasePcpHandler::EvaluateConnectionResult(ClientProxy* client,
bool can_close_immediately) {
// Short-circuit immediately if we're not in an actionable state yet. We will
// be called again once the other side has made their decision.
if (!client->IsConnectionAccepted(endpoint_id) &&
!client->IsConnectionRejected(endpoint_id)) {
bool is_connection_accepted = client->IsConnectionAccepted(endpoint_id);
if (!is_connection_accepted && !client->IsConnectionRejected(endpoint_id)) {
if (!client->HasLocalEndpointResponded(endpoint_id)) {
NEARBY_LOGS(INFO)
<< "ConnectionResult: local client did not respond; endpoint_id="
@@ -2150,7 +2156,8 @@ void BasePcpHandler::EvaluateConnectionResult(ClientProxy* client,
auto pair = pending_connections_.extract(it);
BasePcpHandler::PendingConnectionInfo& connection_info = pair.mapped();
bool is_connection_accepted = client->IsConnectionAccepted(endpoint_id);
Medium medium =
channel_manager_->GetChannelForEndpoint(endpoint_id)->GetMedium();
Status response_code;
if (is_connection_accepted) {
@@ -2173,6 +2180,17 @@ void BasePcpHandler::EvaluateConnectionResult(ClientProxy* client,
std::move(context))) {
response_code = {Status::kEndpointUnknown};
}
std::shared_ptr<EndpointChannel> channel =
channel_manager_->GetChannelForEndpoint(endpoint_id);
if (channel != nullptr) {
if (!channel->EnableMultiplexSocket()) {
NEARBY_LOGS(INFO)
<< "MultiplexSocket is not implemented for this channel.";
}
} else {
NEARBY_LOGS(INFO) << "channel is null";
}
} else {
NEARBY_LOGS(INFO) << "Pending connection rejected; endpoint_id="
<< endpoint_id;
@@ -2202,8 +2220,6 @@ void BasePcpHandler::EvaluateConnectionResult(ClientProxy* client,
return;
}
Medium medium =
channel_manager_->GetChannelForEndpoint(endpoint_id)->GetMedium();
client->GetAnalyticsRecorder().OnConnectionEstablished(
endpoint_id, medium, connection_info.connection_token);
@@ -62,5 +62,12 @@ void BluetoothEndpointChannel::CloseImpl() {
}
}
bool BluetoothEndpointChannel::EnableMultiplexSocket() {
NEARBY_LOGS(INFO) << "BluetoothEndpointChannel MultiplexSocket will be "
"enabled if the Bluetooth MultiplexSocket is valid";
bluetooth_socket_.EnableMultiplexSocket();
return true;
}
} // namespace connections
} // namespace nearby
@@ -33,6 +33,7 @@ class BluetoothEndpointChannel final : public BaseEndpointChannel {
location::nearby::proto::connections::Medium GetMedium() const override;
int GetMaxTransmitPacketSize() const override;
bool EnableMultiplexSocket() override;
private:
static constexpr int kDefaultBTMaxTransmitPacketSize = 1980; // 990 * 2 Bytes
+21 -6
View File
@@ -757,15 +757,14 @@ bool ClientProxy::HasRemoteEndpointResponded(
void ClientProxy::LocalEndpointAcceptedConnection(
const std::string& endpoint_id, PayloadListener listener) {
MutexLock lock(&mutex_);
if (HasLocalEndpointResponded(endpoint_id)) {
NEARBY_LOGS(INFO)
<< "ClientProxy [Local Accepted]: local endpoint has responded; id="
<< endpoint_id;
return;
}
AppendConnectionStatus(endpoint_id, Connection::kLocalEndpointAccepted);
NEARBY_LOGS(INFO) << "ClientProxy [Local Accepted]: id=" << endpoint_id;
ConnectionPair* item = LookupConnection(endpoint_id);
if (item != nullptr) {
item->second = std::move(listener);
@@ -1196,6 +1195,8 @@ std::int32_t ClientProxy::GetLocalMultiplexSocketBitmask() const {
if (NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableMultiplex)) {
NEARBY_LOGS(INFO) << "ClientProxy [GetLocalMultiplexSocketBitmask]: "
<< kBtMultiplexEnabled;
return kBtMultiplexEnabled;
}
return 0;
@@ -1203,33 +1204,47 @@ std::int32_t ClientProxy::GetLocalMultiplexSocketBitmask() const {
void ClientProxy::SetRemoteMultiplexSocketBitmask(
absl::string_view endpoint_id, int remote_multiplex_socket_bitmask) {
MutexLock lock(&mutex_);
ConnectionPair* item = LookupConnection(endpoint_id);
if (item != nullptr) {
item->first.remote_multiplex_socket_bitmask =
remote_multiplex_socket_bitmask;
NEARBY_LOGS(INFO) << "ClientProxy [SetRemoteMultiplexSocketBitmask]: "
<< remote_multiplex_socket_bitmask;
}
}
bool ClientProxy::IsLocalMultiplexSocketSupported(Medium medium) {
int bitmask = GetLocalMultiplexSocketBitmask();
switch (medium) {
case Medium::BLUETOOTH:
NEARBY_LOGS(INFO) << "ClientProxy [IsLocalMultiplexSocketSupported]: "
<< (bitmask & kBtMultiplexEnabled);
return (bitmask & kBtMultiplexEnabled) != 0;
case Medium::WIFI_LAN:
return (bitmask & kWifiLanMultiplexEnabled) != 0;
default:
return false;
}
}
std::optional<std::int32_t> ClientProxy::GetRemoteMultiplexSocketBitmask(
absl::string_view endpoint_id) const {
MutexLock lock(&mutex_);
const ConnectionPair* item = LookupConnection(endpoint_id);
if (item != nullptr) {
return item->first.remote_multiplex_socket_bitmask;
}
return std::nullopt;
}
bool ClientProxy::IsMultiplexSocketSupported(absl::string_view endpoint_id,
Medium medium) {
MutexLock lock(&mutex_);
ConnectionPair* item = LookupConnection(endpoint_id);
if (item == nullptr) {
return false;
}
int combined_result = GetLocalMultiplexSocketBitmask() &
item->first.remote_multiplex_socket_bitmask;
switch (medium) {
case Medium::BLUETOOTH:
return (combined_result & kBtMultiplexEnabled) != 0;
+15 -12
View File
@@ -314,12 +314,27 @@ class ClientProxy final {
// Sets the multiplex socket supports status for remote device.
void SetRemoteMultiplexSocketBitmask(absl::string_view endpoint_id,
int remote_multiplex_socket_bitmask);
// Returns true if the multiplex socket is supported for the given medium.
bool IsLocalMultiplexSocketSupported(Medium medium);
// Gets the multiplex socket supports status for remote device.
std::optional<std::int32_t> GetRemoteMultiplexSocketBitmask(
absl::string_view endpoint_id) const;
// Returns true if the multiplex socket is supported for the given medium.
bool IsMultiplexSocketSupported(absl::string_view endpoint_id, Medium medium);
/** Bitmask for bt multiplex connection support. */
// Note. Deprecates the first and second bit of BT_MULTIPLEX_ENABLED and
// WIFI_LAN_MULTIPLEX_ENABLED and shift them to the third and the forth bit.
// The reason is we need to escape the (0, 1) bit which has been set in some
// devices without salt enabled. If accompany with the devices with salted
// enabled, the frames passed cannot be decrypted and the connection shall be
// failed. Please refer to b/295925531#comment#14 for the details.
enum MultiplexSocketBitmask : uint32_t {
kBtMultiplexEnabled = 1 << 2,
kWifiLanMultiplexEnabled = 1 << 3,
};
private:
struct Connection {
// Status: may be either:
@@ -487,18 +502,6 @@ class ClientProxy final {
bool supports_safe_to_disconnect_;
bool support_auto_reconnect_;
std::int32_t local_safe_to_disconnect_version_;
/** Bitmask for bt multiplex connection support. */
// Note. Deprecates the first and second bit of BT_MULTIPLEX_ENABLED and
// WIFI_LAN_MULTIPLEX_ENABLED and shift them to the third and the forth bit.
// The reason is we need to escape the (0, 1) bit which has been set in some
// devices without salt enabled. If accompany with the devices with salted
// enabled, the frames passed cannot be decrypted and the connection shall be
// failed. Please refer to b/295925531#comment#14 for the details.
enum MultiplexSocketBitmask : uint32_t {
kBtMultiplexEnabled = 1 << 2,
kWifiLanMultiplexEnabled = 1 << 3,
};
};
} // namespace connections
@@ -1552,6 +1552,48 @@ TEST_F(ClientProxyTest, TestAutoBwuWhenListeningWithAutoBwu) {
EXPECT_TRUE(client1()->AutoUpgradeBandwidth());
}
TEST_F(ClientProxyTest, TestMultiplexSocketBitmask) {
EXPECT_EQ(client1()->GetLocalMultiplexSocketBitmask(), 0);
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::kEnableMultiplex,
true);
EXPECT_EQ(client1()->GetLocalMultiplexSocketBitmask(),
ClientProxy::kBtMultiplexEnabled);
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::kEnableMultiplex,
false);
}
TEST_F(ClientProxyTest, TestRemoteMultiplexSocketBitmask) {
EXPECT_EQ(client1()->GetLocalMultiplexSocketBitmask(), 0);
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::kEnableMultiplex,
true);
Endpoint advertising_endpoint =
StartAdvertising(client1(), advertising_connection_listener_);
OnAdvertisingConnectionInitiated(client1(), advertising_endpoint);
client1()->SetRemoteMultiplexSocketBitmask(
advertising_endpoint.id,
ClientProxy::kBtMultiplexEnabled | ClientProxy::kWifiLanMultiplexEnabled);
ASSERT_TRUE(client1()
->GetRemoteMultiplexSocketBitmask(advertising_endpoint.id)
.has_value());
EXPECT_EQ(
client1()
->GetRemoteMultiplexSocketBitmask(advertising_endpoint.id)
.value(),
ClientProxy::kBtMultiplexEnabled | ClientProxy::kWifiLanMultiplexEnabled);
EXPECT_TRUE(client1()->IsMultiplexSocketSupported(advertising_endpoint.id,
Medium::BLUETOOTH));
EXPECT_FALSE(client1()->IsMultiplexSocketSupported(advertising_endpoint.id,
Medium::WIFI_LAN));
EXPECT_FALSE(client1()->IsMultiplexSocketSupported(advertising_endpoint.id,
Medium::WIFI_AWARE));
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::kEnableMultiplex,
false);
}
} // namespace
} // namespace connections
} // namespace nearby
@@ -15,7 +15,6 @@
#ifndef CORE_INTERNAL_ENDPOINT_CHANNEL_H_
#define CORE_INTERNAL_ENDPOINT_CHANNEL_H_
#include <cstdint>
#include <string>
#include "securegcm/d2d_connection_context_v1.h"
@@ -23,7 +22,6 @@
#include "connections/implementation/analytics/packet_meta_data.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "internal/platform/mutex.h"
namespace nearby {
namespace connections {
@@ -121,6 +119,9 @@ class EndpointChannel {
virtual void SetAnalyticsRecorder(
analytics::AnalyticsRecorder* analytics_recorder,
const std::string& endpoint_id) = 0;
// Enables the multiplex socket on the EndpointChannel.
virtual bool EnableMultiplexSocket() {return false;}
};
inline bool operator==(const EndpointChannel& lhs, const EndpointChannel& rhs) {
@@ -18,12 +18,17 @@
#include <string>
#include <utility>
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
#include "connections/implementation/mediums/bluetooth_radio.h"
#include "connections/implementation/mediums/multiplex/multiplex_socket.h"
#include "connections/medium_selector.h"
#include "internal/flags/nearby_flags.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/bluetooth_classic.h"
#include "internal/platform/cancellation_flag.h"
#include "internal/platform/logging.h"
#include "internal/platform/mutex_lock.h"
#include "internal/platform/socket.h"
#include "internal/platform/uuid.h"
namespace nearby {
@@ -44,6 +49,8 @@ std::string ScanModeToString(BluetoothAdapter::ScanMode mode) {
}
} // namespace
using MultiplexSocket = mediums::multiplex::MultiplexSocket;
BluetoothClassic::BluetoothClassic(BluetoothRadio& radio)
: BluetoothClassic(radio, std::make_unique<BluetoothClassicMedium>(
radio.GetBluetoothAdapter())) {}
@@ -52,7 +59,10 @@ BluetoothClassic::BluetoothClassic(
BluetoothRadio& radio, std::unique_ptr<BluetoothClassicMedium> medium)
: radio_(radio),
adapter_(radio_.GetBluetoothAdapter()),
medium_(std::move(medium)) {}
medium_(std::move(medium)) {
is_multiplex_enabled_ = NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::kEnableMultiplex);
}
BluetoothClassic::~BluetoothClassic() {
// Destructor is not taking locks, but methods it is calling are.
@@ -62,6 +72,19 @@ BluetoothClassic::~BluetoothClassic() {
}
TurnOffDiscoverability();
{
MutexLock lock(&mutex_);
NEARBY_LOGS(INFO) << "Closing multiplex sockets for "
<< multiplex_sockets_.size() << " devices";
if (is_multiplex_enabled_) {
for (auto& [bt_mac, multiplex_socket] : multiplex_sockets_) {
NEARBY_LOGS(INFO) << "Closing multiplex sockets for "
<< GetRemoteDevice(bt_mac).GetName();
multiplex_socket->Shutdown();
}
}
}
// All the AcceptLoopRunnable objects in here should already have gotten an
// opportunity to shut themselves down cleanly in the calls to
// StopAcceptingConnections() above.
@@ -348,20 +371,61 @@ bool BluetoothClassic::StartAcceptingConnections(
auto owned_socket =
server_sockets_.emplace(service_id, std::move(socket)).first->second;
if (is_multiplex_enabled_) {
MultiplexSocket::ListenForIncomingConnection(
service_id, Medium::BLUETOOTH,
[&callback](const std::string& listening_service_id,
MediumSocket* virtual_socket) mutable {
if (callback) {
callback(listening_service_id,
*(dynamic_cast<BluetoothSocket*>(virtual_socket)));
}
});
}
// 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(
"bt-accept",
[callback = std::move(callback), server_socket = std::move(owned_socket),
service_id]() mutable {
service_id, this]() mutable {
while (true) {
BluetoothSocket client_socket = server_socket.Accept();
if (!client_socket.IsValid()) {
NEARBY_LOGS(INFO) << "Failed to accept connection for "
<< service_id;
server_socket.Close();
break;
}
if (callback) {
NEARBY_LOGS(INFO) << "Accepted connection for " << service_id;
bool callback_called = false;
{
MutexLock lock(&mutex_);
if (is_multiplex_enabled_) {
MultiplexSocket* multiplex_socket =
MultiplexSocket::CreateIncomingSocket(&client_socket,
service_id);
if (multiplex_socket != nullptr &&
multiplex_socket->GetVirtualSocket(service_id)) {
multiplex_sockets_.emplace(
client_socket.GetRemoteDevice().GetMacAddress(),
multiplex_socket);
MultiplexSocket::StopListeningForIncomingConnection(
service_id, Medium::BLUETOOTH);
NEARBY_LOGS(INFO) << "Multiplex virtaul socket created for "
<< client_socket.GetRemoteDevice().GetName();
if (callback) {
callback(
service_id,
*(dynamic_cast<BluetoothSocket*>(
multiplex_socket->GetVirtualSocket(service_id))));
callback_called = true;
}
}
}
}
if (callback && !callback_called) {
NEARBY_LOGS(INFO) << "Call back triggered for physical socket.";
callback(service_id, std::move(client_socket));
}
}
@@ -396,6 +460,10 @@ bool BluetoothClassic::StopAcceptingConnections(const std::string& service_id) {
<< service_id << " because it was never started.";
return false;
}
if (is_multiplex_enabled_) {
MultiplexSocket::StopListeningForIncomingConnection(service_id,
Medium::BLUETOOTH);
}
// Closing the BluetoothServerSocket will kick off the suicide of the thread
// in accept_loops_thread_pool_ that blocks on
@@ -424,6 +492,31 @@ bool BluetoothClassic::StopAcceptingConnections(const std::string& service_id) {
BluetoothSocket BluetoothClassic::Connect(BluetoothDevice& bluetooth_device,
const std::string& service_id,
CancellationFlag* cancellation_flag) {
{
MutexLock lock(&mutex_);
if (is_multiplex_enabled_) {
NEARBY_LOGS(INFO) << "multiplex_sockets_ size:"
<< multiplex_sockets_.size();
auto it = multiplex_sockets_.find(bluetooth_device.GetMacAddress());
if (it != multiplex_sockets_.end()) {
MultiplexSocket* multiplex_socket = it->second;
if (multiplex_socket->IsEnabled()) {
auto* virtual_socket =
multiplex_socket->EstablishVirtualSocket(service_id);
// Should not happen.
auto* bluetooth_socket =
dynamic_cast<BluetoothSocket*>(virtual_socket);
if (bluetooth_socket == nullptr) {
NEARBY_LOGS(INFO)
<< "Failed to cast to BluetoothSocket for " << service_id
<< " with " << bluetooth_device.GetName();
return BluetoothSocket{};
}
return *bluetooth_socket;
}
}
}
}
service_id_to_connect_attempts_count_map_[service_id] = 1;
while (service_id_to_connect_attempts_count_map_[service_id] <=
kConnectAttemptsLimit) {
@@ -432,14 +525,14 @@ BluetoothSocket BluetoothClassic::Connect(BluetoothDevice& bluetooth_device,
<< "Attempt #"
<< service_id_to_connect_attempts_count_map_[service_id]
<< ": Cannot start creating client BT socket due to cancel.";
return BluetoothSocket();
return BluetoothSocket{};
}
NEARBY_LOGS(INFO) << "Attempt #"
<< service_id_to_connect_attempts_count_map_[service_id]
<< " to connect.";
auto wrapper_result =
AttemptToConnect(bluetooth_device, service_id, cancellation_flag);
NEARBY_LOGS(INFO) << "Attempt #"
<< service_id_to_connect_attempts_count_map_[service_id]
<< " to connect: " << wrapper_result.IsValid();
if (wrapper_result.IsValid()) {
return wrapper_result;
}
@@ -449,7 +542,7 @@ BluetoothSocket BluetoothClassic::Connect(BluetoothDevice& bluetooth_device,
NEARBY_LOGS(WARNING) << "Giving up after " << kConnectAttemptsLimit
<< " attempts";
return BluetoothSocket();
return BluetoothSocket{};
}
BluetoothSocket BluetoothClassic::AttemptToConnect(
@@ -458,9 +551,8 @@ BluetoothSocket BluetoothClassic::AttemptToConnect(
MutexLock lock(&mutex_);
NEARBY_LOGS(INFO) << "BluetoothClassic::Connect: service_id=" << service_id
<< ", device=" << &bluetooth_device;
// Socket to return. To allow for NRVO to work, it has to be a single
// object.
BluetoothSocket socket;
// Socket to return. To allow for NRVO to work, it has to be a single object.
BluetoothSocket socket{};
if (service_id.empty()) {
NEARBY_LOGS(INFO)
@@ -489,7 +581,27 @@ BluetoothSocket BluetoothClassic::AttemptToConnect(
if (!socket.IsValid() || cancellation_flag->Cancelled()) {
NEARBY_LOGS(INFO) << "Failed to Connect via BT [service=" << service_id
<< "]";
return BluetoothSocket();
return BluetoothSocket{};
}
if (is_multiplex_enabled_) {
// New MultiplexSocket but default disabled, should be enabled after
// negotiated
MultiplexSocket* multiplex_socket =
MultiplexSocket::CreateOutgoingSocket(&socket, service_id);
auto* virtual_socket = multiplex_socket->GetVirtualSocket(service_id);
// Should not happen.
auto* bluetooth_socket = dynamic_cast<BluetoothSocket*>(virtual_socket);
if (bluetooth_socket == nullptr) {
NEARBY_LOGS(INFO) << "Failed to cast to BluetoothSocket for "
<< service_id << " with " << bluetooth_device.GetName();
return BluetoothSocket{};
}
NEARBY_LOGS(INFO) << "Multiplex socket created for "
<< bluetooth_device.GetName();
multiplex_sockets_.emplace(bluetooth_device.GetMacAddress(),
multiplex_socket);
return *bluetooth_socket;
}
return socket;
@@ -22,7 +22,10 @@
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/functional/any_invocable.h"
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
#include "connections/implementation/mediums/bluetooth_radio.h"
#include "connections/implementation/mediums/multiplex/multiplex_socket.h"
#include "internal/flags/nearby_flags.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/bluetooth_classic.h"
#include "internal/platform/cancellation_flag.h"
@@ -228,6 +231,16 @@ class BluetoothClassic {
mutable Mutex discovery_callbacks_mutex_;
absl::flat_hash_map<std::string, DiscoveredDeviceCallback>
discovery_callbacks_ ABSL_GUARDED_BY(discovery_callbacks_mutex_);
// Whether the multiplex feature is enabled.
bool is_multiplex_enabled_ = NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableMultiplex);
// A map of Bluetooth MacAddress -> MultiplexSocket.
absl::flat_hash_map<std::string,
mediums::multiplex::MultiplexSocket*>
multiplex_sockets_ ABSL_GUARDED_BY(mutex_);
};
} // namespace connections
@@ -30,6 +30,7 @@ cc_library(
"//connections/implementation:__subpackages__",
],
deps = [
"//connections:core_types",
"//connections/implementation/flags:connections_flags",
"//connections/implementation/mediums:utils",
"//internal/flags:nearby_flags",
@@ -42,7 +43,6 @@ cc_library(
"//internal/platform/implementation:types",
"//proto:connections_enums_cc_proto",
"//proto/mediums:multiplex_frames_cc_proto",
"@aappleby_smhasher//:libmurmur3",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
@@ -20,7 +20,9 @@
#include <utility>
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "connections/implementation/mediums/multiplex/multiplex_frames.h"
#include "internal/platform/array_blocking_queue.h"
#include "internal/platform/atomic_boolean.h"
#include "internal/platform/base64_utils.h"
#include "internal/platform/byte_array.h"
@@ -38,56 +40,9 @@ namespace multiplex {
namespace {
using ::location::nearby::mediums::ConnectionResponseFrame;
constexpr absl::string_view TAG = "MultiplexOutputStream:";
constexpr absl::string_view kFakeSalt = "RECEIVER_CONDIMENT";
} // namespace
// Implementation for class ArrayBlockingQueue
template <typename T>
void ArrayBlockingQueue<T>::Put(const T& value) {
MutexLock lock(&queue_mutex_);
if (queue_.size() >= capacity_) {
has_space_.Wait();
}
queue_.push(value);
has_data_.Notify();
}
template <typename T>
T ArrayBlockingQueue<T>::Take() {
MutexLock lock(&queue_mutex_);
if (queue_.empty()) {
has_data_.Wait();
}
T front = queue_.front();
queue_.pop();
has_space_.Notify();
return front;
}
template <typename T>
bool ArrayBlockingQueue<T>::TryPut(const T& value) {
MutexLock lock(&queue_mutex_);
if (queue_.size() < capacity_) {
queue_.push(value);
has_data_.Notify();
return true;
}
return false;
}
template <typename T>
std::optional<T> ArrayBlockingQueue<T>::TryTake() {
MutexLock lock(&queue_mutex_);
if (!queue_.empty()) {
T front = queue_.front();
queue_.pop();
has_space_.Notify();
return front;
}
return std::nullopt;
}
// Implementation for class MultiplexOutputStream
MultiplexOutputStream::MultiplexOutputStream(OutputStream* physical_writer,
AtomicBoolean& is_enabled)
@@ -98,25 +53,25 @@ MultiplexOutputStream::MultiplexOutputStream(OutputStream* physical_writer,
Exception MultiplexOutputStream::WaitForResult(const std::string& method_name,
Future<bool>* future) {
if (!future) {
NEARBY_LOGS(INFO) << TAG << "No future to wait for; return with error.";
NEARBY_LOGS(INFO) << "No future to wait for; return with error.";
return {Exception::kFailed};
}
NEARBY_LOGS(INFO) << TAG << "Waiting for future to complete: " << method_name;
NEARBY_LOGS(INFO) << "Waiting for future to complete: " << method_name;
ExceptionOr<bool> result =
future->Get(FeatureFlags::GetInstance()
.GetFlags()
.mediums_frame_write_timeout_millis);
if (!result.ok()) {
NEARBY_LOGS(INFO) << TAG << "Future:[" << method_name
NEARBY_LOGS(INFO) << "Future:[" << method_name
<< "] completed with exception:" << result.exception();
return {Exception::kFailed};
}
if (result.result()) {
NEARBY_LOGS(INFO) << TAG << "Future:[" << method_name
NEARBY_LOGS(INFO) << "Future:[" << method_name
<< "] completed with success.";
return {Exception::kSuccess};
}
NEARBY_LOGS(INFO) << TAG << "Future:[" << method_name
NEARBY_LOGS(INFO) << "Future:[" << method_name
<< "] completed with failure.";
return {Exception::kFailed};
}
@@ -156,7 +111,7 @@ bool MultiplexOutputStream::WriteConnectionResponseFrame(
bool MultiplexOutputStream::Close(const std::string& service_id) {
auto item = virtual_output_streams_.find(service_id);
if (item == virtual_output_streams_.end()) {
NEARBY_LOGS(WARNING) << TAG << "Failed to close VirtualOutputStream("
NEARBY_LOGS(INFO) << "Don't need to close VirtualOutputStream("
<< service_id << ") because it's already gone.";
return false;
}
@@ -223,7 +178,7 @@ MultiplexOutputStream::MultiplexWriter::MultiplexWriter(
MultiplexOutputStream::MultiplexWriter::~MultiplexWriter() {
Close();
writer_thread_.Shutdown();
// writer_thread_.Shutdown();
physical_writer_ = nullptr;
}
@@ -245,7 +200,7 @@ void MultiplexOutputStream::MultiplexWriter::EnqueueToSend(
}
void MultiplexOutputStream::MultiplexWriter::StartWriting() {
NEARBY_LOGS(INFO) << TAG << "Writing loop started.";
NEARBY_LOGS(INFO) << "Writing loop started.";
while (true) {
auto enqueued_frame = data_queue_.TryTake();
if (enqueued_frame != std::nullopt) {
@@ -256,19 +211,23 @@ void MultiplexOutputStream::MultiplexWriter::StartWriting() {
MutexLock lock(&writing_mutex_);
if (data_queue_.Empty() && is_writing_ && !is_closed_) {
is_writing_ = false;
NEARBY_LOGS(INFO) << TAG << "Waiting for data_queue_ has data.";
NEARBY_LOGS(INFO) << "Waiting for data_queue_ has data.";
Exception wait_succeeded = is_writing_cond_.Wait();
if (!wait_succeeded.Ok()) {
NEARBY_LOGS(WARNING)
<< TAG << __func__
<< ": Failure waiting to wait: " << wait_succeeded.value;
<< "Failure waiting to wait: " << wait_succeeded.value;
return;
}
}
if (is_closed_) break;
if (is_closed_) {
NEARBY_LOGS(INFO) << "Notify to close_writing_thread";
MutexLock lock(&close_writing_thread_mutex_);
close_writing_thread_cond_.Notify();
break;
}
}
}
NEARBY_LOGS(INFO) << TAG << "Writing loop stopped.";
NEARBY_LOGS(INFO) << "Writing loop stopped.";
}
void MultiplexOutputStream::MultiplexWriter::Write(
@@ -292,13 +251,28 @@ void MultiplexOutputStream::MultiplexWriter::Write(
}
void MultiplexOutputStream::MultiplexWriter::Close() {
MutexLock lock(&writing_mutex_);
is_closed_ = true;
if (is_write_loop_running_) {
NEARBY_LOGS(INFO) << TAG << "Stop writing loop and Shutdown writer thread.";
if (is_closed_) {
NEARBY_LOGS(INFO) << "MultiplexWriter is already closed.";
return;
}
NEARBY_LOGS(INFO) << "Stop writing loop and Shutdown writer thread.";
{
MutexLock lock(&writing_mutex_);
is_closed_ = true;
if (!is_write_loop_running_) {
writer_thread_.Shutdown();
return;
}
is_write_loop_running_ = false;
is_writing_cond_.Notify();
}
NEARBY_LOGS(INFO) << "Wait to close_writing_thread";
{
MutexLock lock(&close_writing_thread_mutex_);
close_writing_thread_cond_.Wait(absl::Milliseconds(20));
NEARBY_LOGS(INFO) << "Shutdown writer thread.";
writer_thread_.Shutdown();
}
}
MultiplexOutputStream::VirtualOutputStream::VirtualOutputStream(
@@ -315,9 +289,9 @@ MultiplexOutputStream::VirtualOutputStream::VirtualOutputStream(
Exception MultiplexOutputStream::VirtualOutputStream::Write(
const ByteArray& data) {
if (is_closed_) {
if (is_closed_.Get()) {
NEARBY_LOGS(WARNING)
<< TAG << "Failed to write data because the VirtualOutputStream for "
<< "Failed to write data because the VirtualOutputStream for "
<< service_id_ << " closed";
return {Exception::kIo};
}
@@ -336,8 +310,7 @@ Exception MultiplexOutputStream::VirtualOutputStream::Write(
// true to let the remote handle correctly.
if ((service_id_hash_salt_ == kFakeSalt) && !should_pass_salt) {
should_pass_salt = true;
NEARBY_LOGS(INFO) << TAG
<< "service_idHashSalt is still a fake one and "
NEARBY_LOGS(INFO) << "service_idHashSalt is still a fake one and "
"not changed yet; continue to pass salt.";
}
}
@@ -15,14 +15,12 @@
#ifndef CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_OUTPUT_STREAM_H_
#define CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_OUTPUT_STREAM_H_
#include <cstddef>
#include <memory>
#include <optional>
#include <queue>
#include <string>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "internal/platform/array_blocking_queue.h"
#include "internal/platform/atomic_boolean.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
@@ -37,40 +35,6 @@ namespace nearby {
namespace connections {
namespace mediums {
namespace multiplex {
/**
* Payload from different services/clients will be put into an
* ArrayBlockingQueue before sending to ensure each client has equal chance to
* send its data. Since C++ doesn't provide ArrayBlockingQueue as Java, we
* implement one here.
*/
template <typename T>
class ArrayBlockingQueue {
public:
explicit ArrayBlockingQueue(size_t capacity) : capacity_(capacity) {}
void Put(const T& value);
T Take();
bool TryPut(const T& value);
// Returns std::nullopt if the queue is empty.
std::optional<T> TryTake();
size_t Size() const {
MutexLock lock(&queue_mutex_);
return queue_.size();
}
bool Empty() const {
MutexLock lock(&queue_mutex_);
return queue_.empty();
}
private:
std::queue<T> queue_;
mutable Mutex queue_mutex_;
ConditionVariable has_data_{&queue_mutex_};
ConditionVariable has_space_{&queue_mutex_};
const size_t capacity_;
};
/**
* A helper class to send out the {@code MultiplexControlFrame} and the outgoing
* data from clients. It schedules control and data frames with priority below
@@ -173,6 +137,8 @@ class MultiplexOutputStream {
ConditionVariable is_writing_cond_{&writing_mutex_};
bool is_writing_ ABSL_GUARDED_BY(writing_mutex_) = false;
bool is_closed_ = false;
mutable Mutex close_writing_thread_mutex_;
ConditionVariable close_writing_thread_cond_{&close_writing_thread_mutex_};
// The single thread to write all enqueued frames.
SingleThreadExecutor writer_thread_;
@@ -23,11 +23,14 @@
#include "absl/container/flat_hash_map.h"
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "connections/implementation/mediums/multiplex/multiplex_frames.h"
#include "connections/implementation/mediums/multiplex/multiplex_output_stream.h"
#include "connections/implementation/mediums/utils.h"
#include "internal/platform/base64_utils.h"
#include "internal/platform/bluetooth_classic.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/exception.h"
#include "internal/platform/feature_flags.h"
#include "internal/platform/future.h"
@@ -74,7 +77,20 @@ void MultiplexSocket::StopListeningForIncomingConnection(
MultiplexSocket::MultiplexSocket(MediumSocket* physical_socket)
: physical_socket_(physical_socket),
multiplex_output_stream_{&physical_socket->GetOutputStream(), enabled_},
physical_reader_(&physical_socket->GetInputStream()) {}
physical_reader_(&physical_socket->GetInputStream()) {
NEARBY_LOGS(INFO) << "physical_socket_: " << physical_socket_;
switch (physical_socket_->GetMedium()) {
case Medium::BLUETOOTH:
medium_ = Medium::BLUETOOTH;
bluetooth_socket_ =
std::move(*static_cast<BluetoothSocket*>(physical_socket));
break;
default:
medium_ = Medium::UNKNOWN_MEDIUM;
NEARBY_LOGS(ERROR) << __func__ << "Unsupported medium: "
<< physical_socket_->GetMedium();
}
}
absl::flat_hash_map<std::pair<std::string, Medium>,
MultiplexIncomingConnectionCb>&
@@ -104,6 +120,7 @@ MultiplexSocket* MultiplexSocket::CreateIncomingSocket(
storage_bt;
multiplex_incoming_socket =
new (&storage_bt) MultiplexSocket(physical_socket);
break;
case Medium::BLE:
static std::aligned_storage_t<sizeof(MultiplexSocket),
@@ -125,15 +142,9 @@ MultiplexSocket* MultiplexSocket::CreateIncomingSocket(
multiplex_incoming_socket = nullptr;
return multiplex_incoming_socket;
}
auto on_physical_socket_closed_listener =
std::make_unique<absl::AnyInvocable<void()>>(
[]() { multiplex_incoming_socket->OnPhysicalSocketClosed(); });
physical_socket->AddOnSocketClosedListener(
std::move(on_physical_socket_closed_listener));
NEARBY_LOGS(INFO) << __func__
<< "CreateIncomingSocket with serviceId=" << service_id
NEARBY_LOGS(INFO) << "CreateIncomingSocket with serviceId=" << service_id
<< ", serviceIdHashSalt=" << kFakeSalt;
multiplex_incoming_socket->CreateFirstVirtualSocket(service_id,
(std::string)kFakeSalt);
multiplex_incoming_socket->StartReaderThread();
@@ -172,17 +183,9 @@ MultiplexSocket* MultiplexSocket::CreateOutgoingSocket(
<< physical_socket->GetMedium();
return multiplex_outgoing_socket;
}
auto on_physical_socket_closed_listener =
std::make_unique<absl::AnyInvocable<void()>>(
[]() { multiplex_outgoing_socket->OnPhysicalSocketClosed(); });
physical_socket->AddOnSocketClosedListener(
std::move(on_physical_socket_closed_listener));
NEARBY_LOGS(INFO) << __func__
<< "CreateOutgoingSocket with serviceId=" << service_id
NEARBY_LOGS(INFO) << "CreateOutgoingSocket with serviceId=" << service_id
<< ", serviceIdHashSalt=" << service_id_hash_salt;
NEARBY_LOGS(INFO) << __func__ << "multiplex_outgoing_socket:"
<< multiplex_outgoing_socket;
multiplex_outgoing_socket->CreateFirstVirtualSocket(service_id,
service_id_hash_salt);
multiplex_outgoing_socket->StartReaderThread();
@@ -204,16 +207,20 @@ MediumSocket* MultiplexSocket::CreateFirstVirtualSocket(
MutexLock lock(&virtual_socket_mutex_);
std::string salted_service_id_hash_key =
GenerateServiceIdHashKeyWithSalt(service_id, service_id_hash_salt);
NEARBY_LOGS(INFO) << __func__ << " for service_id=" << service_id
<< ", salt=" << service_id_hash_salt
<< ", salted_service_id_hash_key="
<< salted_service_id_hash_key;
MediumSocket* virtual_socket = physical_socket_->CreateVirtualSocket(
salted_service_id_hash_key, output_stream, physical_socket_->GetMedium(),
&virtual_sockets_);
virtual_socket->AddOnSocketClosedListener(
std::make_unique<absl::AnyInvocable<void()>>(
[this, &service_id]() { OnVirtualSocketClosed(service_id); }));
[this, service_id]() { OnVirtualSocketClosed(service_id); }));
if (!IsEnabled()) {
NEARBY_LOGS(INFO) << __func__ << ": Register multiplex enabled callback";
virtual_socket->RegisterMultiplexEnabledCallback(enable_cb_);
}
@@ -228,22 +235,31 @@ MediumSocket* MultiplexSocket::CreateVirtualSocket(
std::string salted_service_id_hash_key =
GenerateServiceIdHashKeyWithSalt(service_id, service_id_hash_salt);
NEARBY_LOGS(INFO) << __func__ << "service_id=" << service_id
<< ", salt=" << service_id_hash_salt
<< ", salted_service_id_hash_key="
<< salted_service_id_hash_key;
MediumSocket* virtual_socket = physical_socket_->CreateVirtualSocket(
salted_service_id_hash_key, output_stream, physical_socket_->GetMedium(),
&virtual_sockets_);
virtual_socket->AddOnSocketClosedListener(
std::make_unique<absl::AnyInvocable<void()>>(
[this, &service_id]() { OnVirtualSocketClosed(service_id); }));
[this, service_id]() { OnVirtualSocketClosed(service_id); }));
return virtual_socket;
}
MediumSocket* MultiplexSocket::GetVirtualSocket(const std::string& service_id) {
MutexLock lock(&virtual_socket_mutex_);
NEARBY_LOGS(INFO) << __func__ << " service_id=" << service_id << ", Salt="
<< multiplex_output_stream_.GetServiceIdHashSalt(service_id)
<< ", virtual_sockets_.size()=" << virtual_sockets_.size();
auto item = virtual_sockets_.find(GenerateServiceIdHashKeyWithSalt(
service_id, multiplex_output_stream_.GetServiceIdHashSalt(service_id)));
if (item == virtual_sockets_.end()) {
NEARBY_LOGS(INFO) << "Not found!";
return nullptr;
}
return item->second.get();
@@ -254,6 +270,16 @@ int MultiplexSocket::GetVirtualSocketCount() {
return virtual_sockets_.size();
}
void MultiplexSocket::ListVirtualSocket() {
NEARBY_LOGS(INFO) << __func__ << " virtual_sockets_.size()="
<< virtual_sockets_.size();
for (auto& [service_id_hash_key, virtual_socket] : virtual_sockets_) {
NEARBY_LOGS(INFO) << __func__
<< " service_id_hash_key=" << service_id_hash_key
<< ", virtual_socket=" << virtual_socket;
}
}
std::shared_ptr<Future<ConnectionResponseCode>>
MultiplexSocket::RegisterConnectionResponse(const std::string& service_id) {
auto future = std::make_shared<Future<ConnectionResponseCode>>();
@@ -270,7 +296,8 @@ void MultiplexSocket::UnRegisterConnectionResponse(
MediumSocket* MultiplexSocket::EstablishVirtualSocket(
const std::string& service_id) {
if (!IsEnabled()) {
NEARBY_LOGS(ERROR) << __func__ << "EstablishVirtualSocket disabled";
NEARBY_LOGS(ERROR)
<< "MultiplexSocket is disabled, cannot establish virtual socket.";
return nullptr;
}
@@ -293,22 +320,19 @@ MediumSocket* MultiplexSocket::EstablishVirtualSocket(
ConnectionResponseCode response_code = result.GetResult();
switch (response_code) {
case ConnectionResponseFrame::CONNECTION_ACCEPTED:
NEARBY_LOGS(INFO) << __func__
<< "EstablishVirtualSocket after remote response to"
NEARBY_LOGS(INFO) << "EstablishVirtualSocket after remote response to"
" accept the connection with service_id="
<< service_id
<< ", service_id_hash_salt=" << service_id_hash_salt;
return CreateVirtualSocket(service_id, service_id_hash_salt);
case ConnectionResponseFrame::NOT_LISTENING:
NEARBY_LOGS(ERROR) << __func__
<< "EstablishVirtualSocket failed for service_id="
NEARBY_LOGS(ERROR) << "EstablishVirtualSocket failed for service_id="
<< service_id
<< ", service_id_hash_salt=" << service_id_hash_salt
<< " with response code=NOT_LISTENING";
break;
default:
NEARBY_LOGS(ERROR) << __func__
<< "EstablishVirtualSocket failed for service_id="
NEARBY_LOGS(ERROR) << "EstablishVirtualSocket failed for service_id="
<< service_id
<< ", service_id_hash_salt=" << service_id_hash_salt
<< " with response code=UNKNOWN_RESPONSE_CODE";
@@ -319,8 +343,7 @@ MediumSocket* MultiplexSocket::EstablishVirtualSocket(
void MultiplexSocket::StartReaderThread() {
if (is_shutdown_) {
NEARBY_LOGS(WARNING) << __func__
<< "Stop to start reader thread since socket is "
NEARBY_LOGS(WARNING) << "Stop to start reader thread since socket is "
"shutdown.";
return;
}
@@ -358,21 +381,9 @@ void MultiplexSocket::StartReaderThread() {
}
}
if (fail) {
{
MutexLock lock(&virtual_socket_mutex_);
if (virtual_sockets_.empty()) {
NEARBY_LOGS(INFO)
<< __func__
<< "The reader thread stopped because all virtual socket "
"closed.";
} else {
NEARBY_LOGS(ERROR) << __func__
<< "The reader thread stopped because "
"unexpected IOException";
}
}
return;
}
ExceptionOr<MultiplexFrame> frame_exc =
multiplex::FromBytes(bytes.result());
if (!frame_exc.ok()) {
@@ -388,11 +399,11 @@ void MultiplexSocket::StartReaderThread() {
// the feature at this point.
NEARBY_LOGS(INFO)
<< __func__
<< "Received a multiplex frame while not enabled, enable "
<< " Received a multiplex frame while not enabled, enable "
"multiplex.";
Enable();
}
auto frame = frame_exc.result();
const auto& frame = frame_exc.result();
auto salted_service_id_hash =
ByteArray{std::move(frame.header().salted_service_id_hash())};
auto service_id_hash_salt = frame.header().has_service_id_hash_salt()
@@ -404,12 +415,14 @@ void MultiplexSocket::StartReaderThread() {
frame.control_frame());
break;
case MultiplexFrame::DATA_FRAME:
NEARBY_LOGS(VERBOSE)
<< "service_id_hash_salt: " << service_id_hash_salt;
HandleDataFrame(salted_service_id_hash, service_id_hash_salt,
frame.data_frame());
break;
default:
NEARBY_LOGS(WARNING)
<< __func__ << "Received MultiplexFrame with unknown frame type "
<< __func__ << " Received MultiplexFrame with unknown frame type "
<< frame.frame_type();
}
}
@@ -417,7 +430,6 @@ void MultiplexSocket::StartReaderThread() {
}
void MultiplexSocket::HandleOfflineFrame(const ByteArray& bytes) {
// Only pass the data when there's only 1 VirtualSocket.
MutexLock lock(&virtual_socket_mutex_);
NEARBY_LOGS(INFO) << __func__
<< " Virtual_socket num:" << virtual_sockets_.size();
@@ -427,6 +439,7 @@ void MultiplexSocket::HandleOfflineFrame(const ByteArray& bytes) {
NEARBY_LOGS(WARNING) << "Expected one live socket, but found null.";
return;
}
NEARBY_LOGS(INFO) << __func__ << "FeedIncomingData:" << std::string(bytes);
item->second->FeedIncomingData(Base64Utils::IntToBytes(bytes.size()));
item->second->FeedIncomingData(bytes);
}
@@ -474,8 +487,7 @@ void MultiplexSocket::HandleConnectionRequest(
const ByteArray& salted_service_id_hash,
const std::string& service_id_hash_salt) {
if (!IsEnabled()) {
NEARBY_LOGS(WARNING) << __func__
<< "Received a CONNECTION_REQUEST frame on medium "
NEARBY_LOGS(WARNING) << "Received a CONNECTION_REQUEST frame on medium "
<< Medium_Name(physical_socket_->GetMedium())
<< " but status is disabled, ignore it.";
return;
@@ -496,14 +508,13 @@ void MultiplexSocket::HandleConnectionRequest(
}
if (incoming_connection_callback == nullptr || listening_service_id.empty()) {
NEARBY_LOGS(INFO) << __func__
<< "There's no client listening for hash salt : "
NEARBY_LOGS(INFO) << "There's no client listening for hash salt : "
<< service_id_hash_salt
<< ", hash key : " << salted_service_id_hash_key
<< " on medium "
<< Medium_Name(physical_socket_->GetMedium());
NEARBY_LOGS(INFO) << __func__ << "Dump incomingConnectionCallbacks : "
NEARBY_LOGS(INFO) << "The size of incomingConnectionCallbacks : "
<< GetIncomingConnectionCallbacks().size();
if (!multiplex_output_stream_.WriteConnectionResponseFrame(
salted_service_id_hash, service_id_hash_salt,
@@ -512,8 +523,7 @@ void MultiplexSocket::HandleConnectionRequest(
}
return;
}
NEARBY_LOGS(INFO) << __func__
<< "Accept new virtual socket request service ID : "
NEARBY_LOGS(INFO) << "Accept new virtual socket request service ID : "
<< listening_service_id
<< ", hash salt : " << service_id_hash_salt
<< ", hash key : " << salted_service_id_hash_key
@@ -523,14 +533,12 @@ void MultiplexSocket::HandleConnectionRequest(
if (!multiplex_output_stream_.WriteConnectionResponseFrame(
salted_service_id_hash, service_id_hash_salt,
ConnectionResponseFrame::CONNECTION_ACCEPTED)) {
NEARBY_LOGS(INFO) << __func__
<< "Failed to write CONNECTION_ACCEPTED frame.";
NEARBY_LOGS(INFO) << "Failed to write CONNECTION_ACCEPTED frame.";
return;
}
NEARBY_LOGS(VERBOSE)
<< __func__
<< "establishVirtualSocket after local device accept the connection "
<< "EstablishVirtualSocket after local device accept the connection "
"with serviceId="
<< listening_service_id << ", serviceIdHashSalt=" << service_id_hash_salt;
MediumSocket* virtual_socket =
@@ -576,25 +584,11 @@ void MultiplexSocket::HandleDisconnection(
auto item = virtual_sockets_.find(salted_service_id_hash_key);
if (item != virtual_sockets_.end()) {
NEARBY_LOGS(INFO)
<< __func__
<< "Received a DISCONNECTION frame to disconnect virtual socket for "
"salted service ID Hash Key "
<< salted_service_id_hash_key;
if (item->second != nullptr) {
item->second->Close();
}
virtual_sockets_.erase(item);
// physical_socket_->RemoveVirtualSocket(salted_service_id_hash_key);
if (virtual_sockets_.empty()) {
NEARBY_LOGS(INFO) << __func__
<< "Close the physical socket because all services "
"disconnected.";
physical_socket_->Close();
}
} else {
NEARBY_LOGS(WARNING)
<< __func__
<< "Received a DISCONNECTION frame but there's no alive socket to "
"disconnect for service ID Hash Key "
<< salted_service_id_hash_key;
@@ -622,15 +616,13 @@ void MultiplexSocket::HandleDataFrame(const ByteArray& salted_service_id_hash,
}
if (virtual_socket != nullptr) {
NEARBY_LOGS(INFO)
<< __func__
NEARBY_LOGS(VERBOSE)
<< "Received a DATA frame to feed virtual socket for salted service ID "
"Hash Key "
<< salted_service_id_hash_key;
virtual_socket->FeedIncomingData(ByteArray(frame.data()));
} else {
NEARBY_LOGS(WARNING)
<< __func__
<< "Received a DATA frame but there's no alive socket to feed for "
"salted service ID Hash Key "
<< salted_service_id_hash_key;
@@ -642,32 +634,50 @@ void MultiplexSocket::OnPhysicalSocketClosed() {
}
void MultiplexSocket::OnVirtualSocketClosed(const std::string& service_id) {
RunOffloadThread("VirtualSocketClosed", [this, service_id]() {
NEARBY_LOGS(INFO) << __func__ << " for service_id:" << service_id;
CountDownLatch latch(1);
bool shutdown = false;
RunOffloadThread("VirtualSocketClosed", [this, service_id, &latch,
&shutdown]() {
NEARBY_LOGS(INFO) << "Try to close Virtual socket: " << service_id;
MediumSocket* virtual_socket = GetVirtualSocket(service_id);
{
MutexLock lock(&virtual_socket_mutex_);
MediumSocket* virtual_socket = GetVirtualSocket(service_id);
NEARBY_LOGS(INFO) << "virtual_socket:" << virtual_socket;
if (virtual_socket != nullptr) {
virtual_sockets_.erase(GenerateServiceIdHashKeyWithSalt(
auto salted_service_id_hash_key = GenerateServiceIdHashKeyWithSalt(
service_id,
multiplex_output_stream_.GetServiceIdHashSalt(service_id)));
NEARBY_LOGS(INFO) << __func__ << "Virtual socket(" << service_id
<< ") disconnected";
multiplex_output_stream_.GetServiceIdHashSalt(service_id));
multiplex_output_stream_.Close(service_id);
virtual_socket->Close();
virtual_sockets_.erase(salted_service_id_hash_key);
NEARBY_LOGS(INFO) << "Erase Virtual socket with service_id: "
<< service_id
<< ", hash_key: " << salted_service_id_hash_key;
ListVirtualSocket();
if (virtual_sockets_.empty()) {
NEARBY_LOGS(INFO) << __func__
<< "Close the physical socket because all virtual "
NEARBY_LOGS(INFO) << "Close the physical socket because all virtual "
"sockets disconnected.";
physical_socket_->Close();
Shutdown();
shutdown = true;
}
return;
} else {
NEARBY_LOGS(INFO) << "Virtual socket(" << service_id
<< ") not found";
}
NEARBY_LOGS(INFO) << __func__ << "Virtual socket(" << service_id
<< ") not found";
}
latch.CountDown();
});
if (!latch.Await(absl::Milliseconds(1000)).result()) {
NEARBY_LOGS(ERROR) << "Timeout to close virtual socket";
}
if (shutdown) {
NEARBY_LOGS(INFO)
<< "Shutdown single_thread_offloader_ and physical_reader_thread_";
single_thread_offloader_.Shutdown();
physical_reader_thread_.Shutdown();
}
}
MediumSocket* MultiplexSocket::ReMapAndGetVirtualSocket(
@@ -675,8 +685,7 @@ MediumSocket* MultiplexSocket::ReMapAndGetVirtualSocket(
const std::string& service_id_hash_salt) {
std::string salted_service_id_hash_key =
GenerateServiceIdHashKey(salted_service_id_hash);
NEARBY_LOGS(VERBOSE) << __func__
<< "reMapAndGetVirtualSocket with serviceIdHashSalt="
NEARBY_LOGS(VERBOSE) << "ReMapAndGetVirtualSocket with serviceIdHashSalt="
<< service_id_hash_salt << ", saltedServiceIdHashKey="
<< salted_service_id_hash_key;
{
@@ -695,17 +704,19 @@ MediumSocket* MultiplexSocket::ReMapAndGetVirtualSocket(
(hash_key == salted_service_id_hash_key)) {
return virtual_socket.get();
} else {
NEARBY_LOGS(INFO) << __func__ << "Remap the virtualSockets.";
virtual_sockets_.erase(hash_key);
NEARBY_LOGS(INFO) << "Remap the virtualSockets.";
output_stream->SetserviceIdHashSalt(service_id_hash_salt);
virtual_sockets_.emplace(salted_service_id_hash_key, virtual_socket);
return virtual_socket.get();
auto virtual_socket_tmp = virtual_socket;
NEARBY_LOGS(INFO) << "virtual_socket before:" << virtual_socket;
virtual_sockets_.erase(hash_key);
virtual_sockets_[salted_service_id_hash_key] = virtual_socket_tmp;
ListVirtualSocket();
return virtual_socket_tmp.get();
}
}
}
NEARBY_LOGS(INFO) << __func__ << "Failed to remap the virtualSockets.";
NEARBY_LOGS(INFO) << "Failed to remap the virtualSockets.";
return nullptr;
}
@@ -715,9 +726,13 @@ void MultiplexSocket::RunOffloadThread(const std::string& name,
}
void MultiplexSocket::Shutdown() {
NEARBY_LOGS(INFO) << __func__ << " shutdown";
NEARBY_LOGS(INFO) << __func__ << " start";
if (is_shutdown_) {
NEARBY_LOGS(INFO) << __func__ << " Already shutdown";
return;
}
{
MutexLock lock(&virtual_socket_mutex_);
// MutexLock lock(&virtual_socket_mutex_);
for (auto& [hash_key, virtual_socket] : virtual_sockets_) {
if (virtual_socket != nullptr) {
virtual_socket->Close();
@@ -727,14 +742,23 @@ void MultiplexSocket::Shutdown() {
}
multiplex_output_stream_.Shutdown();
physical_socket_->Close();
switch (medium_) {
case Medium::BLUETOOTH:
bluetooth_socket_.Close();
break;
case Medium::UNKNOWN_MEDIUM:
NEARBY_LOGS(INFO) << __func__ << " Unknown medium";
break;
default:
break;
}
GetIncomingConnectionCallbacks().clear();
connection_response_futures_.clear();
physical_reader_thread_.Shutdown();
single_thread_offloader_.Shutdown();
is_shutdown_ = true;
enabled_.Set(false);
NEARBY_LOGS(INFO) << __func__ << " end";
}
} // namespace multiplex
@@ -15,31 +15,25 @@
#ifndef CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_SOCKET_H_
#define CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_SOCKET_H_
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <queue>
#include <string>
#include <utility>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/functional/any_invocable.h"
#include "connections/implementation/mediums/multiplex/multiplex_output_stream.h"
#include "connections/medium_selector.h"
#include "internal/platform/atomic_boolean.h"
#include "internal/platform/ble.h"
#include "internal/platform/bluetooth_classic.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/exception.h"
#include "internal/platform/feature_flags.h"
#include "internal/platform/future.h"
#include "internal/platform/input_stream.h"
#include "internal/platform/logging.h"
#include "internal/platform/mutex.h"
#include "internal/platform/output_stream.h"
#include "internal/platform/settable_future.h"
#include "internal/platform/single_thread_executor.h"
#include "internal/platform/socket.h"
#include "internal/platform/wifi_lan.h"
#include "proto/connections_enums.pb.h"
#include "proto/mediums/multiplex_frames.pb.h"
@@ -69,9 +63,10 @@ class MultiplexSocket {
static MultiplexSocket* CreateOutgoingSocket(MediumSocket* physical_socket,
const std::string& service_id);
// A Table of service Id as row key, medium type as column key, and {@link
// IncomingConnectionCallback} as value. Non-empty while the client starts
// listening for incoming virtual socket.
// A Table of service Id as row key, medium type as column key, and
// MultiplexIncomingConnectionCb as value. Non-empty while the client starts
// listening for incoming virtual socket. The MultiplexIncomingConnectionCb
// will be called when the incoming virtual socket is established.
static absl::flat_hash_map<
std::pair<std::string, ::location::nearby::proto::connections::Medium>,
MultiplexIncomingConnectionCb>&
@@ -106,10 +101,14 @@ class MultiplexSocket {
// Gets the virtual socket count.
int GetVirtualSocketCount();
void ListVirtualSocket();
// Establishes the virtual socket by service id.
MediumSocket* EstablishVirtualSocket(const std::string& service_id);
// Shuts down the multiplex socket.
void Shutdown();
bool IsShutdown() { return is_shutdown_; }
void SetShutdown(bool is_shutdown) { is_shutdown_ = is_shutdown; }
private:
explicit MultiplexSocket(MediumSocket* physical_socket);
@@ -167,10 +166,18 @@ class MultiplexSocket {
// The physical socket connect to the remote device.
MediumSocket* physical_socket_;
// The output stream to manage all outgoing frames from all clients.
MultiplexOutputStream multiplex_output_stream_;
// The {@link InputStream} of the physical socket.
// The {@link InputStream} of the physical socket. It is used to read the
// incoming MultiplexFrame from the physical socket.
InputStream* physical_reader_;
// The medium type of the physical socket.
Medium medium_;
// Save the phyical socket here, so it can be closed when all the virtual
// socket is gone.
BluetoothSocket bluetooth_socket_;
WifiLanSocket wifi_lan_socket_;
// The callback to enable the MultiplexSocket.
std::shared_ptr<absl::AnyInvocable<void()>> enable_cb_ =
@@ -191,7 +198,8 @@ class MultiplexSocket {
// MultiplexSocket object
mutable Mutex virtual_socket_mutex_;
absl::flat_hash_map<std::string, std::shared_ptr<MediumSocket>>
virtual_sockets_ ABSL_GUARDED_BY(virtual_socket_mutex_);
// virtual_sockets_ ABSL_GUARDED_BY(virtual_socket_mutex_);
virtual_sockets_;
// The thread to receive incoming MultiplexFrame from the physical socket.
SingleThreadExecutor physical_reader_thread_;
@@ -90,11 +90,18 @@ class FakeSocket : public MediumSocket {
InputStream& GetInputStream() override { return *reader_1_; }
OutputStream& GetOutputStream() override { return *writer_2_; }
void Close() override {
Exception Close() override {
if (IsVirtualSocket()) {
NEARBY_LOGS(INFO) << "Multiplex: Closing virtual socket: " << this;
CloseLocal();
return {Exception::kSuccess};
}
NEARBY_LOGS(INFO) << "Multiplex: Closing physical socket: " << this;
reader_1_->Close();
reader_2_->Close();
writer_1_->Close();
writer_2_->Close();
return {Exception::kSuccess};
}
MediumSocket* CreateVirtualSocket(
@@ -148,13 +155,12 @@ class FakeSocket : public MediumSocket {
};
TEST(MultiplexSocketTest, CreateSuccessAndReaderThreadStarted) {
testing::NiceMock<FakeSocket> fake_socket{Medium::WIFI_LAN};
testing::NiceMock<FakeSocket> fake_socket{Medium::BLUETOOTH};
MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_1),
Medium::WIFI_LAN);
Medium::BLUETOOTH);
MultiplexSocket* multiplex_socket_incoming =
MultiplexSocket::CreateIncomingSocket(&fake_socket,
std::string(SERVICE_ID_1));
ASSERT_NE(multiplex_socket_incoming, nullptr);
FakeSocket* virtual_socket =
(FakeSocket*)multiplex_socket_incoming->GetVirtualSocket(
@@ -189,10 +195,9 @@ TEST(MultiplexSocketTest, CreateSuccessAndReaderThreadStarted) {
ByteArray data = result.result();
NEARBY_LOGS(INFO) << "Received " << data.size() << " bytes of data.";
EXPECT_NE(data.size(), 0);
absl::SleepFor(absl::Milliseconds(100));
fake_socket.reader_1_->Close();
EXPECT_EQ(multiplex_socket_incoming->GetVirtualSocketCount(), 1);
multiplex_socket_incoming->Shutdown();
EXPECT_EQ(multiplex_socket_incoming->GetVirtualSocketCount(), 0);
}
TEST(MultiplexSocketTest, CreateFail_MediumNotSupport) {
@@ -208,11 +213,11 @@ TEST(MultiplexSocketTest, CreateFail_MediumNotSupport) {
TEST(MultiplexSocketTest,
EstablishVirtualSocket_ReturnNullWhenMultiplexSocketDisabled) {
testing::NiceMock<FakeSocket> fake_socket{Medium::WIFI_LAN};
testing::NiceMock<FakeSocket> fake_socket{Medium::BLE};
MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_1),
Medium::WIFI_LAN);
Medium::BLE);
MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_2),
Medium::WIFI_LAN);
Medium::BLE);
MultiplexSocket* multiplex_socket = MultiplexSocket::CreateOutgoingSocket(
&fake_socket, std::string(SERVICE_ID_1));
ASSERT_NE(multiplex_socket, nullptr);
@@ -221,24 +226,36 @@ TEST(MultiplexSocketTest,
multiplex_socket->EstablishVirtualSocket(std::string(SERVICE_ID_2));
EXPECT_EQ(socket, nullptr);
absl::SleepFor(absl::Milliseconds(100));
FakeSocket* virtual_socket =
(FakeSocket*)multiplex_socket->GetVirtualSocket(
std::string(SERVICE_ID_1));
if (virtual_socket == nullptr) {
NEARBY_LOGS(INFO) << "Virtual socket not found for " << SERVICE_ID_1;
return;
}
fake_socket.reader_1_->Close();
EXPECT_EQ(multiplex_socket->GetVirtualSocketCount(), 1);
multiplex_socket->Shutdown();
virtual_socket->Close();
EXPECT_EQ(multiplex_socket->GetVirtualSocketCount(), 0);
}
TEST(MultiplexSocketTest,
EstablishVirtualSocket_TimeoutBecauseNoConnectionResponse) {
testing::NiceMock<FakeSocket> fake_socket{Medium::BLUETOOTH};
testing::NiceMock<FakeSocket> fake_socket{Medium::WIFI_LAN};
MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_1),
Medium::BLUETOOTH);
Medium::WIFI_LAN);
MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_2),
Medium::BLUETOOTH);
Medium::WIFI_LAN);
MultiplexSocket* multiplex_socket = MultiplexSocket::CreateOutgoingSocket(
&fake_socket, std::string(SERVICE_ID_1));
ASSERT_NE(multiplex_socket, nullptr);
multiplex_socket->Enable();
FakeSocket* virtual_socket = (FakeSocket*)multiplex_socket->GetVirtualSocket(
std::string(SERVICE_ID_1));
if (virtual_socket == nullptr) {
NEARBY_LOGS(INFO) << "Virtual socket not found for " << SERVICE_ID_1;
return;
}
SingleThreadExecutor executor;
CountDownLatch latch(1);
@@ -268,17 +285,17 @@ TEST(MultiplexSocketTest,
absl::SleepFor(absl::Milliseconds(100));
fake_socket.reader_1_->Close();
EXPECT_EQ(multiplex_socket->GetVirtualSocketCount(), 1);
multiplex_socket->Shutdown();
virtual_socket->Close();
EXPECT_EQ(multiplex_socket->GetVirtualSocketCount(), 0);
}
TEST(MultiplexSocketTest,
EstablishVirtualSocket_RemoteAccepted) {
testing::NiceMock<FakeSocket> fake_socket{Medium::BLE};
testing::NiceMock<FakeSocket> fake_socket{Medium::BLUETOOTH};
MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_1),
Medium::BLE);
Medium::BLUETOOTH);
MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_2),
Medium::BLE);
Medium::BLUETOOTH);
MultiplexSocket* multiplex_socket = MultiplexSocket::CreateOutgoingSocket(
&fake_socket, std::string(SERVICE_ID_1));
@@ -351,8 +368,6 @@ TEST(MultiplexSocketTest,
fake_socket.reader_1_->Close();
EXPECT_EQ(multiplex_socket->GetVirtualSocketCount(), 2);
multiplex_socket->Shutdown();
EXPECT_EQ(multiplex_socket->GetVirtualSocketCount(), 0);
}
} // namespace multiplex
@@ -90,9 +90,9 @@ InputStream& WebRtcSocket::GetInputStream() { return *pipe_input_; }
OutputStream& WebRtcSocket::GetOutputStream() { return output_stream_; }
void WebRtcSocket::Close() {
Exception WebRtcSocket::Close() {
NEARBY_LOGS(INFO) << "WebRtcSocket::Close(" << name_ << ") this: " << this;
if (closed_.Set(true)) return;
if (closed_.Set(true)) return {Exception::kSuccess};
ClosePipe();
// NOTE: This call blocks and triggers a state change on the siginaling thread
@@ -101,6 +101,7 @@ void WebRtcSocket::Close() {
data_channel_->Close();
NEARBY_LOGS(INFO) << "WebRtcSocket::Close(" << name_ << ") this: " << this
<< " done";
return {Exception::kSuccess};
}
void WebRtcSocket::OnStateChange() {
@@ -15,17 +15,20 @@
#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_SOCKET_IMPL_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_SOCKET_IMPL_H_
#ifndef NO_WEBRTC
#include <cstdint>
#include <string>
#include <memory>
#include "connections/listeners.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "internal/platform/listeners.h"
#include "internal/platform/runnable.h"
#ifndef NO_WEBRTC
#include "internal/platform/atomic_boolean.h"
#include "internal/platform/condition_variable.h"
#include "internal/platform/input_stream.h"
#include "internal/platform/mutex.h"
#include "internal/platform/output_stream.h"
#include "internal/platform/pipe.h"
#include "internal/platform/single_thread_executor.h"
#include "internal/platform/socket.h"
#include "webrtc/api/data_channel_interface.h"
@@ -54,7 +57,7 @@ class WebRtcSocket : public Socket, public webrtc::DataChannelObserver {
// Overrides for nearby::Socket:
InputStream& GetInputStream() override;
OutputStream& GetOutputStream() override;
void Close() override;
Exception Close() override;
// webrtc::DataChannelObserver:
void OnStateChange() override;
@@ -15,6 +15,7 @@
#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_SOCKET_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_SOCKET_H_
#include "internal/platform/exception.h"
#ifndef NO_WEBRTC
#include <memory>
@@ -38,7 +39,7 @@ class WebRtcSocketWrapper final {
OutputStream& GetOutputStream() { return impl_->GetOutputStream(); }
void Close() { return impl_->Close(); }
Exception Close() { return impl_->Close(); }
bool IsValid() const { return impl_ != nullptr; }
@@ -549,7 +549,7 @@ void P2pClusterPcpHandler::BlePeripheralDiscoveredHandler(
NEARBY_LOGS(ERROR) << ble_status_or.status().ToString();
return;
}
auto advertisement = ble_status_or.value();
const auto& advertisement = ble_status_or.value();
// Make sure the BLE advertisement points to a valid
// endpoint we're discovering.
@@ -726,7 +726,7 @@ void P2pClusterPcpHandler::BleV2PeripheralDiscoveredHandler(
NEARBY_LOGS(ERROR) << ble_status_or.status();
return;
}
auto advertisement = ble_status_or.value();
const auto& advertisement = ble_status_or.value();
// Make sure the BLE advertisement points to a valid
// endpoint we're discovering.
+3
View File
@@ -280,6 +280,7 @@ cc_test(
cc_library(
name = "types",
srcs = [
"blocking_queue_stream.cc",
"clock_impl.cc",
"device_info_impl.cc",
"monitored_runnable.cc",
@@ -289,8 +290,10 @@ cc_library(
"timer_impl.cc",
],
hdrs = [
"array_blocking_queue.h",
"atomic_boolean.h",
"atomic_reference.h",
"blocking_queue_stream.h",
"borrowable.h",
"cancelable.h",
"cancelable_alarm.h",
+104
View File
@@ -0,0 +1,104 @@
// Copyright 2024 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.
#ifndef PLATFORM_PUBLIC_ARRAY_BLOCKING_QUEUE_H_
#define PLATFORM_PUBLIC_ARRAY_BLOCKING_QUEUE_H_
#include <cstddef>
#include <optional>
#include <queue>
#include "internal/platform/condition_variable.h"
#include "internal/platform/logging.h"
#include "internal/platform/mutex.h"
#include "internal/platform/mutex_lock.h"
namespace nearby {
/**
* Payload from different services/clients will be put into an
* ArrayBlockingQueue before sending to ensure each client has equal chance to
* send its data. Since C++ doesn't provide ArrayBlockingQueue as Java, we
* implement one here.
*/
template <typename T>
class ArrayBlockingQueue {
public:
explicit ArrayBlockingQueue(size_t capacity) : capacity_(capacity) {}
void Put(const T& value) {
MutexLock lock(&queue_mutex_);
if (queue_.size() >= capacity_) {
has_space_.Wait();
}
queue_.push(value);
NEARBY_LOGS(INFO) << "ArrayBlockingQueue::Put()";
has_data_.Notify();
}
T Take() {
MutexLock lock(&queue_mutex_);
if (queue_.empty()) {
has_data_.Wait();
}
T front = queue_.front();
queue_.pop();
NEARBY_LOGS(INFO) << "ArrayBlockingQueue::Take()";
has_space_.Notify();
return front;
}
bool TryPut(const T& value) {
MutexLock lock(&queue_mutex_);
if (queue_.size() < capacity_) {
queue_.push(value);
has_data_.Notify();
return true;
}
return false;
}
// Returns std::nullopt if the queue is empty.
std::optional<T> TryTake() {
MutexLock lock(&queue_mutex_);
if (!queue_.empty()) {
T front = queue_.front();
queue_.pop();
has_space_.Notify();
return front;
}
return std::nullopt;
}
size_t Size() const {
MutexLock lock(&queue_mutex_);
return queue_.size();
}
bool Empty() const {
MutexLock lock(&queue_mutex_);
return queue_.empty();
}
private:
std::queue<T> queue_;
mutable Mutex queue_mutex_;
ConditionVariable has_data_{&queue_mutex_};
ConditionVariable has_space_{&queue_mutex_};
const size_t capacity_;
};
} // namespace nearby
#endif // PLATFORM_PUBLIC_ARRAY_BLOCKING_QUEUE_H_
@@ -0,0 +1,72 @@
// Copyright 2024 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 "internal/platform/blocking_queue_stream.h"
#include <cstdint>
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "internal/platform/feature_flags.h"
#include "internal/platform/logging.h"
namespace nearby {
BlockingQueueStream::BlockingQueueStream() {
NEARBY_LOGS(INFO) << "Create a BlockingQueueStream with size "
<< FeatureFlags::GetInstance()
.GetFlags()
.blocking_queue_stream_queue_capacity;
}
ExceptionOr<ByteArray> BlockingQueueStream::Read(std::int64_t size) {
if (is_closed_) {
NEARBY_LOGS(INFO)
<< "Failed to read BlockingQueueStream because it was closed.";
return ExceptionOr<ByteArray>(Exception::kInterrupted);
}
NEARBY_LOGS(INFO) << "BlockingQueueStream read " << size << " bytes";
return ExceptionOr<ByteArray>(blocking_queue_.Take());
}
void BlockingQueueStream::Write(const ByteArray& bytes) {
if (is_closed_) {
NEARBY_LOGS(INFO)
<< "Failed to write BlockingQueueStream because it was closed.";
return;
}
is_writing_ = true;
blocking_queue_.Put(bytes);
is_writing_ = false;
NEARBY_LOGS(VERBOSE) << "BlockingQueueStream wrote " << bytes.size()
<< " bytes";
}
Exception BlockingQueueStream::Close() {
if (is_closed_) {
NEARBY_LOGS(INFO) << "InputBlockingQueueStream has already been closed.";
return {Exception::kSuccess};
}
if (is_writing_) {
NEARBY_LOGS(INFO)
<< "BlockingQueueStream is waiting for writing, read first to unblock";
blocking_queue_.TryTake();
}
blocking_queue_.TryPut(queue_end_);
is_closed_ = true;
NEARBY_LOGS(INFO) << "InputBlockingQueueStream is closed.";
return {Exception::kSuccess};
}
} // namespace nearby
+52
View File
@@ -0,0 +1,52 @@
// Copyright 2024 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.
#ifndef PLATFORM_PUBLIC_BLOCKING_QUEUE_STREAM_H_
#define PLATFORM_PUBLIC_BLOCKING_QUEUE_STREAM_H_
#include <cstdint>
#include "internal/platform/array_blocking_queue.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "internal/platform/feature_flags.h"
#include "internal/platform/input_stream.h"
#include "internal/platform/mutex.h"
namespace nearby {
class BlockingQueueStream : public InputStream {
public:
BlockingQueueStream();
~BlockingQueueStream() override = default;
ExceptionOr<ByteArray> Read(std::int64_t size) override;
void Write(const ByteArray& bytes);
Exception Close() override;
bool IsWriting() const {
return is_writing_;
}
private:
mutable Mutex mutex_;
ArrayBlockingQueue<ByteArray> blocking_queue_{FeatureFlags::GetInstance()
.GetFlags()
.blocking_queue_stream_queue_capacity};
ByteArray queue_end_{0};
bool is_writing_ = false;
bool is_closed_ = false;
};
} // namespace nearby
#endif // #ifndef PLATFORM_PUBLIC_BLOCKING_QUEUE_STREAM_H_
+48 -1
View File
@@ -14,10 +14,57 @@
#include "internal/platform/bluetooth_classic.h"
#include <memory>
#include <string>
#include <utility>
#include "absl/container/flat_hash_map.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/cancellation_flag.h"
#include "internal/platform/implementation/bluetooth_classic.h"
#include "internal/platform/logging.h"
#include "internal/platform/mutex_lock.h"
#include "internal/platform/output_stream.h"
#include "internal/platform/socket.h"
namespace nearby {
using location::nearby::proto::connections::Medium;
MediumSocket* BluetoothSocket::CreateVirtualSocket(OutputStream* outputstream) {
if (IsVirtualSocket()) {
NEARBY_LOGS(WARNING)
<< "Creating the virtual socket on a virtual socket is not allowed.";
return nullptr;
}
auto virtual_socket = std::make_shared<BluetoothSocket>(outputstream);
return virtual_socket.get();
}
MediumSocket* BluetoothSocket::CreateVirtualSocket(
const std::string& salted_service_id_hash_key, OutputStream* outputstream,
Medium medium,
absl::flat_hash_map<std::string, std::shared_ptr<MediumSocket>>*
virtual_sockets_ptr) {
if (IsVirtualSocket()) {
NEARBY_LOGS(WARNING)
<< "Creating the virtual socket on a virtual socket is not allowed.";
return nullptr;
}
auto virtual_socket = std::make_shared<BluetoothSocket>(outputstream);
virtual_socket->impl_ = this->impl_;
NEARBY_LOGS(WARNING) << "Created the virtual socket for Medium: "
<< Medium_Name(virtual_socket->GetMedium());
if (virtual_sockets_ptr_ == nullptr) {
virtual_sockets_ptr_ = virtual_sockets_ptr;
}
(*virtual_sockets_ptr_)[salted_service_id_hash_key] = virtual_socket;
NEARBY_LOGS(INFO) << "virtual_sockets_ size: "
<< virtual_sockets_ptr_->size();
return virtual_socket.get();
}
BluetoothClassicMedium::~BluetoothClassicMedium() {
NEARBY_LOG(INFO, "~BluetoothClassicMedium: observer_list_ size: %d",
@@ -54,7 +101,7 @@ bool BluetoothClassicMedium::StartDiscovery(DiscoveryCallback callback) {
device.GetName().c_str());
MutexLock lock(&mutex_);
auto pair = devices_.emplace(
&device, absl::make_unique<DeviceDiscoveryInfo>());
&device, std::make_unique<DeviceDiscoveryInfo>());
auto& context = *pair.first->second;
if (!pair.second) {
NEARBY_LOG(INFO, "Adding (again) device=%p, impl=%p",
+68 -9
View File
@@ -15,14 +15,15 @@
#ifndef PLATFORM_PUBLIC_BLUETOOTH_CLASSIC_H_
#define PLATFORM_PUBLIC_BLUETOOTH_CLASSIC_H_
#include <stdbool.h>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "internal/base/observer_list.h"
#include "internal/platform/blocking_queue_stream.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/cancellation_flag.h"
@@ -34,29 +35,79 @@
#include "internal/platform/logging.h"
#include "internal/platform/mutex.h"
#include "internal/platform/output_stream.h"
#include "internal/platform/socket.h"
namespace nearby {
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html.
class BluetoothSocket final {
class BluetoothSocket : public MediumSocket {
public:
BluetoothSocket() = default;
BluetoothSocket()
: MediumSocket(location::nearby::proto::connections::Medium::BLUETOOTH) {
};
BluetoothSocket(const BluetoothSocket&) = default;
BluetoothSocket& operator=(const BluetoothSocket&) = default;
// Creates a physical BluetoothSocket from a platform implementation.
explicit BluetoothSocket(std::unique_ptr<api::BluetoothSocket> socket)
: impl_(socket.release()) {}
~BluetoothSocket() = default;
: MediumSocket(location::nearby::proto::connections::Medium::BLUETOOTH),
impl_(socket.release()) {}
// Creates a virtual BluetoothSocket from a virtual output stream.
explicit BluetoothSocket(OutputStream* virtual_output_stream)
: MediumSocket(location::nearby::proto::connections::Medium::BLUETOOTH),
blocking_queue_input_stream_(std::make_shared<BlockingQueueStream>()),
virtual_output_stream_(virtual_output_stream),
is_virtual_socket_(true) {}
~BluetoothSocket() override = default;
// Returns the InputStream of this connected BluetoothSocket.
InputStream& GetInputStream() { return impl_->GetInputStream(); }
InputStream& GetInputStream() override {
return IsVirtualSocket() ? *blocking_queue_input_stream_
: impl_->GetInputStream();
}
// Returns the OutputStream of this connected BluetoothSocket.
OutputStream& GetOutputStream() { return impl_->GetOutputStream(); }
OutputStream& GetOutputStream() override {
return IsVirtualSocket() ? *virtual_output_stream_
: impl_->GetOutputStream();
}
// Closes both input and output streams, marks Socket as closed.
// After this call object should be treated as not connected.
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
Exception Close() { return impl_->Close(); }
Exception Close() override {
if (IsVirtualSocket()) {
NEARBY_LOGS(INFO) << "Multiplex: Closing virtual socket: " << this;
blocking_queue_input_stream_->Close();
virtual_output_stream_->Close();
CloseLocal();
return {Exception::kSuccess};
}
NEARBY_LOGS(INFO) << "Multiplex: Closing physical socket: " << this;
return impl_->Close();
}
// Returns true if this is a virtual socket.
bool IsVirtualSocket() override { return is_virtual_socket_; }
// Creates a virtual socket only with outputstream.
MediumSocket* CreateVirtualSocket(OutputStream* outputstream) override;
MediumSocket* CreateVirtualSocket(
const std::string& salted_service_id_hash_key, OutputStream* outputstream,
location::nearby::proto::connections::Medium medium,
absl::flat_hash_map<std::string, std::shared_ptr<MediumSocket>>*
virtual_sockets_ptr) override;
/** Feeds the received incoming data to the client. */
void FeedIncomingData(ByteArray data) override {
if (!IsVirtualSocket()) {
NEARBY_LOGS(INFO) << "Feeding data on a physical socket is not allowed.";
return;
}
blocking_queue_input_stream_->Write(data);
}
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#getRemoteDevice()
BluetoothDevice GetRemoteDevice() {
@@ -73,7 +124,10 @@ class BluetoothSocket final {
// BluetoothServerSocket::Accept().
// These methods may also return an invalid socket if connection failed for
// any reason.
bool IsValid() const { return impl_ != nullptr; }
bool IsValid() const {
if (is_virtual_socket_) return true;
return impl_ != nullptr;
}
// Returns reference to platform implementation.
// This is used to communicate with platform code, and for debugging purposes.
@@ -84,6 +138,11 @@ class BluetoothSocket final {
private:
std::shared_ptr<api::BluetoothSocket> impl_;
absl::flat_hash_map<std::string, std::shared_ptr<MediumSocket>>*
virtual_sockets_ptr_ = nullptr;
std::shared_ptr<BlockingQueueStream> blocking_queue_input_stream_ = nullptr;
OutputStream* virtual_output_stream_ = nullptr;
bool is_virtual_socket_ = false;
};
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html.
+1
View File
@@ -110,6 +110,7 @@ class FeatureFlags {
// The maximum size of frame we'll attempt to read, to avoid a remote device
// from triggering an OutOfMemory error.
std::uint32_t connection_max_frame_length = 1048576;
std::uint32_t blocking_queue_stream_queue_capacity = 10;
};
static const FeatureFlags& GetInstance() {
+12 -5
View File
@@ -23,6 +23,7 @@
#include "absl/container/flat_hash_set.h"
#include "absl/functional/any_invocable.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "internal/platform/input_stream.h"
#include "internal/platform/output_stream.h"
#include "proto/connections_enums.pb.h"
@@ -38,7 +39,7 @@ class Socket {
virtual InputStream& GetInputStream() = 0;
virtual OutputStream& GetOutputStream() = 0;
virtual void Close() = 0;
virtual Exception Close() = 0;
};
class MediumSocket : public Socket {
@@ -52,6 +53,11 @@ class MediumSocket : public Socket {
return medium_;
}
/** Creates a virtual socket only with outputstream. */
virtual MediumSocket* CreateVirtualSocket(OutputStream* outputstream) {
return this;
}
/** Creates a virtual socket. */
virtual MediumSocket* CreateVirtualSocket(
const std::string& salted_service_id_hash_key, OutputStream* outputstream,
@@ -62,7 +68,9 @@ class MediumSocket : public Socket {
}
/** Feeds the received incoming data to the client. */
virtual void FeedIncomingData(ByteArray data) {}
virtual void FeedIncomingData(ByteArray data) {
// NEARBY_LOGS(INFO) << "FeedIncomingData: do nothing";
}
/** Returns true if the socket is a virtual socket. */
virtual bool IsVirtualSocket() {
@@ -86,16 +94,15 @@ class MediumSocket : public Socket {
if (!IsVirtualSocket()) {
return;
}
for (auto& callback : multiplex_socket_enabled_cbs_) {
callback.get();
(*callback)();
}
}
/** Closes the local socket. */
void CloseLocal() {
for (auto& listener : socket_closed_listeners_) {
listener.get();
(*listener)();
}
}