Files
nearby/internal/platform/implementation/windows/wifi_medium.cc
T
2025-10-02 12:23:47 -07:00

326 lines
12 KiB
C++

// Copyright 2022 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 <cstring>
#include <exception>
#include <string>
#include "absl/strings/str_format.h"
#include "internal/platform/implementation/wifi.h"
#include "internal/platform/implementation/wifi_utils.h"
#include "internal/platform/implementation/windows/utils.h"
#include "internal/platform/implementation/windows/wifi.h"
#include "internal/platform/logging.h"
#include "winrt/Windows.Security.Authorization.AppCapabilityAccess.h"
namespace nearby {
namespace windows {
namespace {
using winrt::Windows::Security::Authorization::AppCapabilityAccess::
AppCapability;
using winrt::Windows::Security::Authorization::AppCapabilityAccess::
AppCapabilityAccessStatus;
constexpr int kMacAddrLen = 6;
constexpr int kDefaultApFreq = -1;
constexpr int kUseEthernet = -2;
} // namespace
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.";
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) {
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)
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);
}
// 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();
wifi_information_.bssid.clear();
wifi_information_.ip_address_dot_decimal.clear();
wifi_information_.ip_address_4_bytes.clear();
AppCapability app_capability = AppCapability::Create(L"wiFiControl");
AppCapabilityAccessStatus access_status = app_capability.CheckAccess();
LOG(INFO) << "Wi-Fi access status:" << static_cast<int>(access_status);
if (access_status != AppCapabilityAccessStatus::Allowed) {
LOG(WARNING) << "Wi-Fi access is not granted";
FillupEthernetParams();
return wifi_information_;
}
p_intf_list = EnumInterface(&client_handle);
if (!client_handle) {
LOG(INFO) << "Client Handle is nullptr";
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(
wifi_information_.ip_address_dot_decimal);
} else {
FillupEthernetParams();
}
return wifi_information_;
}
std::string WifiMedium::GetIpAddress() {
GetInformation();
return wifi_information_.ip_address_dot_decimal;
}
std::string WifiMedium::InternalGetWifiIpAddress() {
try {
auto host_names = NetworkInformation::GetHostNames();
for (auto host_name : host_names) {
if (host_name.IPInformation() != nullptr &&
host_name.IPInformation().NetworkAdapter() != nullptr &&
host_name.Type() == HostNameType::Ipv4) {
std::string ipv4_s = winrt::to_string(host_name.ToString());
LOG(INFO) << "Found IP: " << ipv4_s;
auto profile = host_name.IPInformation()
.NetworkAdapter()
.GetConnectedProfileAsync()
.get();
if (profile != nullptr && profile.IsWlanConnectionProfile()) {
auto profile_details = profile.WlanConnectionProfileDetails();
if (profile_details != nullptr &&
wifi_information_.ssid ==
winrt::to_string(profile_details.GetConnectedSsid())) {
LOG(INFO)
<< "SSID of this IP matches with this WiFi interface's SSID:"
<< wifi_information_.ssid << ", return this IP: " << ipv4_s;
return ipv4_s;
}
}
}
}
return {};
} catch (std::exception exception) {
LOG(ERROR) << __func__ << ": Exception: " << exception.what();
return {};
} catch (const winrt::hresult_error& error) {
LOG(ERROR) << __func__ << ": WinRT exception: " << error.code() << ": "
<< winrt::to_string(error.message());
return {};
} catch (...) {
LOG(ERROR) << __func__ << ": Unknown exeption.";
return {};
}
}
std::string WifiMedium::InternalGetEthernetIpAddress() {
try {
auto host_names = NetworkInformation::GetHostNames();
for (auto host_name : host_names) {
if (host_name.IPInformation() != nullptr &&
host_name.IPInformation().NetworkAdapter() != nullptr &&
host_name.Type() == HostNameType::Ipv4) {
std::string ipv4_s = winrt::to_string(host_name.ToString());
if (host_name.IPInformation().NetworkAdapter().IanaInterfaceType() ==
Constants::kInterfaceTypeEthernet) {
LOG(INFO) << "Found IP: " << ipv4_s;
return ipv4_s;
}
}
}
return {};
} catch (std::exception exception) {
LOG(ERROR) << __func__ << ": Exception: " << exception.what();
return {};
} catch (const winrt::hresult_error& error) {
LOG(ERROR) << __func__ << ": WinRT exception: " << error.code() << ": "
<< winrt::to_string(error.message());
return {};
} catch (...) {
LOG(ERROR) << __func__ << ": Unknown exeption.";
return {};
}
}
void WifiMedium::FillupEthernetParams() {
wifi_information_.ip_address_dot_decimal = InternalGetEthernetIpAddress();
if (wifi_information_.ip_address_dot_decimal.empty()) {
LOG(INFO) << "No Etherent IP Addr found.";
return;
}
wifi_information_.ip_address_4_bytes = ipaddr_dotdecimal_to_4bytes_string(
wifi_information_.ip_address_dot_decimal);
wifi_information_.ap_frequency = kUseEthernet;
}
} // namespace windows
} // namespace nearby