From 13dd4d7696e6a70b66c724013eddafcc6ef4fb1e Mon Sep 17 00:00:00 2001 From: hai007 Date: Tue, 28 Oct 2025 01:52:05 -0700 Subject: [PATCH] Wi-Fi Direct Service Implementation(4) PiperOrigin-RevId: 824914003 --- internal/platform/implementation/BUILD | 1 + .../implementation/wifi_direct_service.h | 120 ++++++++++ .../platform/implementation/windows/BUILD | 2 + .../windows/wifi_direct_service.h | 200 ++++++++++++++-- .../windows/wifi_direct_service_medium.cc | 226 +++++++++++++++++- .../wifi_direct_service_server_socket.cc | 117 +++++++++ .../windows/wifi_direct_service_socket.cc | 39 +++ 7 files changed, 690 insertions(+), 15 deletions(-) create mode 100644 internal/platform/implementation/wifi_direct_service.h create mode 100644 internal/platform/implementation/windows/wifi_direct_service_server_socket.cc create mode 100644 internal/platform/implementation/windows/wifi_direct_service_socket.cc diff --git a/internal/platform/implementation/BUILD b/internal/platform/implementation/BUILD index 4af3c20c..f0d57e21 100644 --- a/internal/platform/implementation/BUILD +++ b/internal/platform/implementation/BUILD @@ -161,6 +161,7 @@ cc_library( "webrtc.h", "wifi.h", "wifi_direct.h", + "wifi_direct_service.h", "wifi_hotspot.h", "wifi_lan.h", ], diff --git a/internal/platform/implementation/wifi_direct_service.h b/internal/platform/implementation/wifi_direct_service.h new file mode 100644 index 00000000..19214946 --- /dev/null +++ b/internal/platform/implementation/wifi_direct_service.h @@ -0,0 +1,120 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef PLATFORM_IMPL_WIFI_DIRECT_SERVICE_H_ +#define PLATFORM_IMPL_WIFI_DIRECT_SERVICE_H_ + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "internal/platform/cancellation_flag.h" +#include "internal/platform/exception.h" +#include "internal/platform/input_stream.h" +#include "internal/platform/output_stream.h" + +namespace nearby { +namespace api { + +class WifiDirectServiceSocket { + public: + virtual ~WifiDirectServiceSocket() = default; + + // Returns the InputStream of the WifiDirectServiceSocket. + // 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 WifiDirectServiceSocket object is destroyed. + virtual InputStream& GetInputStream() = 0; + + // Returns the OutputStream of the WifiDirectServiceSocket. + // 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 WifiDirectServiceSocket object is destroyed. + virtual OutputStream& GetOutputStream() = 0; + + // Returns Exception::kIo on error, Exception::kSuccess otherwise. + virtual Exception Close() = 0; +}; + +class WifiDirectServiceServerSocket { + public: + virtual ~WifiDirectServiceServerSocket() = default; + + virtual std::string GetIPAddress() const = 0; + + virtual int GetPort() const = 0; + + // Blocks until either: + // - at least one incoming connection request is available, or + // - ServerSocket is closed. + // On success, returns connected socket, ready to exchange data. + // Returns nullptr on error. + // Once error is reported, it is permanent, and ServerSocket has to be closed. + virtual std::unique_ptr Accept() = 0; + + // Returns Exception::kIo on error, Exception::kSuccess otherwise. + virtual Exception Close() = 0; +}; + +// Container of operations that can be performed over the WifiDirectService +// medium. +class WifiDirectServiceMedium { + public: + virtual ~WifiDirectServiceMedium() = default; + + // If the WiFi Adaptor supports to start a Hotspot interface. + virtual bool IsInterfaceValid() const = 0; + + // Connects to a WifiDirectService by ip address and port. + // On success, returns a new WifiDirectServiceSocket. + // On error, returns nullptr. + virtual std::unique_ptr ConnectToService( + absl::string_view ip_address, int port, + CancellationFlag* cancellation_flag) = 0; + + // Listens for incoming connection. + // + // port - A port number. + // 0 : use a random port. + // 1~65536 : open a server socket on that exact port. + // On success, returns a new WifiDirectServiceServerSocket. + // On error, returns nullptr. + virtual std::unique_ptr ListenForService( + int port) = 0; + + // Start a softAP as Hotspot with platform dependent APIs and set the + // SSID/password pair back to the credentials. BWU module will retrieve these + // credentials and send to the client device through established channel and + // then client may connect to this Hotspot with these credentials. + virtual bool StartWifiDirectService() = 0; + virtual bool StopWifiDirectService() = 0; + + // Client device connect to a softAP with specified credential. + virtual bool ConnectWifiDirectService() = 0; + virtual bool DisconnectWifiDirectService() = 0; + + // Returns the port range as a pair of min and max port. + virtual absl::optional> + GetDynamicPortRange() = 0; +}; + +} // namespace api +} // namespace nearby + +#endif // PLATFORM_IMPL_WIFI_DIRECT_SERVICE_H_ diff --git a/internal/platform/implementation/windows/BUILD b/internal/platform/implementation/windows/BUILD index 7140cf3f..ff570229 100644 --- a/internal/platform/implementation/windows/BUILD +++ b/internal/platform/implementation/windows/BUILD @@ -189,6 +189,8 @@ cc_library( "wifi_direct_medium.cc", "wifi_direct_server_socket.cc", "wifi_direct_service_medium.cc", + "wifi_direct_service_server_socket.cc", + "wifi_direct_service_socket.cc", "wifi_direct_socket.cc", "wifi_hotspot_medium.cc", "wifi_hotspot_native.cc", diff --git a/internal/platform/implementation/windows/wifi_direct_service.h b/internal/platform/implementation/windows/wifi_direct_service.h index 913f4111..323f1a6b 100644 --- a/internal/platform/implementation/windows/wifi_direct_service.h +++ b/internal/platform/implementation/windows/wifi_direct_service.h @@ -20,12 +20,27 @@ #include // Standard C/C++ headers +#include +#include #include #include +#include // Nearby connections headers +#include "absl/base/thread_annotations.h" +#include "absl/functional/any_invocable.h" +#include "absl/types/optional.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 "absl/base/nullability.h" #include "absl/container/flat_hash_map.h" #include "absl/synchronization/mutex.h" +#include "internal/platform/implementation/wifi_direct_service.h" +#include "internal/platform/implementation/windows/nearby_client_socket.h" +#include "internal/platform/implementation/windows/nearby_server_socket.h" +#include "internal/platform/implementation/windows/submittable_executor.h" // WinRT headers #include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Enumeration.h" @@ -39,18 +54,18 @@ #include "internal/platform/implementation/windows/generated/winrt/Windows.System.h" #include "internal/platform/implementation/windows/generated/winrt/base.h" -namespace nearby { -namespace windows { +namespace nearby::windows { + using ::winrt::event_token; using ::winrt::fire_and_forget; using ::winrt::Windows::Devices::Enumeration::DeviceInformation; using ::winrt::Windows::Devices::Enumeration::DeviceInformationUpdate; using ::winrt::Windows::Devices::Enumeration::DeviceWatcher; using ::winrt::Windows::Devices::WiFiDirect::Services::WiFiDirectService; -using ::winrt::Windows::Devices::WiFiDirect::Services:: - WiFiDirectServiceAdvertiser; using ::winrt::Windows::Devices::WiFiDirect::Services:: WiFiDirectServiceAdvertisementStatus; +using ::winrt::Windows::Devices::WiFiDirect::Services:: + WiFiDirectServiceAdvertiser; using ::winrt::Windows::Devices::WiFiDirect::Services:: WiFiDirectServiceAutoAcceptSessionConnectedEventArgs; using ::winrt::Windows::Devices::WiFiDirect::Services:: @@ -62,6 +77,136 @@ using ::winrt::Windows::Devices::WiFiDirect::Services::WiFiDirectServiceStatus; using ::winrt::Windows::Foundation::AsyncStatus; using ::winrt::Windows::Foundation::IInspectable; +// WifiDirectServiceSocket wraps the socket functions to read and write stream. +// In WiFi HOTSPOT, A WifiDirectServiceSocket will be passed to +// StartAcceptingConnections's callback when Winsock Server Socket receives a +// new connection. When call API to connect to remote WiFi Hotspot service, also +// will return a WifiDirectServiceSocket to caller. +class WifiDirectServiceSocket : public api::WifiDirectServiceSocket { + public: + WifiDirectServiceSocket(); + explicit WifiDirectServiceSocket( + absl_nonnull std::unique_ptr socket); + WifiDirectServiceSocket(WifiDirectServiceSocket&&) = default; + ~WifiDirectServiceSocket() override; + WifiDirectServiceSocket& operator=(WifiDirectServiceSocket&&) = default; + + // Returns the InputStream of the WifiDirectServiceSocket. + // 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 WifiDirectServiceSocket object is destroyed. + InputStream& GetInputStream() override { return input_stream_; } + + // Returns the OutputStream of the WifiDirectServiceSocket. + // 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 WifiDirectServiceSocket object is destroyed. + OutputStream& GetOutputStream() override { return output_stream_; } + + // Returns Exception::kIo on error, Exception::kSuccess otherwise. + Exception Close() override { return client_socket_->Close(); } + + bool Connect(const SocketAddress& server_address) { + return client_socket_->Connect(server_address); + } + + private: + // A simple wrapper to handle input stream of socket + class SocketInputStream : public InputStream { + public: + explicit SocketInputStream(NearbyClientSocket* absl_nonnull client_socket) + : client_socket_(client_socket) {} + ~SocketInputStream() override = default; + + ExceptionOr Read(std::int64_t size) override { + return client_socket_->Read(size); + } + ExceptionOr Skip(size_t offset) override { + return client_socket_->Skip(offset); + } + Exception Close() override { return client_socket_->Close(); } + + private: + NearbyClientSocket* absl_nonnull const client_socket_; + }; + + // A simple wrapper to handle output stream of socket + class SocketOutputStream : public OutputStream { + public: + explicit SocketOutputStream(NearbyClientSocket* absl_nonnull client_socket) + : client_socket_(client_socket) {} + ~SocketOutputStream() override = default; + + Exception Write(const ByteArray& data) override { + return client_socket_->Write(data); + } + Exception Flush() override { return client_socket_->Flush(); } + Exception Close() override { return client_socket_->Close(); } + + private: + NearbyClientSocket* absl_nonnull const client_socket_; + }; + + absl_nonnull std::unique_ptr client_socket_; + SocketInputStream input_stream_; + SocketOutputStream output_stream_; +}; + +// WifiDirectServiceServerSocket provides the support to server socket, this +// server socket accepts connection from clients. +class WifiDirectServiceServerSocket + : public api::WifiDirectServiceServerSocket { + public: + explicit WifiDirectServiceServerSocket(int port = 0); + WifiDirectServiceServerSocket(const WifiDirectServiceServerSocket&) = default; + WifiDirectServiceServerSocket(WifiDirectServiceServerSocket&&) = default; + ~WifiDirectServiceServerSocket() override; + WifiDirectServiceServerSocket& operator=( + const WifiDirectServiceServerSocket&) = default; + WifiDirectServiceServerSocket& operator=(WifiDirectServiceServerSocket&&) = + default; + + std::string GetIPAddress() const override; + int GetPort() const override; + + // Blocks until either: + // - at least one incoming connection request is available, or + // - ServerSocket is closed. + // On success, returns connected socket, ready to exchange data. + // Returns nullptr on error. + // Once error is reported, it is permanent, and ServerSocket has to be closed. + std::unique_ptr Accept() override; + + // Called by the server side of a connection before passing ownership of + // WifiDirectServiceServerSocker to user, to track validity of a pointer to + // this server socket. + void SetCloseNotifier(absl::AnyInvocable notifier); + + // Returns Exception::kIo on error, Exception::kSuccess otherwise. + Exception Close() override; + + // Binds to local port + bool Listen(bool dual_stack, std::string& ip_address); + + NearbyServerSocket server_socket_; + + private: + // Retrieves hotspot IP address from local machine + std::string GetWifiDirectServiceIpAddress() const; + + const int port_; + mutable absl::Mutex mutex_; + + // Close notifier + absl::AnyInvocable close_notifier_ = nullptr; + + // IP addresses of the server socket. + std::string wifi_direct_service_ipaddr_ = {}; + bool closed_ = false; +}; + class WifiDirectServiceDiscovered { public: explicit WifiDirectServiceDiscovered(const DeviceInformation& device_info); @@ -86,22 +231,39 @@ class WifiDirectServiceDiscovered { // std::string name_; }; -class WifiDirectServiceMedium { +class WifiDirectServiceMedium : public api::WifiDirectServiceMedium { public: WifiDirectServiceMedium(); - ~WifiDirectServiceMedium(); + ~WifiDirectServiceMedium() override; // WifiDirectServiceMedium is neither copyable nor movable. WifiDirectServiceMedium(const WifiDirectServiceMedium&) = delete; WifiDirectServiceMedium& operator=(const WifiDirectServiceMedium&) = delete; + // If the WiFi Adaptor supports to start WifiDirect Service GO. + bool IsInterfaceValid() const override; + + // Discoverer connects to server socket + std::unique_ptr ConnectToService( + absl::string_view ip_address, int port, + CancellationFlag* cancellation_flag) override; + + // Advertiser starts to listen on server socket + std::unique_ptr ListenForService( + int port) override; + // Starts to advertising - bool StartWifiDirectService(); + bool StartWifiDirectService() override; // Stops to advertising - bool StopWifiDirectService(); + bool StopWifiDirectService() override; // Connects to a WifiDirectService - bool ConnectWifiDirectService(); + bool ConnectWifiDirectService() override; // Disconnects from a WifiDirectService - bool DisconnectWifiDirectService(); + bool DisconnectWifiDirectService() override; + + absl::optional> GetDynamicPortRange() + override { + return absl::nullopt; + } private: enum Value : char { @@ -109,6 +271,7 @@ class WifiDirectServiceMedium { kMediumStatusAccepting = (1 << 0), kMediumStatusServiceStarted = (1 << 1), kMediumStatusConnecting = (1 << 2), + kMediumStatusConnected = (1 << 3), }; // Medium Status int medium_status_ = kMediumStatusIdle; @@ -116,14 +279,18 @@ class WifiDirectServiceMedium { bool IsIdle() { return medium_status_ == kMediumStatusIdle; } // Advertiser is accepting connection on server socket bool IsAccepting() { return (medium_status_ & kMediumStatusAccepting) != 0; } - // Advertiser started Hotspot and sending beacon + // Advertiser started WifiDirectService bool IsServiceStarted() { return (medium_status_ & kMediumStatusServiceStarted) != 0; } - // Discoverer is connected with the Hotspot + // Discoverer is connecting with the WifiDirectService bool IsConnecting() { return (medium_status_ & kMediumStatusConnecting) != 0; } + // Discoverer is connected with the WifiDirectService + bool IsConnected() { + return (medium_status_ & kMediumStatusConnected) != 0; + } // Converts WiFiDirectServiceConfigurationMethod enum to a string. static std::string ConfigMethodToString( @@ -171,6 +338,12 @@ class WifiDirectServiceMedium { std::string ip_address_remote_; absl::Mutex mutex_; + absl::CondVar is_ip_address_ready_; + // Keep the server socket listener pointer + WifiDirectServiceServerSocket* server_socket_ptr_ ABSL_GUARDED_BY(mutex_) = + nullptr; + SubmittableExecutor listener_executor_; + absl::flat_hash_map> discovered_devices_by_id_; @@ -180,7 +353,6 @@ class WifiDirectServiceMedium { connection_requested_devices_by_id_; }; -} // namespace windows -} // namespace nearby +} // namespace nearby::windows #endif // PLATFORM_IMPL_WINDOWS_WIFI_DIRECT_SERVICE_H_ diff --git a/internal/platform/implementation/windows/wifi_direct_service_medium.cc b/internal/platform/implementation/windows/wifi_direct_service_medium.cc index 9fc694a1..2ec249b1 100644 --- a/internal/platform/implementation/windows/wifi_direct_service_medium.cc +++ b/internal/platform/implementation/windows/wifi_direct_service_medium.cc @@ -12,12 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include +#include #include #include #include +#include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" +#include "absl/time/time.h" +#include "internal/flags/nearby_flags.h" +#include "internal/platform/cancellation_flag.h" +#include "internal/platform/cancellation_flag_listener.h" +#include "internal/platform/flags/nearby_platform_feature_flags.h" +#include "internal/platform/implementation/wifi_direct_service.h" +#include "internal/platform/implementation/windows/socket_address.h" +#include "internal/platform/implementation/windows/utils.h" #include "internal/platform/implementation/windows/wifi_direct_service.h" #include "internal/platform/logging.h" @@ -26,6 +37,7 @@ namespace windows { namespace { constexpr std::wstring_view kServiceName = L"QuickShare"; constexpr std::wstring_view kPin = L"1234"; +constexpr int kWaitingForConnectionTimeoutSeconds = 90; // seconds } // namespace WifiDirectServiceMedium::WifiDirectServiceMedium() { @@ -41,6 +53,7 @@ WifiDirectServiceMedium::WifiDirectServiceMedium() { } WifiDirectServiceMedium::~WifiDirectServiceMedium() { + listener_executor_.Shutdown(); StopWifiDirectService(); DisconnectWifiDirectService(); if (controller_) { @@ -55,6 +68,206 @@ WifiDirectServiceMedium::~WifiDirectServiceMedium() { } } +bool WifiDirectServiceMedium::IsInterfaceValid() const { + HANDLE wifi_direct_handle = nullptr; + DWORD negotiated_version = 0; + DWORD result = 0; + + result = + WFDOpenHandle(WFD_API_VERSION, &negotiated_version, &wifi_direct_handle); + if (result == ERROR_SUCCESS) { + LOG(INFO) << "WiFi can support WifiDirect"; + WFDCloseHandle(wifi_direct_handle); + return true; + } + + LOG(ERROR) << "WiFi can't support WifiDirect"; + return false; +} + +// Discoverer connects to server socket +std::unique_ptr +WifiDirectServiceMedium::ConnectToService(absl::string_view ip_address, + int port, + CancellationFlag* cancellation_flag) { + LOG(INFO) << "WifiDirectServiceMedium::ConnectToService Server Socket"; + // check current status + if (!IsConnecting()) { + LOG(WARNING) << "GC is not connecting to GO, skip."; + return nullptr; + } + + std::string remote_ip_address; + if (ip_address.empty()) { + remote_ip_address = ip_address_remote_; + } else { + remote_ip_address = std::string(ip_address); + } + // when this API is called, GC may not finish connecting to GO, so we need to + // wait the connection is finished and IP address is ready. + if (remote_ip_address.empty()) { + LOG(INFO) << "Waiting for IP address to be ready."; + absl::MutexLock lock(mutex_); + is_ip_address_ready_.WaitWithTimeout( + &mutex_, absl::Seconds(kWaitingForConnectionTimeoutSeconds)); + if (ip_address_remote_.empty()) { + LOG(WARNING) + << "IP address is still empty, probably GC connecting to GO failed."; + return nullptr; + } + LOG(INFO) << "IP address is ready."; + remote_ip_address = ip_address_remote_; + } + + if (remote_ip_address.empty() || port == 0) { + LOG(ERROR) << "no valid service address and port to connect: " + << "ip_address = " << remote_ip_address << ", port = " << port; + return nullptr; + } + + bool dual_stack = NearbyFlags::GetInstance().GetBoolFlag( + platform::config_package_nearby::nearby_platform_feature:: + kEnableIpv6DualStack); + SocketAddress server_address(dual_stack); + if (!server_address.FromString(server_address, remote_ip_address, port)) { + LOG(ERROR) << "no valid service address and port to connect."; + return nullptr; + } + VLOG(1) << "ConnectToService server address: " << server_address.ToString(); + + // Try connecting to the service up to wifi_direct_max_connection_retries, + // because it may fail first time if DHCP procedure is not finished yet. + int64_t wifi_direct_max_connection_retries = + NearbyFlags::GetInstance().GetInt64Flag( + platform::config_package_nearby::nearby_platform_feature:: + kWifiHotspotConnectionMaxRetries); + int64_t wifi_direct_retry_interval_millis = + NearbyFlags::GetInstance().GetInt64Flag( + platform::config_package_nearby::nearby_platform_feature:: + kWifiHotspotConnectionIntervalMillis); + int64_t wifi_direct_client_socket_connect_timeout_millis = + NearbyFlags::GetInstance().GetInt64Flag( + platform::config_package_nearby::nearby_platform_feature:: + kWifiHotspotConnectionTimeoutMillis); + + VLOG(1) << "maximum connection retries=" << wifi_direct_max_connection_retries + << ", connection interval=" << wifi_direct_retry_interval_millis + << "ms, connection timeout=" + << wifi_direct_client_socket_connect_timeout_millis << "ms"; + + LOG(INFO) << "Connect to service "; + for (int i = 0; i < wifi_direct_max_connection_retries; ++i) { + auto wifi_direct_socket = std::make_unique(); + + // setup cancel listener + std::unique_ptr connection_cancellation_listener = + nullptr; + if (cancellation_flag != nullptr) { + if (cancellation_flag->Cancelled()) { + LOG(INFO) << "connect has been cancelled to service "; + return nullptr; + } + + connection_cancellation_listener = + std::make_unique( + cancellation_flag, [socket = wifi_direct_socket.get()]() { + LOG(WARNING) << "connect is closed due to it is cancelled."; + socket->Close(); + }); + } + + bool result = wifi_direct_socket->Connect(server_address); + if (!result) { + LOG(WARNING) << "reconnect to service at " << (i + 1) << "th times"; + Sleep(wifi_direct_retry_interval_millis); + continue; + } + + LOG(INFO) << "connected to remote service "; + return wifi_direct_socket; + } + + LOG(ERROR) << "Failed to connect to service "; + return nullptr; +} + +// Advertiser starts to listen on server socket +std::unique_ptr +WifiDirectServiceMedium::ListenForService(int port) { + LOG(INFO) << "WifiDirectServiceMedium::ListenForService"; + + absl::MutexLock lock(mutex_); + if (!IsServiceStarted()) { + LOG(WARNING) << "WifiDirect service is not started, skip."; + return nullptr; + } + // check current status + if (IsAccepting()) { + LOG(WARNING) << "Accepting connections already started on port " + << server_socket_ptr_->GetPort(); + return nullptr; + } + + auto server_socket = std::make_unique(port); + server_socket_ptr_ = server_socket.get(); + + // Start to listen on server socket in a separate thread. Before GC + // connects to GO, GO doesn't have IP address. BWU calls this API right away + // after it starts GO, we need to spin out the following logic to another + // thread to avoid blocking BWU sending out of band upgrade frame to GC. + listener_executor_.Execute([this]() mutable { + absl::MutexLock lock(mutex_); + bool dual_stack = NearbyFlags::GetInstance().GetBoolFlag( + platform::config_package_nearby::nearby_platform_feature:: + kEnableIpv6DualStack); + if (ip_address_local_.empty()) { + if (server_socket_ptr_) { + LOG(INFO) << "Waiting for IP address is ready."; + is_ip_address_ready_.WaitWithTimeout( + &mutex_, absl::Seconds(kWaitingForConnectionTimeoutSeconds)); + if (!server_socket_ptr_) { + LOG(WARNING) + << "Server socket was closed before IP address is ready."; + return; + } + if (ip_address_local_.empty()) { + LOG(WARNING) << "IP address is still empty, probably the GO doesn't " + "receive GC's connection request."; + server_socket_ptr_->Close(); + server_socket_ptr_ = nullptr; + return; + } + LOG(INFO) << "IP address is ready."; + } + } + + if (server_socket_ptr_ && + server_socket_ptr_->Listen(dual_stack, ip_address_local_)) { + medium_status_ |= kMediumStatusAccepting; + + // Setup close notifier after listen started. + server_socket_ptr_->SetCloseNotifier([this]() { + absl::MutexLock lock(mutex_); + LOG(INFO) << "Server socket was closed."; + medium_status_ &= (~kMediumStatusAccepting); + server_socket_ptr_ = nullptr; + }); + LOG(INFO) << "Started to listen serive on port " + << server_socket_ptr_->GetPort(); + } else { + LOG(WARNING) << "server_socket_ptr_ is null or Listen failed."; + if (server_socket_ptr_) { + server_socket_ptr_->Close(); + server_socket_ptr_ = nullptr; + } + } + }); + + LOG(INFO) << "Started to listen service on port " << port; + + return server_socket; +} + bool WifiDirectServiceMedium::StartWifiDirectService() { LOG(INFO) << "WifiDirectServiceMedium::StartWifiDirectService"; @@ -137,6 +350,9 @@ bool WifiDirectServiceMedium::StopWifiDirectService() { session_ = nullptr; } medium_status_ &= (~kMediumStatusServiceStarted); + medium_status_ &= (~kMediumStatusConnected); + server_socket_ptr_ = nullptr; + listener_executor_.Shutdown(); return true; } catch (std::exception exception) { LOG(ERROR) << __func__ << ": Stop WifiDirect GO failed. Exception: " @@ -272,9 +488,11 @@ fire_and_forget WifiDirectServiceMedium::OnSessionRequested( winrt::to_string(pair.RemoteHostName().DisplayName()); LOG(INFO) << "GO: Local IP: " << ip_address_local_ << ", Remote IP: " << ip_address_remote_; + is_ip_address_ready_.SignalAll(); } else { LOG(WARNING) << "GO: No connection endpoint pairs found."; } + medium_status_ |= kMediumStatusConnected; LOG(INFO) << "Service Address: " << winrt::to_string(session_.ServiceAddress()) @@ -417,6 +635,11 @@ fire_and_forget WifiDirectServiceMedium::Watcher_DeviceAdded( << ", Session Address: " << winrt::to_string(session_.SessionAddress()) << ", Session ID: " << session_.SessionId(); + { + absl::MutexLock lock(mutex_); + is_ip_address_ready_.SignalAll(); + } + medium_status_ |= kMediumStatusConnected; // Subscribe to events to prevent early teardown session_.SessionStatusChanged([](auto const& s, auto const& e) { @@ -456,7 +679,7 @@ fire_and_forget WifiDirectServiceMedium::Watcher_DeviceEnumerationCompleted( fire_and_forget WifiDirectServiceMedium::Watcher_DeviceStopped( DeviceWatcher sender, IInspectable inspectable) { - LOG(INFO) << "WifiDirectServiceMedium::Watcher_DeviceStopped"; + medium_status_ &= (~kMediumStatusConnecting); return fire_and_forget(); } @@ -476,6 +699,7 @@ bool WifiDirectServiceMedium::DisconnectWifiDirectService() { device_watcher_.Removed(device_watcher_removed_event_token_); device_watcher_.Stopped(device_watcher_stopped_event_token_); medium_status_ &= (~kMediumStatusConnecting); + medium_status_ &= (~kMediumStatusConnected); device_watcher_ = nullptr; service_ = nullptr; session_ = nullptr; diff --git a/internal/platform/implementation/windows/wifi_direct_service_server_socket.cc b/internal/platform/implementation/windows/wifi_direct_service_server_socket.cc new file mode 100644 index 00000000..95a00e8f --- /dev/null +++ b/internal/platform/implementation/windows/wifi_direct_service_server_socket.cc @@ -0,0 +1,117 @@ +// Copyright 2021 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 + +#include +#include +#include +#include +#include + +// Nearby connections headers +#include "absl/functional/any_invocable.h" +#include "absl/synchronization/mutex.h" +#include "internal/platform/exception.h" +#include "internal/platform/implementation/wifi_direct_service.h" +#include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Collections.h" +#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Connectivity.h" +#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Sockets.h" +#include "internal/platform/implementation/windows/socket_address.h" +#include "internal/platform/implementation/windows/utils.h" +#include "internal/platform/implementation/windows/wifi_direct_service.h" +#include "internal/platform/logging.h" + +namespace nearby::windows { + +namespace { +using ::winrt::Windows::Networking::Connectivity::NetworkInformation; +using ::winrt::Windows::Networking::Sockets::SocketQualityOfService; +} // namespace + +WifiDirectServiceServerSocket::WifiDirectServiceServerSocket(int port) + : port_(port) {} + +WifiDirectServiceServerSocket::~WifiDirectServiceServerSocket() { Close(); } + +std::string WifiDirectServiceServerSocket::GetIPAddress() const { + return wifi_direct_service_ipaddr_; +} + +int WifiDirectServiceServerSocket::GetPort() const { + return server_socket_.GetPort(); +} + +std::unique_ptr +WifiDirectServiceServerSocket::Accept() { + auto client_socket = server_socket_.Accept(); + if (client_socket == nullptr) { + return nullptr; + } + + LOG(INFO) << __func__ << ": Accepted a remote connection."; + return std::make_unique(std::move(client_socket)); +} + +void WifiDirectServiceServerSocket::SetCloseNotifier( + absl::AnyInvocable notifier) { + close_notifier_ = std::move(notifier); +} + +Exception WifiDirectServiceServerSocket::Close() { + absl::MutexLock lock(&mutex_); + if (closed_) { + return {Exception::kSuccess}; + } + + server_socket_.Close(); + closed_ = true; + + if (close_notifier_ != nullptr) { + close_notifier_(); + } + + LOG(INFO) << __func__ << ": Close completed succesfully."; + return {Exception::kSuccess}; +} + +bool WifiDirectServiceServerSocket::Listen(bool dual_stack, + std::string& ip_address) { + // Get current IP addresses of the device. + if (ip_address.empty()) { + return false; + } + wifi_direct_service_ipaddr_ = ip_address; + LOG(INFO) << "Listen wifi_direct_service on IP:port " << ip_address << ":" + << port_; + SocketAddress address(dual_stack); + if (!SocketAddress::FromString(address, ip_address, port_)) { + LOG(ERROR) << "Failed to parse wifi_direct_service IP address: " + << ip_address << " and port: " << port_; + return false; + } + if (!server_socket_.Listen(address)) { + LOG(ERROR) << "Failed to listen socket."; + return false; + } + + return true; +} + +std::string WifiDirectServiceServerSocket::GetWifiDirectServiceIpAddress() + const { + return wifi_direct_service_ipaddr_; +} + +} // namespace nearby::windows diff --git a/internal/platform/implementation/windows/wifi_direct_service_socket.cc b/internal/platform/implementation/windows/wifi_direct_service_socket.cc new file mode 100644 index 00000000..74865f44 --- /dev/null +++ b/internal/platform/implementation/windows/wifi_direct_service_socket.cc @@ -0,0 +1,39 @@ +// Copyright 2021 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 +#include + +#include "absl/base/nullability.h" +#include "internal/platform/implementation/windows/nearby_client_socket.h" +#include "internal/platform/implementation/windows/wifi_direct_service.h" + +namespace nearby { +namespace windows { + +WifiDirectServiceSocket::WifiDirectServiceSocket() + : client_socket_(std::make_unique()), + input_stream_(client_socket_.get()), + output_stream_(client_socket_.get()) {} + +WifiDirectServiceSocket::WifiDirectServiceSocket( + absl_nonnull std::unique_ptr socket) + : client_socket_(std::move(socket)), + input_stream_(client_socket_.get()), + output_stream_(client_socket_.get()) {} + +WifiDirectServiceSocket::~WifiDirectServiceSocket() { Close(); } + +} // namespace windows +} // namespace nearby