From 9fef2ce878ec58a719b1e0f0593e3ef27175805a Mon Sep 17 00:00:00 2001 From: hai007 Date: Mon, 13 Oct 2025 11:36:12 -0700 Subject: [PATCH] Wi-Fi Direct Service Implementation(1) PiperOrigin-RevId: 818758923 --- .../platform/implementation/windows/BUILD | 3 + .../windows/wifi_direct_service.h | 180 ++++++++ .../windows/wifi_direct_service_medium.cc | 419 ++++++++++++++++++ .../windows/wifi_direct_service_test.cc | 81 ++++ 4 files changed, 683 insertions(+) create mode 100644 internal/platform/implementation/windows/wifi_direct_service.h create mode 100644 internal/platform/implementation/windows/wifi_direct_service_medium.cc create mode 100644 internal/platform/implementation/windows/wifi_direct_service_test.cc diff --git a/internal/platform/implementation/windows/BUILD b/internal/platform/implementation/windows/BUILD index da899ee8..8d2d6841 100644 --- a/internal/platform/implementation/windows/BUILD +++ b/internal/platform/implementation/windows/BUILD @@ -126,6 +126,7 @@ cc_library( "webrtc.h", "wifi.h", "wifi_direct.h", + "wifi_direct_service.h", "wifi_hotspot.h", "wifi_hotspot_native.h", "wifi_intel.h", @@ -255,6 +256,7 @@ cc_library( "webrtc.cc", "wifi_direct_medium.cc", "wifi_direct_server_socket.cc", + "wifi_direct_service_medium.cc", "wifi_direct_socket.cc", "wifi_hotspot_medium.cc", "wifi_hotspot_native.cc", @@ -377,6 +379,7 @@ cc_test( "timer_test.cc", "utils_test.cc", "webrtc_test.cc", + "wifi_direct_service_test.cc", "wifi_hotspot_test.cc", "wifi_medium_test.cc", ], diff --git a/internal/platform/implementation/windows/wifi_direct_service.h b/internal/platform/implementation/windows/wifi_direct_service.h new file mode 100644 index 00000000..57e937df --- /dev/null +++ b/internal/platform/implementation/windows/wifi_direct_service.h @@ -0,0 +1,180 @@ +// 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_WINDOWS_WIFI_DIRECT_SERVICE_H_ +#define PLATFORM_IMPL_WINDOWS_WIFI_DIRECT_SERVICE_H_ + +// Windows headers +#include +#include + +// Standard C/C++ headers +#include +#include + +// Nearby connections headers +#include "absl/container/flat_hash_map.h" +#include "absl/synchronization/mutex.h" + +// WinRT headers +#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Enumeration.h" +#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.WiFiDirect.Services.h" +#include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Collections.h" +#include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.h" +#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.h" +#include "internal/platform/implementation/windows/generated/winrt/Windows.Security.Credentials.h" +#include "internal/platform/implementation/windows/generated/winrt/Windows.Security.Cryptography.h" +#include "internal/platform/implementation/windows/generated/winrt/Windows.Storage.Streams.h" +#include "internal/platform/implementation/windows/generated/winrt/Windows.System.h" +#include "internal/platform/implementation/windows/generated/winrt/base.h" + +namespace nearby { +namespace 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:: + WiFiDirectServiceAutoAcceptSessionConnectedEventArgs; +using ::winrt::Windows::Devices::WiFiDirect::Services:: + WiFiDirectServiceConfigurationMethod; +using ::winrt::Windows::Devices::WiFiDirect::Services::WiFiDirectServiceSession; +using ::winrt::Windows::Devices::WiFiDirect::Services:: + WiFiDirectServiceSessionRequestedEventArgs; +using ::winrt::Windows::Devices::WiFiDirect::Services::WiFiDirectServiceStatus; +using ::winrt::Windows::Foundation::AsyncStatus; +using ::winrt::Windows::Foundation::IInspectable; + +class WifiDirectServiceDiscovered { + public: + explicit WifiDirectServiceDiscovered(const DeviceInformation& device_info); + + ~WifiDirectServiceDiscovered() = default; + WifiDirectServiceDiscovered(WifiDirectServiceDiscovered&&) = default; + WifiDirectServiceDiscovered& operator=(WifiDirectServiceDiscovered&&) = + default; + + std::string GetId() { return id_; } + DeviceInformation GetDeviceInformation() { + return windows_wifi_direct_device_; + } + + private: + DeviceInformation windows_wifi_direct_device_; + + // Once the device is lost, we can no longer access it's id. + std::string id_; + + // Once the device is lost, we can no longer access it's mac address. + // std::string name_; +}; + +class WifiDirectServiceMedium { + public: + WifiDirectServiceMedium(); + ~WifiDirectServiceMedium(); + // WifiDirectServiceMedium is neither copyable nor movable. + WifiDirectServiceMedium(const WifiDirectServiceMedium&) = delete; + WifiDirectServiceMedium& operator=(const WifiDirectServiceMedium&) = delete; + + // Starts to advertising + bool StartWifiDirectService(); + // Stops to advertising + bool StopWifiDirectService(); + // Connects to a WifiDirectService + bool ConnectWifiDirectService(); + // Disconnects from a WifiDirectService + bool DisconnectWifiDirectService(); + + private: + enum Value : char { + kMediumStatusIdle = 0, + kMediumStatusAccepting = (1 << 0), + kMediumStatusServiceStarted = (1 << 1), + kMediumStatusConnecting = (1 << 2), + }; + // Medium Status + int medium_status_ = kMediumStatusIdle; + + 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 + bool IsServiceStarted() { + return (medium_status_ & kMediumStatusServiceStarted) != 0; + } + // Discoverer is connected with the Hotspot + bool IsConnecting() { + return (medium_status_ & kMediumStatusConnecting) != 0; + } + + WiFiDirectServiceAdvertiser advertiser_ = nullptr; + WiFiDirectService service_ = nullptr; + WiFiDirectServiceSession session_ = nullptr; + winrt::Windows::System::DispatcherQueueController controller_ = nullptr; + winrt::Windows::System::DispatcherQueue dispatcher_queue_ = nullptr; + DeviceInformation device_info_ = nullptr; + + fire_and_forget OnAdvertisementStatusChanged( + WiFiDirectServiceAdvertiser sender, IInspectable const& event); + fire_and_forget OnAutoAcceptSessionConnected( + WiFiDirectServiceAdvertiser sender, + WiFiDirectServiceAutoAcceptSessionConnectedEventArgs const& args); + fire_and_forget OnSessionRequested( + WiFiDirectServiceAdvertiser const& sender, + WiFiDirectServiceSessionRequestedEventArgs const& args); + + event_token advertisement_status_changed_token_; + event_token auto_accept_session_connected_token_; + event_token session_requested_token_; + + // Discovery properties + DeviceWatcher device_watcher_{nullptr}; + event_token device_watcher_added_event_token_; + event_token device_watcher_updated_event_token_; + event_token device_watcher_removed_event_token_; + event_token device_watcher_enumeration_completed_event_token_; + event_token device_watcher_stopped_event_token_; + + fire_and_forget Watcher_DeviceAdded(DeviceWatcher sender, + DeviceInformation device_info); + fire_and_forget Watcher_DeviceUpdated( + DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate); + fire_and_forget Watcher_DeviceRemoved( + DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate); + fire_and_forget Watcher_DeviceEnumerationCompleted(DeviceWatcher sender, + IInspectable inspectable); + fire_and_forget Watcher_DeviceStopped(DeviceWatcher sender, + IInspectable inspectable); + + absl::Mutex mutex_; + absl::flat_hash_map> + discovered_devices_by_id_; + + absl::flat_hash_map> + connection_requested_devices_by_id_; +}; + +} // namespace windows +} // namespace nearby + +#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 new file mode 100644 index 00000000..72329a31 --- /dev/null +++ b/internal/platform/implementation/windows/wifi_direct_service_medium.cc @@ -0,0 +1,419 @@ +// 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. + +#include +#include +#include + +#include "absl/synchronization/mutex.h" +#include "internal/platform/implementation/windows/wifi_direct_service.h" +#include "internal/platform/logging.h" + +namespace nearby { +namespace windows { +namespace { +constexpr std::wstring_view kServiceName = L"QuickShare"; +} // namespace + +WifiDirectServiceMedium::WifiDirectServiceMedium() { + LOG(INFO) << "WifiDirectServiceMedium::WifiDirectServiceMedium"; + // Create a DispatcherQueue for this thread. + controller_ = winrt::Windows::System::DispatcherQueueController:: + CreateOnDedicatedThread(); + dispatcher_queue_ = controller_.DispatcherQueue(); + if (!dispatcher_queue_) { + LOG(WARNING) << "Failed to get DispatcherQueue for current thread. " + "ConnectAsync might fail if not called from UI thread."; + } +} + +WifiDirectServiceMedium::~WifiDirectServiceMedium() { + StopWifiDirectService(); + DisconnectWifiDirectService(); + if (controller_) { + // Asynchronously shut down the dispatcher queue. + winrt::Windows::Foundation::IAsyncAction shutdown_async = + controller_.ShutdownQueueAsync(); + + // Block and wait for the shutdown to complete. This ensures that any + // in-progress event handlers on the dedicated thread are finished + // before this object is fully destroyed. + shutdown_async.get(); + } +} + +bool WifiDirectServiceMedium::StartWifiDirectService() { + LOG(INFO) << "WifiDirectServiceMedium::StartWifiDirectService"; + + absl::MutexLock lock(mutex_); + if (IsServiceStarted()) { + LOG(WARNING) << "Already started WifiDirect service, skip."; + return true; + } + + // Create Advertiser object + advertiser_ = WiFiDirectServiceAdvertiser(kServiceName); + advertisement_status_changed_token_ = advertiser_.AdvertisementStatusChanged( + {this, &WifiDirectServiceMedium::OnAdvertisementStatusChanged}); + auto_accept_session_connected_token_ = advertiser_.AutoAcceptSessionConnected( + {this, &WifiDirectServiceMedium::OnAutoAcceptSessionConnected}); + session_requested_token_ = advertiser_.SessionRequested( + {this, &WifiDirectServiceMedium::OnSessionRequested}); + + advertiser_.AutoAcceptSession(false); + advertiser_.PreferGroupOwnerMode(true); + advertiser_.ServiceStatus(WiFiDirectServiceStatus::Available); + // Config Methods + auto config_method = WiFiDirectServiceConfigurationMethod::Default; + advertiser_.PreferredConfigurationMethods().Clear(); + advertiser_.PreferredConfigurationMethods().Append(config_method); + + try { + advertiser_.Start(); + LOG(INFO) << "Start WifiDirect GO succeeded. Status: " + << (int)advertiser_.AdvertisementStatus(); + if ((advertiser_.AdvertisementStatus() == + WiFiDirectServiceAdvertisementStatus::Created) || + (advertiser_.AdvertisementStatus() == + WiFiDirectServiceAdvertisementStatus::Started)) { + medium_status_ |= kMediumStatusServiceStarted; + return true; + } + LOG(ERROR) << "Start WifiDirect GO failed."; + return false; + } catch (std::exception exception) { + LOG(ERROR) << __func__ << ": Stop WifiDirect GO failed. Exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + LOG(ERROR) << __func__ << ": Stop WifiDirect GO failed. WinRT exception: " + << error.code() << ": " << winrt::to_string(error.message()); + } catch (...) { + LOG(ERROR) << __func__ << ": Unknown exeption."; + } + advertiser_.AdvertisementStatusChanged(advertisement_status_changed_token_); + advertiser_.AutoAcceptSessionConnected(auto_accept_session_connected_token_); + advertiser_.SessionRequested(session_requested_token_); + advertiser_.PreferredConfigurationMethods().Clear(); + advertiser_ = nullptr; + return false; +} + +bool WifiDirectServiceMedium::StopWifiDirectService() { + LOG(INFO) << "WifiDirectServiceMedium::StopWifiDirectService"; + absl::MutexLock lock(mutex_); + if (!IsServiceStarted()) { + LOG(WARNING) << "Cannot stop Service because no Service is started."; + return true; + } + + try { + if (advertiser_) { + advertiser_.Stop(); + advertiser_.AdvertisementStatusChanged( + advertisement_status_changed_token_); + advertiser_.AutoAcceptSessionConnected( + auto_accept_session_connected_token_); + advertiser_.SessionRequested(session_requested_token_); + advertiser_ = nullptr; + device_info_ = nullptr; + session_ = nullptr; + } + medium_status_ &= (~kMediumStatusServiceStarted); + return true; + } catch (std::exception exception) { + LOG(ERROR) << __func__ << ": Stop WifiDirect GO failed. Exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + LOG(ERROR) << __func__ << ": Stop WifiDirect GO failed. WinRT exception: " + << error.code() << ": " << winrt::to_string(error.message()); + } catch (...) { + LOG(ERROR) << __func__ << ": Unknown exeption."; + } + return false; +} + +fire_and_forget WifiDirectServiceMedium::OnAdvertisementStatusChanged( + WiFiDirectServiceAdvertiser sender, IInspectable const& event) { + LOG(INFO) << "WiFiDirectServiceAdvertiser status changed: " + << (int)sender.AdvertisementStatus(); + auto status = sender.ServiceStatus(); + switch (status) { + case WiFiDirectServiceStatus ::Available: + LOG(INFO) << "WiFiDirectServiceAdvertiser service status changed: " + "status: Available"; + break; + case WiFiDirectServiceStatus ::Busy: + LOG(INFO) << "WiFiDirectServiceAdvertiser service status changed: " + "status: Busy"; + break; + case WiFiDirectServiceStatus ::Custom: + LOG(INFO) << "WiFiDirectServiceAdvertiser service status changed: " + "status: Custom"; + break; + default: + LOG(INFO) << "WiFiDirectServiceAdvertiser service status changed: " + "Code: " + << (int)status; + break; + } + return winrt::fire_and_forget(); +} + +fire_and_forget WifiDirectServiceMedium::OnAutoAcceptSessionConnected( + WiFiDirectServiceAdvertiser sender, + WiFiDirectServiceAutoAcceptSessionConnectedEventArgs const& args) { + LOG(INFO) << "WifiDirectServiceMedium::OnAutoAcceptSessionConnected"; + try { + auto session = args.Session(); + if (!session) { + LOG(ERROR) << "OnAutoAcceptSessionConnected returned null session"; + co_return; + } + session_ = std::move(session); + LOG(INFO) << "Service Address: " + << winrt::to_string(session_.ServiceAddress()) + << ", Service Name: " << winrt::to_string(session_.ServiceName()) + << ", Advertisement ID: " << session_.AdvertisementId() + << ", Session Address: " + << winrt::to_string(session_.SessionAddress()) + << ", Session ID: " << session_.SessionId(); + // Subscribe to events to prevent early teardown + session_.SessionStatusChanged([](auto const& s, auto const& e) { + LOG(INFO) << "GO: Session status changed"; + }); + co_return; + } catch (std::exception exception) { + LOG(ERROR) << __func__ + << ": Failed to get session. Exception: " << exception.what(); + } catch (const winrt::hresult_error& error) { + LOG(ERROR) << __func__ + << ": Failed to get session. WinRT exception: " << error.code() + << ": " << winrt::to_string(error.message()); + } catch (...) { + LOG(ERROR) << __func__ << ": Unknown exeption."; + } +} + +fire_and_forget WifiDirectServiceMedium::OnSessionRequested( + WiFiDirectServiceAdvertiser const& sender, + WiFiDirectServiceSessionRequestedEventArgs const& args) { + try { + auto request = args.GetSessionRequest(); + if (!request) { + LOG(ERROR) << "OnSessionRequested returned null session request"; + co_return; + } + device_info_ = request.DeviceInformation(); + LOG(INFO) << "GO: OnSessionRequested: " + << winrt::to_string(device_info_.Id()); + + LOG(INFO) << "GO: Dispatch to UI thread to call ConnectAsync"; + dispatcher_queue_.TryEnqueue([this]() { + LOG(INFO) << "GO: TryEnqueue: calling ConnectAsync"; + + absl::MutexLock lock(mutex_); + auto session = advertiser_.ConnectAsync(device_info_).get(); + LOG(INFO) << "GO: TryEnqueue: Wait for ConnectAsync finish"; + if (!session) { + LOG(ERROR) << "OnSessionRequested returned null session"; + return; + } + LOG(INFO) << "GO: TryEnqueue: OnSessionRequested: ConnectAsync succeeded"; + session_ = std::move(session); + LOG(INFO) << "Service Address: " + << winrt::to_string(session_.ServiceAddress()) + << ", Service Name: " + << winrt::to_string(session_.ServiceName()) + << ", Advertisement ID: " << session_.AdvertisementId() + << ", Session Address: " + << winrt::to_string(session_.SessionAddress()) + << ", Session ID: " << session_.SessionId(); + // Subscribe to events to prevent early teardown + session_.SessionStatusChanged([](auto const& s, auto const& e) { + LOG(INFO) << "GO: TryEnqueue: Session status changed"; + }); + }); + LOG(INFO) << "GC: Dispatch to UI thread to call ConnectAsync finish"; + } catch (std::exception exception) { + LOG(ERROR) << __func__ + << ": Failed to get session. Exception: " << exception.what(); + } catch (const winrt::hresult_error& error) { + LOG(ERROR) << __func__ + << ": Failed to get session. WinRT exception: " << error.code() + << ": " << winrt::to_string(error.message()); + } catch (...) { + LOG(ERROR) << __func__ << ": Unknown exeption."; + } +} + +bool WifiDirectServiceMedium::ConnectWifiDirectService() { + LOG(INFO) << "WifiDirectServiceMedium::ConnectWifiDirectService"; + absl::MutexLock lock(mutex_); + if (IsConnecting()) { + LOG(WARNING) << "Service discovery already running"; + return false; + } + + if (device_watcher_) { + LOG(WARNING) + << "Device Watcher has already been set, please investigate! Skip"; + return false; + } + + discovered_devices_by_id_.clear(); + connection_requested_devices_by_id_.clear(); + + winrt::hstring device_selector = WiFiDirectService::GetSelector(kServiceName); + const winrt::param::iterable requested_properties = + winrt::single_threaded_vector({ + winrt::to_hstring("System.Devices.WiFiDirectServices.ServiceAddress"), + winrt::to_hstring("System.Devices.WiFiDirectServices.ServiceName"), + winrt::to_hstring( + "System.Devices.WiFiDirectServices.ServiceInformation"), + winrt::to_hstring( + "System.Devices.WiFiDirectServices.AdvertisementId"), + winrt::to_hstring( + "System.Devices.WiFiDirectServices.ServiceConfigMethods"), + }); + LOG(INFO) << "Create device watcher"; + device_watcher_ = + DeviceInformation::CreateWatcher(device_selector, requested_properties); + device_watcher_added_event_token_ = device_watcher_.Added( + {this, &WifiDirectServiceMedium::Watcher_DeviceAdded}); + device_watcher_updated_event_token_ = device_watcher_.Updated( + {this, &WifiDirectServiceMedium::Watcher_DeviceUpdated}); + device_watcher_removed_event_token_ = device_watcher_.Removed( + {this, &WifiDirectServiceMedium::Watcher_DeviceRemoved}); + device_watcher_enumeration_completed_event_token_ = + device_watcher_.EnumerationCompleted( + {this, &WifiDirectServiceMedium::Watcher_DeviceEnumerationCompleted}); + device_watcher_stopped_event_token_ = device_watcher_.Stopped( + {this, &WifiDirectServiceMedium::Watcher_DeviceStopped}); + device_watcher_.Start(); + medium_status_ |= kMediumStatusConnecting; + LOG(INFO) << "Started to discover WifiDirect service and connect."; + return true; +} + +fire_and_forget WifiDirectServiceMedium::Watcher_DeviceAdded( + DeviceWatcher sender, DeviceInformation device_info) { + LOG(INFO) << "Device Service founded for device ID " + << winrt::to_string(device_info.Id()) + << "; device name: " << winrt::to_string(device_info.Name()); + + auto props = device_info.Properties(); + if (props.HasKey(L"System.Devices.WiFiDirectServices.ServiceName")) { + winrt::hstring svc_name = winrt::unbox_value( + props.Lookup(L"System.Devices.WiFiDirectServices.ServiceName")); + LOG(INFO) << "Discovered service: " << winrt::to_string(svc_name); + } + try { + service_ = co_await WiFiDirectService::FromIdAsync(device_info.Id()); + if (!service_) { + LOG(ERROR) << "FromIdAsync returned null service"; + co_return; + } + LOG(INFO) << "GC: ConnectAsync in Watcher_DeviceAdded"; + + auto session = service_.ConnectAsync().get(); + if (!session) { + LOG(ERROR) << "GC: ConnectAsync returned null session"; + co_return; + } + LOG(INFO) << "GC: ConnectAsync succeeded"; + session_ = std::move(session); + LOG(INFO) << "Service Address: " + << winrt::to_string(session_.ServiceAddress()) + << ", Service Name: " << winrt::to_string(session_.ServiceName()) + << ", Advertisement ID: " << session_.AdvertisementId() + << ", Session Address: " + << winrt::to_string(session_.SessionAddress()) + << ", Session ID: " << session_.SessionId(); + + // Subscribe to events to prevent early teardown + session_.SessionStatusChanged([](auto const& s, auto const& e) { + LOG(INFO) << "GC: TryEnqueue: Session status changed"; + }); + } catch (std::exception exception) { + LOG(ERROR) << __func__ + << ": Failed to resolve WiFiDirectService from Id. Exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + LOG(ERROR) + << __func__ + << ": Failed to resolve WiFiDirectService from Id. WinRT exception: " + << error.code() << ": " << winrt::to_string(error.message()); + } catch (...) { + LOG(ERROR) << __func__ << ": Unknown exeption."; + } +} + +fire_and_forget WifiDirectServiceMedium::Watcher_DeviceUpdated( + DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) { + VLOG(1) << "WifiDirectServiceMedium::Watcher_DeviceUpdated"; + return fire_and_forget(); +} + +fire_and_forget WifiDirectServiceMedium::Watcher_DeviceRemoved( + DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) { + LOG(INFO) << "WifiDirectServiceMedium::Watcher_DeviceRemoved"; + return fire_and_forget(); +} + +fire_and_forget WifiDirectServiceMedium::Watcher_DeviceEnumerationCompleted( + DeviceWatcher sender, IInspectable inspectable) { + LOG(INFO) << "WifiDirectServiceMedium::Watcher_DeviceEnumerationCompleted"; + return fire_and_forget(); +} + +fire_and_forget WifiDirectServiceMedium::Watcher_DeviceStopped( + DeviceWatcher sender, IInspectable inspectable) { + LOG(INFO) << "WifiDirectServiceMedium::Watcher_DeviceStopped"; + return fire_and_forget(); +} + +bool WifiDirectServiceMedium::DisconnectWifiDirectService() { + LOG(WARNING) << "Stop connecting."; + absl::MutexLock lock(mutex_); + if (!IsConnecting()) { + LOG(WARNING) << "no discovering service to stop."; + return false; + } + try { + device_watcher_.Stop(); + device_watcher_.Added(device_watcher_added_event_token_); + device_watcher_.Updated(device_watcher_updated_event_token_); + device_watcher_.EnumerationCompleted( + device_watcher_enumeration_completed_event_token_); + device_watcher_.Removed(device_watcher_removed_event_token_); + device_watcher_.Stopped(device_watcher_stopped_event_token_); + medium_status_ &= (~kMediumStatusConnecting); + device_watcher_ = nullptr; + service_ = nullptr; + session_ = nullptr; + return true; + } catch (std::exception exception) { + LOG(ERROR) << __func__ << ": Stop WifiDirect GC failed. Exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + LOG(ERROR) << __func__ << ": Stop WifiDirect GC failed. WinRT exception: " + << error.code() << ": " << winrt::to_string(error.message()); + } catch (...) { + LOG(ERROR) << __func__ << ": Unknown exeption."; + } + return false; +} + +} // namespace windows +} // namespace nearby diff --git a/internal/platform/implementation/windows/wifi_direct_service_test.cc b/internal/platform/implementation/windows/wifi_direct_service_test.cc new file mode 100644 index 00000000..dee0730b --- /dev/null +++ b/internal/platform/implementation/windows/wifi_direct_service_test.cc @@ -0,0 +1,81 @@ +// 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. + +#include "internal/platform/implementation/windows/wifi_direct_service.h" + +#include +#include + +#include "gtest/gtest.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "internal/platform/logging.h" + +namespace nearby { +namespace windows { +namespace { + +TEST(WifiDirectServiceMedium, DISABLED_StartWifiDirectService) { + int run_test; + LOG(INFO) << "Run StartWifiDirectService test case? input 0 or 1:"; + std::cin >> run_test; + + if (run_test) { + winrt::init_apartment(); + WifiDirectServiceMedium wifi_direct_service_medium; + + EXPECT_TRUE(wifi_direct_service_medium.StartWifiDirectService()); + + while (true) { + LOG(INFO) << "Enter \"s\" to stop test:"; + std::string stop; + std::cin >> stop; + if (stop == "s") { + LOG(INFO) << "Exit WiFi WifiDirectService GO"; + EXPECT_TRUE(wifi_direct_service_medium.StopWifiDirectService()); + break; + } + } + } else { + LOG(INFO) << "Skip the test"; + } +} + +TEST(WifiDirectServiceMedium, DISABLED_ConnectWifiDirectService) { + int run_test; + LOG(INFO) << "Run ConnectWifiDirectService GO test case? input 0 or 1:"; + std::cin >> run_test; + + if (run_test) { + WifiDirectServiceMedium wifi_direct_service_medium; + EXPECT_TRUE(wifi_direct_service_medium.ConnectWifiDirectService()); + + absl::SleepFor(absl::Seconds(2)); + while (true) { + LOG(INFO) << "Enter \"s\" to stop test:"; + std::string stop; + std::cin >> stop; + if (stop == "s") { + LOG(INFO) << "Disconnect WiFiDirectService GO"; + EXPECT_TRUE(wifi_direct_service_medium.DisconnectWifiDirectService()); + break; + } + } + } else { + LOG(INFO) << "Skip the test"; + } +} +} // namespace +} // namespace windows +} // namespace nearby