diff --git a/connections/implementation/base_endpoint_channel.cc b/connections/implementation/base_endpoint_channel.cc index 4e142839..ec8814e1 100644 --- a/connections/implementation/base_endpoint_channel.cc +++ b/connections/implementation/base_endpoint_channel.cc @@ -331,6 +331,11 @@ void BaseEndpointChannel::Close( } } +bool BaseEndpointChannel::IsClosed() const { + MutexLock lock(&is_paused_mutex_); + return is_closed_; +} + std::string BaseEndpointChannel::GetType() const { MutexLock crypto_lock(&crypto_mutex_); std::string subtype = IsEncryptionEnabledLocked() ? "ENCRYPTED_" : ""; diff --git a/connections/implementation/base_endpoint_channel.h b/connections/implementation/base_endpoint_channel.h index 570e96ac..7864e382 100644 --- a/connections/implementation/base_endpoint_channel.h +++ b/connections/implementation/base_endpoint_channel.h @@ -28,7 +28,6 @@ #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 { @@ -63,6 +62,7 @@ class BaseEndpointChannel : public EndpointChannel { location::nearby::proto::connections::DisconnectionReason reason, location::nearby::analytics::proto::ConnectionsLog:: EstablishedConnection::SafeDisconnectionResult result) override; + bool IsClosed() const ABSL_LOCKS_EXCLUDED(is_paused_mutex_) override; std::string GetType() const override; std::string GetServiceId() const override; std::string GetName() const override; diff --git a/connections/implementation/client_proxy.cc b/connections/implementation/client_proxy.cc index b6d77e77..174a1a40 100644 --- a/connections/implementation/client_proxy.cc +++ b/connections/implementation/client_proxy.cc @@ -1193,9 +1193,11 @@ std::int32_t ClientProxy::GetLocalMultiplexSocketBitmask() const { if (NearbyFlags::GetInstance().GetBoolFlag( config_package_nearby::nearby_connections_feature:: kEnableMultiplex)) { + std::int32_t multiplex_bitmask = + kBtMultiplexEnabled | kWifiLanMultiplexEnabled; NEARBY_LOGS(INFO) << "ClientProxy [GetLocalMultiplexSocketBitmask]: " - << kBtMultiplexEnabled; - return kBtMultiplexEnabled; + << multiplex_bitmask; + return multiplex_bitmask; } return 0; } diff --git a/connections/implementation/client_proxy_test.cc b/connections/implementation/client_proxy_test.cc index 7113806a..f40ed070 100644 --- a/connections/implementation/client_proxy_test.cc +++ b/connections/implementation/client_proxy_test.cc @@ -1554,19 +1554,23 @@ TEST_F(ClientProxyTest, TestAutoBwuWhenListeningWithAutoBwu) { } TEST_F(ClientProxyTest, TestMultiplexSocketBitmask) { - EXPECT_EQ(client1()->GetLocalMultiplexSocketBitmask(), 0); + if (!NearbyFlags::GetInstance().GetBoolFlag( + config_package_nearby::nearby_connections_feature:: + kEnableMultiplex)) { + EXPECT_EQ(client1()->GetLocalMultiplexSocketBitmask(), 0); + } NearbyFlags::GetInstance().OverrideBoolFlagValue( config_package_nearby::nearby_connections_feature::kEnableMultiplex, true); EXPECT_EQ(client1()->GetLocalMultiplexSocketBitmask(), - ClientProxy::kBtMultiplexEnabled); + ClientProxy::kBtMultiplexEnabled | + ClientProxy::kWifiLanMultiplexEnabled); 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); @@ -1586,7 +1590,7 @@ TEST_F(ClientProxyTest, TestRemoteMultiplexSocketBitmask) { ClientProxy::kBtMultiplexEnabled | ClientProxy::kWifiLanMultiplexEnabled); EXPECT_TRUE(client1()->IsMultiplexSocketSupported(advertising_endpoint.id, Medium::BLUETOOTH)); - EXPECT_FALSE(client1()->IsMultiplexSocketSupported(advertising_endpoint.id, + EXPECT_TRUE(client1()->IsMultiplexSocketSupported(advertising_endpoint.id, Medium::WIFI_LAN)); EXPECT_FALSE(client1()->IsMultiplexSocketSupported(advertising_endpoint.id, Medium::WIFI_AWARE)); diff --git a/connections/implementation/connections_authentication_transport_test.cc b/connections/implementation/connections_authentication_transport_test.cc index fc5058da..c471a801 100644 --- a/connections/implementation/connections_authentication_transport_test.cc +++ b/connections/implementation/connections_authentication_transport_test.cc @@ -51,6 +51,7 @@ class MockEndpointChannel : public EndpointChannel { location::nearby::analytics::proto::ConnectionsLog:: EstablishedConnection::SafeDisconnectionResult result), (override)); + MOCK_METHOD(bool, IsClosed, (), (const, override)); MOCK_METHOD(std::string, GetType, (), (const, override)); MOCK_METHOD(std::string, GetServiceId, (), (const, override)); MOCK_METHOD(std::string, GetName, (), (const, override)); diff --git a/connections/implementation/encryption_runner_test.cc b/connections/implementation/encryption_runner_test.cc index 5702c284..a691efb9 100644 --- a/connections/implementation/encryption_runner_test.cc +++ b/connections/implementation/encryption_runner_test.cc @@ -28,7 +28,6 @@ #include "internal/platform/input_stream.h" #include "internal/platform/output_stream.h" #include "internal/platform/pipe.h" -#include "internal/platform/system_clock.h" #include "proto/connections_enums.pb.h" #include "third_party/ukey2/src/main/cpp/include/securegcm/ukey2_handshake.h" @@ -74,6 +73,7 @@ class FakeEndpointChannel : public EndpointChannel { EstablishedConnection::SafeDisconnectionResult result) override { Close(); } + bool IsClosed() const override { return false; } location::nearby::proto::connections::ConnectionTechnology GetTechnology() const override { return location::nearby::proto::connections::ConnectionTechnology:: diff --git a/connections/implementation/endpoint_channel.h b/connections/implementation/endpoint_channel.h index 19d8b0f3..2de11abb 100644 --- a/connections/implementation/endpoint_channel.h +++ b/connections/implementation/endpoint_channel.h @@ -59,6 +59,9 @@ class EndpointChannel { location::nearby::analytics::proto::ConnectionsLog:: EstablishedConnection::SafeDisconnectionResult result) = 0; + // True if the EndpointChannel is currently closed. + virtual bool IsClosed() const = 0; + // Returns a one-word type descriptor for the concrete EndpointChannel // implementation that can be used in log messages; eg: BLUETOOTH, BLE, WIFI. virtual std::string GetType() const = 0; diff --git a/connections/implementation/endpoint_channel_manager.cc b/connections/implementation/endpoint_channel_manager.cc index 5ee27a48..b88b757e 100644 --- a/connections/implementation/endpoint_channel_manager.cc +++ b/connections/implementation/endpoint_channel_manager.cc @@ -23,12 +23,10 @@ #include "connections/implementation/endpoint_channel.h" #include "connections/implementation/offline_frames.h" #include "internal/platform/condition_variable.h" -#include "internal/platform/feature_flags.h" #include "internal/platform/implementation/system_clock.h" #include "internal/platform/logging.h" #include "internal/platform/mutex.h" #include "internal/platform/mutex_lock.h" -#include "proto/connections_enums.pb.h" namespace nearby { namespace connections { @@ -229,13 +227,14 @@ bool EndpointChannelManager::ChannelState::RemoveEndpoint( auto item = endpoints_.find(endpoint_id); if (item == endpoints_.end()) return false; - MarkEndpointStopWaitToDisconnect(endpoint_id, - /* is_safe_to_disconnect */ true, - /* notify_stop_waiting */ true); + MarkEndpointStopWaitToDisconnect( + endpoint_id, + /* is_safe_to_disconnect */ true, + /* notify_stop_waiting */ true); item->second.disconnect_reason = reason; auto channel = item->second.channel; - if (channel && !safe_to_disconnect_enabled) { + if (channel && !channel->IsClosed() && !safe_to_disconnect_enabled) { // If the channel was paused (i.e. during a bandwidth upgrade negotiation) // we resume to ensure the thread won't hang when trying to write to it. channel->Resume(); diff --git a/connections/implementation/endpoint_manager_test.cc b/connections/implementation/endpoint_manager_test.cc index 09466d63..8c10c07f 100644 --- a/connections/implementation/endpoint_manager_test.cc +++ b/connections/implementation/endpoint_manager_test.cc @@ -101,7 +101,7 @@ class MockEndpointChannel : public EndpointChannel { MOCK_METHOD(void, SetAnalyticsRecorder, (analytics::AnalyticsRecorder*, const std::string&), (override)); - bool IsClosed() const { + bool IsClosed() const override { absl::MutexLock lock(&mutex_); return closed_; } diff --git a/connections/implementation/fake_endpoint_channel.h b/connections/implementation/fake_endpoint_channel.h index 72725980..ffdd0453 100644 --- a/connections/implementation/fake_endpoint_channel.h +++ b/connections/implementation/fake_endpoint_channel.h @@ -65,6 +65,7 @@ class FakeEndpointChannel : public EndpointChannel { EstablishedConnection::SafeDisconnectionResult result) override { Close(reason); } + bool IsClosed() const override { return is_closed_; } location::nearby::proto::connections::ConnectionTechnology GetTechnology() const override { return location::nearby::proto::connections::ConnectionTechnology:: diff --git a/connections/implementation/mediums/BUILD b/connections/implementation/mediums/BUILD index 243e4b3a..38a8245a 100644 --- a/connections/implementation/mediums/BUILD +++ b/connections/implementation/mediums/BUILD @@ -59,6 +59,7 @@ cc_library( "//internal/platform:types", "//internal/platform:uuid", "//internal/platform/implementation:comm", + "//internal/platform/implementation:wifi_utils", "//proto/mediums:web_rtc_signaling_frames_cc_proto", # TODO: Support WebRTC "@com_google_absl//absl/base:core_headers", diff --git a/connections/implementation/mediums/bluetooth_classic.cc b/connections/implementation/mediums/bluetooth_classic.cc index f78ed08f..d477c8f2 100644 --- a/connections/implementation/mediums/bluetooth_classic.cc +++ b/connections/implementation/mediums/bluetooth_classic.cc @@ -401,7 +401,7 @@ bool BluetoothClassic::StartAcceptingConnections( std::make_shared(client_socket_bak); MultiplexSocket* multiplex_socket = MultiplexSocket::CreateIncomingSocket(physical_socket_ptr, - service_id); + service_id, 0); if (multiplex_socket != nullptr && multiplex_socket->GetVirtualSocket(service_id)) { diff --git a/connections/implementation/mediums/multiplex/multiplex_output_stream.h b/connections/implementation/mediums/multiplex/multiplex_output_stream.h index 24925e9b..b711fcb5 100644 --- a/connections/implementation/mediums/multiplex/multiplex_output_stream.h +++ b/connections/implementation/mediums/multiplex/multiplex_output_stream.h @@ -65,7 +65,7 @@ class MultiplexOutputStream { MultiplexOutputStream(OutputStream* physical_writer, AtomicBoolean& is_enabled); - ~MultiplexOutputStream() { Shutdown(); } + ~MultiplexOutputStream() = default; // Writes the connection request frame to the physical output stream. bool WriteConnectionRequestFrame(const std::string& service_id, diff --git a/connections/implementation/mediums/multiplex/multiplex_socket.cc b/connections/implementation/mediums/multiplex/multiplex_socket.cc index 8edcac66..7e983980 100644 --- a/connections/implementation/mediums/multiplex/multiplex_socket.cc +++ b/connections/implementation/mediums/multiplex/multiplex_socket.cc @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -23,10 +24,12 @@ #include "absl/container/flat_hash_map.h" #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" +#include "absl/time/clock.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/atomic_boolean.h" #include "internal/platform/base64_utils.h" #include "internal/platform/byte_array.h" #include "internal/platform/count_down_latch.h" @@ -64,6 +67,10 @@ using ::location::nearby::proto::connections::Medium; using ::location::nearby::proto::connections::Medium_Name; using ConnectionResponseCode = ConnectionResponseFrame::ConnectionResponseCode; +// AtomicBoolean is trivial destructible, so it is safe to use it as a static +// variable. +AtomicBoolean MultiplexSocket::is_shutting_down_{false}; // NOLINT + void MultiplexSocket::ListenForIncomingConnection( const std::string& service_id, Medium type, MultiplexIncomingConnectionCb incoming_connection_cb) { @@ -105,7 +112,12 @@ MultiplexSocket::GetIncomingConnectionCallbacks() { MultiplexSocket* MultiplexSocket::CreateIncomingSocket( std::shared_ptr physical_socket, - const std::string& service_id) { + const std::string& service_id, std::int32_t first_frame_len) { + while (is_shutting_down_.Get()) { + NEARBY_LOGS(WARNING) + << "Shutting down is going on, wait for 2ms to create incoming socket"; + absl::SleepFor(absl::Milliseconds(2)); + } static MultiplexSocket* multiplex_incoming_socket = nullptr; switch (physical_socket->GetMedium()) { case Medium::BLUETOOTH: @@ -114,7 +126,6 @@ MultiplexSocket* MultiplexSocket::CreateIncomingSocket( storage_bt; multiplex_incoming_socket = new (&storage_bt) MultiplexSocket(physical_socket); - break; case Medium::BLE: static std::aligned_storage_tCreateFirstVirtualSocket(service_id, (std::string)kFakeSalt); - multiplex_incoming_socket->StartReaderThread(); + multiplex_incoming_socket->StartReaderThread(first_frame_len); return multiplex_incoming_socket; } @@ -149,6 +160,11 @@ MultiplexSocket* MultiplexSocket::CreateIncomingSocket( MultiplexSocket* MultiplexSocket::CreateOutgoingSocket( std::shared_ptr physical_socket, const std::string& service_id, const std::string& service_id_hash_salt) { + while (is_shutting_down_.Get()) { + NEARBY_LOGS(WARNING) + << "Shutting down is going on, wait for 2ms to create outgoing socket"; + absl::SleepFor(absl::Milliseconds(2)); + } static MultiplexSocket* multiplex_outgoing_socket = nullptr; switch (physical_socket->GetMedium()) { case Medium::BLUETOOTH: @@ -182,7 +198,7 @@ MultiplexSocket* MultiplexSocket::CreateOutgoingSocket( multiplex_outgoing_socket->CreateFirstVirtualSocket(service_id, service_id_hash_salt); - multiplex_outgoing_socket->StartReaderThread(); + multiplex_outgoing_socket->StartReaderThread(0); return multiplex_outgoing_socket; } @@ -334,20 +350,26 @@ MediumSocket* MultiplexSocket::EstablishVirtualSocket( return nullptr; } -void MultiplexSocket::StartReaderThread() { +void MultiplexSocket::StartReaderThread(std::int32_t first_frame_len) { if (is_shutdown_) { NEARBY_LOGS(WARNING) << "Stop to start reader thread since socket is " "shutdown."; return; } reader_thread_shutdown_barrier_ = std::make_unique(1); - physical_reader_thread_.Execute([this]() { + physical_reader_thread_.Execute([this, first_frame_len]() { NEARBY_LOGS(INFO) << __func__ << " Reader thread starts."; + auto first_frame_len_copy = first_frame_len; while (!is_shutdown_) { bool fail = false; ExceptionOr bytes; - ExceptionOr read_int = - Base64Utils::ReadInt(physical_reader_); + ExceptionOr read_int; + if (first_frame_len_copy > 0) { + read_int = ExceptionOr(first_frame_len); + first_frame_len_copy = 0; + } else { + read_int = Base64Utils::ReadInt(physical_reader_); + } if (!read_int.ok()) { NEARBY_LOGS(WARNING) << __func__ << "Failed to read. Exception:" << read_int.exception(); @@ -650,6 +672,7 @@ void MultiplexSocket::OnVirtualSocketClosed(const std::string& service_id) { if (virtual_sockets_.empty()) { NEARBY_LOGS(INFO) << "Close the physical socket because all virtual " "sockets disconnected."; + is_shutting_down_.Set(true); Shutdown(); shutdown = true; } @@ -669,6 +692,7 @@ void MultiplexSocket::OnVirtualSocketClosed(const std::string& service_id) { << "Shutdown single_thread_offloader_ and physical_reader_thread_"; single_thread_offloader_.Shutdown(); physical_reader_thread_.Shutdown(); + is_shutting_down_.Set(false); } } @@ -761,9 +785,9 @@ void MultiplexSocket::ShutdownAll() { if (!latch .Await(FeatureFlags::GetInstance() .GetFlags() - .mediums_frame_write_timeout_millis) - .result() + - 200) { + .mediums_frame_write_timeout_millis + + absl::Milliseconds(100)) + .result()) { NEARBY_LOGS(ERROR) << "Timeout to close virtual socket"; } diff --git a/connections/implementation/mediums/multiplex/multiplex_socket.h b/connections/implementation/mediums/multiplex/multiplex_socket.h index 5575a013..dbc17713 100644 --- a/connections/implementation/mediums/multiplex/multiplex_socket.h +++ b/connections/implementation/mediums/multiplex/multiplex_socket.h @@ -15,6 +15,7 @@ #ifndef CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_SOCKET_H_ #define CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_SOCKET_H_ +#include #include #include #include @@ -48,11 +49,12 @@ class MultiplexSocket { public: MultiplexSocket(const MultiplexSocket&) = delete; MultiplexSocket& operator=(const MultiplexSocket&) = delete; + ~MultiplexSocket() { ShutdownAll(); }; // Creates a new incoming MultiplexSocket. static MultiplexSocket* CreateIncomingSocket( std::shared_ptr physical_socket, - const std::string& service_id); + const std::string& service_id, std::int32_t first_frame_len); // Creates a new outgoing MultiplexSocket. static MultiplexSocket* CreateOutgoingSocket( std::shared_ptr physical_socket, @@ -111,7 +113,6 @@ class MultiplexSocket { private: explicit MultiplexSocket(std::shared_ptr physical_socket); - ~MultiplexSocket() { ShutdownAll(); }; // Creates the first virtual socket for the service id. The first virtual // socket is created by the sender. @@ -128,7 +129,7 @@ class MultiplexSocket { void UnRegisterConnectionResponse(const std::string& service_id); // Starts the reader thread to read the incoming MultiplexFrame from the // physical socket. - void StartReaderThread(); + void StartReaderThread(std::int32_t first_frame_len); // Handles the offline frame from the physical socket. void HandleOfflineFrame(const ByteArray& bytes); // Handles the control frame from the physical socket. @@ -206,8 +207,10 @@ class MultiplexSocket { // enable it once two devices negotiated finished. AtomicBoolean enabled_{false}; + std::int32_t first_frame_len_ = 0; // If the socket is already shutdown and no longer in use. bool is_shutdown_ = false; + static AtomicBoolean is_shutting_down_; std::unique_ptr reader_thread_shutdown_barrier_; }; diff --git a/connections/implementation/mediums/multiplex/multiplex_socket_test.cc b/connections/implementation/mediums/multiplex/multiplex_socket_test.cc index 2e841cd5..b156d275 100644 --- a/connections/implementation/mediums/multiplex/multiplex_socket_test.cc +++ b/connections/implementation/mediums/multiplex/multiplex_socket_test.cc @@ -160,8 +160,8 @@ TEST(MultiplexSocketTest, CreateSuccessAndReaderThreadStarted) { MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_1), Medium::BLUETOOTH); MultiplexSocket* multiplex_socket_incoming = - MultiplexSocket::CreateIncomingSocket(fake_socket_ptr, - std::string(SERVICE_ID_1)); + MultiplexSocket::CreateIncomingSocket( + fake_socket_ptr, std::string(SERVICE_ID_1), /*first_frame_len*/ 0); ASSERT_NE(multiplex_socket_incoming, nullptr); FakeSocket* virtual_socket = (FakeSocket*)multiplex_socket_incoming->GetVirtualSocket( @@ -209,8 +209,8 @@ TEST(MultiplexSocketTest, CreateFail_MediumNotSupport) { MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_1), Medium::WEB_RTC); MultiplexSocket* multiplex_socket_incoming = - MultiplexSocket::CreateIncomingSocket(fake_socket_ptr, - std::string(SERVICE_ID_1)); + MultiplexSocket::CreateIncomingSocket( + fake_socket_ptr, std::string(SERVICE_ID_1), /*first_frame_len*/ 0); ASSERT_EQ(multiplex_socket_incoming, nullptr); } diff --git a/connections/implementation/mediums/wifi_lan.cc b/connections/implementation/mediums/wifi_lan.cc index e336e5f8..d9458437 100644 --- a/connections/implementation/mediums/wifi_lan.cc +++ b/connections/implementation/mediums/wifi_lan.cc @@ -15,22 +15,34 @@ #include "connections/implementation/mediums/wifi_lan.h" #include +#include #include #include #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" +#include "connections/implementation/mediums/multiplex/multiplex_socket.h" #include "connections/implementation/mediums/utils.h" +#include "connections/medium_selector.h" +#include "internal/platform/base64_utils.h" #include "internal/platform/byte_array.h" #include "internal/platform/cancellation_flag.h" +#include "internal/platform/exception.h" +#include "internal/platform/implementation/wifi_utils.h" #include "internal/platform/logging.h" #include "internal/platform/mutex_lock.h" #include "internal/platform/nsd_service_info.h" +#include "internal/platform/socket.h" +#include "internal/platform/types.h" #include "internal/platform/wifi_lan.h" namespace nearby { namespace connections { +namespace { +using MultiplexSocket = mediums::multiplex::MultiplexSocket; +} // namespace + WifiLan::~WifiLan() { // Destructor is not taking locks, but methods it is calling are. while (!discovering_info_.service_ids.empty()) { @@ -42,7 +54,18 @@ WifiLan::~WifiLan() { while (!advertising_info_.nsd_service_infos.empty()) { StopAdvertising(advertising_info_.nsd_service_infos.begin()->first); } - + { + MutexLock lock(&mutex_); + if (is_multiplex_enabled_) { + NEARBY_LOGS(INFO) << "Closing multiplex sockets for " + << multiplex_sockets_.size() << " IPs"; + for (auto& [ip_addr, multiplex_socket] : multiplex_sockets_) { + NEARBY_LOGS(INFO) << "Closing multiplex sockets for: " << ip_addr; + multiplex_socket->~MultiplexSocket(); + } + multiplex_sockets_.clear(); + } + } // All the AcceptLoopRunnable objects in here should already have gotten an // opportunity to shut themselves down cleanly in the calls to // StopAcceptingConnections() above. @@ -256,20 +279,82 @@ bool WifiLan::StartAcceptingConnections(const std::string& service_id, server_sockets_.insert({service_id, std::move(server_socket)}) .first->second; + // Register the callback to listen for incoming multiplex virtual socket. + if (is_multiplex_enabled_) { + MultiplexSocket::ListenForIncomingConnection( + service_id, Medium::WIFI_LAN, + [&callback](const std::string& listening_service_id, + MediumSocket* virtual_socket) mutable { + if (callback) { + callback(listening_service_id, + *(down_cast(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( - "wifi-lan-accept", - [callback = std::move(callback), - server_socket = std::move(owned_server_socket), service_id]() mutable { + "wifi-lan-accept", [callback = std::move(callback), + server_socket = std::move(owned_server_socket), + service_id, this]() mutable { while (true) { WifiLanSocket client_socket = server_socket.Accept(); if (!client_socket.IsValid()) { 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_) { + // Observed from the log that when the sender tries to connect to + // the receiver's server socket, the server side will somehow + // receive 3 connection request events(don’t know what’s happening + // in Windows’s lower layer code). The 2nd normally is the real + // one. The other two will result in a failed data receiving in + // Windows platform layer. To avoid creating multiplex + // IncomingSocket, we will check if the first read is successful + // or not. If not, discard it. If yes, save that packet + // content(the first frame length), then create the multiplex + // socket, then feed that content to that multiplex socket. + ExceptionOr read_int = + Base64Utils::ReadInt(&client_socket.GetInputStream()); + if (!read_int.ok()) { + NEARBY_LOGS(WARNING) + << __func__ + << "Failed to read. Exception:" << read_int.exception() + << "Discard the connection."; + continue; + } + WifiLanSocket client_socket_bak = client_socket; + auto physical_socket_ptr = + std::make_shared(client_socket_bak); + + MultiplexSocket* multiplex_socket = + MultiplexSocket::CreateIncomingSocket( + physical_socket_ptr, service_id, read_int.result()); + if (multiplex_socket != nullptr && + multiplex_socket->GetVirtualSocket(service_id)) { + multiplex_sockets_.emplace(server_socket.GetIPAddress(), + multiplex_socket); + MultiplexSocket::StopListeningForIncomingConnection( + service_id, Medium::WIFI_LAN); + NEARBY_LOGS(INFO) << "Multiplex virtaul socket created for " + << server_socket.GetIPAddress(); + if (callback) { + callback( + service_id, + *(down_cast( + 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)); } } @@ -293,6 +378,10 @@ bool WifiLan::StopAcceptingConnections(const std::string& service_id) { << service_id << " because it was never started."; return false; } + if (is_multiplex_enabled_) { + MultiplexSocket::StopListeningForIncomingConnection(service_id, + Medium::WIFI_LAN); + } // Closing the WifiLanServerSocket will kick off the suicide of the thread // in accept_loops_thread_pool_ that blocks on WifiLanServerSocket.accept(). @@ -352,12 +441,31 @@ WifiLanSocket WifiLan::Connect(const std::string& service_id, return socket; } + ExceptionOr virtual_socket = + ConnectWithMultiplexSocketLocked(service_id, service_info.GetIPAddress()); + if (virtual_socket.ok()) { + return virtual_socket.result(); + } + socket = medium_.ConnectToService(service_info, cancellation_flag); if (!socket.IsValid()) { NEARBY_LOGS(INFO) << "Failed to Connect via WifiLan [service_id=" << service_id << "]"; + return socket; + } else { + ExceptionOr virtual_socket = + CreateOutgoingMultiplexSocketLocked(socket, service_id, + service_info.GetIPAddress()); + if (virtual_socket.ok()) { + NEARBY_LOGS(INFO) + << "Successfully connected via Multiplex WifiLan [service_id=" + << service_id << "]"; + return virtual_socket.result(); + } } + NEARBY_LOGS(INFO) << "Successfully connected via WifiLan [service_id=" + << service_id << "]"; return socket; } @@ -385,15 +493,95 @@ WifiLanSocket WifiLan::Connect(const std::string& service_id, return socket; } + ExceptionOr virtual_socket = + ConnectWithMultiplexSocketLocked(service_id, ip_address); + if (virtual_socket.ok()) { + return virtual_socket.result(); + } + socket = medium_.ConnectToService(ip_address, port, cancellation_flag); if (!socket.IsValid()) { NEARBY_LOGS(INFO) << "Failed to Connect via WifiLan [service_id=" << service_id << "]"; + return socket; + } else { + ExceptionOr virtual_socket = + CreateOutgoingMultiplexSocketLocked(socket, service_id, ip_address); + if (virtual_socket.ok()) { + NEARBY_LOGS(INFO) + << "Successfully connected via Multiplex WifiLan [service_id=" + << service_id << "]"; + return virtual_socket.result(); + } } + NEARBY_LOGS(INFO) << "Successfully connected via WifiLan [service_id=" + << service_id << "]"; return socket; } +ExceptionOr WifiLan::ConnectWithMultiplexSocketLocked( + const std::string& service_id, const std::string& ip_address) { + if (is_multiplex_enabled_) { + NEARBY_LOGS(INFO) << "multiplex_sockets_ size:" + << multiplex_sockets_.size(); + auto it = multiplex_sockets_.find(ip_address); + if (it != multiplex_sockets_.end()) { + MultiplexSocket* multiplex_socket = it->second; + if (multiplex_socket->IsShutdown()) { + NEARBY_LOGS(INFO) + << "Erase multiplex_socket(already shutdown) for ip_address: " + << WifiUtils::GetHumanReadableIpAddress(ip_address); + multiplex_socket->~MultiplexSocket(); + multiplex_sockets_.erase(it); + return ExceptionOr(Exception::kFailed); + } + if (multiplex_socket->IsEnabled()) { + auto* virtual_socket = + multiplex_socket->EstablishVirtualSocket(service_id); + // Should not happen. + auto* wlan_socket = down_cast(virtual_socket); + if (wlan_socket == nullptr) { + NEARBY_LOGS(INFO) << "Failed to cast to WifiLanSocket for " + << service_id << " with ip_address: " + << WifiUtils::GetHumanReadableIpAddress(ip_address); + return ExceptionOr(Exception::kFailed); + } + return ExceptionOr(*wlan_socket); + } + } + } + return ExceptionOr(Exception::kFailed); +} + +ExceptionOr WifiLan::CreateOutgoingMultiplexSocketLocked( + WifiLanSocket& socket, const std::string& service_id, + const std::string& ip_address) { + if (is_multiplex_enabled_) { + // Create MultiplexSocket, but set it to be disabled as default. It will be + // enabled if both side support multiplex for WIFI_LAN + auto physical_socket_ptr = std::make_shared(socket); + MultiplexSocket* multiplex_socket = + MultiplexSocket::CreateOutgoingSocket(physical_socket_ptr, service_id); + + auto* virtual_socket = multiplex_socket->GetVirtualSocket(service_id); + // Should not happen. + auto* wlan_socket = down_cast(virtual_socket); + if (wlan_socket == nullptr) { + NEARBY_LOGS(INFO) << "Failed to cast to WifiLanSocket for " << service_id + << " with ip_address: " + << WifiUtils::GetHumanReadableIpAddress(ip_address); + return ExceptionOr(Exception::kFailed); + } + NEARBY_LOGS(INFO) << "Multiplex socket created for ip_address: " + << WifiUtils::GetHumanReadableIpAddress(ip_address); + multiplex_sockets_.emplace(ip_address, + multiplex_socket); + return ExceptionOr(*wlan_socket); + } + return ExceptionOr(Exception::kFailed); +} + std::pair WifiLan::GetCredentials( const std::string& service_id) { MutexLock lock(&mutex_); diff --git a/connections/implementation/mediums/wifi_lan.h b/connections/implementation/mediums/wifi_lan.h index b9d53f22..37f95d9f 100644 --- a/connections/implementation/mediums/wifi_lan.h +++ b/connections/implementation/mediums/wifi_lan.h @@ -16,13 +16,18 @@ #define CORE_INTERNAL_MEDIUMS_WIFI_LAN_H_ #include -#include #include +#include +#include "absl/base/thread_annotations.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" -#include "internal/platform/byte_array.h" +#include "absl/functional/any_invocable.h" +#include "connections/implementation/flags/nearby_connections_feature_flags.h" +#include "connections/implementation/mediums/multiplex/multiplex_socket.h" +#include "internal/flags/nearby_flags.h" #include "internal/platform/cancellation_flag.h" +#include "internal/platform/exception.h" #include "internal/platform/multi_thread_executor.h" #include "internal/platform/mutex.h" #include "internal/platform/nsd_service_info.h" @@ -150,6 +155,18 @@ class WifiLan { static constexpr int kMaxConcurrentAcceptLoops = 5; + // Establishes connection to WifiLan service by ip address through + // MultiplexSocket. + ExceptionOr ConnectWithMultiplexSocketLocked( + const std::string& service_id, const std::string& ip_address) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Creates a MultiplexSocket for outgoing connection based on connected + // WifiLanSocket physical socket for specific service_id and ip address. + ExceptionOr CreateOutgoingMultiplexSocketLocked( + WifiLanSocket& socket, const std::string& service_id, + const std::string& ip_address) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + // Same as IsAvailable(), but must be called with mutex_ held. bool IsAvailableLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); @@ -187,6 +204,16 @@ class WifiLan { // and thus require pointer stability. absl::flat_hash_map server_sockets_ ABSL_GUARDED_BY(mutex_); + + // Whether the multiplex feature is enabled. + bool is_multiplex_enabled_ = NearbyFlags::GetInstance().GetBoolFlag( + config_package_nearby::nearby_connections_feature:: + kEnableMultiplex); + + // A map of IpAddress -> MultiplexSocket. + absl::flat_hash_map + multiplex_sockets_ ABSL_GUARDED_BY(mutex_); }; } // namespace connections diff --git a/connections/implementation/mediums/wifi_lan_test.cc b/connections/implementation/mediums/wifi_lan_test.cc index acd1df81..fa6649d1 100644 --- a/connections/implementation/mediums/wifi_lan_test.cc +++ b/connections/implementation/mediums/wifi_lan_test.cc @@ -15,16 +15,22 @@ #include "connections/implementation/mediums/wifi_lan.h" #include +#include #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" #include "absl/strings/string_view.h" +#include "connections/implementation/flags/nearby_connections_feature_flags.h" +#include "internal/flags/nearby_flags.h" +#include "internal/platform/cancellation_flag.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" #include "internal/platform/nsd_service_info.h" +#include "internal/platform/single_thread_executor.h" #include "internal/platform/wifi_lan.h" +#include "internal/platform/base64_utils.h" namespace nearby { namespace connections { @@ -106,6 +112,71 @@ TEST_P(WifiLanTest, CanConnect) { env_.Stop(); } +TEST_P(WifiLanTest, CanConnectWithMultiplex) { + bool is_multiplex_enabled = NearbyFlags::GetInstance().GetBoolFlag( + config_package_nearby::nearby_connections_feature::kEnableMultiplex); + NearbyFlags::GetInstance().OverrideBoolFlagValue( + config_package_nearby::nearby_connections_feature::kEnableMultiplex, + true); + FeatureFlags feature_flags = GetParam(); + env_.SetFeatureFlags(feature_flags); + env_.Start(); + WifiLan wifi_lan_client; + WifiLan wifi_lan_server; + std::string service_id(kServiceID); + std::string service_info_name(kServiceInfoName); + std::string endpoint_info_name(kEndpointName); + CountDownLatch discovered_latch(1); + CountDownLatch accept_latch(1); + + WifiLanSocket socket_for_server; + EXPECT_TRUE(wifi_lan_server.StartAcceptingConnections( + service_id, [&](const std::string& service_id, WifiLanSocket socket) { + socket_for_server = std::move(socket); + accept_latch.CountDown(); + })); + + NsdServiceInfo nsd_service_info; + nsd_service_info.SetServiceName(service_info_name); + nsd_service_info.SetTxtRecord(std::string(kEndpointInfoKey), + endpoint_info_name); + wifi_lan_server.StartAdvertising(service_id, nsd_service_info); + + WifiLanSocket socket_for_client; + SingleThreadExecutor client_executor; + client_executor.Execute([&]() { + NsdServiceInfo discovered_service_info; + wifi_lan_client.StartDiscovery( + service_id, + { + .service_discovered_cb = + [&discovered_latch, &discovered_service_info]( + NsdServiceInfo service_info, const std::string& service_id) { + NEARBY_LOGS(INFO) + << "Discovered service_info=" << &service_info; + discovered_service_info = service_info; + discovered_latch.CountDown(); + }, + }); + discovered_latch.Await(kWaitDuration).result(); + ASSERT_TRUE(discovered_service_info.IsValid()); + + CancellationFlag flag; + socket_for_client = + wifi_lan_client.Connect(service_id, discovered_service_info, &flag); + Base64Utils::WriteInt(&socket_for_client.GetOutputStream(), 4); + }); + EXPECT_TRUE(accept_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(wifi_lan_server.StopAcceptingConnections(service_id)); + EXPECT_TRUE(wifi_lan_server.StopAdvertising(service_id)); + EXPECT_TRUE(socket_for_server.IsValid()); + EXPECT_TRUE(socket_for_client.IsValid()); + env_.Stop(); + NearbyFlags::GetInstance().OverrideBoolFlagValue( + config_package_nearby::nearby_connections_feature::kEnableMultiplex, + is_multiplex_enabled); +} + TEST_P(WifiLanTest, CanCancelConnect) { FeatureFlags feature_flags = GetParam(); env_.SetFeatureFlags(feature_flags); diff --git a/connections/implementation/p2p_cluster_pcp_handler_test.cc b/connections/implementation/p2p_cluster_pcp_handler_test.cc index b8714525..ef183012 100644 --- a/connections/implementation/p2p_cluster_pcp_handler_test.cc +++ b/connections/implementation/p2p_cluster_pcp_handler_test.cc @@ -1057,14 +1057,21 @@ TEST_P(P2pClusterPcpHandlerTestWithParam, CanConnect) { EXPECT_EQ(client_b_.GetApFrequency(discovered.endpoint_id), kFreq); EXPECT_EQ(client_b_.GetIPAddress(discovered.endpoint_id), std::string(kIp4Bytes)); - EXPECT_EQ(client_a_.Is5GHzSupported(client_b_local_endpoint), - mediums_b.GetWifi().GetCapability().supports_5_ghz); - EXPECT_EQ(client_a_.GetBssid(client_b_local_endpoint), - mediums_b.GetWifi().GetInformation().bssid); - EXPECT_EQ(client_a_.GetApFrequency(client_b_local_endpoint), - mediums_b.GetWifi().GetInformation().ap_frequency); - EXPECT_EQ(client_a_.GetIPAddress(client_b_local_endpoint), - mediums_b.GetWifi().GetInformation().ip_address_4_bytes); + // When connection is established, EndpointManager will setup KeepAliveManager + // loop. When it fails, the connection will be dismantled. Since this a unit + // test, KeepAliveManager won't be really up. The disconnection may happen + // before the following check, which cause the check fail. So we check the + // connection status first. + if (client_b_.IsConnectedToEndpoint(discovered.endpoint_id)) { + EXPECT_EQ(client_a_.Is5GHzSupported(client_b_local_endpoint), + mediums_b.GetWifi().GetCapability().supports_5_ghz); + EXPECT_EQ(client_a_.GetBssid(client_b_local_endpoint), + mediums_b.GetWifi().GetInformation().bssid); + EXPECT_EQ(client_a_.GetApFrequency(client_b_local_endpoint), + mediums_b.GetWifi().GetInformation().ap_frequency); + EXPECT_EQ(client_a_.GetIPAddress(client_b_local_endpoint), + mediums_b.GetWifi().GetInformation().ip_address_4_bytes); + } handler_b.StopDiscovery(&client_b_); bwu_a.Shutdown(); diff --git a/connections/implementation/p2p_point_to_point_pcp_handler_test.cc b/connections/implementation/p2p_point_to_point_pcp_handler_test.cc index 0287f001..ed9d1c7e 100644 --- a/connections/implementation/p2p_point_to_point_pcp_handler_test.cc +++ b/connections/implementation/p2p_point_to_point_pcp_handler_test.cc @@ -237,14 +237,21 @@ TEST_P(P2pPointToPointPcpHandlerTest, CanConnect) { EXPECT_EQ(client_b_.GetApFrequency(discovered.endpoint_id), kFreq); EXPECT_EQ(client_b_.GetIPAddress(discovered.endpoint_id), std::string(kIp4Bytes)); - EXPECT_EQ(client_a_.Is5GHzSupported(client_b_local_endpoint), - mediums_b.GetWifi().GetCapability().supports_5_ghz); - EXPECT_EQ(client_a_.GetBssid(client_b_local_endpoint), - mediums_b.GetWifi().GetInformation().bssid); - EXPECT_EQ(client_a_.GetApFrequency(client_b_local_endpoint), - mediums_b.GetWifi().GetInformation().ap_frequency); - EXPECT_EQ(client_a_.GetIPAddress(client_b_local_endpoint), - mediums_b.GetWifi().GetInformation().ip_address_4_bytes); + // When connection is established, EndpointManager will setup KeepAliveManager + // loop. When it fails, the connection will be dismantled. Since this a unit + // test, KeepAliveManager won't be really up. The disconnection may happen + // before the following check, which cause the check fail. So we check the + // connection status first. + if (client_b_.IsConnectedToEndpoint(discovered.endpoint_id)) { + EXPECT_EQ(client_a_.Is5GHzSupported(client_b_local_endpoint), + mediums_b.GetWifi().GetCapability().supports_5_ghz); + EXPECT_EQ(client_a_.GetBssid(client_b_local_endpoint), + mediums_b.GetWifi().GetInformation().bssid); + EXPECT_EQ(client_a_.GetApFrequency(client_b_local_endpoint), + mediums_b.GetWifi().GetInformation().ap_frequency); + EXPECT_EQ(client_a_.GetIPAddress(client_b_local_endpoint), + mediums_b.GetWifi().GetInformation().ip_address_4_bytes); + } handler_b.StopDiscovery(&client_b_); bwu_a.Shutdown(); diff --git a/connections/implementation/wifi_lan_endpoint_channel.cc b/connections/implementation/wifi_lan_endpoint_channel.cc index 0c6081f2..5c46fa29 100644 --- a/connections/implementation/wifi_lan_endpoint_channel.cc +++ b/connections/implementation/wifi_lan_endpoint_channel.cc @@ -16,6 +16,7 @@ #include +#include "connections/implementation/base_endpoint_channel.h" #include "internal/platform/logging.h" #include "internal/platform/wifi_lan.h" @@ -42,5 +43,12 @@ void WifiLanEndpointChannel::CloseImpl() { } } +bool WifiLanEndpointChannel::EnableMultiplexSocket() { + NEARBY_LOGS(INFO) << "WifiLanEndpointChannel MultiplexSocket will be " + "enabled if the WifiLan MultiplexSocket is valid"; + socket_.EnableMultiplexSocket(); + return true; +} + } // namespace connections } // namespace nearby diff --git a/connections/implementation/wifi_lan_endpoint_channel.h b/connections/implementation/wifi_lan_endpoint_channel.h index 1129be7c..587f5002 100644 --- a/connections/implementation/wifi_lan_endpoint_channel.h +++ b/connections/implementation/wifi_lan_endpoint_channel.h @@ -30,6 +30,7 @@ class WifiLanEndpointChannel final : public BaseEndpointChannel { const std::string& channel_name, WifiLanSocket socket); location::nearby::proto::connections::Medium GetMedium() const override; + bool EnableMultiplexSocket() override; private: void CloseImpl() override; diff --git a/internal/platform/bluetooth_classic.cc b/internal/platform/bluetooth_classic.cc index 2fc38fd5..02851551 100644 --- a/internal/platform/bluetooth_classic.cc +++ b/internal/platform/bluetooth_classic.cc @@ -31,16 +31,6 @@ namespace nearby { using location::nearby::proto::connections::Medium; -MediumSocket* BluetoothSocket::CreateVirtualSocket(OutputStream* outputstream) { - if (IsVirtualSocket()) { - LOG(WARNING) - << "Creating the virtual socket on a virtual socket is not allowed."; - return nullptr; - } - auto virtual_socket = std::make_shared(outputstream); - return virtual_socket.get(); -} - MediumSocket* BluetoothSocket::CreateVirtualSocket( const std::string& salted_service_id_hash_key, OutputStream* outputstream, Medium medium, diff --git a/internal/platform/bluetooth_classic.h b/internal/platform/bluetooth_classic.h index 6e47dade..c21972bf 100644 --- a/internal/platform/bluetooth_classic.h +++ b/internal/platform/bluetooth_classic.h @@ -96,8 +96,7 @@ class BluetoothSocket : public MediumSocket { // 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; + // Creates a virtual socket. MediumSocket* CreateVirtualSocket( const std::string& salted_service_id_hash_key, OutputStream* outputstream, location::nearby::proto::connections::Medium medium, diff --git a/internal/platform/socket.h b/internal/platform/socket.h index c1a34d64..9cc319e2 100644 --- a/internal/platform/socket.h +++ b/internal/platform/socket.h @@ -53,11 +53,6 @@ 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, diff --git a/internal/platform/wifi_lan.cc b/internal/platform/wifi_lan.cc index 6719ac46..c84cedbc 100644 --- a/internal/platform/wifi_lan.cc +++ b/internal/platform/wifi_lan.cc @@ -14,13 +14,47 @@ #include "internal/platform/wifi_lan.h" +#include #include #include -#include "internal/platform/implementation/wifi_utils.h" +#include "absl/container/flat_hash_map.h" +#include "internal/platform/cancellation_flag.h" +#include "internal/platform/logging.h" #include "internal/platform/mutex_lock.h" +#include "internal/platform/nsd_service_info.h" +#include "internal/platform/output_stream.h" +#include "internal/platform/socket.h" +#include "internal/platform/implementation/wifi_utils.h" namespace nearby { +using location::nearby::proto::connections::Medium; + +MediumSocket* WifiLanSocket::CreateVirtualSocket( + const std::string& salted_service_id_hash_key, OutputStream* outputstream, + Medium medium, + absl::flat_hash_map>* + 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(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(); +} bool WifiLanMedium::StartAdvertising(const NsdServiceInfo& nsd_service_info) { return impl_->StartAdvertising(nsd_service_info); @@ -117,7 +151,7 @@ bool WifiLanMedium::StartDiscovery(const std::string& service_id, // Insert callback to the map first no matter it succeeds or not. MutexLock lock(&mutex_); auto pair = service_type_to_callback_map_.insert( - {service_type, absl::make_unique()}); + {service_type, std::make_unique()}); auto& context = *pair.first->second; context.medium_callback = std::move(callback); context.service_id = service_id; diff --git a/internal/platform/wifi_lan.h b/internal/platform/wifi_lan.h index 99b8b466..e18f775e 100644 --- a/internal/platform/wifi_lan.h +++ b/internal/platform/wifi_lan.h @@ -15,47 +15,100 @@ #ifndef PLATFORM_PUBLIC_WIFI_LAN_H_ #define PLATFORM_PUBLIC_WIFI_LAN_H_ +#include #include #include #include #include "absl/container/flat_hash_map.h" +#include "absl/functional/any_invocable.h" +#include "absl/types/optional.h" +#include "internal/platform/blocking_queue_stream.h" +#include "internal/platform/byte_array.h" #include "internal/platform/cancellation_flag.h" +#include "internal/platform/exception.h" #include "internal/platform/implementation/platform.h" #include "internal/platform/implementation/wifi_lan.h" #include "internal/platform/input_stream.h" +#include "internal/platform/listeners.h" #include "internal/platform/logging.h" #include "internal/platform/mutex.h" #include "internal/platform/nsd_service_info.h" #include "internal/platform/output_stream.h" +#include "internal/platform/socket.h" namespace nearby { -class WifiLanSocket final { +class WifiLanSocket : public MediumSocket { public: - WifiLanSocket() = default; + WifiLanSocket() + : MediumSocket(location::nearby::proto::connections::Medium::WIFI_LAN) {} WifiLanSocket(const WifiLanSocket&) = default; WifiLanSocket& operator=(const WifiLanSocket&) = default; - ~WifiLanSocket() = default; + ~WifiLanSocket() override = default; + + // Creates a physical WifiLanSocket from a platform implementation. explicit WifiLanSocket(std::unique_ptr socket) - : impl_(std::move(socket)) {} + : MediumSocket(location::nearby::proto::connections::Medium::WIFI_LAN), + impl_(socket.release()) {} + + // Creates a virtual WifiLanSocket from a virtual output stream. + explicit WifiLanSocket(OutputStream* virtual_output_stream) + : MediumSocket(location::nearby::proto::connections::Medium::WIFI_LAN), + blocking_queue_input_stream_(std::make_shared()), + virtual_output_stream_(virtual_output_stream), + is_virtual_socket_(true) {} // Returns the InputStream of the WifiLanSocket. // On error, returned stream will report Exception::kIo on any operation. // // The returned object is not owned by the caller, and can be invalidated once // the WifiLanSocket object is destroyed. - InputStream& GetInputStream() { return impl_->GetInputStream(); } + InputStream& GetInputStream() override { + return IsVirtualSocket() ? *blocking_queue_input_stream_ + : impl_->GetInputStream(); + } // Returns the OutputStream of the WifiLanSocket. // On error, returned stream will report Exception::kIo on any operation. // // The returned object is not owned by the caller, and can be invalidated once // the WifiLanSocket object is destroyed. - OutputStream& GetOutputStream() { return impl_->GetOutputStream(); } + OutputStream& GetOutputStream() override { + return IsVirtualSocket() ? *virtual_output_stream_ + : impl_->GetOutputStream(); + } // 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(); // This will trigger MultiplexSocket::OnVirtualSocketClosed + return {Exception::kSuccess}; + } + 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( + const std::string& salted_service_id_hash_key, OutputStream* outputstream, + location::nearby::proto::connections::Medium medium, + absl::flat_hash_map>* + 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); + } // Returns true if a socket is usable. If this method returns false, // it is not safe to call any other method. @@ -66,7 +119,10 @@ class WifiLanSocket final { // an object returned by WifiLanMedium::Connect // 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. @@ -77,6 +133,11 @@ class WifiLanSocket final { private: std::shared_ptr impl_; + absl::flat_hash_map>* + virtual_sockets_ptr_ = nullptr; + std::shared_ptr blocking_queue_input_stream_ = nullptr; + OutputStream* virtual_output_stream_ = nullptr; + bool is_virtual_socket_ = false; }; class WifiLanServerSocket final {