Add WlanClient class.

PiperOrigin-RevId: 825864006
This commit is contained in:
Francis Tsui
2025-10-29 22:31:24 -07:00
committed by Copybara-Service
parent 88f37eeb88
commit dc963d93bf
10 changed files with 556 additions and 208 deletions
+66 -4
View File
@@ -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",
],
)
@@ -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 <windows.h>
#include <wlanapi.h>
#include <utility>
#include "absl/base/attributes.h"
namespace nearby::windows {
// Smart pointer class for memory allocated by the WLAN API.
template <typename T>
class ScopedWlanMemory {
public:
explicit ScopedWlanMemory(T* ptr = nullptr) : ptr_(ptr) {}
~ScopedWlanMemory() {
Reset();
}
ScopedWlanMemory& operator=(ScopedWlanMemory<T>&& 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_
@@ -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 <windows.h>
#include <wlanapi.h>
#include <utility>
#include "gtest/gtest.h"
namespace nearby::windows {
namespace {
TEST(ScopedWlanMemoryTest, Empty) {
ScopedWlanMemory<WLAN_AVAILABLE_NETWORK_LIST> wlan_list;
EXPECT_EQ(wlan_list.get(), nullptr);
}
TEST(ScopedWlanMemoryTest, Reset) {
ScopedWlanMemory<WLAN_INTERFACE_INFO_LIST> wlan_list;
EXPECT_EQ(wlan_list.get(), nullptr);
WLAN_INTERFACE_INFO_LIST* wlan_list_ptr =
reinterpret_cast<WLAN_INTERFACE_INFO_LIST*>(
::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<WLAN_INTERFACE_INFO_LIST*>(
::WlanAllocateMemory(sizeof(WLAN_INTERFACE_INFO_LIST)));
ScopedWlanMemory<WLAN_INTERFACE_INFO_LIST> 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<WLAN_INTERFACE_INFO_LIST*>(
::WlanAllocateMemory(sizeof(WLAN_INTERFACE_INFO_LIST)));
wlan_list_ptr->dwIndex = 9834;
ScopedWlanMemory<WLAN_INTERFACE_INFO_LIST> 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<WLAN_INTERFACE_INFO_LIST*>(
::WlanAllocateMemory(sizeof(WLAN_INTERFACE_INFO_LIST)));
(*wlan_list_ptr)->dwIndex = 9834;
}
TEST(ScopedWlanMemoryTest, OutParam) {
ScopedWlanMemory<WLAN_INTERFACE_INFO_LIST> wlan_list;
TestFunc(wlan_list.OutParam());
EXPECT_EQ(wlan_list->dwIndex, 9834);
}
TEST(ScopedWlanMemoryTest, MoveAssignment) {
WLAN_INTERFACE_INFO_LIST* wlan_list_ptr =
reinterpret_cast<WLAN_INTERFACE_INFO_LIST*>(
::WlanAllocateMemory(sizeof(WLAN_INTERFACE_INFO_LIST)));
ScopedWlanMemory<WLAN_INTERFACE_INFO_LIST> wlan_list(wlan_list_ptr);
EXPECT_EQ(wlan_list.get(), wlan_list_ptr);
ScopedWlanMemory<WLAN_INTERFACE_INFO_LIST> 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<WLAN_INTERFACE_INFO_LIST*>(
::WlanAllocateMemory(sizeof(WLAN_INTERFACE_INFO_LIST)));
ScopedWlanMemory<WLAN_INTERFACE_INFO_LIST> 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
@@ -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();
@@ -27,6 +27,7 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>
#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<WlanClient::InterfaceInfo> 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=*/&parameters, /*pReserved=*/nullptr);
DWORD result = WlanConnect(wlan_client_.GetHandle(), &interface_guid,
&parameters, /*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 {
@@ -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<CountDownLatch> connect_latch_;
std::wstring backup_profile_name_;
};
@@ -15,12 +15,15 @@
#include <cstring>
#include <exception>
#include <string>
#include <vector>
#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<WlanClient::InterfaceInfo> interface_infos =
wlan_client.GetInterfaceInfos();
for (const auto& interface_info : interface_infos) {
ScopedWlanMemory<WLAN_INTERFACE_CAPABILITY> 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<ULONG> channel;
result = WlanQueryInterface(wlan_client.GetHandle(), &interface_info.guid,
wlan_intf_opcode_channel_number, nullptr,
&channel_size,
reinterpret_cast<PVOID*>(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<WLAN_CONNECTION_ATTRIBUTES> 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<PVOID*>(connect_info.OutParam()),
/*pWlanOpcodeValueType=*/nullptr);
if (result != ERROR_SUCCESS) {
LOG(INFO) << "WlanQueryInterface current AP error = " << result;
return false;
}
wifi_information_.ssid.assign(
reinterpret_cast<const char*>(
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<const char*>(
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<const char*>(
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<const char*>(
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(
@@ -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 <windows.h>
#include <wlanapi.h>
#include <vector>
#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::InterfaceInfo> WlanClient::PopulateInterfaceInfos(
PWLAN_INTERFACE_INFO_LIST interfaces) const {
if (interfaces == nullptr) {
return {};
}
std::vector<InterfaceInfo> 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::InterfaceInfo> WlanClient::GetInterfaceInfos() const {
if (wifi_ == nullptr) {
LOG(ERROR) << "WlanClient is not initialized.";
return {};
}
ScopedWlanMemory<WLAN_INTERFACE_INFO_LIST> 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
@@ -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 <windows.h>
#include <wlanapi.h>
#include <coguid.h>
#include <vector>
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<InterfaceInfo> GetInterfaceInfos() const;
private:
friend class WlanClientTest;
std::vector<InterfaceInfo> PopulateInterfaceInfos(
PWLAN_INTERFACE_INFO_LIST interfaces) const;
HANDLE wifi_ = nullptr;
};
} // namespace nearby::windows
#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_WLAN_CLIENT_H_
@@ -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 <vector>
#include "gtest/gtest.h"
namespace nearby::windows {
class WlanClientTest : public ::testing::Test {
protected:
std::vector<WlanClient::InterfaceInfo> 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<PWLAN_INTERFACE_INFO_LIST>(
::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<WlanClient::InterfaceInfo> 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