diff --git a/internal/platform/implementation/windows/BUILD b/internal/platform/implementation/windows/BUILD index 4626a1ab..9af770db 100644 --- a/internal/platform/implementation/windows/BUILD +++ b/internal/platform/implementation/windows/BUILD @@ -145,10 +145,6 @@ cc_library( "network_info.h", ], compatible_with = ["//buildenv/target:non_prod"], - linkopts = [ - "iphlpapi.lib", - "ole32.lib", - ], tags = ["windows"], visibility = [ "//:__subpackages__", @@ -161,6 +157,39 @@ cc_library( ], ) +cc_library( + name = "scoped_wlan_memory", + hdrs = [ + "scoped_wlan_memory.h", + ], + compatible_with = ["//buildenv/target:non_prod"], + tags = ["windows"], + visibility = [ + "//:__subpackages__", + "//location/nearby:__subpackages__", + ], +) + +cc_library( + name = "wlan_client", + srcs = [ + "wlan_client.cc", + ], + hdrs = [ + "wlan_client.h", + ], + compatible_with = ["//buildenv/target:non_prod"], + tags = ["windows"], + visibility = [ + "//:__subpackages__", + "//location/nearby:__subpackages__", + ], + deps = [ + ":scoped_wlan_memory", + "//internal/platform:logging", + ], +) + cc_library( name = "windows", srcs = [ @@ -256,9 +285,11 @@ cc_library( deps = [ ":crypto", # build_cleaner: keep ":network_info", + ":scoped_wlan_memory", ":socket_address", ":string_utils", ":types", + ":wlan_client", "//connections/implementation/flags:connections_flags", "//connections/implementation/mediums:utils", "//connections/implementation/mediums/ble:ble_advertisement_header", @@ -451,6 +482,7 @@ cc_test( ], deps = [ ":network_info", + "//sharing/internal/impl/windows:platform_windows_libs", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_googletest//:gtest_main", ], @@ -469,3 +501,33 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "scoped_wlan_memory_test", + size = "small", + timeout = "short", + srcs = [ + "scoped_wlan_memory_test.cc", + ], + deps = [ + ":scoped_wlan_memory", + "//sharing/internal/impl/windows:platform_windows_libs", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "wlan_client_test", + size = "small", + timeout = "short", + srcs = [ + "wlan_client_test.cc", + ], + deps = [ + ":wlan_client", + "//sharing/internal/impl/windows:platform_windows_libs", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/internal/platform/implementation/windows/scoped_wlan_memory.h b/internal/platform/implementation/windows/scoped_wlan_memory.h new file mode 100644 index 00000000..d7c72f4b --- /dev/null +++ b/internal/platform/implementation/windows/scoped_wlan_memory.h @@ -0,0 +1,69 @@ +// 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 THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_SCOPED_WLAN_MEMORY_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_SCOPED_WLAN_MEMORY_H_ + +#include +#include + +#include + +#include "absl/base/attributes.h" + +namespace nearby::windows { + +// Smart pointer class for memory allocated by the WLAN API. +template +class ScopedWlanMemory { + public: + explicit ScopedWlanMemory(T* ptr = nullptr) : ptr_(ptr) {} + ~ScopedWlanMemory() { + Reset(); + } + ScopedWlanMemory& operator=(ScopedWlanMemory&& other) { + Reset(other.Release()); + return *this; + } + T* operator->() { return ptr_; } + T* get() { return ptr_; } + + // Returns a pointer to the managed object and releases the ownership. + ABSL_MUST_USE_RESULT T* Release() { return std::exchange(ptr_, nullptr); } + + // Used as an output parameter for functions that return a pointer as an + // output parameter. + // If the current managed object is not null, it will be freed. + T** OutParam() { + Reset(); + return &ptr_; + } + + // Swaps a new pointer into this instance. Frees the old pointer if it is not + // null. + void Reset(T* new_ptr = nullptr) { + if (ptr_ != nullptr) { + ::WlanFreeMemory(ptr_); + } + ptr_ = new_ptr; + } + explicit operator bool() const { return ptr_ != nullptr; } + + private: + T* ptr_; +}; + +} // namespace nearby::windows + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_SCOPED_WLAN_MEMORY_H_ diff --git a/internal/platform/implementation/windows/scoped_wlan_memory_test.cc b/internal/platform/implementation/windows/scoped_wlan_memory_test.cc new file mode 100644 index 00000000..2fa5bead --- /dev/null +++ b/internal/platform/implementation/windows/scoped_wlan_memory_test.cc @@ -0,0 +1,97 @@ +// 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/scoped_wlan_memory.h" + +#include +#include +#include + +#include "gtest/gtest.h" + +namespace nearby::windows { +namespace { + +TEST(ScopedWlanMemoryTest, Empty) { + ScopedWlanMemory wlan_list; + EXPECT_EQ(wlan_list.get(), nullptr); +} + +TEST(ScopedWlanMemoryTest, Reset) { + ScopedWlanMemory wlan_list; + EXPECT_EQ(wlan_list.get(), nullptr); + WLAN_INTERFACE_INFO_LIST* wlan_list_ptr = + reinterpret_cast( + ::WlanAllocateMemory(sizeof(WLAN_INTERFACE_INFO_LIST))); + wlan_list.Reset(wlan_list_ptr); + EXPECT_EQ(wlan_list.get(), wlan_list_ptr); + wlan_list.Reset(); + EXPECT_EQ(wlan_list.get(), nullptr); +} + +TEST(ScopedWlanMemoryTest, ConstructWithPointer) { + WLAN_INTERFACE_INFO_LIST* wlan_list_ptr = + reinterpret_cast( + ::WlanAllocateMemory(sizeof(WLAN_INTERFACE_INFO_LIST))); + ScopedWlanMemory wlan_list(wlan_list_ptr); + EXPECT_EQ(wlan_list.get(), wlan_list_ptr); +} + +TEST(ScopedWlanMemoryTest, PointerAccess) { + WLAN_INTERFACE_INFO_LIST* wlan_list_ptr = + reinterpret_cast( + ::WlanAllocateMemory(sizeof(WLAN_INTERFACE_INFO_LIST))); + wlan_list_ptr->dwIndex = 9834; + ScopedWlanMemory wlan_list(wlan_list_ptr); + EXPECT_EQ(wlan_list->dwIndex, 9834); +} + +void TestFunc(WLAN_INTERFACE_INFO_LIST** wlan_list_ptr) { + *wlan_list_ptr = reinterpret_cast( + ::WlanAllocateMemory(sizeof(WLAN_INTERFACE_INFO_LIST))); + (*wlan_list_ptr)->dwIndex = 9834; +} + +TEST(ScopedWlanMemoryTest, OutParam) { + ScopedWlanMemory wlan_list; + TestFunc(wlan_list.OutParam()); + EXPECT_EQ(wlan_list->dwIndex, 9834); +} + +TEST(ScopedWlanMemoryTest, MoveAssignment) { + WLAN_INTERFACE_INFO_LIST* wlan_list_ptr = + reinterpret_cast( + ::WlanAllocateMemory(sizeof(WLAN_INTERFACE_INFO_LIST))); + ScopedWlanMemory wlan_list(wlan_list_ptr); + EXPECT_EQ(wlan_list.get(), wlan_list_ptr); + ScopedWlanMemory wlan_list2; + + wlan_list2 = std::move(wlan_list); + + EXPECT_EQ(wlan_list2.get(), wlan_list_ptr); +} + +TEST(ScopedWlanMemoryTest, Release) { + WLAN_INTERFACE_INFO_LIST* wlan_list_ptr = + reinterpret_cast( + ::WlanAllocateMemory(sizeof(WLAN_INTERFACE_INFO_LIST))); + ScopedWlanMemory wlan_list(wlan_list_ptr); + EXPECT_EQ(wlan_list.get(), wlan_list_ptr); + + EXPECT_EQ(wlan_list.Release(), wlan_list_ptr); + EXPECT_EQ(wlan_list.get(), nullptr); +} + +} // namespace +} // namespace nearby::windows diff --git a/internal/platform/implementation/windows/wifi.h b/internal/platform/implementation/windows/wifi.h index 5c5a51b0..e3310aae 100644 --- a/internal/platform/implementation/windows/wifi.h +++ b/internal/platform/implementation/windows/wifi.h @@ -103,6 +103,7 @@ class WifiMedium : public api::WifiMedium { // Since the WiFi interface capability won't change in the connection session, // we only need to query it once at the beginning void InitCapability(); + bool GetWifiInformation(); std::string InternalGetWifiIpAddress(); std::string InternalGetEthernetIpAddress(); void FillupEthernetParams(); diff --git a/internal/platform/implementation/windows/wifi_hotspot_native.cc b/internal/platform/implementation/windows/wifi_hotspot_native.cc index 3377c421..6dbf4471 100644 --- a/internal/platform/implementation/windows/wifi_hotspot_native.cc +++ b/internal/platform/implementation/windows/wifi_hotspot_native.cc @@ -27,6 +27,7 @@ #include #include #include +#include #include "absl/base/nullability.h" #include "absl/strings/str_format.h" @@ -37,6 +38,7 @@ #include "internal/platform/exception.h" #include "internal/platform/implementation/windows/network_info.h" #include "internal/platform/implementation/windows/string_utils.h" +#include "internal/platform/implementation/windows/wlan_client.h" #include "internal/platform/logging.h" namespace nearby::windows { @@ -84,17 +86,10 @@ std::string ReasonCodeToString(DWORD reason_code) { WifiHotspotNative::WifiHotspotNative() : network_info_(NetworkInfo::GetNetworkInfo()) { - // Open WLAN handle - DWORD negotiated_version; - DWORD result = WlanOpenHandle(/*dwClientVersion=*/2, /*pReserved=*/nullptr, - /*pdwNegotiatedVersion=*/&negotiated_version, - /*phClientHandle=*/&wifi_); - - if (result != ERROR_SUCCESS) { - LOG(ERROR) << "Failed to open WLAN handle."; + if (!wlan_client_.Initialize()) { + LOG(ERROR) << "Failed to initialize WLAN client."; return; } - VLOG(1) << "WifiHotspotNative created successfully."; GUID interface_guid = GetInterfaceGuid(); if (interface_guid != GUID_NULL) { @@ -103,11 +98,6 @@ WifiHotspotNative::WifiHotspotNative() } WifiHotspotNative::~WifiHotspotNative() { - if (wifi_ != nullptr) { - WlanCloseHandle(wifi_, nullptr); - wifi_ = nullptr; - } - VLOG(1) << "WifiHotspotNative destroyed successfully."; } @@ -143,9 +133,8 @@ bool WifiHotspotNative::DisconnectWifiNetwork() { return false; } absl::MutexLock lock(mutex_); - DWORD result = WlanDisconnect( - /*hClientHandle=*/wifi_, /*pInterfaceGuid=*/&interface_guid, - /*pReserved=*/nullptr); + DWORD result = WlanDisconnect(wlan_client_.GetHandle(), &interface_guid, + /*pReserved=*/nullptr); if (result != ERROR_SUCCESS) { LOG(ERROR) << "Failed to disconnect WLAN profile with error: " << result; } else { @@ -229,29 +218,18 @@ std::wstring WifiHotspotNative::BuildWlanProfile(absl::string_view ssid, } GUID WifiHotspotNative::GetInterfaceGuid() const { - if (wifi_ == nullptr) { + if (wlan_client_.GetHandle() == nullptr) { return GUID_NULL; } // Find WLAN interface, only support one interface for now. - PWLAN_INTERFACE_INFO_LIST interfaces = nullptr; - DWORD result = - WlanEnumInterfaces(/*hClientHandle=*/wifi_, /*pReserved=*/nullptr, - /*ppInterfaceList=*/&interfaces); - if (result != ERROR_SUCCESS) { - LOG(ERROR) << "Failed to enum WLAN interfaces."; - return GUID_NULL; - } - - if (interfaces->dwNumberOfItems == 0) { + std::vector interfaces = + wlan_client_.GetInterfaceInfos(); + if (interfaces.empty()) { LOG(ERROR) << "No WLAN interfaces found."; - WlanFreeMemory(interfaces); return GUID_NULL; } - - GUID interface_guid = interfaces->InterfaceInfo[0].InterfaceGuid; - WlanFreeMemory(interfaces); - return interface_guid; + return interfaces[0].guid; } bool WifiHotspotNative::ConnectToWifiNetworkInternal( @@ -261,8 +239,8 @@ bool WifiHotspotNative::ConnectToWifiNetworkInternal( return false; } WlanNotificationContext context = { - .wifi_hotspot_native = *this, - .connecting_profile_name = profile_name, + .wifi_hotspot_native = *this, + .connecting_profile_name = profile_name, }; if (!RegisterWlanNotificationCallback(&context)) { LOG(ERROR) << "Failed to register WLAN notification callback."; @@ -279,9 +257,8 @@ bool WifiHotspotNative::ConnectToWifiNetworkInternal( parameters.dot11BssType = dot11_BSS_type_infrastructure; parameters.dwFlags = 0; - DWORD result = - WlanConnect(/*hClientHandle=*/wifi_, /*pInterfaceGuid=*/&interface_guid, - /*pConnectionParameters=*/¶meters, /*pReserved=*/nullptr); + DWORD result = WlanConnect(wlan_client_.GetHandle(), &interface_guid, + ¶meters, /*pReserved=*/nullptr); if (result != ERROR_SUCCESS) { LOG(ERROR) << "Failed to connect to WLAN profile " << string_utils::WideStringToString(profile_name); @@ -311,10 +288,9 @@ bool WifiHotspotNative::ConnectToWifiNetworkInternal( bool WifiHotspotNative::RegisterWlanNotificationCallback( WlanNotificationContext* absl_nonnull context) { DWORD result = WlanRegisterNotification( - /*hClientHandle=*/wifi_, /*dwNotifSource=*/WLAN_NOTIFICATION_SOURCE_ACM, - /*bIgnoreDuplicate=*/TRUE, - /*funcCallback=*/WifiHotspotNative::WlanNotificationCallback, - /*pCallbackContext=*/context, /*pReserved=*/nullptr, + wlan_client_.GetHandle(), WLAN_NOTIFICATION_SOURCE_ACM, + /*bIgnoreDuplicate=*/TRUE, WifiHotspotNative::WlanNotificationCallback, + context, /*pReserved=*/nullptr, /*pdwPrevNotifSource=*/nullptr); if (result != ERROR_SUCCESS) { LOG(ERROR) << "Failed to register WLAN notification with error: " << result; @@ -326,7 +302,7 @@ bool WifiHotspotNative::RegisterWlanNotificationCallback( bool WifiHotspotNative::UnregisterWlanNotificationCallback() { DWORD result = WlanRegisterNotification( - /*hClientHandle=*/wifi_, /*dwNotifSource=*/WLAN_NOTIFICATION_SOURCE_NONE, + wlan_client_.GetHandle(), WLAN_NOTIFICATION_SOURCE_NONE, /*bIgnoreDuplicate=*/TRUE, /*funcCallback=*/nullptr, /*pCallbackContext=*/nullptr, /*pReserved=*/nullptr, /*pdwPrevNotifSource=*/nullptr); @@ -339,15 +315,16 @@ bool WifiHotspotNative::UnregisterWlanNotificationCallback() { return true; } -bool WifiHotspotNative::SetWlanProfile( - GUID interface_guid, absl::string_view ssid, absl::string_view password) { +bool WifiHotspotNative::SetWlanProfile(GUID interface_guid, + absl::string_view ssid, + absl::string_view password) { std::wstring profile = BuildWlanProfile(ssid, password); DWORD reason = 0; - DWORD result = WlanSetProfile( - /*hClientHandle=*/wifi_, /*pInterfaceGuid=*/&interface_guid, - /*dwFlags=*/WLAN_PROFILE_USER, /*strProfileXml=*/profile.data(), - /*strAllUserProfileSecurity=*/nullptr, /*bOverwrite=*/TRUE, - /*pReserved=*/nullptr, /*pdwReasonCode*/ &reason); + DWORD result = + WlanSetProfile(wlan_client_.GetHandle(), &interface_guid, + WLAN_PROFILE_USER, profile.data(), + /*strAllUserProfileSecurity=*/nullptr, /*bOverwrite=*/TRUE, + /*pReserved=*/nullptr, /*pdwReasonCode*/ &reason); if (result != ERROR_SUCCESS) { LOG(ERROR) << "Failed to set WLAN profile with error: " << result; if (result == ERROR_BAD_PROFILE) { @@ -367,8 +344,9 @@ bool WifiHotspotNative::SetWlanProfile( } bool WifiHotspotNative::RemoveCreatedWlanProfile(GUID interface_guid) { - return RemoveWlanProfile(interface_guid, - string_utils::StringToWideString(std::string(kHotspotProfileName))); + return RemoveWlanProfile( + interface_guid, + string_utils::StringToWideString(std::string(kHotspotProfileName))); } bool WifiHotspotNative::RemoveWlanProfile(GUID interface_guid, @@ -376,9 +354,8 @@ bool WifiHotspotNative::RemoveWlanProfile(GUID interface_guid, if (profile_name.empty()) { return false; } - DWORD result = WlanDeleteProfile( - /*hClientHandle=*/wifi_, /*pInterfaceGuid=*/&interface_guid, - /*strProfileName=*/profile_name.data(), /*pReserved=*/nullptr); + DWORD result = WlanDeleteProfile(wlan_client_.GetHandle(), &interface_guid, + profile_name.data(), /*pReserved=*/nullptr); if (result != ERROR_SUCCESS && result != ERROR_NOT_FOUND) { LOG(ERROR) << "Failed to remove WLAN profile " @@ -387,8 +364,7 @@ bool WifiHotspotNative::RemoveWlanProfile(GUID interface_guid, return false; } - LOG(INFO) << "WLAN profile " - << string_utils::WideStringToString(profile_name) + LOG(INFO) << "WLAN profile " << string_utils::WideStringToString(profile_name) << " removed successfully."; return true; } @@ -403,9 +379,8 @@ bool WifiHotspotNative::RestoreWifiProfile() { RemoveCreatedWlanProfile(interface_guid); if (backup_profile_name_.empty()) { LOG(ERROR) << "No backup WLAN profile to restore."; - DWORD result = WlanDisconnect( - /*hClientHandle=*/wifi_, /*pInterfaceGuid=*/&interface_guid, - /*pReserved=*/nullptr); + DWORD result = WlanDisconnect(wlan_client_.GetHandle(), &interface_guid, + /*pReserved=*/nullptr); if (result != ERROR_SUCCESS) { LOG(ERROR) << "Failed to disconnect WLAN with error: " << result; } else { diff --git a/internal/platform/implementation/windows/wifi_hotspot_native.h b/internal/platform/implementation/windows/wifi_hotspot_native.h index 83486f4c..880d8ec4 100644 --- a/internal/platform/implementation/windows/wifi_hotspot_native.h +++ b/internal/platform/implementation/windows/wifi_hotspot_native.h @@ -31,6 +31,7 @@ #include "absl/synchronization/mutex.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/implementation/windows/network_info.h" +#include "internal/platform/implementation/windows/wlan_client.h" namespace nearby::windows { @@ -87,7 +88,7 @@ class WifiHotspotNative { NetworkInfo& network_info_; mutable absl::Mutex mutex_; - HANDLE wifi_ = nullptr; + WlanClient wlan_client_; std::unique_ptr connect_latch_; std::wstring backup_profile_name_; }; diff --git a/internal/platform/implementation/windows/wifi_medium.cc b/internal/platform/implementation/windows/wifi_medium.cc index dd9e9812..28e404d2 100644 --- a/internal/platform/implementation/windows/wifi_medium.cc +++ b/internal/platform/implementation/windows/wifi_medium.cc @@ -15,12 +15,15 @@ #include #include #include +#include #include "absl/strings/str_format.h" #include "internal/platform/implementation/wifi.h" #include "internal/platform/implementation/wifi_utils.h" +#include "internal/platform/implementation/windows/scoped_wlan_memory.h" #include "internal/platform/implementation/windows/utils.h" #include "internal/platform/implementation/windows/wifi.h" +#include "internal/platform/implementation/windows/wlan_client.h" #include "internal/platform/logging.h" #include "winrt/Windows.Security.Authorization.AppCapabilityAccess.h" @@ -28,6 +31,7 @@ namespace nearby { namespace windows { namespace { +using ::nearby::windows::ScopedWlanMemory; using winrt::Windows::Security::Authorization::AppCapabilityAccess:: AppCapability; using winrt::Windows::Security::Authorization::AppCapabilityAccess:: @@ -40,93 +44,101 @@ constexpr int kUseEthernet = -2; WifiMedium::WifiMedium() { InitCapability(); } -PWLAN_INTERFACE_INFO_LIST EnumInterface(PHANDLE client_handle) { - DWORD client_version = 2; - DWORD negotiated_version = 0; - DWORD result = 0; - - /* variables used for WlanEnumInterfaces */ - PWLAN_INTERFACE_INFO_LIST p_intf_list = nullptr; - - result = WlanOpenHandle(client_version, nullptr, &negotiated_version, - client_handle); - if (result != ERROR_SUCCESS) { - LOG(INFO) << "WlanOpenHandle failed with error: " << result; - return p_intf_list; - } - - result = WlanEnumInterfaces(*client_handle, nullptr, &p_intf_list); - if (result != ERROR_SUCCESS) { - LOG(INFO) << "WlanEnumInterfaces failed with error: " << result; - } - return p_intf_list; -} - bool WifiMedium::IsInterfaceValid() const { return wifi_interface_valid_; } void WifiMedium::InitCapability() { - HANDLE client_handle = nullptr; - - /* variables used for WlanEnumInterfaces */ - PWLAN_INTERFACE_INFO_LIST p_intf_list = nullptr; - PWLAN_INTERFACE_INFO p_intf_info = nullptr; - PWLAN_INTERFACE_CAPABILITY p_intf_capability = nullptr; - wifi_capability_.supports_5_ghz = false; wifi_capability_.supports_6_ghz = false; wifi_interface_valid_ = false; - p_intf_list = EnumInterface(&client_handle); - if (!client_handle) { - LOG(INFO) - << "Client Handle is null, wifi maybe not supported on this device."; + WlanClient wlan_client; + if (!wlan_client.Initialize()) { return; } - if (!p_intf_list) { - WlanCloseHandle(client_handle, nullptr); - return; - } - wifi_interface_valid_ = true; - - for (int i = 0; i < (int)p_intf_list->dwNumberOfItems; i++) { - p_intf_info = (WLAN_INTERFACE_INFO*)&p_intf_list->InterfaceInfo[i]; - if (WlanGetInterfaceCapability(client_handle, &p_intf_info->InterfaceGuid, - nullptr, - &p_intf_capability) != ERROR_SUCCESS) { + std::vector interface_infos = + wlan_client.GetInterfaceInfos(); + for (const auto& interface_info : interface_infos) { + ScopedWlanMemory intf_capability; + wifi_interface_valid_ = true; + if (WlanGetInterfaceCapability( + wlan_client.GetHandle(), &interface_info.guid, nullptr, + intf_capability.OutParam()) != ERROR_SUCCESS) { LOG(INFO) << "Get Capability failed"; - WlanFreeMemory(p_intf_list); - p_intf_list = nullptr; - WlanCloseHandle(client_handle, nullptr); return; } - for (int j = 0; j < p_intf_capability->dwNumberOfSupportedPhys; j++) { - if (p_intf_capability->dot11PhyTypes[j] == dot11_phy_type_ofdm) + for (int j = 0; j < intf_capability->dwNumberOfSupportedPhys; j++) { + if (intf_capability->dot11PhyTypes[j] == dot11_phy_type_ofdm) wifi_capability_.supports_5_ghz = true; } } +} - WlanFreeMemory(p_intf_capability); - p_intf_capability = nullptr; - WlanFreeMemory(p_intf_list); - p_intf_list = nullptr; - WlanCloseHandle(client_handle, nullptr); +bool WifiMedium::GetWifiInformation() { + WlanClient wlan_client; + if (!wlan_client.Initialize()) { + return false; + } + bool result = false; + for (const auto& interface_info : wlan_client.GetInterfaceInfos()) { + if (!interface_info.connected) { + continue; + } + LOG(INFO) << "Found connected WiFi interface"; + wifi_information_.is_connected = true; + + DWORD result = 0; + DWORD channel_size; + ScopedWlanMemory channel; + result = WlanQueryInterface(wlan_client.GetHandle(), &interface_info.guid, + wlan_intf_opcode_channel_number, nullptr, + &channel_size, + reinterpret_cast(channel.OutParam()), + /*pWlanOpcodeValueType=*/nullptr); + if (result != ERROR_SUCCESS || channel.get() == nullptr) { + LOG(INFO) << "WlanQueryInterface channel error = " << result; + return false; + } + wifi_information_.ap_frequency = WifiUtils::ConvertChannelToFrequencyMhz( + *channel.get(), api::WifiBandType::kUnknown); + LOG(INFO) << "Channel: " << (channel.get() == nullptr ? 0 : *channel.get()) + << "; ap_frequency: " << wifi_information_.ap_frequency; + + ScopedWlanMemory connect_info; + DWORD connect_info_size; + result = WlanQueryInterface( + wlan_client.GetHandle(), &interface_info.guid, + wlan_intf_opcode_current_connection, nullptr, &connect_info_size, + reinterpret_cast(connect_info.OutParam()), + /*pWlanOpcodeValueType=*/nullptr); + if (result != ERROR_SUCCESS) { + LOG(INFO) << "WlanQueryInterface current AP error = " << result; + return false; + } + + wifi_information_.ssid.assign( + reinterpret_cast( + connect_info->wlanAssociationAttributes.dot11Ssid.ucSSID), + connect_info->wlanAssociationAttributes.dot11Ssid.uSSIDLength); + LOG(INFO) << "wifi ssid is: " << wifi_information_.ssid + << "; length is:" << wifi_information_.ssid.length(); + + char str_tmp[kMacAddrLen]; + strncpy(str_tmp, + reinterpret_cast( + connect_info->wlanAssociationAttributes.dot11Bssid), + kMacAddrLen); + wifi_information_.bssid = absl::StrFormat( + "%02llx:%02llx:%02llx:%02llx:%02llx:%02llx", str_tmp[0], str_tmp[1], + str_tmp[2], str_tmp[3], str_tmp[4], str_tmp[5]); + LOG(INFO) << "wifi bssid is: " << wifi_information_.bssid; + result = true; + } + return result; } // TODO(b/259414512): the return type should be optional. api::WifiInformation& WifiMedium::GetInformation() { - HANDLE client_handle = nullptr; - PWLAN_AVAILABLE_NETWORK_LIST pWLAN_AVAILABLE_NETWORK_LIST = nullptr; - DWORD result = 0; - DWORD connect_info_size = sizeof(WLAN_CONNECTION_ATTRIBUTES); - WLAN_OPCODE_VALUE_TYPE op_code_value_type = wlan_opcode_value_type_invalid; - - /* variables used for WlanEnumInterfaces */ - PWLAN_INTERFACE_INFO_LIST p_intf_list = nullptr; - PWLAN_INTERFACE_INFO p_intf_info = nullptr; - PWLAN_CONNECTION_ATTRIBUTES p_connect_info = nullptr; - ULONG* channel = nullptr; - wifi_information_.is_connected = false; wifi_information_.ap_frequency = kDefaultApFreq; wifi_information_.ssid.clear(); @@ -144,86 +156,10 @@ api::WifiInformation& WifiMedium::GetInformation() { return wifi_information_; } - p_intf_list = EnumInterface(&client_handle); - if (!client_handle) { - LOG(INFO) << "Client Handle is nullptr"; + if (!GetWifiInformation()) { FillupEthernetParams(); return wifi_information_; } - if (!p_intf_list) { - LOG(INFO) << "WlanEnumInterfaces failed with error: "; - WlanCloseHandle(client_handle, nullptr); - FillupEthernetParams(); - return wifi_information_; - } - - for (int i = 0; i < (int)p_intf_list->dwNumberOfItems; i++) { - p_intf_info = (WLAN_INTERFACE_INFO*)&p_intf_list->InterfaceInfo[i]; - if (p_intf_info->isState == wlan_interface_state_connected) { - LOG(INFO) << "Found connected WiFi interface No: " << i; - wifi_information_.is_connected = true; - - DWORD channel_size; - result = WlanQueryInterface(client_handle, &p_intf_info->InterfaceGuid, - wlan_intf_opcode_channel_number, nullptr, - &channel_size, (PVOID*)&channel, - &op_code_value_type); - if (result != ERROR_SUCCESS) { - LOG(INFO) << "WlanQueryInterface channel error = " << result; - WlanFreeMemory(p_intf_list); - p_intf_list = nullptr; - WlanCloseHandle(client_handle, nullptr); - FillupEthernetParams(); - return wifi_information_; - } - wifi_information_.ap_frequency = WifiUtils::ConvertChannelToFrequencyMhz( - *channel, api::WifiBandType::kUnknown); - LOG(INFO) << "Channel: " << (channel == nullptr ? 0 : *channel) - << "; ap_frequency: " << wifi_information_.ap_frequency; - WlanFreeMemory(channel); - channel = nullptr; - - result = WlanQueryInterface(client_handle, &p_intf_info->InterfaceGuid, - wlan_intf_opcode_current_connection, nullptr, - &connect_info_size, (PVOID*)&p_connect_info, - &op_code_value_type); - if (result != ERROR_SUCCESS) { - LOG(INFO) << "WlanQueryInterface current AP error = " << result; - WlanFreeMemory(p_intf_list); - p_intf_list = nullptr; - WlanCloseHandle(client_handle, nullptr); - FillupEthernetParams(); - return wifi_information_; - } - - wifi_information_.ssid.resize( - p_connect_info->wlanAssociationAttributes.dot11Ssid.uSSIDLength); - std::memcpy( - wifi_information_.ssid.data(), - reinterpret_cast( - p_connect_info->wlanAssociationAttributes.dot11Ssid.ucSSID), - wifi_information_.ssid.size()); - LOG(INFO) << "wifi ssid is: " << wifi_information_.ssid - << "; length is:" << wifi_information_.ssid.length(); - - char str_tmp[kMacAddrLen]; - strncpy(str_tmp, - reinterpret_cast( - p_connect_info->wlanAssociationAttributes.dot11Bssid), - kMacAddrLen); - wifi_information_.bssid = absl::StrFormat( - "%02llx:%02llx:%02llx:%02llx:%02llx:%02llx", str_tmp[0], str_tmp[1], - str_tmp[2], str_tmp[3], str_tmp[4], str_tmp[5]); - LOG(INFO) << "wifi bssid is: " << wifi_information_.bssid; - } - } - - WlanFreeMemory(p_connect_info); - p_connect_info = nullptr; - WlanFreeMemory(p_intf_list); - p_intf_list = nullptr; - WlanCloseHandle(client_handle, nullptr); - if (wifi_information_.is_connected) { wifi_information_.ip_address_dot_decimal = InternalGetWifiIpAddress(); wifi_information_.ip_address_4_bytes = ipaddr_dotdecimal_to_4bytes_string( diff --git a/internal/platform/implementation/windows/wlan_client.cc b/internal/platform/implementation/windows/wlan_client.cc new file mode 100644 index 00000000..cfafb061 --- /dev/null +++ b/internal/platform/implementation/windows/wlan_client.cc @@ -0,0 +1,82 @@ +// 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/wlan_client.h" + +#include +#include + +#include + +#include "internal/platform/implementation/windows/scoped_wlan_memory.h" +#include "internal/platform/logging.h" + +namespace nearby::windows { + +WlanClient::~WlanClient() { + if (wifi_ != nullptr) { + WlanCloseHandle(wifi_, /*pReserved=*/nullptr); + } +} + +bool WlanClient::Initialize() { + if (wifi_ != nullptr) { + return true; + } + DWORD client_version = 2; + DWORD negotiated_version = 0; + DWORD result = 0; + + result = WlanOpenHandle(client_version, /*pReserved=*/nullptr, + &negotiated_version, &wifi_); + if (result == ERROR_SUCCESS) { + return true; + } + LOG(INFO) << "WlanOpenHandle failed with error: " << result; + return false; +} + +std::vector WlanClient::PopulateInterfaceInfos( + PWLAN_INTERFACE_INFO_LIST interfaces) const { + if (interfaces == nullptr) { + return {}; + } + std::vector interface_infos; + for (int i = 0; i < interfaces->dwNumberOfItems; ++i) { + bool connected = + interfaces->InterfaceInfo[i].isState == wlan_interface_state_connected; + bool ready = + interfaces->InterfaceInfo[i].isState != wlan_interface_state_not_ready; + interface_infos.push_back( + {connected, ready, interfaces->InterfaceInfo[i].InterfaceGuid}); + } + return interface_infos; +} + +std::vector WlanClient::GetInterfaceInfos() const { + if (wifi_ == nullptr) { + LOG(ERROR) << "WlanClient is not initialized."; + return {}; + } + ScopedWlanMemory interfaces; + DWORD result = + WlanEnumInterfaces(wifi_, /*pReserved=*/nullptr, interfaces.OutParam()); + if (result != ERROR_SUCCESS) { + LOG(ERROR) << "Failed to enum WLAN interfaces."; + return {}; + } + return PopulateInterfaceInfos(interfaces.get()); +} + +} // namespace nearby::windows diff --git a/internal/platform/implementation/windows/wlan_client.h b/internal/platform/implementation/windows/wlan_client.h new file mode 100644 index 00000000..7aed5a24 --- /dev/null +++ b/internal/platform/implementation/windows/wlan_client.h @@ -0,0 +1,64 @@ +// 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 THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_WLAN_CLIENT_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_WLAN_CLIENT_H_ + +#include +#include +#include + +#include + +namespace nearby::windows { + +// A wrapper for Windows wlan client handle and some APIs. +class WlanClient { + public: + struct InterfaceInfo { + // Whether the interface is connected to an AP. + bool connected = false; + // Whether the interface is ready to use. + bool ready = false; + // Interface GUID of the WLAN interface. + GUID guid = GUID_NULL; + }; + + WlanClient() = default; + ~WlanClient(); + + // Initializes the wlan client handle. Returns true if successful. + // If the client has already been initialized, this is a no-op. + bool Initialize(); + + // Returns the wlan client handle. + // If the client has not been initialized, this returns nullptr. + HANDLE GetHandle() const { return wifi_; } + + // Returns the state of all WLAN interfaces. + // If the client has not been initialized, this returns an empty vector. + std::vector GetInterfaceInfos() const; + + private: + friend class WlanClientTest; + + std::vector PopulateInterfaceInfos( + PWLAN_INTERFACE_INFO_LIST interfaces) const; + + HANDLE wifi_ = nullptr; +}; + +} // namespace nearby::windows + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_WLAN_CLIENT_H_ diff --git a/internal/platform/implementation/windows/wlan_client_test.cc b/internal/platform/implementation/windows/wlan_client_test.cc new file mode 100644 index 00000000..aa6b7997 --- /dev/null +++ b/internal/platform/implementation/windows/wlan_client_test.cc @@ -0,0 +1,61 @@ +// 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/wlan_client.h" + +#include + +#include "gtest/gtest.h" + +namespace nearby::windows { + +class WlanClientTest : public ::testing::Test { + protected: + std::vector PopulateInterfaceInfos( + PWLAN_INTERFACE_INFO_LIST interfaces) const { + return wlan_client_.PopulateInterfaceInfos(interfaces); + } + + WlanClient wlan_client_; +}; + +TEST_F(WlanClientTest, Initialize) { + // Forge machines don't have wifi. + EXPECT_FALSE(wlan_client_.Initialize()); + EXPECT_EQ(wlan_client_.GetHandle(), nullptr); +} + +TEST_F(WlanClientTest, PopulateInterfaceInfos) { + int num_of_items = 3; + PWLAN_INTERFACE_INFO_LIST interfaces = + reinterpret_cast( + ::WlanAllocateMemory(sizeof(WLAN_INTERFACE_INFO_LIST) + + num_of_items * sizeof(WLAN_INTERFACE_INFO))); + interfaces->dwNumberOfItems = num_of_items; + interfaces->InterfaceInfo[0].isState = wlan_interface_state_connected; + interfaces->InterfaceInfo[1].isState = wlan_interface_state_not_ready; + interfaces->InterfaceInfo[2].isState = wlan_interface_state_disconnected; + std::vector interface_infos = + PopulateInterfaceInfos(interfaces); + EXPECT_EQ(interface_infos.size(), 3); + EXPECT_TRUE(interface_infos[0].connected); + EXPECT_TRUE(interface_infos[0].ready); + EXPECT_FALSE(interface_infos[1].connected); + EXPECT_FALSE(interface_infos[1].ready); + EXPECT_FALSE(interface_infos[2].connected); + EXPECT_TRUE(interface_infos[2].ready); + ::WlanFreeMemory(interfaces); +} + +} // namespace nearby::windows