WIP: wifi direct support. merged from upstream

This commit is contained in:
Lasan Mahaliyana
2026-07-26 22:48:27 +05:30
parent 37e916ecbd
commit f8a9dfc1c7
15 changed files with 988 additions and 259 deletions
+6 -3
View File
@@ -115,9 +115,10 @@ cc_library(
"network_manager_access_point.h",
"stream.h",
"tcp_server_socket.h",
# "wifi_direct.h",
"wifi_direct.h",
"wifi_direct_utils.h",
# "wifi_direct_server_socket.h",
# "wifi_direct_socket.h",
"wifi_direct_socket.h",
"wifi_hotspot.h",
"wifi_hotspot_server_socket.h",
"wifi_hotspot_socket.h",
@@ -224,7 +225,8 @@ cc_library(
"submittable_executor.cc",
"thread_pool.cc",
"utils.cc",
# "wifi_direct.cc",
"wifi_direct.cc",
"wifi_direct_utils.cc",
# "wifi_direct_server_socket.cc",
"wifi_hotspot.cc",
"wifi_hotspot_server_socket.cc",
@@ -308,6 +310,7 @@ cc_test(
"atomic_reference_test.cc",
"mutex_test.cc",
"utils_test.cc",
"wifi_direct_utils_test.cc",
"ble_l2cap_socket_test.cc",
"bluetooth_classic_socket_test.cc",
# "bluetooth_adapter_test.cc",
@@ -16,7 +16,6 @@
#define PLATFORM_IMPL_LINUX_API_BLUEZ_BLE_ADVERTISEMENT_H_
#include <future>
#include <absl/log/log.h>
#include <sdbus-c++/AdaptorInterfaces.h>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/ProxyInterfaces.h>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<node name="/">
<interface name="org.freedesktop.NetworkManager.WifiP2PPeer">
<property name="Name" type="s" access="read"/>
<property name="Flags" type="u" access="read"/>
<property name="Manufacturer" type="s" access="read"/>
<property name="Model" type="s" access="read"/>
<property name="ModelNumber" type="s" access="read"/>
<property name="Serial" type="s" access="read"/>
<property name="WfdIEs" type="ay" access="read"/>
<property name="HwAddress" type="s" access="read"/>
<property name="Strength" type="y" access="read"/>
<property name="LastSeen" type="i" access="read"/>
</interface>
</node>
@@ -0,0 +1,96 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__internal_platform_implementation_linux_generated_dbus_networkmanager_wifi_p2p_peer_client_h__proxy__H__
#define __sdbuscpp__internal_platform_implementation_linux_generated_dbus_networkmanager_wifi_p2p_peer_client_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace freedesktop {
namespace NetworkManager {
class WifiP2PPeer_proxy
{
public:
static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager.WifiP2PPeer";
protected:
WifiP2PPeer_proxy(sdbus::IProxy& proxy)
: m_proxy(proxy)
{
}
WifiP2PPeer_proxy(const WifiP2PPeer_proxy&) = delete;
WifiP2PPeer_proxy& operator=(const WifiP2PPeer_proxy&) = delete;
WifiP2PPeer_proxy(WifiP2PPeer_proxy&&) = delete;
WifiP2PPeer_proxy& operator=(WifiP2PPeer_proxy&&) = delete;
~WifiP2PPeer_proxy() = default;
void registerProxy()
{
}
public:
std::string Name()
{
return m_proxy.getProperty("Name").onInterface(INTERFACE_NAME).get<std::string>();
}
uint32_t Flags()
{
return m_proxy.getProperty("Flags").onInterface(INTERFACE_NAME).get<uint32_t>();
}
std::string Manufacturer()
{
return m_proxy.getProperty("Manufacturer").onInterface(INTERFACE_NAME).get<std::string>();
}
std::string Model()
{
return m_proxy.getProperty("Model").onInterface(INTERFACE_NAME).get<std::string>();
}
std::string ModelNumber()
{
return m_proxy.getProperty("ModelNumber").onInterface(INTERFACE_NAME).get<std::string>();
}
std::string Serial()
{
return m_proxy.getProperty("Serial").onInterface(INTERFACE_NAME).get<std::string>();
}
std::vector<uint8_t> WfdIEs()
{
return m_proxy.getProperty("WfdIEs").onInterface(INTERFACE_NAME).get<std::vector<uint8_t>>();
}
std::string HwAddress()
{
return m_proxy.getProperty("HwAddress").onInterface(INTERFACE_NAME).get<std::string>();
}
uint8_t Strength()
{
return m_proxy.getProperty("Strength").onInterface(INTERFACE_NAME).get<uint8_t>();
}
int32_t LastSeen()
{
return m_proxy.getProperty("LastSeen").onInterface(INTERFACE_NAME).get<int32_t>();
}
private:
sdbus::IProxy& m_proxy;
};
}}} // namespaces
#endif
@@ -79,7 +79,7 @@ std::vector<std::string> ActiveConnection::GetIP4Addresses() {
sdbus::ObjectPath ip4config_path;
try {
ip4config_path = Ip4Config();
} catch (const sdbus::Error &e) {
} catch (const sdbus::Error& e) {
DBUS_LOG_PROPERTY_GET_ERROR(this, "Ip4Config", e);
return {};
}
@@ -88,13 +88,13 @@ std::vector<std::string> ActiveConnection::GetIP4Addresses() {
std::vector<std::map<std::string, sdbus::Variant>> address_data;
try {
address_data = ip4config.AddressData();
} catch (const sdbus::Error &e) {
} catch (const sdbus::Error& e) {
DBUS_LOG_PROPERTY_GET_ERROR(&ip4config, "AddressData", e);
return {};
}
std::vector<std::string> ip4addresses;
for (auto &data : address_data) {
for (auto& data : address_data) {
if (data.count("address") == 1) {
ip4addresses.push_back(data["address"].get<std::string>());
}
@@ -102,10 +102,32 @@ std::vector<std::string> ActiveConnection::GetIP4Addresses() {
return ip4addresses;
}
std::string ActiveConnection::GetIP4Gateway() {
sdbus::ObjectPath ip4config_path;
try {
ip4config_path = Ip4Config();
} catch (const sdbus::Error& e) {
DBUS_LOG_PROPERTY_GET_ERROR(this, "Ip4Config", e);
return {};
}
if (ip4config_path.empty() || ip4config_path == "/") {
return {};
}
IP4Config ip4config(system_bus_, ip4config_path);
try {
return ip4config.Gateway();
} catch (const sdbus::Error& e) {
DBUS_LOG_PROPERTY_GET_ERROR(&ip4config, "Gateway", e);
return {};
}
}
std::pair<std::optional<ActiveConnection::ActiveConnectionStateReason>, bool>
ActiveConnection::WaitForConnection(absl::Duration timeout) {
LOG(INFO) << __func__ << ": Waiting for an update to "
<< getProxy().getObjectPath() << "'s state";
<< getProxy().getObjectPath() << "'s state";
auto state_changed = [this]() {
this->state_mutex_.AssertReaderHeld();
@@ -61,10 +61,10 @@ class ActiveConnection
std::string ToString() const;
};
ActiveConnection(const ActiveConnection &) = delete;
ActiveConnection(ActiveConnection &&) = delete;
ActiveConnection &operator=(const ActiveConnection &) = delete;
ActiveConnection &operator=(ActiveConnection &&) = delete;
ActiveConnection(const ActiveConnection&) = delete;
ActiveConnection(ActiveConnection&&) = delete;
ActiveConnection& operator=(const ActiveConnection&) = delete;
ActiveConnection& operator=(ActiveConnection&&) = delete;
explicit ActiveConnection(std::shared_ptr<sdbus::IConnection> system_bus,
sdbus::ObjectPath active_connection_path)
: ProxyInterfaces(*system_bus,
@@ -80,14 +80,14 @@ class ActiveConnection
if (state >= kStateUnknown && state <= kStateDeactivated) {
state_ = static_cast<ActiveConnectionState>(state);
}
} catch (const sdbus::Error &e) {
} catch (const sdbus::Error& e) {
DBUS_LOG_PROPERTY_GET_ERROR(this, "State", e);
}
}
virtual ~ActiveConnection() { unregisterProxy(); }
protected:
void onStateChanged(const uint32_t &state, const uint32_t &reason) override
void onStateChanged(const uint32_t& state, const uint32_t& reason) override
ABSL_LOCKS_EXCLUDED(state_mutex_) {
absl::MutexLock l(&state_mutex_);
if (state >= kStateUnknown && state <= kStateDeactivated) {
@@ -107,6 +107,7 @@ class ActiveConnection
absl::Duration timeout = absl::Seconds(10))
ABSL_LOCKS_EXCLUDED(state_mutex_);
std::vector<std::string> GetIP4Addresses();
std::string GetIP4Gateway();
private:
std::shared_ptr<sdbus::IConnection> system_bus_;
@@ -116,9 +117,9 @@ class ActiveConnection
ActiveConnectionStateReason reason_ ABSL_GUARDED_BY(state_mutex_);
};
extern std::ostream &operator<<(
std::ostream &stream,
const ActiveConnection::ActiveConnectionStateReason &reason);
extern std::ostream& operator<<(
std::ostream& stream,
const ActiveConnection::ActiveConnectionStateReason& reason);
} // namespace networkmanager
} // namespace linux
@@ -29,7 +29,7 @@
#include "internal/platform/implementation/input_file.h"
#include "internal/platform/implementation/linux/atomic_boolean.h"
#include "internal/platform/implementation/linux/atomic_uint32.h"
//#include "internal/platform/implementation/linux/ble_v2_medium.h"
// #include "internal/platform/implementation/linux/ble_v2_medium.h"
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
#include "internal/platform/implementation/linux/bluetooth_classic_medium.h"
#include "internal/platform/implementation/linux/bluez.h"
@@ -40,7 +40,7 @@
#include "internal/platform/implementation/linux/preferences_manager.h"
#include "internal/platform/implementation/linux/submittable_executor.h"
#include "internal/platform/implementation/linux/timer.h"
// #include "internal/platform/implementation/linux/wifi_direct.h"
#include "internal/platform/implementation/linux/wifi_direct.h"
// #include "internal/platform/implementation/linux/wifi_hotspot.h"
#include "internal/platform/implementation/linux/wifi_lan.h"
// #include "internal/platform/implementation/linux/wifi_medium.h"
@@ -56,22 +56,22 @@
#include "internal/platform/implementation/wifi_lan.h"
#include "internal/platform/payload_id.h"
#include "scheduled_executor.h"
// #include "wifi_direct.h"
#include "wifi_direct.h"
#include "wifi_hotspot.h"
namespace nearby {
namespace api {
std::string ImplementationPlatform::GetCustomSavePath(
const std::string &parent_folder, const std::string &file_name) {
const std::string& parent_folder, const std::string& file_name) {
auto fs = std::filesystem::path(parent_folder);
return (fs / file_name).string();
}
std::string ImplementationPlatform::GetDownloadPath(
const std::string &parent_folder, const std::string &file_name) {
const std::string& parent_folder, const std::string& file_name) {
std::filesystem::path downloads;
const char* download_dir = getenv("XDG_DOWNLOAD_DIR");
if (download_dir != nullptr) {
downloads = std::filesystem::path(download_dir);
} else {
@@ -83,16 +83,17 @@ std::string ImplementationPlatform::GetDownloadPath(
downloads = "/tmp/Downloads";
}
}
return (downloads / std::filesystem::path(parent_folder).filename() /
std::filesystem::path(file_name).filename()).string();
std::filesystem::path(file_name).filename())
.string();
}
std::string ImplementationPlatform::GetDownloadPath(
const std::string &file_name) {
const std::string& file_name) {
std::filesystem::path downloads;
const char* download_dir = getenv("XDG_DOWNLOAD_DIR");
if (download_dir != nullptr) {
downloads = std::filesystem::path(download_dir);
} else {
@@ -104,15 +105,15 @@ std::string ImplementationPlatform::GetDownloadPath(
downloads = "/tmp/Downloads";
}
}
return (downloads / std::filesystem::path(file_name).filename()).string();
}
std::string ImplementationPlatform::GetAppDataPath(
const std::string &file_name) {
const std::string& file_name) {
std::filesystem::path state;
const char* state_home = getenv("XDG_STATE_HOME");
if (state_home != nullptr) {
state = std::filesystem::path(state_home);
} else {
@@ -124,11 +125,13 @@ std::string ImplementationPlatform::GetAppDataPath(
state = "/tmp/state";
}
}
return (state / std::filesystem::path(file_name).filename()).string();
}
OSName ImplementationPlatform::GetCurrentOS() { return OSName::kWindows; }
OSName ImplementationPlatform::GetCurrentOS() {
return OSName::kWindows;
}
std::unique_ptr<api::AtomicBoolean> ImplementationPlatform::CreateAtomicBoolean(
bool initial_value) {
@@ -154,7 +157,7 @@ std::unique_ptr<api::Mutex> ImplementationPlatform::CreateMutex(
#pragma pop_macro("CreateMutex")
std::unique_ptr<api::ConditionVariable>
ImplementationPlatform::CreateConditionVariable(api::Mutex *mutex) {
ImplementationPlatform::CreateConditionVariable(api::Mutex* mutex) {
return std::make_unique<linux::ConditionVariable>(mutex);
}
@@ -171,7 +174,7 @@ std::unique_ptr<api::InputFile> ImplementationPlatform::CreateInputFile(
}
std::unique_ptr<InputFile> ImplementationPlatform::CreateInputFile(
const std::string &file_path) {
const std::string& file_path) {
return nearby::shared::IOFile::CreateInputFile(file_path);
}
@@ -182,20 +185,20 @@ std::unique_ptr<OutputFile> ImplementationPlatform::CreateOutputFile(
}
std::unique_ptr<OutputFile> ImplementationPlatform::CreateOutputFile(
const std::string &file_path) {
const std::string& file_path) {
std::filesystem::path path(file_path);
try {
std::filesystem::create_directories(path.parent_path());
} catch (std::filesystem::filesystem_error const &err) {
} catch (std::filesystem::filesystem_error const& err) {
LOG(ERROR) << __func__ << ": error creating directory tree "
<< path.parent_path() << ": " << err.what();
<< path.parent_path() << ": " << err.what();
}
return nearby::shared::IOFile::CreateOutputFile(path.string());
}
std::unique_ptr<api::LogMessage> ImplementationPlatform::CreateLogMessage(
const char *file, int line, LogMessage::Severity severity) {
const char* file, int line, LogMessage::Severity severity) {
return nullptr;
// Disabled LogMessage
// return std::make_unique<linux::LogMessage>(file, line, severity);
@@ -223,33 +226,34 @@ ImplementationPlatform::CreateBluetoothAdapter() {
auto manager = linux::bluez::BluezObjectManager(*system_bus);
try {
auto interfaces = manager.GetManagedObjects();
for (auto &[object, properties] : interfaces) {
if (properties.count(sdbus::InterfaceName(org::bluez::Adapter1_proxy::INTERFACE_NAME)) == 1) {
for (auto& [object, properties] : interfaces) {
if (properties.count(sdbus::InterfaceName(
org::bluez::Adapter1_proxy::INTERFACE_NAME)) == 1) {
LOG(INFO) << __func__ << ": found bluetooth adapter " << object;
return std::make_unique<linux::BluetoothAdapter>(system_bus, object);
}
}
} catch (const sdbus::Error &e) {
} catch (const sdbus::Error& e) {
DBUS_LOG_METHOD_CALL_ERROR(&manager, "GetManagedObjects", e);
return nullptr;
}
LOG(ERROR) << __func__
<< ": couldn't find a bluetooth adapter on this system";
<< ": couldn't find a bluetooth adapter on this system";
return nullptr;
}
std::unique_ptr<api::BluetoothClassicMedium>
ImplementationPlatform::CreateBluetoothClassicMedium(
BluetoothAdapter &adapter) {
BluetoothAdapter& adapter) {
return std::make_unique<linux::BluetoothClassicMedium>(
dynamic_cast<linux::BluetoothAdapter &>(adapter));
dynamic_cast<linux::BluetoothAdapter&>(adapter));
}
std::unique_ptr<api::ble::BleMedium> ImplementationPlatform::CreateBleMedium(
BluetoothAdapter &adapter) {
BluetoothAdapter& adapter) {
return std::make_unique<linux::BleV2Medium>(
dynamic_cast<linux::BluetoothAdapter &>(adapter));
dynamic_cast<linux::BluetoothAdapter&>(adapter));
}
std::unique_ptr<api::CredentialStorage>
@@ -265,7 +269,7 @@ static std::unique_ptr<linux::NetworkManagerWifiMedium> createWifiMedium(
try {
device_paths = nm->GetAllDevices();
} catch (const sdbus::Error &e) {
} catch (const sdbus::Error& e) {
DBUS_LOG_METHOD_CALL_ERROR(nm, "GetAllDevices", e);
return nullptr;
}
@@ -273,38 +277,39 @@ static std::unique_ptr<linux::NetworkManagerWifiMedium> createWifiMedium(
auto manager = linux::networkmanager::ObjectManager(nm->GetConnection());
std::map<sdbus::ObjectPath,
std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>>
std::map<sdbus::InterfaceName,
std::map<sdbus::PropertyName, sdbus::Variant>>>
objects;
try {
objects = manager.GetManagedObjects();
} catch (const sdbus::Error &e) {
} catch (const sdbus::Error& e) {
DBUS_LOG_METHOD_CALL_ERROR(nm, "GetManagedObjects", e);
return nullptr;
}
for (auto &device_path : device_paths) {
for (auto& device_path : device_paths) {
if (objects.count(device_path) == 1) {
auto device = objects[device_path];
if (device.count(sdbus::InterfaceName(org::freedesktop::NetworkManager::Device::
Wireless_proxy::INTERFACE_NAME)) == 1) {
if (device.count(
sdbus::InterfaceName(org::freedesktop::NetworkManager::Device::
Wireless_proxy::INTERFACE_NAME)) == 1) {
LOG(INFO) << __func__
<< ": Found a wireless device at :" << device_path;
<< ": Found a wireless device at :" << device_path;
return std::make_unique<linux::NetworkManagerWifiMedium>(nm,
device_path);
}
}
}
LOG(ERROR) << __func__
<< ": couldn't find a wireless device on this system";
LOG(ERROR) << __func__ << ": couldn't find a wireless device on this system";
return nullptr;
}
} // namespace
std::unique_ptr<api::WifiMedium> ImplementationPlatform::CreateWifiMedium() {
// return nullptr;
auto nm =
std::make_shared<linux::networkmanager::NetworkManager>(linux::getSystemBusConnection());
auto nm = std::make_shared<linux::networkmanager::NetworkManager>(
linux::getSystemBusConnection());
return createWifiMedium(nm);
}
@@ -317,8 +322,8 @@ ImplementationPlatform::CreateWifiLanMedium() {
std::unique_ptr<api::WifiHotspotMedium>
ImplementationPlatform::CreateWifiHotspotMedium() {
auto nm =
std::make_shared<linux::networkmanager::NetworkManager>(linux::getSystemBusConnection());
auto nm = std::make_shared<linux::networkmanager::NetworkManager>(
linux::getSystemBusConnection());
auto wifiMedium = createWifiMedium(nm);
if (wifiMedium == nullptr) {
@@ -332,18 +337,45 @@ ImplementationPlatform::CreateWifiHotspotMedium() {
std::unique_ptr<api::WifiDirectMedium>
ImplementationPlatform::CreateWifiDirectMedium() {
auto nm = std::make_shared<linux::networkmanager::NetworkManager>(
linux::getSystemBusConnection());
auto manager = linux::networkmanager::ObjectManager(nm->GetConnection());
std::map<sdbus::ObjectPath,
std::map<sdbus::InterfaceName,
std::map<sdbus::PropertyName, sdbus::Variant>>>
objects;
try {
objects = manager.GetManagedObjects();
} catch (const sdbus::Error& error) {
DBUS_LOG_METHOD_CALL_ERROR(nm, "GetManagedObjects", error);
return nullptr;
}
const sdbus::InterfaceName wifi_p2p_interface(
org::freedesktop::NetworkManager::Device::WifiP2P_proxy::INTERFACE_NAME);
const sdbus::InterfaceName device_interface(
"org.freedesktop.NetworkManager.Device");
const sdbus::PropertyName managed_property("Managed");
for (const auto& [object_path, interfaces] : objects) {
if (!interfaces.contains(wifi_p2p_interface)) {
continue;
}
auto device = interfaces.find(device_interface);
if (device != interfaces.end()) {
auto managed = device->second.find(managed_property);
if (managed != device->second.end() && !managed->second.get<bool>()) {
continue;
}
}
LOG(INFO) << __func__ << ": Found Wi-Fi P2P device at " << object_path;
return std::make_unique<linux::NetworkManagerWifiDirectMedium>(
nm, object_path);
}
LOG(WARNING) << __func__
<< ": NetworkManager exposes no managed Wi-Fi P2P device";
return nullptr;
// auto nm =
// std::make_shared<linux::networkmanager::NetworkManager>(linux::getSystemBusConnection());
// auto wifiMedium = createWifiMedium(nm);
//
// if (wifiMedium == nullptr) {
// LOG(ERROR) << __func__ << ": Could not create a WiFi medium";
// return nullptr;
// }
//
// return std::make_unique<linux::NetworkManagerWifiDirectMedium>(
// nm, std::move(wifiMedium));
}
std::unique_ptr<api::Timer> ImplementationPlatform::CreateTimer() {
@@ -354,18 +386,18 @@ std::unique_ptr<api::DeviceInfo> ImplementationPlatform::CreateDeviceInfo() {
return std::make_unique<linux::DeviceInfo>(linux::getSystemBusConnection());
}
std::unique_ptr<AwdlMedium> ImplementationPlatform::CreateAwdlMedium() {
std::unique_ptr<AwdlMedium> ImplementationPlatform::CreateAwdlMedium() {
return nullptr;
}
absl::StatusOr<api::WebResponse> ImplementationPlatform::SendRequest(
const WebRequest &request) {
const WebRequest& request) {
if (request.body.size() >= (8 * 1024 * 1024)) {
return absl::Status(absl::StatusCode::kResourceExhausted,
"request body too large");
}
CURL *handle = curl_easy_init();
CURL* handle = curl_easy_init();
char errbuf[CURL_ERROR_SIZE];
errbuf[0] = '\0';
@@ -381,9 +413,9 @@ absl::StatusOr<api::WebResponse> ImplementationPlatform::SendRequest(
curl_easy_setopt(handle, CURLOPT_UPLOAD, request.body.c_str());
struct curl_slist *headers_slist = nullptr;
struct curl_slist* headers_slist = nullptr;
for (auto &[key, value] : request.headers) {
for (auto& [key, value] : request.headers) {
auto hdr = absl::StrCat(key, ": ", value);
auto temp = curl_slist_append(headers_slist, hdr.c_str());
if (temp == nullptr) {
@@ -400,27 +432,26 @@ absl::StatusOr<api::WebResponse> ImplementationPlatform::SendRequest(
api::WebResponse response;
if (curl_easy_perform(handle) != CURLE_OK) {
LOG(ERROR) << __func__
<< ": Error performing HTTP request: " << errbuf;
LOG(ERROR) << __func__ << ": Error performing HTTP request: " << errbuf;
return absl::Status(absl::StatusCode::kUnknown, errbuf);
}
struct curl_header *prev = nullptr;
struct curl_header *h;
struct curl_header* prev = nullptr;
struct curl_header* h;
h = curl_easy_nextheader(handle, CURLH_HEADER, 0, prev);
while (h != nullptr) {
response.headers.emplace(h->name, h->value);
}
auto writefn = [](char *ptr, size_t size, size_t nmemb, void *userdata) {
std::string *body = static_cast<std::string *>(userdata);
auto writefn = [](char* ptr, size_t size, size_t nmemb, void* userdata) {
std::string* body = static_cast<std::string*>(userdata);
body->append(ptr, size * nmemb);
};
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writefn);
curl_easy_setopt(handle, CURLOPT_WRITEDATA,
static_cast<void *>(&response.body));
static_cast<void*>(&response.body));
long status;
curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status);
response.status_code = status;
@@ -21,6 +21,7 @@
#include <cerrno>
#include <cstring>
#include <functional>
#include <utility>
#include <unistd.h>
#include "internal/platform/exception.h"
@@ -33,18 +34,34 @@ class TCPSocket {
public:
explicit TCPSocket(int fd)
: fd_(fd), closed_(false), output_stream_(fd), input_stream_(fd) {}
TCPSocket(const TCPSocket&) = delete;
TCPSocket& operator=(const TCPSocket&) = delete;
TCPSocket(TCPSocket&& other) noexcept
: fd_(std::exchange(other.fd_, -1)),
closed_(std::exchange(other.closed_, true)),
output_stream_(fd_),
input_stream_(fd_) {}
TCPSocket& operator=(TCPSocket&& other) noexcept {
if (this == &other) return *this;
Close();
fd_ = std::exchange(other.fd_, -1);
closed_ = std::exchange(other.closed_, true);
output_stream_ = OutputStream(fd_);
input_stream_ = InputStream(fd_);
return *this;
}
~TCPSocket() { Close(); }
static std::optional<TCPSocket> Connect(const std::string& ip_address,
int port) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
LOG(ERROR) << __func__
<< ": Error opening socket: " << std::strerror(errno);
<< ": Error opening socket: " << std::strerror(errno);
return std::nullopt;
}
LOG(INFO) << __func__ << ": Connecting to " << ip_address << ":"
<< port;
LOG(INFO) << __func__ << ": Connecting to " << ip_address << ":" << port;
struct sockaddr_in addr;
addr.sin_addr.s_addr = inet_addr(ip_address.c_str());
addr.sin_family = AF_INET;
@@ -53,8 +70,8 @@ class TCPSocket {
auto ret =
connect(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
if (ret < 0) {
LOG(ERROR) << __func__ << ": Error connecting to socket: "
<< std::strerror(errno);
LOG(ERROR) << __func__
<< ": Error connecting to socket: " << std::strerror(errno);
close(sock);
return std::nullopt;
}
@@ -66,7 +83,7 @@ class TCPSocket {
OutputStream& GetOutputStream() { return output_stream_; }
Exception Close() {
if (closed_) return {Exception::kFailed};
if (closed_) return {Exception::kSuccess};
closed_ = true;
input_stream_.Close();
@@ -97,7 +114,7 @@ class TCPServerSocket {
auto sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
LOG(ERROR) << __func__
<< ": Error opening socket: " << std::strerror(errno);
<< ": Error opening socket: " << std::strerror(errno);
return std::nullopt;
}
@@ -109,19 +126,19 @@ class TCPServerSocket {
addr.sin_addr.s_addr = inet_addr(ip_address->get().c_str());
else
addr.sin_addr.s_addr = htonl(INADDR_ANY);
auto ret =
bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
if (ret < 0) {
LOG(ERROR) << __func__ << ": Error binding to socket: "
<< std::strerror(errno);
LOG(ERROR) << __func__
<< ": Error binding to socket: " << std::strerror(errno);
return std::nullopt;
}
ret = listen(sock, 0);
if (ret < 0) {
LOG(ERROR) << __func__ << ": Error listening on socket: "
<< std::strerror(errno);
LOG(ERROR) << __func__
<< ": Error listening on socket: " << std::strerror(errno);
return std::nullopt;
}
@@ -131,12 +148,11 @@ class TCPServerSocket {
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
auto conn =
accept(fd_, reinterpret_cast<struct sockaddr*>(&addr), &len);
auto conn = accept(fd_, reinterpret_cast<struct sockaddr*>(&addr), &len);
if (conn < 0) {
LOG(ERROR) << __func__
<< ": Error accepting incoming connections on socket "
<< fd_ << ": " << std::strerror(errno);
<< ": Error accepting incoming connections on socket " << fd_
<< ": " << std::strerror(errno);
return std::nullopt;
}
@@ -153,7 +169,7 @@ class TCPServerSocket {
auto ret = close(fd);
if (ret < 0) {
LOG(ERROR) << __func__ << ": Error closing socket " << fd << ": "
<< std::strerror(errno);
<< std::strerror(errno);
return {Exception::kFailed};
}
@@ -163,12 +179,10 @@ class TCPServerSocket {
int GetPort() const {
struct sockaddr_in sin;
socklen_t len = sizeof(sin);
auto ret =
getsockname(fd_, reinterpret_cast<struct sockaddr*>(&sin), &len);
auto ret = getsockname(fd_, reinterpret_cast<struct sockaddr*>(&sin), &len);
if (ret < 0) {
LOG(ERROR) << __func__
<< ": Error getting information for socket "
<< fd_ << ": " << std::strerror(errno);
LOG(ERROR) << __func__ << ": Error getting information for socket " << fd_
<< ": " << std::strerror(errno);
return 0;
}
@@ -12,127 +12,426 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <memory>
#include "internal/platform/implementation/linux/tcp_server_socket.h"
#include "internal/platform/implementation/linux/wifi_direct.h"
#include "internal/platform/implementation/linux/wifi_direct_server_socket.h"
#include "internal/platform/implementation/linux/wifi_direct_socket.h"
#include "internal/platform/implementation/linux/wifi_hotspot.h"
#include "internal/platform/implementation/linux/wifi_medium.h"
#include "internal/platform/implementation/wifi_direct.h"
#include "internal/platform/wifi_credential.h"
namespace nearby {
namespace linux {
#include <arpa/inet.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <algorithm>
#include <cerrno>
#include <cstdint>
#include <cstring>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <sdbus-c++/Error.h>
#include <sdbus-c++/ProxyInterfaces.h>
#include <sdbus-c++/Types.h>
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "internal/flags/nearby_flags.h"
#include "internal/platform/flags/nearby_platform_feature_flags.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/implementation/linux/generated/dbus/networkmanager/wifi_p2p_peer_client.h"
#include "internal/platform/implementation/linux/network_manager_active_connection.h"
#include "internal/platform/implementation/linux/tcp_server_socket.h"
#include "internal/platform/implementation/linux/utils.h"
#include "internal/platform/implementation/linux/wifi_direct_socket.h"
#include "internal/platform/implementation/linux/wifi_direct_utils.h"
#include "internal/platform/logging.h"
namespace nearby::linux {
namespace {
constexpr absl::Duration kPeerDiscoveryTimeout = absl::Seconds(30);
constexpr absl::Duration kActivationTimeout = absl::Seconds(30);
constexpr absl::Duration kPeerUpdatePollInterval = absl::Milliseconds(500);
constexpr int kCancellationPollMillis = 100;
class NetworkManagerWifiP2PPeer
: public sdbus::ProxyInterfaces<
org::freedesktop::NetworkManager::WifiP2PPeer_proxy> {
public:
NetworkManagerWifiP2PPeer(
const std::shared_ptr<sdbus::IConnection>& system_bus,
const sdbus::ObjectPath& object_path)
: ProxyInterfaces(*system_bus,
sdbus::ServiceName("org.freedesktop.NetworkManager"),
object_path) {
registerProxy();
}
~NetworkManagerWifiP2PPeer() { unregisterProxy(); }
};
bool WaitForTcpConnect(int fd, absl::Duration timeout,
CancellationFlag* cancellation_flag) {
const absl::Time deadline = absl::Now() + timeout;
while (absl::Now() < deadline) {
if (cancellation_flag != nullptr && cancellation_flag->Cancelled()) {
return false;
}
const int remaining_ms = static_cast<int>(std::max<std::int64_t>(
1, absl::ToInt64Milliseconds(deadline - absl::Now())));
pollfd descriptor{.fd = fd, .events = POLLOUT, .revents = 0};
int result;
do {
result =
poll(&descriptor, 1, std::min(kCancellationPollMillis, remaining_ms));
} while (result < 0 && errno == EINTR);
if (result < 0) {
return false;
}
if (result == 0) {
continue;
}
int socket_error = 0;
socklen_t socket_error_length = sizeof(socket_error);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &socket_error,
&socket_error_length) != 0) {
return false;
}
if (socket_error == 0) {
return true;
}
errno = socket_error;
return false;
}
return false;
}
std::optional<TCPSocket> ConnectTcp(const std::string& ip_address, int port,
absl::Duration timeout,
CancellationFlag* cancellation_flag) {
sockaddr_in address{};
address.sin_family = AF_INET;
address.sin_port = htons(port);
if (inet_pton(AF_INET, ip_address.c_str(), &address.sin_addr) != 1) {
LOG(ERROR) << __func__ << ": Invalid IPv4 address " << ip_address;
return std::nullopt;
}
int fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (fd < 0) {
LOG(ERROR) << __func__ << ": socket failed: " << std::strerror(errno);
return std::nullopt;
}
const int flags = fcntl(fd, F_GETFL, 0);
if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
LOG(ERROR) << __func__ << ": fcntl failed: " << std::strerror(errno);
close(fd);
return std::nullopt;
}
int result =
connect(fd, reinterpret_cast<const sockaddr*>(&address), sizeof(address));
if (result != 0 && errno != EINPROGRESS) {
close(fd);
return std::nullopt;
}
if (result != 0 && !WaitForTcpConnect(fd, timeout, cancellation_flag)) {
close(fd);
return std::nullopt;
}
if (cancellation_flag != nullptr && cancellation_flag->Cancelled()) {
close(fd);
return std::nullopt;
}
return TCPSocket(fd);
}
void SleepWithCancellation(absl::Duration duration,
CancellationFlag* cancellation_flag) {
const absl::Time deadline = absl::Now() + duration;
while (absl::Now() < deadline) {
if (cancellation_flag != nullptr && cancellation_flag->Cancelled()) {
return;
}
absl::SleepFor(std::min(absl::Milliseconds(kCancellationPollMillis),
deadline - absl::Now()));
}
}
} // namespace
NetworkManagerWifiDirectMedium::~NetworkManagerWifiDirectMedium() {
DisconnectWifiDirect();
unregisterProxy();
}
void NetworkManagerWifiDirectMedium::onPeerAdded(const sdbus::ObjectPath&) {
absl::MutexLock lock(state_mutex_);
peer_changed_.SignalAll();
}
void NetworkManagerWifiDirectMedium::onPeerRemoved(const sdbus::ObjectPath&) {
absl::MutexLock lock(state_mutex_);
peer_changed_.SignalAll();
}
std::optional<NetworkManagerWifiDirectMedium::Peer>
NetworkManagerWifiDirectMedium::FindPeerByName(absl::string_view device_name) {
std::vector<sdbus::ObjectPath> paths;
try {
paths = Peers();
} catch (const sdbus::Error& error) {
DBUS_LOG_PROPERTY_GET_ERROR(this, "Peers", error);
return std::nullopt;
}
std::vector<Peer> peers;
peers.reserve(paths.size());
for (const sdbus::ObjectPath& path : paths) {
NetworkManagerWifiP2PPeer peer(system_bus_, path);
try {
peers.push_back(Peer{.path = path,
.name = peer.Name(),
.hardware_address = peer.HwAddress(),
.strength = peer.Strength(),
.last_seen = peer.LastSeen()});
} catch (const sdbus::Error& error) {
LOG(WARNING) << __func__ << ": Failed to inspect peer " << path << ": "
<< error.getName() << ": " << error.getMessage();
}
}
return wifi_direct_internal::SelectPeerByName(peers, device_name);
}
void NetworkManagerWifiDirectMedium::StopDiscovery() {
bool should_stop = false;
{
absl::MutexLock lock(state_mutex_);
should_stop = discovering_;
discovering_ = false;
peer_changed_.SignalAll();
}
if (!should_stop) {
return;
}
try {
StopFind();
} catch (const sdbus::Error& error) {
DBUS_LOG_METHOD_CALL_ERROR(this, "StopFind", error);
}
}
bool NetworkManagerWifiDirectMedium::ActivatePeer(const Peer& peer) {
auto uuid = NewUuidStr();
if (!uuid.has_value()) {
LOG(ERROR) << __func__ << ": Could not generate connection UUID";
return false;
}
wifi_direct_internal::ConnectionSettings settings =
wifi_direct_internal::BuildGcConnectionSettings(*uuid,
peer.hardware_address);
sdbus::ObjectPath active_path;
try {
auto [connection_path, returned_active_path, result] =
network_manager_->AddAndActivateConnection2(
settings, wifi_p2p_device_path_, peer.path,
{{"persist", sdbus::Variant(std::string("volatile"))},
{"bind-activation", sdbus::Variant(std::string("dbus-client"))}});
active_path = std::move(returned_active_path);
} catch (const sdbus::Error& error) {
DBUS_LOG_METHOD_CALL_ERROR(network_manager_, "AddAndActivateConnection2",
error);
return false;
}
networkmanager::ActiveConnection active_connection(system_bus_, active_path);
auto [reason, timed_out] =
active_connection.WaitForConnection(kActivationTimeout);
if (timed_out || reason.has_value()) {
LOG(ERROR) << __func__ << ": Wi-Fi Direct activation failed for "
<< active_path;
try {
network_manager_->DeactivateConnection(active_path);
} catch (const sdbus::Error& error) {
DBUS_LOG_METHOD_CALL_ERROR(network_manager_, "DeactivateConnection",
error);
}
return false;
}
std::vector<std::string> addresses = active_connection.GetIP4Addresses();
if (addresses.empty()) {
LOG(ERROR) << __func__
<< ": Activated Wi-Fi Direct connection has no IPv4 address";
try {
network_manager_->DeactivateConnection(active_path);
} catch (const sdbus::Error& error) {
DBUS_LOG_METHOD_CALL_ERROR(network_manager_, "DeactivateConnection",
error);
}
return false;
}
std::string gateway = active_connection.GetIP4Gateway();
absl::MutexLock lock(state_mutex_);
active_connection_path_ = active_path;
local_ip_address_ = addresses.front();
remote_ip_address_ = std::move(gateway);
connected_ = true;
LOG(INFO) << __func__
<< ": Wi-Fi Direct GC connected; local IPv4=" << local_ip_address_
<< ", GO IPv4=" << remote_ip_address_;
return true;
}
bool NetworkManagerWifiDirectMedium::ConnectWifiDirect(
const WifiDirectCredentials& wifi_direct_credentials) {
if (wifi_direct_credentials.GetDeviceName().empty()) {
LOG(ERROR) << __func__ << ": Device name is empty";
return false;
}
DisconnectWifiDirect();
{
absl::MutexLock lock(state_mutex_);
discovering_ = true;
}
try {
StartFind({{"timeout", sdbus::Variant(std::int32_t(30))}});
} catch (const sdbus::Error& error) {
{
absl::MutexLock lock(state_mutex_);
discovering_ = false;
}
DBUS_LOG_METHOD_CALL_ERROR(this, "StartFind", error);
return false;
}
const absl::Time deadline = absl::Now() + kPeerDiscoveryTimeout;
std::optional<Peer> peer;
while (absl::Now() < deadline) {
peer = FindPeerByName(wifi_direct_credentials.GetDeviceName());
if (peer.has_value()) {
break;
}
absl::MutexLock lock(state_mutex_);
if (!discovering_) {
break;
}
peer_changed_.WaitWithTimeout(
&state_mutex_,
std::min(kPeerUpdatePollInterval, deadline - absl::Now()));
}
StopDiscovery();
if (!peer.has_value()) {
LOG(WARNING) << __func__ << ": Timed out finding Wi-Fi Direct peer "
<< wifi_direct_credentials.GetDeviceName();
return false;
}
return ActivatePeer(*peer);
}
bool NetworkManagerWifiDirectMedium::DisconnectWifiDirect() {
StopDiscovery();
std::string active_path;
{
absl::MutexLock lock(state_mutex_);
active_path = std::move(active_connection_path_);
connected_ = false;
local_ip_address_.clear();
remote_ip_address_.clear();
}
if (active_path.empty()) {
return true;
}
try {
network_manager_->DeactivateConnection(sdbus::ObjectPath(active_path));
return true;
} catch (const sdbus::Error& error) {
DBUS_LOG_METHOD_CALL_ERROR(network_manager_, "DeactivateConnection", error);
return false;
}
}
std::unique_ptr<api::WifiDirectSocket>
NetworkManagerWifiDirectMedium::ConnectToService(
absl::string_view ip_address, int port,
CancellationFlag *cancellation_flag) {
auto socket = TCPSocket::Connect(std::string(ip_address), port);
if (!socket.has_value()) return nullptr;
CancellationFlag* cancellation_flag) {
std::string remote_ip;
{
absl::MutexLock lock(state_mutex_);
if (!connected_) {
LOG(ERROR) << __func__ << ": Wi-Fi Direct GC is not connected";
return nullptr;
}
remote_ip = remote_ip_address_;
}
if (remote_ip.empty()) {
remote_ip = std::string(ip_address);
}
if (remote_ip.empty() || port <= 0 || port > 65535) {
LOG(ERROR) << __func__ << ": Invalid service address or port";
return nullptr;
}
return std::make_unique<WifiDirectSocket>(std::move(*socket));
const std::int64_t retries = std::max<std::int64_t>(
1, NearbyFlags::GetInstance().GetInt64Flag(
platform::config_package_nearby::nearby_platform_feature::
kWifiHotspotConnectionMaxRetries));
const absl::Duration retry_interval =
absl::Milliseconds(NearbyFlags::GetInstance().GetInt64Flag(
platform::config_package_nearby::nearby_platform_feature::
kWifiHotspotConnectionIntervalMillis));
const absl::Duration connect_timeout =
absl::Milliseconds(NearbyFlags::GetInstance().GetInt64Flag(
platform::config_package_nearby::nearby_platform_feature::
kWifiHotspotConnectionTimeoutMillis));
for (std::int64_t attempt = 0; attempt < retries; ++attempt) {
if (cancellation_flag != nullptr && cancellation_flag->Cancelled()) {
return nullptr;
}
auto socket =
ConnectTcp(remote_ip, port, connect_timeout, cancellation_flag);
if (socket.has_value()) {
return std::make_unique<WifiDirectSocket>(std::move(*socket));
}
if (attempt + 1 < retries) {
SleepWithCancellation(retry_interval, cancellation_flag);
}
}
LOG(ERROR) << __func__ << ": Failed to connect to Wi-Fi Direct service "
<< remote_ip << ":" << port;
return nullptr;
}
std::unique_ptr<api::WifiDirectServerSocket>
NetworkManagerWifiDirectMedium::ListenForService(int port) {
auto active_connection = wireless_device_->GetActiveConnection();
if (active_connection == nullptr) {
return nullptr;
}
auto ip4addresses = active_connection->GetIP4Addresses();
if (ip4addresses.empty()) {
LOG(ERROR)
<< __func__
<< "Could not find any IPv4 addresses for active connection "
<< active_connection->getProxy().getObjectPath();
return nullptr;
}
auto socket = TCPServerSocket::Listen(std::ref(ip4addresses[0]), port);
if (!socket.has_value()) return nullptr;
return std::make_unique<NetworkManagerWifiDirectServerSocket>(
std::move(*socket), std::move(active_connection), network_manager_);
}
bool NetworkManagerWifiDirectMedium::ConnectWifiDirect(
WifiDirectCredentials *wifi_direct_credentials) {
if (wifi_direct_credentials == nullptr) {
LOG(ERROR) << __func__ << ": hotspot_credentials cannot be null";
return false;
}
auto ssid = wifi_direct_credentials->GetSSID();
auto password = wifi_direct_credentials->GetPassword();
return wireless_device_->ConnectToNetwork(ssid, password,
api::WifiAuthType::kWpaPsk) ==
api::WifiConnectionStatus::kConnected;
}
bool NetworkManagerWifiDirectMedium::DisconnectWifiDirect() {
if (!ConnectedToWifi()) {
LOG(ERROR) << __func__ << ": Not connected to a WiFi hotspot";
return false;
}
auto active_connection = wireless_device_->GetActiveConnection();
if (active_connection == nullptr) {
return false;
}
try {
network_manager_->DeactivateConnection(active_connection->getProxy().getObjectPath());
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(network_manager_, "DeactivateConnection", e);
return false;
}
return true;
}
bool NetworkManagerWifiDirectMedium::ConnectedToWifi() {
try {
auto mode = wireless_device_->Mode();
return mode == 2; // NM_802_11_MODE_INFRA
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(wireless_device_, "Mode", e);
return false;
}
LOG(WARNING) << __func__
<< ": Linux Wi-Fi Direct is GC-only; listening is unsupported";
return nullptr;
}
bool NetworkManagerWifiDirectMedium::StartWifiDirect(
WifiDirectCredentials *wifi_direct_credentials) {
// According to the comments in the windows implementation, the wifi direct
// medium is currently just a regular wifi hotspot.
auto wireless_device = std::make_unique<NetworkManagerWifiMedium>(
network_manager_, wireless_device_->getProxy().getObjectPath());
auto hotspot = NetworkManagerWifiHotspotMedium(network_manager_,
std::move(wireless_device));
HotspotCredentials hotspot_creds;
if (!hotspot.StartWifiHotspot(&hotspot_creds)) return false;
wifi_direct_credentials->SetSSID(hotspot_creds.GetSSID());
wifi_direct_credentials->SetPassword(hotspot_creds.GetPassword());
wifi_direct_credentials->SetFrequency(hotspot_creds.GetFrequency());
return true;
WifiDirectCredentials* wifi_direct_credentials) {
LOG(WARNING) << __func__
<< ": Linux Wi-Fi Direct autonomous GO is unsupported";
return false;
}
bool NetworkManagerWifiDirectMedium::StopWifiDirect() {
auto wireless_device = std::make_unique<NetworkManagerWifiMedium>(
network_manager_, wireless_device_->getProxy().getObjectPath());
auto hotspot = NetworkManagerWifiHotspotMedium(network_manager_,
std::move(wireless_device));
return hotspot.DisconnectWifiHotspot();
return true;
}
} // namespace linux
} // namespace nearby
} // namespace nearby::linux
@@ -14,51 +14,89 @@
#ifndef PLATFORM_IMPL_LINUX_WIFI_DIRECT_H_
#define PLATFORM_IMPL_LINUX_WIFI_DIRECT_H_
#include <memory>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/ProxyInterfaces.h>
#include "absl/base/thread_annotations.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/implementation/linux/generated/dbus/networkmanager/device_wifip2p_client.h"
#include "internal/platform/implementation/linux/network_manager.h"
#include "internal/platform/implementation/linux/wifi_medium.h"
#include "internal/platform/implementation/linux/wifi_direct_utils.h"
#include "internal/platform/implementation/wifi_direct.h"
namespace nearby {
namespace linux {
class NetworkManagerWifiDirectMedium : public api::WifiDirectMedium {
class NetworkManagerWifiDirectMedium
: public api::WifiDirectMedium,
public sdbus::ProxyInterfaces<
org::freedesktop::NetworkManager::Device::WifiP2P_proxy> {
public:
NetworkManagerWifiDirectMedium(
std::shared_ptr<networkmanager::NetworkManager> network_manager,
std::unique_ptr<NetworkManagerWifiMedium> wireless_device)
: system_bus_(network_manager->GetConnection()),
const sdbus::ObjectPath& wifi_p2p_device_path)
: ProxyInterfaces(*network_manager->GetConnection(),
sdbus::ServiceName("org.freedesktop.NetworkManager"),
wifi_p2p_device_path),
system_bus_(network_manager->GetConnection()),
network_manager_(std::move(network_manager)),
wireless_device_(std::move(wireless_device)) {}
wifi_p2p_device_path_(wifi_p2p_device_path) {
registerProxy();
}
~NetworkManagerWifiDirectMedium() override;
bool IsInterfaceValid() const override { return true; }
bool IsInterfaceValid() const override {
return !wifi_p2p_device_path_.empty();
}
std::unique_ptr<api::WifiDirectSocket> ConnectToService(
absl::string_view ip_address, int port,
CancellationFlag *cancellation_flag) override;
CancellationFlag* cancellation_flag) override;
std::unique_ptr<api::WifiDirectServerSocket> ListenForService(
int port) override;
bool ConnectWifiDirect(
WifiDirectCredentials *wifi_direct_credentials) override;
const WifiDirectCredentials& wifi_direct_credentials) override;
bool DisconnectWifiDirect() override;
bool StartWifiDirect(WifiDirectCredentials *wifi_direct_credentials) override;
bool StartWifiDirect(WifiDirectCredentials* wifi_direct_credentials) override;
bool StopWifiDirect() override;
absl::optional<std::pair<std::int32_t, std::int32_t>> GetDynamicPortRange()
std::optional<std::pair<std::int32_t, std::int32_t>> GetDynamicPortRange()
override {
return std::nullopt;
}
std::vector<WifiDirectAuthType> GetSupportedWifiDirectAuthTypes()
const override {
return {WifiDirectAuthType::WIFI_DIRECT_WITH_DEVICE_NAME};
}
protected:
void onPeerAdded(const sdbus::ObjectPath& peer) override;
void onPeerRemoved(const sdbus::ObjectPath& peer) override;
private:
bool ConnectedToWifi();
using Peer = wifi_direct_internal::PeerInfo;
std::optional<Peer> FindPeerByName(absl::string_view device_name);
bool ActivatePeer(const Peer& peer);
void StopDiscovery();
std::shared_ptr<sdbus::IConnection> system_bus_;
std::shared_ptr<networkmanager::NetworkManager> network_manager_;
std::unique_ptr<NetworkManagerWifiMedium> wireless_device_;
sdbus::ObjectPath wifi_p2p_device_path_;
mutable absl::Mutex state_mutex_;
bool discovering_ ABSL_GUARDED_BY(state_mutex_) = false;
bool connected_ ABSL_GUARDED_BY(state_mutex_) = false;
std::string active_connection_path_ ABSL_GUARDED_BY(state_mutex_);
std::string local_ip_address_ ABSL_GUARDED_BY(state_mutex_);
std::string remote_ip_address_ ABSL_GUARDED_BY(state_mutex_);
absl::CondVar peer_changed_;
};
} // namespace linux
} // namespace nearby
@@ -25,9 +25,10 @@ namespace linux {
class WifiDirectSocket : public api::WifiDirectSocket {
public:
explicit WifiDirectSocket(TCPSocket socket) : socket_(std::move(socket)) {}
~WifiDirectSocket() override { socket_.Close(); }
InputStream &GetInputStream() override { return socket_.GetInputStream(); }
OutputStream &GetOutputStream() override { return socket_.GetOutputStream(); }
InputStream& GetInputStream() override { return socket_.GetInputStream(); }
OutputStream& GetOutputStream() override { return socket_.GetOutputStream(); }
Exception Close() override { return socket_.Close(); };
@@ -0,0 +1,65 @@
// Copyright 2026 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/linux/wifi_direct_utils.h"
#include <cstdint>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/match.h"
namespace nearby::linux::wifi_direct_internal {
namespace {
constexpr std::uint32_t kWpsMethodPushButton = 0x4;
} // namespace
std::optional<PeerInfo> SelectPeerByName(const std::vector<PeerInfo>& peers,
absl::string_view device_name) {
std::optional<PeerInfo> selected;
for (const PeerInfo& peer : peers) {
if (peer.hardware_address.empty() ||
!absl::EqualsIgnoreCase(peer.name, device_name)) {
continue;
}
if (!selected.has_value() || peer.strength > selected->strength ||
(peer.strength == selected->strength &&
peer.last_seen > selected->last_seen)) {
selected = peer;
}
}
return selected;
}
ConnectionSettings BuildGcConnectionSettings(absl::string_view uuid,
absl::string_view peer_address) {
return {
{"connection",
{{"uuid", sdbus::Variant(std::string(uuid))},
{"id", sdbus::Variant(std::string("Nearby Wi-Fi Direct"))},
{"type", sdbus::Variant(std::string("wifi-p2p"))},
{"autoconnect", sdbus::Variant(false)}}},
{"wifi-p2p",
{{"peer", sdbus::Variant(std::string(peer_address))},
{"wps-method", sdbus::Variant(kWpsMethodPushButton)}}},
{"ipv4", {{"method", sdbus::Variant(std::string("auto"))}}},
{"ipv6", {{"method", sdbus::Variant(std::string("disabled"))}}},
};
}
} // namespace nearby::linux::wifi_direct_internal
@@ -0,0 +1,49 @@
// Copyright 2026 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_LINUX_WIFI_DIRECT_UTILS_H_
#define PLATFORM_IMPL_LINUX_WIFI_DIRECT_UTILS_H_
#include <cstdint>
#include <map>
#include <optional>
#include <string>
#include <vector>
#include <sdbus-c++/Types.h>
#include "absl/strings/string_view.h"
namespace nearby::linux::wifi_direct_internal {
struct PeerInfo {
sdbus::ObjectPath path;
std::string name;
std::string hardware_address;
std::uint8_t strength = 0;
std::int32_t last_seen = -1;
};
using ConnectionSettings =
std::map<std::string, std::map<std::string, sdbus::Variant>>;
std::optional<PeerInfo> SelectPeerByName(const std::vector<PeerInfo>& peers,
absl::string_view device_name);
ConnectionSettings BuildGcConnectionSettings(absl::string_view uuid,
absl::string_view peer_address);
} // namespace nearby::linux::wifi_direct_internal
#endif // PLATFORM_IMPL_LINUX_WIFI_DIRECT_UTILS_H_
@@ -0,0 +1,96 @@
// Copyright 2026 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/linux/wifi_direct_utils.h"
#include <cstdint>
#include <string>
#include <vector>
#include "gtest/gtest.h"
namespace nearby::linux::wifi_direct_internal {
namespace {
TEST(WifiDirectUtilsTest, SelectsPeerCaseInsensitively) {
std::vector<PeerInfo> peers{
{.path = sdbus::ObjectPath("/peer/1"),
.name = "Android_1234",
.hardware_address = "02:00:00:00:00:01",
.strength = 50,
.last_seen = 10},
};
auto selected = SelectPeerByName(peers, "android_1234");
ASSERT_TRUE(selected.has_value());
EXPECT_EQ(selected->path, "/peer/1");
}
TEST(WifiDirectUtilsTest, SelectsStrongestThenMostRecentDuplicate) {
std::vector<PeerInfo> peers{
{.path = sdbus::ObjectPath("/peer/old"),
.name = "Android",
.hardware_address = "02:00:00:00:00:01",
.strength = 60,
.last_seen = 10},
{.path = sdbus::ObjectPath("/peer/weak"),
.name = "Android",
.hardware_address = "02:00:00:00:00:02",
.strength = 40,
.last_seen = 30},
{.path = sdbus::ObjectPath("/peer/new"),
.name = "Android",
.hardware_address = "02:00:00:00:00:03",
.strength = 60,
.last_seen = 20},
};
auto selected = SelectPeerByName(peers, "Android");
ASSERT_TRUE(selected.has_value());
EXPECT_EQ(selected->path, "/peer/new");
}
TEST(WifiDirectUtilsTest, IgnoresWrongNameAndMissingAddress) {
std::vector<PeerInfo> peers{
{.path = sdbus::ObjectPath("/peer/1"),
.name = "Other",
.hardware_address = "aa:bb"},
{.path = sdbus::ObjectPath("/peer/2"),
.name = "Android",
.hardware_address = ""},
};
EXPECT_FALSE(SelectPeerByName(peers, "Android").has_value());
}
TEST(WifiDirectUtilsTest, BuildsVolatileP2pProfilePayload) {
ConnectionSettings settings =
BuildGcConnectionSettings("uuid-1", "02:00:00:00:00:01");
EXPECT_EQ(settings.at("connection").at("uuid").get<std::string>(), "uuid-1");
EXPECT_EQ(settings.at("connection").at("type").get<std::string>(),
"wifi-p2p");
EXPECT_FALSE(settings.at("connection").at("autoconnect").get<bool>());
EXPECT_EQ(settings.at("wifi-p2p").at("peer").get<std::string>(),
"02:00:00:00:00:01");
EXPECT_EQ(settings.at("wifi-p2p").at("wps-method").get<std::uint32_t>(),
std::uint32_t{0x4});
EXPECT_EQ(settings.at("ipv4").at("method").get<std::string>(), "auto");
EXPECT_EQ(settings.at("ipv6").at("method").get<std::string>(), "disabled");
}
} // namespace
} // namespace nearby::linux::wifi_direct_internal
@@ -41,23 +41,23 @@ namespace {
constexpr char kDeviceIpv4TxtRecord[] = "IPv4";
std::string GetActiveIpv4Address(
const std::shared_ptr<networkmanager::NetworkManager> &network_manager,
const std::shared_ptr<sdbus::IConnection> &system_bus) {
const std::shared_ptr<networkmanager::NetworkManager>& network_manager,
const std::shared_ptr<sdbus::IConnection>& system_bus) {
std::vector<sdbus::ObjectPath> connection_paths;
try {
connection_paths = network_manager->ActiveConnections();
} catch (const sdbus::Error &e) {
} catch (const sdbus::Error& e) {
DBUS_LOG_PROPERTY_GET_ERROR(network_manager, "ActiveConnections", e);
return {};
}
for (auto &path : connection_paths) {
for (auto& path : connection_paths) {
auto active_connection =
std::make_unique<networkmanager::ActiveConnection>(system_bus, path);
std::string conn_type;
try {
conn_type = active_connection->Type();
} catch (const sdbus::Error &e) {
} catch (const sdbus::Error& e) {
DBUS_LOG_PROPERTY_GET_ERROR(active_connection, "Type", e);
continue;
}
@@ -87,7 +87,7 @@ bool WifiLanMedium::IsNetworkConnected() const {
}
std::optional<std::pair<std::string, std::string>> entry_group_key(
const NsdServiceInfo &nsd_service_info) {
const NsdServiceInfo& nsd_service_info) {
auto name = nsd_service_info.GetServiceName();
if (name.empty()) {
LOG(ERROR) << __func__ << ": service name cannot be empty";
@@ -103,7 +103,7 @@ std::optional<std::pair<std::string, std::string>> entry_group_key(
return std::make_pair(std::move(name), std::move(type));
}
bool WifiLanMedium::StartAdvertising(const NsdServiceInfo &nsd_service_info) {
bool WifiLanMedium::StartAdvertising(const NsdServiceInfo& nsd_service_info) {
auto key = entry_group_key(nsd_service_info);
if (!key.has_value()) {
return false;
@@ -113,7 +113,7 @@ bool WifiLanMedium::StartAdvertising(const NsdServiceInfo &nsd_service_info) {
absl::ReaderMutexLock l(&entry_groups_mutex_);
if (entry_groups_.count(*key) == 1) {
LOG(ERROR) << __func__
<< ": advertising is already active for this service";
<< ": advertising is already active for this service";
return false;
}
}
@@ -137,14 +137,15 @@ bool WifiLanMedium::StartAdvertising(const NsdServiceInfo &nsd_service_info) {
sdbus::ObjectPath entry_group_path;
try {
entry_group_path = avahi_->EntryGroupNew();
} catch (const sdbus::Error &e) {
} catch (const sdbus::Error& e) {
DBUS_LOG_METHOD_CALL_ERROR(avahi_, "EntryGroupNew", e);
return false;
}
auto entry_group =
std::make_unique<avahi::EntryGroup>(*system_bus_, entry_group_path);
LOG(INFO) << __func__ << ": Adding avahi service with service type: " << nsd_service_info.GetServiceType();
LOG(INFO) << __func__ << ": Adding avahi service with service type: "
<< nsd_service_info.GetServiceType();
try {
entry_group->AddService(
@@ -153,10 +154,10 @@ bool WifiLanMedium::StartAdvertising(const NsdServiceInfo &nsd_service_info) {
0, nsd_service_info.GetServiceName(), nsd_service_info.GetServiceType(),
std::string(), std::string(), nsd_service_info.GetPort(), txt_records);
entry_group->Commit();
} catch (const sdbus::Error &e) {
} catch (const sdbus::Error& e) {
LOG(ERROR) << __func__ << ": Got error '" << e.getName()
<< "' with message '" << e.getMessage()
<< "' while adding service";
<< "' with message '" << e.getMessage()
<< "' while adding service";
return false;
}
@@ -166,7 +167,7 @@ bool WifiLanMedium::StartAdvertising(const NsdServiceInfo &nsd_service_info) {
return true;
}
bool WifiLanMedium::StopAdvertising(const NsdServiceInfo &nsd_service_info) {
bool WifiLanMedium::StopAdvertising(const NsdServiceInfo& nsd_service_info) {
auto key = entry_group_key(nsd_service_info);
if (!key.has_value()) {
return false;
@@ -175,7 +176,7 @@ bool WifiLanMedium::StopAdvertising(const NsdServiceInfo &nsd_service_info) {
absl::MutexLock l(&entry_groups_mutex_);
if (entry_groups_.count(*key) == 0) {
LOG(ERROR) << __func__
<< ": Advertising is already inactive for this service.";
<< ": Advertising is already inactive for this service.";
return false;
}
@@ -184,15 +185,15 @@ bool WifiLanMedium::StopAdvertising(const NsdServiceInfo &nsd_service_info) {
}
bool WifiLanMedium::StartDiscovery(
const std::string &service_type,
const std::string& service_type,
api::WifiLanMedium::DiscoveredServiceCallback callback) {
{
absl::ReaderMutexLock l(&service_browsers_mutex_);
if (service_browsers_.count(service_type) != 0) {
auto &object = service_browsers_[service_type];
auto& object = service_browsers_[service_type];
LOG(ERROR) << __func__ << ": A service browser for service type "
<< service_type << " already exists at "
<< object->getProxy().getObjectPath();
<< service_type << " already exists at "
<< object->getProxy().getObjectPath();
return false;
}
}
@@ -210,24 +211,23 @@ bool WifiLanMedium::StartDiscovery(
<< browser_object_path << " for service_type: " << service_type;
absl::MutexLock l(&service_browsers_mutex_);
service_browsers_.emplace(
service_type,
std::make_unique<avahi::ServiceBrowser>(
*system_bus_, browser_object_path, avahi_));
} catch (const sdbus::Error &e) {
service_browsers_.emplace(service_type,
std::make_unique<avahi::ServiceBrowser>(
*system_bus_, browser_object_path, avahi_));
} catch (const sdbus::Error& e) {
DBUS_LOG_METHOD_CALL_ERROR(avahi_, "ServiceBrowserPrepare", e);
return false;
}
service_browsers_mutex_.ReaderLock();
auto &browser = service_browsers_[service_type];
auto& browser = service_browsers_[service_type];
service_browsers_mutex_.ReaderUnlock();
try {
LOG(INFO) << __func__ << ": Starting service discovery for "
<< browser->getProxy().getObjectPath();
<< browser->getProxy().getObjectPath();
browser->Start();
} catch (const sdbus::Error &e) {
} catch (const sdbus::Error& e) {
DBUS_LOG_METHOD_CALL_ERROR(browser, "Start", e);
return false;
}
@@ -235,12 +235,12 @@ bool WifiLanMedium::StartDiscovery(
return true;
}
bool WifiLanMedium::StopDiscovery(const std::string &service_type) {
bool WifiLanMedium::StopDiscovery(const std::string& service_type) {
absl::MutexLock l(&service_browsers_mutex_);
if (service_browsers_.count(service_type) == 0) {
LOG(ERROR) << __func__ << ": Service type " << service_type
<< " has not been registered for discovery";
<< " has not been registered for discovery";
return false;
}
service_browsers_.erase(service_type);
@@ -249,16 +249,16 @@ bool WifiLanMedium::StopDiscovery(const std::string &service_type) {
}
std::unique_ptr<api::WifiLanSocket> WifiLanMedium::ConnectToService(
const std::string &ip_address, int port,
CancellationFlag *cancellation_flag) {
const std::string& ip_address, int port,
CancellationFlag* cancellation_flag) {
auto socket = TCPSocket::Connect(ip_address, port);
if (!socket.has_value()) return nullptr;
return std::make_unique<WifiLanSocket>(*socket);
return std::make_unique<WifiLanSocket>(std::move(*socket));
}
std::unique_ptr<api::WifiLanServerSocket> WifiLanMedium::ListenForService(
int port) {
LOG(INFO)<< __func__ << ": Listening for service WifiLanMedium";
LOG(INFO) << __func__ << ": Listening for service WifiLanMedium";
auto socket = TCPServerSocket::Listen(std::nullopt, port);
if (!socket.has_value()) return nullptr;
@@ -330,7 +330,7 @@ api::UpgradeAddressInfo WifiLanMedium::GetUpgradeAddressCandidates(
// Convert to vector of chars in network byte order
std::vector<char> addr_bytes(4);
std::memcpy(addr_bytes.data(), &addr.s_addr, 4);
ipv4_addresses.push_back(
ServiceAddress{.address = std::move(addr_bytes), .port = port});
has_ipv4_address = true;