Refactored WiFi LAN implementation based on new WiFi LAN API

PiperOrigin-RevId: 407453537
This commit is contained in:
guogang
2021-11-03 16:32:17 -07:00
committed by Copybara-Service
parent 21a379d242
commit ebfaba3e22
7 changed files with 664 additions and 938 deletions
+4
View File
@@ -64,6 +64,7 @@ cc_library(
"thread_pool.h",
"webrtc.h",
"wifi.h",
"wifi_lan.h",
],
compatible_with = ["//buildenv/target:non_prod"],
visibility = ["//visibility:private"],
@@ -104,6 +105,9 @@ cc_library(
"system_clock.cc",
"thread_pool.cc",
"utils.cc",
"wifi_lan_medium.cc",
"wifi_lan_server_socket.cc",
"wifi_lan_socket.cc",
],
compatible_with = ["//buildenv/target:non_prod"],
copts = ["-Ithird_party/nearby_connections/cpp/platform/impl/windows/generated"],
+2 -2
View File
@@ -36,6 +36,7 @@
#include "platform/impl/windows/submittable_executor.h"
#include "platform/impl/windows/webrtc.h"
#include "platform/impl/windows/wifi.h"
#include "platform/impl/windows/wifi_lan.h"
namespace location {
namespace nearby {
@@ -159,9 +160,8 @@ std::unique_ptr<WifiMedium> ImplementationPlatform::CreateWifiMedium() {
return std::unique_ptr<WifiMedium>();
}
// TODO(b/184975123): replace with real implementation.
std::unique_ptr<WifiLanMedium> ImplementationPlatform::CreateWifiLanMedium() {
return std::unique_ptr<WifiLanMedium>();
return absl::make_unique<windows::WifiLanMedium>();
}
// TODO(b/184975123): replace with real implementation.
+123 -197
View File
@@ -21,6 +21,7 @@
// Standard C/C++ headers
#include <exception>
#include <functional>
#include <memory>
#include <string>
@@ -76,40 +77,13 @@ using winrt::Windows::Storage::Streams::IInputStream;
using winrt::Windows::Storage::Streams::InputStreamOptions;
using winrt::Windows::Storage::Streams::IOutputStream;
class WifiLanMedium;
// WifiLanService includes NSD service information and
// related medium information.
class WifiLanService : public api::WifiLanService {
public:
WifiLanService() = default;
explicit WifiLanService(NsdServiceInfo nsd_service_info)
: nsd_service_info_(std::move(nsd_service_info)) {}
~WifiLanService() override = default;
NsdServiceInfo GetServiceInfo() const override { return nsd_service_info_; }
void SetServiceInfo(NsdServiceInfo nsd_service_info) {
nsd_service_info_ = std::move(nsd_service_info);
}
WifiLanMedium* GetMedium() { return medium_; }
void SetMedium(WifiLanMedium* medium) { medium_ = medium; }
private:
NsdServiceInfo nsd_service_info_;
WifiLanMedium* medium_ = nullptr;
};
// WifiLanSocket wraps the socket functions to read and write stream.
// In WiFi LAN, A WifiLanSocket will be passed to StartAcceptingConnections's
// call back when StreamSocketListener got connect. When call API to connect to
// remote WiFi LAN service, also will return a WifiLanSocket to caller.
class WifiLanSocket : public api::WifiLanSocket {
public:
explicit WifiLanSocket(api::WifiLanService* wifi_lan_service,
StreamSocket socket);
explicit WifiLanSocket(StreamSocket socket);
WifiLanSocket(WifiLanSocket&) = default;
WifiLanSocket(WifiLanSocket&&) = default;
~WifiLanSocket() override;
@@ -133,22 +107,6 @@ class WifiLanSocket : public api::WifiLanSocket {
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
Exception Close() override;
// Returns valid WifiLanService pointer if there is a connection, and
// nullptr otherwise.
api::WifiLanService* GetRemoteWifiLanService() override;
// Sets service id binding to the socket
void SetServiceId(std::string service_id);
// Sets medium information
void SetMedium(WifiLanMedium* medium);
// Returns the socket IP address
std::string GetLocalAddress();
// Returns the socket port, range is between 49152 and 65535
int GetLocalPort();
private:
// A simple wrapper to handle input stream of socket
class SocketInputStream : public InputStream {
@@ -182,45 +140,107 @@ class WifiLanSocket : public api::WifiLanSocket {
StreamSocket stream_soket_{nullptr};
SocketInputStream input_stream_{nullptr};
SocketOutputStream output_stream_{nullptr};
api::WifiLanService* remote_wifi_lan_service_ = nullptr;
WifiLanMedium* medium_ = nullptr;
std::string service_id_;
};
// WifiLanNsd implements the NSD functions for a specific service ID.
// WifiLan Medium separates NSD functions using WifiLanNsd. WifiLanNsd
// maintians the states of mDNS service.
class WifiLanNsd {
// WifiLanServerSocket provides the support to server socket, this server socket
// accepts connection from clients.
class WifiLanServerSocket : public api::WifiLanServerSocket {
public:
explicit WifiLanNsd(WifiLanMedium* medium, const std::string service_id);
WifiLanNsd(WifiLanNsd&&) = default;
WifiLanNsd& operator=(WifiLanNsd&&) = default;
~WifiLanNsd() = default;
explicit WifiLanServerSocket(int port = 0);
WifiLanServerSocket(WifiLanServerSocket&) = default;
WifiLanServerSocket(WifiLanServerSocket&&) = default;
~WifiLanServerSocket() override;
WifiLanServerSocket& operator=(const WifiLanServerSocket&) = default;
WifiLanServerSocket& operator=(WifiLanServerSocket&&) = default;
// Implements medium functions based on service id
bool StartAcceptingConnections(
api::WifiLanMedium::AcceptedConnectionCallback callback);
bool StopAcceptingConnections();
bool StartAdvertising(const NsdServiceInfo& nsd_service_info);
bool StopAdvertising();
bool StartDiscovery(api::WifiLanMedium::DiscoveredServiceCallback callback);
bool StopDiscovery();
// Returns ip address.
std::string GetIPAddress() const override;
// In the class, not using ENUM to describe the mDNS states, because a little
// complicate to combine all states based on accepting, advertising and
// discovery.
bool IsIdle() { return nsd_status_ == 0; }
// Returns port.
int GetPort() const override;
bool IsAccepting() { return (nsd_status_ & NSD_STATUS_ACCEPTING) != 0; }
// Sets port
void SetPort(int port) { port_ = port; }
bool IsAdvertising() { return (nsd_status_ & NSD_STATUS_ADVERTISING) != 0; }
StreamSocketListener GetSocketListener() const {
return stream_socket_listener_;
}
bool IsDiscovering() { return (nsd_status_ & NSD_STATUS_DISCOVERING) != 0; }
// Blocks until either:
// - at least one incoming connection request is available, or
// - ServerSocket is closed.
// On success, returns connected socket, ready to exchange data.
// Returns nullptr on error.
// Once error is reported, it is permanent, and ServerSocket has to be closed.
std::unique_ptr<api::WifiLanSocket> Accept() override;
// A pair of IP Address and Port. A remote device can use this information
// to connect to us. This is non-null while IsAccepting is true.
std::pair<std::string, int> GetCredentials();
// Called by the server side of a connection before passing ownership of
// WifiLanServerSocker to user, to track validity of a pointer to this
// server socket.
void SetCloseNotifier(std::function<void()> notifier);
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
Exception Close() override;
// Binds to local port
bool listen();
private:
// The listener is accepting incoming connections
fire_and_forget Listener_ConnectionReceived(
StreamSocketListener listener,
StreamSocketListenerConnectionReceivedEventArgs const& args);
// Retrieves IP addresses from local machine
std::vector<std::string> GetIpAddresses();
mutable absl::Mutex mutex_;
absl::CondVar cond_;
std::deque<StreamSocket> pending_sockets_ ABSL_GUARDED_BY(mutex_);
StreamSocketListener stream_socket_listener_{nullptr};
winrt::event_token listener_event_token_{};
// Close notifier
std::function<void()> close_notifier_ = nullptr;
// IP addresses of the computer. mDNS uses them to advertise.
std::vector<std::string> ip_addresses_{};
// Cache socket not be picked by upper layer
int port_ = 0;
bool closed_ = false;
};
// Container of operations that can be performed over the WifiLan medium.
class WifiLanMedium : public api::WifiLanMedium {
public:
~WifiLanMedium() override = default;
// Starts to advertising
bool StartAdvertising(const NsdServiceInfo& nsd_service_info) override;
// Stops to advertising
bool StopAdvertising(const NsdServiceInfo& nsd_service_info) override;
// Starts to discovery
bool StartDiscovery(const std::string& service_type,
DiscoveredServiceCallback callback) override;
// Returns true once WifiLan discovery for service_type is well and truly
// stopped; after this returns, there must be no more invocations of the
// DiscoveredServiceCallback passed in to StartDiscovery() for service_type.
bool StopDiscovery(const std::string& service_type) override;
std::unique_ptr<api::WifiLanSocket> ConnectToService(
const NsdServiceInfo& remote_service_info,
CancellationFlag* cancellation_flag) override;
std::unique_ptr<api::WifiLanSocket> ConnectToService(
const std::string& ip_address, int port,
CancellationFlag* cancellation_flag) override;
std::unique_ptr<api::WifiLanServerSocket> ListenForService(
int port = 0) override;
// DnsServiceDeRegister is a async process, after operation finish, callback
// will call this method to notify the waiting method StopAdvertising to
@@ -228,46 +248,38 @@ class WifiLanNsd {
void NotifyDnsServiceUnregistered(DWORD status);
private:
// Nsd status
static const int NSD_STATUS_IDLE = 0;
static const int NSD_STATUS_ACCEPTING = (1 << 0);
static const int NSD_STATUS_ADVERTISING = (1 << 1);
static const int NSD_STATUS_DISCOVERING = (1 << 2);
//
// Constants
//
// Socket listening ports
static const uint16 PORT_MIN = 49152;
static const uint16 PORT_MAX = 65535;
static const uint16 PORT_RANGE = PORT_MAX - PORT_MIN;
// mDNS text attributes
static constexpr std::string_view KEY_ENDPOINT_INFO = "n";
// mDNS information for advertising and discovery
static constexpr std::wstring_view MDNS_HOST_NAME = L"Windows.local";
static constexpr std::string_view MDNS_INSTANCE_NAME_FORMAT =
"%s.%s._tcp.local";
static constexpr std::string_view MDNS_INSTANCE_NAME_FORMAT = "%s.%slocal";
static constexpr std::string_view MDNS_DEVICE_SELECTOR_FORMAT =
"System.Devices.AepService.ProtocolId:=\"{4526e8c1-8aac-4153-9b16-"
"55e86ada0e54}\" "
"AND System.Devices.Dnssd.ServiceName:=\"%s._tcp\" AND "
"System.Devices.Dnssd.Domain:=\"local\"";
static const int SERVICE_ID_HASH_LENGTH = 6;
static constexpr std::string_view SERVICE_ID_FORMAT =
"_%02X%02X%02X%02X%02X%02X";
//
// Private methods
//
// Nsd status
static const int MEDIUM_STATUS_IDLE = 0;
static const int MEDIUM_STATUS_ACCEPTING = (1 << 0);
static const int MEDIUM_STATUS_ADVERTISING = (1 << 1);
static const int MEDIUM_STATUS_DISCOVERING = (1 << 2);
// Generates preferred listening port. If cannot bind to this port,
// NSD will assign a random port for the service.
// TODO: Windows firewall may break the solution, need to further solution to
// resolve the potential issue
uint16 GenerateSocketPort(const std::string& service_id);
// In the class, not using ENUM to describe the mDNS states, because a little
// complicate to combine all states based on accepting, advertising and
// discovery.
bool IsIdle() { return medium_status_ == 0; }
bool IsAccepting() { return (medium_status_ & MEDIUM_STATUS_ACCEPTING) != 0; }
bool IsAdvertising() {
return (medium_status_ & MEDIUM_STATUS_ADVERTISING) != 0;
}
bool IsDiscovering() {
return (medium_status_ & MEDIUM_STATUS_DISCOVERING) != 0;
}
// From mDNS device information, to build NsdServiceInfo.
// the properties are from DeviceInformation and DeviceInformationUpdate.
@@ -277,9 +289,6 @@ class WifiLanNsd {
IMapView<winrt::hstring, IInspectable> properties);
// mDNS callbacks for advertising and discovery
fire_and_forget Listener_ConnectionReceived(
StreamSocketListener listener,
StreamSocketListenerConnectionReceivedEventArgs const& args);
fire_and_forget Watcher_DeviceAdded(DeviceWatcher sender,
DeviceInformation deviceInfo);
fire_and_forget Watcher_DeviceUpdated(
@@ -289,36 +298,14 @@ class WifiLanNsd {
static void Advertising_StopCompleted(DWORD Status, PVOID pQueryContext,
PDNS_SERVICE_INSTANCE pInstance);
// Retrieves IP addresses from local machine
std::vector<std::string> GetIpAddresses();
std::string GetServiceIdHash();
// Manages remote connections
WifiLanService* GetRemoteWifiLanService(
std::string endpoint, std::unique_ptr<WifiLanService> wifi_lan_service);
void RemoveRemoteWifiLanService(std::string endpoint);
// Basic information of Nsd
location::nearby::Mutex mutex_{};
std::string service_id_{};
// TODO(200421848): NsdServiceInfo should support service type
std::string service_type_{};
WifiLanMedium* medium_ = nullptr;
WifiLanService wifi_lan_service_{};
absl::flat_hash_map<std::string, std::unique_ptr<WifiLanService>>
remote_wifi_lan_services_ ABSL_GUARDED_BY(mutex_);
// NSD Status
int nsd_status_ = NSD_STATUS_IDLE;
// Gets error message from exception pointer
std::string GetErrorMessage(std::exception_ptr eptr);
//
// Dns-sd related properties
//
// Advertising properties
winrt::event_token listener_event_token_{};
StreamSocketListener stream_socket_listener_{nullptr};
DnssdServiceInstance dnssd_service_instance_{nullptr};
DnssdRegistrationResult dnssd_regirstraion_result_{nullptr};
@@ -335,78 +322,17 @@ class WifiLanNsd {
winrt::event_token device_watcher_updated_event_token;
winrt::event_token device_watcher_removed_event_token;
// callbacks for advertising and discovery
api::WifiLanMedium::AcceptedConnectionCallback accepted_connection_callback_;
// callback for discovery
api::WifiLanMedium::DiscoveredServiceCallback discovered_service_callback_;
// IP addresses of the computer. mDNS uses them to advertise.
std::vector<std::string> ip_addresses_{};
};
// Protects to access some members
absl::Mutex mutex_;
// Container of operations that can be performed over the WifiLan medium.
class WifiLanMedium : public api::WifiLanMedium {
public:
~WifiLanMedium() override = default;
// Medium Status
int medium_status_ = MEDIUM_STATUS_IDLE;
// Starts to advertising
bool StartAdvertising(const std::string& service_id,
const NsdServiceInfo& nsd_service_info) override;
// Stops to advertising
bool StopAdvertising(const std::string& service_id) override;
// Starts to discovery
bool StartDiscovery(const std::string& service_id,
DiscoveredServiceCallback callback) override;
// Returns true once WifiLan discovery for service_id is well and truly
// stopped; after this returns, there must be no more invocations of the
// DiscoveredServiceCallback passed in to StartDiscovery() for service_id.
bool StopDiscovery(const std::string& service_id) override;
// Returns true once WifiLan socket connection requests to service_id can be
// accepted.
bool StartAcceptingConnections(const std::string& service_id,
AcceptedConnectionCallback callback) override;
// Stops to accept connections
bool StopAcceptingConnections(const std::string& service_id) override;
// Connects to a WifiLan service.
// On success, returns a new WifiLanSocket.
// On error, returns nullptr.
std::unique_ptr<api::WifiLanSocket> Connect(
api::WifiLanService& wifi_lan_service, const std::string& service_id,
CancellationFlag* cancellation_flag) override;
// Returns WiFi LAN service from local ip address and port information
api::WifiLanService* GetRemoteService(const std::string& ip_address,
int port) override;
// returns advertising service address
std::pair<std::string, int> GetCredentials(
const std::string& service_id) override;
// for internal to clean closed connection.
void CloseConnection(WifiLanSocket& socket);
private:
// Accesses NSD by service id
WifiLanNsd* GetNsd(std::string service_id, bool create = false);
bool RemoveNsd(std::string service_id);
// Gets error message from exception pointer
std::string GetErrorMessage(std::exception_ptr eptr);
// Protects the access to NSD and connections
location::nearby::Mutex mutex_{};
// Tracks of active advertising or discovery
absl::flat_hash_map<std::string, std::unique_ptr<WifiLanNsd>>
service_to_nsd_map_ ABSL_GUARDED_BY(mutex_);
// Tracks of active connetions
absl::flat_hash_set<WifiLanSocket*> wifi_lan_sockets_ ABSL_GUARDED_BY(mutex_);
// Keep the server socket listener pointer
WifiLanServerSocket* server_socket_ptr_ ABSL_GUARDED_BY(mutex_) = nullptr;
};
} // namespace windows
+354 -197
View File
@@ -35,258 +35,415 @@ namespace location {
namespace nearby {
namespace windows {
bool WifiLanMedium::StartAcceptingConnections(
const std::string& service_id, AcceptedConnectionCallback callback) {
try {
WifiLanNsd* nsd = GetNsd(service_id, true);
return nsd->StartAcceptingConnections(callback);
} catch (...) {
NEARBY_LOGS(ERROR) << "failed to start accepting connections due to "
<< GetErrorMessage(std::current_exception());
bool WifiLanMedium::StartAdvertising(const NsdServiceInfo& nsd_service_info) {
absl::MutexLock lock(&mutex_);
if (!IsAccepting()) {
NEARBY_LOGS(WARNING)
<< "cannot start advertising without accepting connetions.";
return false;
}
if (IsAdvertising()) {
NEARBY_LOGS(WARNING)
<< "cannot start advertising again when it is running.";
return false;
}
if (nsd_service_info.GetTxtRecord(KEY_ENDPOINT_INFO.data()).empty()) {
NEARBY_LOGS(ERROR) << "cannot start advertising without endpoint info.";
return false;
}
if (nsd_service_info.GetServiceName().empty()) {
NEARBY_LOGS(ERROR) << "cannot start advertising without service name.";
return false;
}
std::string instance_name = absl::StrFormat(
MDNS_INSTANCE_NAME_FORMAT.data(), nsd_service_info.GetServiceName(),
nsd_service_info.GetServiceType());
NEARBY_LOGS(INFO) << "mDNS instance name is " << instance_name;
dnssd_service_instance_ = DnssdServiceInstance{
string_to_wstring(instance_name),
nullptr, // let windows use default computer's local name
(uint16)nsd_service_info.GetPort()};
// Add TextRecords from NsdServiceInfo
auto text_attributes = dnssd_service_instance_.TextAttributes();
auto text_records = nsd_service_info.GetTxtRecords();
auto it = text_records.begin();
while (it != text_records.end()) {
text_attributes.Insert(string_to_wstring(it->first),
string_to_wstring(it->second));
it++;
}
dnssd_regirstraion_result_ = dnssd_service_instance_
.RegisterStreamSocketListenerAsync(
server_socket_ptr_->GetSocketListener())
.get();
if (dnssd_regirstraion_result_.HasInstanceNameChanged()) {
NEARBY_LOGS(WARNING) << "advertising instance name was changed due to have "
"same name instance was running.";
// stop the service and return false
StopAdvertising(nsd_service_info);
return false;
}
if (dnssd_regirstraion_result_.Status() == DnssdRegistrationStatus::Success) {
NEARBY_LOGS(INFO) << "started to advertising.";
medium_status_ |= MEDIUM_STATUS_ADVERTISING;
return true;
}
// Clean up
NEARBY_LOGS(ERROR)
<< "failed to start advertising due to registration failure.";
dnssd_service_instance_ = nullptr;
dnssd_regirstraion_result_ = nullptr;
return false;
}
// Win32 call only can use globel function or static method in class
void WifiLanMedium::Advertising_StopCompleted(DWORD Status, PVOID pQueryContext,
PDNS_SERVICE_INSTANCE pInstance) {
NEARBY_LOGS(INFO) << "unregister with status=" << Status;
try {
WifiLanMedium* medium = static_cast<WifiLanMedium*>(pQueryContext);
medium->NotifyDnsServiceUnregistered(Status);
} catch (...) {
NEARBY_LOGS(ERROR) << "failed to notify the stop of DNS service instance."
<< Status;
}
}
bool WifiLanMedium::StopAcceptingConnections(const std::string& service_id) {
try {
WifiLanNsd* nsd = GetNsd(service_id);
if (nsd == nullptr) {
NEARBY_LOGS(WARNING) << "no running accepting connections.";
return false;
}
if (nsd->StopAcceptingConnections()) {
if (nsd->IsIdle()) {
this->RemoveNsd(service_id);
}
return true;
}
NEARBY_LOGS(ERROR) << "failed to stop accepting connections.";
return false;
} catch (...) {
NEARBY_LOGS(ERROR) << "failed to stop accepting connections due to "
<< GetErrorMessage(std::current_exception());
return false;
void WifiLanMedium::NotifyDnsServiceUnregistered(DWORD status) {
if (dns_service_stop_latch_.get() != nullptr) {
dns_service_stop_status_ = status;
dns_service_stop_latch_.get()->CountDown();
}
}
bool WifiLanMedium::StartAdvertising(const std::string& service_id,
const NsdServiceInfo& nsd_service_info) {
try {
WifiLanNsd* nsd = GetNsd(service_id);
if (nsd == nullptr) {
NEARBY_LOGS(WARNING)
<< "cannot start advertising without accepting connections.";
return false;
}
return nsd->StartAdvertising(nsd_service_info);
} catch (...) {
NEARBY_LOGS(ERROR) << "failed to start advertising due to "
<< GetErrorMessage(std::current_exception());
bool WifiLanMedium::StopAdvertising(const NsdServiceInfo& nsd_service_info) {
// Need to use Win32 API to deregister the Dnssd instance
if (!IsAdvertising()) {
NEARBY_LOGS(WARNING)
<< "Cannot stop advertising because no advertising is running.";
return false;
}
}
bool WifiLanMedium::StopAdvertising(const std::string& service_id) {
try {
WifiLanNsd* nsd = GetNsd(service_id);
if (nsd == nullptr) {
NEARBY_LOGS(WARNING)
<< "cannot stop advertising without accepting connections.";
return false;
}
if (nsd->StopAdvertising()) {
if (nsd->IsIdle()) {
this->RemoveNsd(service_id);
}
return true;
}
// Init DNS service instance
std::string instance_name = absl::StrFormat(
MDNS_INSTANCE_NAME_FORMAT.data(), nsd_service_info.GetServiceName(),
nsd_service_info.GetServiceType());
int port = nsd_service_info.GetPort();
dns_service_instance_name_ =
std::make_unique<std::wstring>(string_to_wstring(instance_name));
NEARBY_LOGS(ERROR) << "failed to stop advertising.";
return false;
} catch (...) {
NEARBY_LOGS(ERROR) << "failed to stop advertising due to "
<< GetErrorMessage(std::current_exception());
dns_service_instance_.pszInstanceName =
(LPWSTR)dns_service_instance_name_->c_str();
dns_service_instance_.pszHostName = (LPWSTR)MDNS_HOST_NAME.data();
dns_service_instance_.wPort = port;
// Init DNS service register request
dns_service_register_request_.Version = DNS_QUERY_REQUEST_VERSION1;
dns_service_register_request_.InterfaceIndex =
0; // all interfaces will be considered
dns_service_register_request_.unicastEnabled = false;
dns_service_register_request_.hCredentials = NULL;
dns_service_register_request_.pServiceInstance = &dns_service_instance_;
dns_service_register_request_.pQueryContext = this; // callback use it
dns_service_register_request_.pRegisterCompletionCallback =
WifiLanMedium::Advertising_StopCompleted;
dns_service_stop_latch_ = std::make_unique<CountDownLatch>(1);
DWORD status = DnsServiceDeRegister(&dns_service_register_request_, nullptr);
if (status != DNS_REQUEST_PENDING) {
NEARBY_LOGS(ERROR) << "failed to stop mDNS advertising for service type ="
<< nsd_service_info.GetServiceType();
return false;
}
// Wait for stop finish
dns_service_stop_latch_.get()->Await();
dns_service_stop_latch_ = nullptr;
if (dns_service_stop_status_ != 0) {
NEARBY_LOGS(INFO) << "failed to stop mDNS advertising for service type ="
<< nsd_service_info.GetServiceType();
return false;
}
NEARBY_LOGS(INFO) << "succeeded to stop mDNS advertising for service type ="
<< nsd_service_info.GetServiceType();
medium_status_ &= (~MEDIUM_STATUS_ADVERTISING);
return true;
}
// Returns true once the WifiLan discovery has been initiated.
bool WifiLanMedium::StartDiscovery(const std::string& service_id,
bool WifiLanMedium::StartDiscovery(const std::string& service_type,
DiscoveredServiceCallback callback) {
try {
WifiLanNsd* nsd = GetNsd(service_id, true);
return nsd->StartDiscovery(callback);
} catch (...) {
NEARBY_LOGS(ERROR) << "failed to start discovery due to "
<< GetErrorMessage(std::current_exception());
if (IsDiscovering()) {
NEARBY_LOGS(WARNING) << "discovery already running for service type ="
<< service_type;
return false;
}
std::string selector =
absl::StrFormat(MDNS_DEVICE_SELECTOR_FORMAT.data(), service_type);
std::vector<winrt::hstring> requestedProperties{
L"System.Devices.IpAddress",
L"System.Devices.Dnssd.HostName",
L"System.Devices.Dnssd.InstanceName",
L"System.Devices.Dnssd.PortNumber",
L"System.Devices.Dnssd.ServiceName",
L"System.Devices.Dnssd.TextAttributes"};
device_watcher_ = DeviceInformation::CreateWatcher(
string_to_wstring(selector), requestedProperties,
DeviceInformationKind::AssociationEndpointService);
device_watcher_added_event_token =
device_watcher_.Added({this, &WifiLanMedium::Watcher_DeviceAdded});
device_watcher_updated_event_token =
device_watcher_.Updated({this, &WifiLanMedium::Watcher_DeviceUpdated});
device_watcher_removed_event_token =
device_watcher_.Removed({this, &WifiLanMedium::Watcher_DeviceRemoved});
device_watcher_.Start();
discovered_service_callback_ = std::move(callback);
medium_status_ |= MEDIUM_STATUS_DISCOVERING;
NEARBY_LOGS(INFO) << "started to discovery.";
return true;
}
// Returns true once WifiLan discovery for service_id is well and truly
// stopped; after this returns, there must be no more invocations of the
// DiscoveredServiceCallback passed in to StartDiscovery() for service_id.
bool WifiLanMedium::StopDiscovery(const std::string& service_id) {
try {
WifiLanNsd* nsd = GetNsd(service_id);
if (nsd == nullptr) {
NEARBY_LOGS(WARNING) << "no running discovery to stop.";
return false;
}
if (nsd->StopDiscovery()) {
if (nsd->IsIdle()) {
this->RemoveNsd(service_id);
}
return true;
}
NEARBY_LOGS(WARNING) << "failed to stop discovery.";
return false;
} catch (...) {
NEARBY_LOGS(ERROR) << "failed to stop discovery due to "
<< GetErrorMessage(std::current_exception());
bool WifiLanMedium::StopDiscovery(const std::string& service_type) {
if (!IsDiscovering()) {
NEARBY_LOGS(WARNING) << "no discovering service to stop.";
return false;
}
device_watcher_.Stop();
device_watcher_.Added(device_watcher_added_event_token);
device_watcher_.Updated(device_watcher_updated_event_token);
device_watcher_.Removed(device_watcher_removed_event_token);
medium_status_ &= (~MEDIUM_STATUS_DISCOVERING);
device_watcher_ = nullptr;
return true;
}
// Connects to a WifiLan service.
// On success, returns a new WifiLanSocket.
// On error, returns nullptr.
std::unique_ptr<api::WifiLanSocket> WifiLanMedium::Connect(
api::WifiLanService& wifi_lan_service, const std::string& service_id,
std::unique_ptr<api::WifiLanSocket> WifiLanMedium::ConnectToService(
const NsdServiceInfo& remote_service_info,
CancellationFlag* cancellation_flag) {
try {
std::string ip_address = wifi_lan_service.GetServiceInfo().GetIPAddress();
int port = wifi_lan_service.GetServiceInfo().GetPort();
if (ip_address.empty() || port == 0) {
NEARBY_LOGS(ERROR) << "no valid service address and port to connect.";
NEARBY_LOGS(ERROR)
<< "connect to service by NSD service info. service type is "
<< remote_service_info.GetServiceType();
return ConnectToService(remote_service_info.GetIPAddress(),
remote_service_info.GetPort(), cancellation_flag);
}
std::unique_ptr<api::WifiLanSocket> WifiLanMedium::ConnectToService(
const std::string& ip_address, int port,
CancellationFlag* cancellation_flag) {
if (ip_address.empty() || port == 0) {
NEARBY_LOGS(ERROR) << "no valid service address and port to connect.";
return nullptr;
}
HostName host_name{string_to_wstring(ip_address)};
winrt::hstring service_name{winrt::to_hstring(port)};
StreamSocket socket{};
// setup cancel listener
if (cancellation_flag != nullptr) {
if (cancellation_flag->Cancelled()) {
NEARBY_LOGS(INFO) << "connect has been cancelled to service "
<< ip_address << ":" << port;
return nullptr;
}
HostName host_name{string_to_wstring(ip_address)};
winrt::hstring service_name{winrt::to_hstring(port)};
StreamSocket socket{};
// setup cancel listener
if (cancellation_flag != nullptr) {
if (cancellation_flag->Cancelled()) {
NEARBY_LOGS(INFO) << "connect has been cancelled: "
"service_id="
<< service_id;
return nullptr;
}
location::nearby::CancellationFlagListener cancellationFlagListener(
cancellation_flag, [socket]() { socket.CancelIOAsync().get(); });
}
// connection to the service
try {
socket.ConnectAsync(host_name, service_name).get();
// connected need to keep connection
std::unique_ptr<WifiLanSocket> wifi_lan_socket =
std::make_unique<WifiLanSocket>(&wifi_lan_service, socket);
wifi_lan_socket->SetServiceId(service_id);
wifi_lan_socket->SetMedium(this);
{
MutexLock lock(&mutex_);
wifi_lan_sockets_.insert(wifi_lan_socket.get());
}
NEARBY_LOGS(INFO) << "connected to remote Wifi LAN service";
return wifi_lan_socket;
} catch (...) {
NEARBY_LOGS(ERROR) << "failed to connect remote service.";
}
return nullptr;
} catch (...) {
NEARBY_LOGS(ERROR) << "failed to connect due to "
<< GetErrorMessage(std::current_exception());
return nullptr;
location::nearby::CancellationFlagListener cancellationFlagListener(
cancellation_flag, [socket]() { socket.CancelIOAsync().get(); });
}
}
api::WifiLanService* WifiLanMedium::GetRemoteService(
const std::string& ip_address, int port) {
// connection to the service
try {
MutexLock lock(&mutex_);
socket.ConnectAsync(host_name, service_name).get();
// connected need to keep connection
for (WifiLanSocket* socket : wifi_lan_sockets_) {
if (socket->GetLocalAddress() == ip_address &&
socket->GetLocalPort() == port) {
return socket->GetRemoteWifiLanService();
}
}
std::unique_ptr<WifiLanSocket> wifi_lan_socket =
std::make_unique<WifiLanSocket>(socket);
return nullptr;
NEARBY_LOGS(INFO) << "connected to remote service " << ip_address << ":"
<< port;
return wifi_lan_socket;
} catch (...) {
NEARBY_LOGS(ERROR) << "failed to get remove service due to "
<< GetErrorMessage(std::current_exception());
NEARBY_LOGS(ERROR) << "failed to connect remote service " << ip_address
<< ":" << port;
}
return nullptr;
}
std::unique_ptr<api::WifiLanServerSocket> WifiLanMedium::ListenForService(
int port) {
absl::MutexLock lock(&mutex_);
// check current status
if (IsAccepting()) {
NEARBY_LOGS(WARNING) << "accepting connections already started on port "
<< server_socket_ptr_->GetPort();
return nullptr;
}
std::unique_ptr<WifiLanServerSocket> server_socket =
std::make_unique<WifiLanServerSocket>(port);
server_socket_ptr_ = server_socket.get();
server_socket->SetCloseNotifier([this]() {
absl::MutexLock lock(&mutex_);
NEARBY_LOGS(INFO) << "server socket was closed on port "
<< server_socket_ptr_->GetPort();
medium_status_ &= (~MEDIUM_STATUS_ACCEPTING);
server_socket_ptr_ = nullptr;
});
if (server_socket->listen()) {
medium_status_ |= MEDIUM_STATUS_ACCEPTING;
NEARBY_LOGS(INFO) << "started to listen serive on port " << port;
return server_socket;
}
NEARBY_LOGS(ERROR) << "Failed to listen service on port " << port;
return nullptr;
}
std::pair<std::string, int> WifiLanMedium::GetCredentials(
const std::string& service_id) {
try {
WifiLanNsd* nsd = GetNsd(service_id, true);
if (nsd == nullptr) {
// no nsd service
NEARBY_LOGS(WARNING) << "no service for service id " << service_id;
return std::pair<std::string, int>{"", 0};
NsdServiceInfo WifiLanMedium::GetNsdServiceInformation(
IMapView<winrt::hstring, IInspectable> properties) {
NsdServiceInfo nsd_service_info{};
// Service name information
IInspectable inspectable =
properties.TryLookup(L"System.Devices.Dnssd.InstanceName");
if (inspectable == nullptr) {
NEARBY_LOGS(WARNING)
<< "no service name information in device information.";
return nsd_service_info;
}
nsd_service_info.SetServiceName(InspectableReader::ReadString(inspectable));
// IP Address information
inspectable = properties.TryLookup(L"System.Devices.IPAddress");
if (inspectable == nullptr) {
NEARBY_LOGS(WARNING) << "no IP address information in device information.";
return nsd_service_info;
}
auto ipaddresses = InspectableReader::ReadStringArray(inspectable);
if (ipaddresses.size() == 0) {
NEARBY_LOGS(WARNING) << "no IP address information in device information.";
return nsd_service_info;
}
std::string ip_address = ipaddresses[0];
// read IP port
inspectable = properties.TryLookup(L"System.Devices.Dnssd.PortNumber");
if (inspectable == nullptr) {
NEARBY_LOGS(WARNING) << "no IP port information in device information.";
return nsd_service_info;
}
int port = InspectableReader::ReadUint16(inspectable);
nsd_service_info.SetIPAddress(ip_address);
nsd_service_info.SetPort(port);
// read text record
inspectable = properties.TryLookup(L"System.Devices.Dnssd.TextAttributes");
if (inspectable == nullptr) {
NEARBY_LOGS(WARNING)
<< "no text attributes information in device information.";
return nsd_service_info;
}
auto text_attributes = InspectableReader::ReadStringArray(inspectable);
for (auto text_attribute : text_attributes) {
// text attribute in format key=value
int pos = text_attribute.find("=");
if (pos <= 0 || pos == text_attribute.size() - 1) {
NEARBY_LOGS(WARNING) << "found invalid text attribute " << text_attribute;
continue;
}
return nsd->GetCredentials();
} catch (...) {
NEARBY_LOGS(ERROR) << "failed to get service address due to "
<< GetErrorMessage(std::current_exception());
return {"", 0};
std::string key = text_attribute.substr(0, pos);
std::string value = text_attribute.substr(pos + 1);
nsd_service_info.SetTxtRecord(key, value);
}
return nsd_service_info;
}
void WifiLanMedium::CloseConnection(WifiLanSocket& socket) {
MutexLock lock(&mutex_);
if (wifi_lan_sockets_.contains(&socket)) {
wifi_lan_sockets_.erase(&socket);
fire_and_forget WifiLanMedium::Watcher_DeviceAdded(
DeviceWatcher sender, DeviceInformation deviceInfo) {
// need to read IP address and port information from deviceInfo
NsdServiceInfo nsd_service_info =
GetNsdServiceInformation(deviceInfo.Properties());
NEARBY_LOGS(INFO) << "device added for service name "
<< nsd_service_info.GetServiceName();
std::string endpoint =
nsd_service_info.GetTxtRecord(KEY_ENDPOINT_INFO.data());
if (endpoint.empty()) {
return fire_and_forget{};
}
discovered_service_callback_.service_discovered_cb(nsd_service_info);
return fire_and_forget();
}
fire_and_forget WifiLanMedium::Watcher_DeviceUpdated(
DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) {
// TODO(b/200421481): discovery servcie callback needs to support device
// update.
NsdServiceInfo nsd_service_info =
GetNsdServiceInformation(deviceInfoUpdate.Properties());
NEARBY_LOGS(INFO) << "device updated for service name "
<< nsd_service_info.GetServiceName();
WifiLanNsd* WifiLanMedium::GetNsd(std::string service_id, bool create) {
MutexLock lock(&mutex_);
if (!service_to_nsd_map_.contains(service_id)) {
if (create) {
// if no the service id, create a new one
std::unique_ptr<WifiLanNsd> nsd =
std::make_unique<WifiLanNsd>(this, service_id);
service_to_nsd_map_[service_id] = std::move(nsd);
}
}
return service_to_nsd_map_[service_id].get();
return fire_and_forget();
}
fire_and_forget WifiLanMedium::Watcher_DeviceRemoved(
DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) {
// need to read IP address and port information from deviceInfo
NsdServiceInfo nsd_service_info =
GetNsdServiceInformation(deviceInfoUpdate.Properties());
bool WifiLanMedium::RemoveNsd(std::string service_id) {
MutexLock lock(&mutex_);
NEARBY_LOGS(INFO) << "device removed for service name "
<< nsd_service_info.GetServiceName();
if (!service_to_nsd_map_.contains(service_id)) {
return true;
}
auto nsd = service_to_nsd_map_.find(service_id);
if (nsd == service_to_nsd_map_.end() || nsd->second == nullptr ||
!nsd->second->IsIdle()) {
return false;
std::string endpoint =
nsd_service_info.GetTxtRecord(KEY_ENDPOINT_INFO.data());
if (endpoint.empty()) {
return fire_and_forget{};
}
service_to_nsd_map_.erase(nsd);
return true;
discovered_service_callback_.service_lost_cb(nsd_service_info);
return fire_and_forget();
}
std::string WifiLanMedium::GetErrorMessage(std::exception_ptr eptr) {
-511
View File
@@ -1,511 +0,0 @@
// Copyright 2021 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.
// Absl headers
#include "absl/strings/str_format.h"
// Nearby connections headers
#include "platform/impl/windows/utils.h"
#include "platform/impl/windows/wifi_lan.h"
#include "platform/public/logging.h"
#include "platform/public/mutex_lock.h"
// WinRT headers
#include "platform/impl/windows/generated/winrt/Windows.Foundation.Collections.h"
namespace location {
namespace nearby {
namespace windows {
WifiLanNsd::WifiLanNsd(WifiLanMedium* medium, const std::string service_id) {
medium_ = medium;
service_id_ = service_id;
service_type_ = GetServiceIdHash();
}
bool WifiLanNsd::StartAcceptingConnections(
api::WifiLanMedium::AcceptedConnectionCallback callback) {
// TODO: check windows version to decide whether support mDNS service
// Check IP address
ip_addresses_ = GetIpAddresses();
if (ip_addresses_.empty()) {
NEARBY_LOGS(WARNING) << "failed to start accepting connection without IP "
"addresses configured on computer.";
return false;
}
// check current status
if (IsAccepting()) {
NEARBY_LOGS(WARNING) << "accepting connections already started. service id="
<< service_id_;
return false;
}
int port = GenerateSocketPort(service_id_);
// Save connection callback
accepted_connection_callback_ = std::move(callback);
stream_socket_listener_ = StreamSocketListener();
// Setup callback
listener_event_token_ = stream_socket_listener_.ConnectionReceived(
{this, &WifiLanNsd::Listener_ConnectionReceived});
try {
stream_socket_listener_.BindServiceNameAsync(winrt::to_hstring(port)).get();
nsd_status_ |= NSD_STATUS_ACCEPTING;
return true;
} catch (...) {
// Cannot bind to the preferred port, will let system to assign port.
NEARBY_LOGS(WARNING) << "cannot accept connection on preferred port.";
}
try {
stream_socket_listener_.BindServiceNameAsync({}).get();
// need to save the port information
port = std::stoi(stream_socket_listener_.Information().LocalPort().c_str());
nsd_status_ |= NSD_STATUS_ACCEPTING;
return true;
} catch (...) {
// Cannot bind to the preferred port, will let system to assign port.
NEARBY_LOGS(ERROR) << "cannot bind to any port.";
}
// clean up
stream_socket_listener_.ConnectionReceived(listener_event_token_);
stream_socket_listener_ = nullptr;
return false;
}
bool WifiLanNsd::StopAcceptingConnections() {
if (!IsAccepting()) {
NEARBY_LOGS(WARNING) << "no accepting connections to stop.";
return false;
}
stream_socket_listener_.ConnectionReceived(listener_event_token_);
stream_socket_listener_ = nullptr;
nsd_status_ &= (~NSD_STATUS_ACCEPTING);
return true;
}
bool WifiLanNsd::StartAdvertising(const NsdServiceInfo& nsd_service_info) {
if (!IsAccepting()) {
NEARBY_LOGS(WARNING)
<< "cannot start advertising without accepting connetions.";
return false;
}
if (IsAdvertising()) {
NEARBY_LOGS(WARNING)
<< "cannot start advertising again when it is running.";
return false;
}
if (nsd_service_info.GetTxtRecord(KEY_ENDPOINT_INFO.data()).empty()) {
NEARBY_LOGS(ERROR) << "cannot start advertising without endpoint info.";
return false;
}
if (nsd_service_info.GetServiceName().empty()) {
NEARBY_LOGS(ERROR) << "cannot start advertising without service name.";
return false;
}
// Setup WiFi LAN service information
int port =
std::stoi(stream_socket_listener_.Information().LocalPort().c_str());
NsdServiceInfo new_nsd_servcie_info = nsd_service_info;
// TODO: need feature enhancement to support multiple network interfaces
new_nsd_servcie_info.SetIPAddress(ip_addresses_[0]);
new_nsd_servcie_info.SetPort(port);
wifi_lan_service_ = WifiLanService(new_nsd_servcie_info);
wifi_lan_service_.SetMedium(medium_);
std::string instance_name = absl::StrFormat(
MDNS_INSTANCE_NAME_FORMAT.data(),
wifi_lan_service_.GetServiceInfo().GetServiceName(), service_type_);
NEARBY_LOGS(INFO) << "mDNS instance name is " << instance_name;
dnssd_service_instance_ = DnssdServiceInstance{
string_to_wstring(instance_name),
nullptr, // let windows use default computer's local name
(uint16)port};
// Add TextRecords from NsdServiceInfo
auto text_attributes = dnssd_service_instance_.TextAttributes();
// TODO(b/200298824): NsdServiceInfo should have function to return all text
// records will be more generic
text_attributes.Insert(string_to_wstring(KEY_ENDPOINT_INFO.data()),
string_to_wstring(nsd_service_info.GetTxtRecord(
KEY_ENDPOINT_INFO.data())));
dnssd_regirstraion_result_ =
dnssd_service_instance_
.RegisterStreamSocketListenerAsync(stream_socket_listener_)
.get();
if (dnssd_regirstraion_result_.HasInstanceNameChanged()) {
NEARBY_LOGS(WARNING) << "advertising instance name was changed due to have "
"same name instance was running.";
// stop the service and return false
StopAdvertising();
return false;
}
if (dnssd_regirstraion_result_.Status() == DnssdRegistrationStatus::Success) {
NEARBY_LOGS(INFO) << "started to advertising.";
nsd_status_ |= NSD_STATUS_ADVERTISING;
return true;
}
// Clean up
NEARBY_LOGS(ERROR)
<< "failed to start advertising due to registration failure.";
dnssd_service_instance_ = nullptr;
dnssd_regirstraion_result_ = nullptr;
return false;
}
// Win32 call only can use globel function or static method in class
void WifiLanNsd::Advertising_StopCompleted(DWORD Status, PVOID pQueryContext,
PDNS_SERVICE_INSTANCE pInstance) {
NEARBY_LOGS(INFO) << "unregister with status=" << Status;
try {
WifiLanNsd* nsd = static_cast<WifiLanNsd*>(pQueryContext);
nsd->NotifyDnsServiceUnregistered(Status);
} catch (...) {
NEARBY_LOGS(ERROR) << "failed to notify the stop of DNS service instance."
<< Status;
}
}
void WifiLanNsd::NotifyDnsServiceUnregistered(DWORD status) {
if (dns_service_stop_latch_.get() != nullptr) {
dns_service_stop_status_ = status;
dns_service_stop_latch_.get()->CountDown();
}
}
bool WifiLanNsd::StopAdvertising() {
// Need to use Win32 API to deregister the Dnssd instance
if (!IsAdvertising()) {
NEARBY_LOGS(WARNING)
<< "Cannot stop advertising because no advertising is running.";
return false;
}
// Init DNS service instance
std::string instance_name = absl::StrFormat(
MDNS_INSTANCE_NAME_FORMAT.data(),
wifi_lan_service_.GetServiceInfo().GetServiceName(), service_type_);
int port = wifi_lan_service_.GetServiceInfo().GetPort();
dns_service_instance_name_ =
std::make_unique<std::wstring>(string_to_wstring(instance_name));
dns_service_instance_.pszInstanceName =
(LPWSTR)dns_service_instance_name_->c_str();
dns_service_instance_.pszHostName = (LPWSTR)MDNS_HOST_NAME.data();
dns_service_instance_.wPort = port;
// Init DNS service register request
dns_service_register_request_.Version = DNS_QUERY_REQUEST_VERSION1;
dns_service_register_request_.InterfaceIndex =
0; // all interfaces will be considered
dns_service_register_request_.unicastEnabled = false;
dns_service_register_request_.hCredentials = NULL;
dns_service_register_request_.pServiceInstance = &dns_service_instance_;
dns_service_register_request_.pQueryContext = this; // callback use it
dns_service_register_request_.pRegisterCompletionCallback =
WifiLanNsd::Advertising_StopCompleted;
dns_service_stop_latch_ = std::make_unique<CountDownLatch>(1);
DWORD status = DnsServiceDeRegister(&dns_service_register_request_, nullptr);
if (status != DNS_REQUEST_PENDING) {
NEARBY_LOGS(ERROR) << "failed to stop mDNS advertising for service id ="
<< service_id_;
return false;
}
// Wait for stop finish
dns_service_stop_latch_.get()->Await();
dns_service_stop_latch_ = nullptr;
if (dns_service_stop_status_ != 0) {
NEARBY_LOGS(INFO) << "failed to stop mDNS advertising for service id ="
<< service_id_;
return false;
}
NEARBY_LOGS(INFO) << "succeeded to stop mDNS advertising for service id ="
<< service_id_;
nsd_status_ &= (~NSD_STATUS_ADVERTISING);
return true;
}
bool WifiLanNsd::StartDiscovery(
api::WifiLanMedium::DiscoveredServiceCallback callback) {
if (IsDiscovering()) {
NEARBY_LOGS(WARNING) << "discovery already running for service id ="
<< service_id_;
return false;
}
std::string selector =
absl::StrFormat(MDNS_DEVICE_SELECTOR_FORMAT.data(), service_type_);
std::vector<winrt::hstring> requestedProperties{
L"System.Devices.IpAddress",
L"System.Devices.Dnssd.HostName",
L"System.Devices.Dnssd.InstanceName",
L"System.Devices.Dnssd.PortNumber",
L"System.Devices.Dnssd.ServiceName",
L"System.Devices.Dnssd.TextAttributes"};
device_watcher_ = DeviceInformation::CreateWatcher(
string_to_wstring(selector), requestedProperties,
DeviceInformationKind::AssociationEndpointService);
device_watcher_added_event_token =
device_watcher_.Added({this, &WifiLanNsd::Watcher_DeviceAdded});
device_watcher_updated_event_token =
device_watcher_.Updated({this, &WifiLanNsd::Watcher_DeviceUpdated});
device_watcher_removed_event_token =
device_watcher_.Removed({this, &WifiLanNsd::Watcher_DeviceRemoved});
device_watcher_.Start();
discovered_service_callback_ = std::move(callback);
nsd_status_ |= NSD_STATUS_DISCOVERING;
NEARBY_LOGS(INFO) << "started to discovery.";
return true;
}
bool WifiLanNsd::StopDiscovery() {
if (!IsDiscovering()) {
NEARBY_LOGS(WARNING) << "no discovering service to stop.";
return false;
}
// TODO: handle exception
device_watcher_.Stop();
device_watcher_.Added(device_watcher_added_event_token);
device_watcher_.Updated(device_watcher_updated_event_token);
device_watcher_.Removed(device_watcher_removed_event_token);
nsd_status_ &= (~NSD_STATUS_DISCOVERING);
device_watcher_ = nullptr;
return true;
}
std::pair<std::string, int> WifiLanNsd::GetCredentials() {
if (!IsAdvertising()) {
// no advertising is running
NEARBY_LOGS(WARNING) << "no advertising for service id " << service_id_;
return std::pair<std::string, int>{"", 0};
}
return std::make_pair(wifi_lan_service_.GetServiceInfo().GetIPAddress(),
wifi_lan_service_.GetServiceInfo().GetPort());
}
std::vector<std::string> WifiLanNsd::GetIpAddresses() {
std::vector<std::string> result{};
auto host_names = NetworkInformation::GetHostNames();
for (auto host_name : host_names) {
if (host_name.IPInformation() != nullptr &&
host_name.IPInformation().NetworkAdapter() != nullptr) {
result.push_back(wstring_to_string(host_name.ToString().c_str()));
}
}
return result;
}
fire_and_forget WifiLanNsd::Listener_ConnectionReceived(
StreamSocketListener listener,
StreamSocketListenerConnectionReceivedEventArgs const& args) {
NEARBY_LOGS(INFO) << "got connection message for service " << service_id_;
StreamSocket stream_socket = args.Socket();
// Send to callback
std::unique_ptr<windows::WifiLanSocket> socket =
std::make_unique<windows::WifiLanSocket>(&wifi_lan_service_,
stream_socket);
socket->SetMedium(medium_);
socket->SetServiceId(service_id_);
accepted_connection_callback_.accepted_cb(*socket.get(), service_id_);
return fire_and_forget{};
}
NsdServiceInfo WifiLanNsd::GetNsdServiceInformation(
IMapView<winrt::hstring, IInspectable> properties) {
NsdServiceInfo nsd_service_info{};
// Service name information
IInspectable inspectable =
properties.TryLookup(L"System.Devices.Dnssd.InstanceName");
if (inspectable == nullptr) {
NEARBY_LOGS(WARNING)
<< "no service name information in device information.";
return nsd_service_info;
}
nsd_service_info.SetServiceName(
InspectableReader::ReadString(inspectable));
// IP Address information
inspectable = properties.TryLookup(L"System.Devices.IPAddress");
if (inspectable == nullptr) {
NEARBY_LOGS(WARNING) << "no IP address information in device information.";
return nsd_service_info;
}
auto ipaddresses = InspectableReader::ReadStringArray(inspectable);
if (ipaddresses.size() == 0) {
NEARBY_LOGS(WARNING) << "no IP address information in device information.";
return nsd_service_info;
}
std::string ip_address = ipaddresses[0];
// read IP port
inspectable = properties.TryLookup(L"System.Devices.Dnssd.PortNumber");
if (inspectable == nullptr) {
NEARBY_LOGS(WARNING) << "no IP port information in device information.";
return nsd_service_info;
}
int port = InspectableReader::ReadUint16(inspectable);
nsd_service_info.SetIPAddress(ip_address);
nsd_service_info.SetPort(port);
// read text record
inspectable = properties.TryLookup(L"System.Devices.Dnssd.TextAttributes");
if (inspectable == nullptr) {
NEARBY_LOGS(WARNING)
<< "no text attributes information in device information.";
return nsd_service_info;
}
auto text_attributes = InspectableReader::ReadStringArray(inspectable);
for (auto text_attribute : text_attributes) {
// text attribute in format key=value
int pos = text_attribute.find("=");
if (pos <= 0 || pos == text_attribute.size() - 1) {
NEARBY_LOGS(WARNING) << "found invalid text attribute " << text_attribute;
continue;
}
std::string key = text_attribute.substr(0, pos);
std::string value = text_attribute.substr(pos + 1);
nsd_service_info.SetTxtRecord(key, value);
}
return nsd_service_info;
}
fire_and_forget WifiLanNsd::Watcher_DeviceAdded(DeviceWatcher sender,
DeviceInformation deviceInfo) {
NEARBY_LOGS(INFO) << "device added for service " << service_id_;
// need to read IP address and port information from deviceInfo
NsdServiceInfo nsd_service_info =
GetNsdServiceInformation(deviceInfo.Properties());
std::string endpoint =
nsd_service_info.GetTxtRecord(KEY_ENDPOINT_INFO.data());
if (endpoint.empty()) {
return fire_and_forget{};
}
std::unique_ptr<WifiLanService> wifi_lan_service =
std::make_unique<WifiLanService>(nsd_service_info);
WifiLanService* pwifi_lan_service =
GetRemoteWifiLanService(endpoint, std::move(wifi_lan_service));
discovered_service_callback_.service_discovered_cb(*pwifi_lan_service,
service_id_);
return fire_and_forget();
}
fire_and_forget WifiLanNsd::Watcher_DeviceUpdated(
DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) {
// TODO(b/200421481): discovery servcie callback needs to support device
// update.
NEARBY_LOGS(INFO) << "device updated for service " << service_id_;
return fire_and_forget();
}
fire_and_forget WifiLanNsd::Watcher_DeviceRemoved(
DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) {
NEARBY_LOGS(INFO) << "device removed for service " << service_id_;
// need to read IP address and port information from deviceInfo
NsdServiceInfo nsd_service_info =
GetNsdServiceInformation(deviceInfoUpdate.Properties());
std::string endpoint =
nsd_service_info.GetTxtRecord(KEY_ENDPOINT_INFO.data());
if (endpoint.empty()) {
return fire_and_forget{};
}
std::unique_ptr<WifiLanService> wifi_lan_service =
std::make_unique<WifiLanService>(nsd_service_info);
WifiLanService* pwifi_lan_service =
GetRemoteWifiLanService(endpoint, std::move(wifi_lan_service));
discovered_service_callback_.service_lost_cb(*pwifi_lan_service, service_id_);
RemoveRemoteWifiLanService(endpoint);
return fire_and_forget();
}
uint16 WifiLanNsd::GenerateSocketPort(const std::string& service_id) {
ByteArray service_id_sha = Sha256(service_id, 4);
char* hash = service_id_sha.data();
int b1 = hash[0] & 0xff;
int b2 = hash[1] & 0xff;
int b3 = hash[2] & 0xff;
int b4 = hash[3] & 0xff;
int hashValue = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
return PORT_MIN + (hashValue % PORT_RANGE);
}
WifiLanService* WifiLanNsd::GetRemoteWifiLanService(
std::string endpoint, std::unique_ptr<WifiLanService> wifi_lan_service) {
MutexLock lock(&mutex_);
remote_wifi_lan_services_[endpoint] = std::move(wifi_lan_service);
return remote_wifi_lan_services_[endpoint].get();
}
void WifiLanNsd::RemoveRemoteWifiLanService(std::string endpoint) {
MutexLock lock(&mutex_);
if (remote_wifi_lan_services_.contains(endpoint)) {
remote_wifi_lan_services_.erase(endpoint);
}
}
std::string WifiLanNsd::GetServiceIdHash() {
// Get hashed service id
ByteArray service_id_sha = Sha256(service_id_, SERVICE_ID_HASH_LENGTH);
char* sha_data = service_id_sha.data();
std::string service_id_hash = absl::StrFormat(
SERVICE_ID_FORMAT.data(), sha_data[0] & 0xFF, sha_data[1] & 0xFF,
sha_data[2] & 0xFF, sha_data[3] & 0xFF, sha_data[4] & 0xFF,
sha_data[5] & 0xFF);
return service_id_hash;
}
} // namespace windows
} // namespace nearby
} // namespace location
@@ -0,0 +1,180 @@
// Copyright 2021 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 "platform/impl/windows/utils.h"
#include "platform/impl/windows/wifi_lan.h"
#include "platform/public/logging.h"
namespace location {
namespace nearby {
namespace windows {
WifiLanServerSocket::WifiLanServerSocket(int port) : port_(port) {}
WifiLanServerSocket::~WifiLanServerSocket() { Close(); }
// Returns ip address.
std::string WifiLanServerSocket::GetIPAddress() const {
if (stream_socket_listener_ == nullptr) {
return {};
}
auto host_names = NetworkInformation::GetHostNames();
for (auto host_name : host_names) {
if (host_name.IPInformation() != nullptr &&
host_name.IPInformation().NetworkAdapter() != nullptr) {
return wstring_to_string(host_name.ToString().c_str());
}
}
return {};
}
// Returns port.
int WifiLanServerSocket::GetPort() const {
if (stream_socket_listener_ == nullptr) {
return 0;
}
return std::stoi(stream_socket_listener_.Information().LocalPort().c_str());
}
// Blocks until either:
// - at least one incoming connection request is available, or
// - ServerSocket is closed.
// On success, returns connected socket, ready to exchange data.
// Returns nullptr on error.
// Once error is reported, it is permanent, and ServerSocket has to be closed.
std::unique_ptr<api::WifiLanSocket> WifiLanServerSocket::Accept() {
absl::MutexLock lock(&mutex_);
while (!closed_ && pending_sockets_.empty()) {
cond_.Wait(&mutex_);
}
if (closed_) return {};
StreamSocket wifi_lan_socket = pending_sockets_.front();
pending_sockets_.pop_front();
return std::make_unique<WifiLanSocket>(wifi_lan_socket);
}
void WifiLanServerSocket::SetCloseNotifier(std::function<void()> notifier) {
close_notifier_ = std::move(notifier);
}
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
Exception WifiLanServerSocket::Close() {
try {
absl::MutexLock lock(&mutex_);
if (closed_) {
return {Exception::kSuccess};
}
if (stream_socket_listener_ != nullptr) {
stream_socket_listener_.ConnectionReceived(listener_event_token_);
stream_socket_listener_.Close();
stream_socket_listener_ = nullptr;
if (!pending_sockets_.empty()) {
auto it = pending_sockets_.begin();
while (it != pending_sockets_.end()) {
it->Close();
}
}
cond_.SignalAll();
}
closed_ = true;
if (close_notifier_ != nullptr) {
close_notifier_();
}
return {Exception::kSuccess};
} catch (...) {
return {Exception::kIo};
}
}
bool WifiLanServerSocket::listen() {
// Check IP address
ip_addresses_ = GetIpAddresses();
if (ip_addresses_.empty()) {
NEARBY_LOGS(WARNING) << "failed to start accepting connection without IP "
"addresses configured on computer.";
return false;
}
// Save connection callback
stream_socket_listener_ = StreamSocketListener();
// Setup callback
listener_event_token_ = stream_socket_listener_.ConnectionReceived(
{this, &WifiLanServerSocket::Listener_ConnectionReceived});
try {
stream_socket_listener_.BindServiceNameAsync(winrt::to_hstring(port_))
.get();
if (port_ == 0) {
port_ =
std::stoi(stream_socket_listener_.Information().LocalPort().c_str());
}
return true;
} catch (...) {
// Cannot bind to the preferred port, will let system to assign port.
NEARBY_LOGS(WARNING) << "cannot accept connection on preferred port.";
}
try {
stream_socket_listener_.BindServiceNameAsync({}).get();
// need to save the port information
port_ =
std::stoi(stream_socket_listener_.Information().LocalPort().c_str());
return true;
} catch (...) {
// Cannot bind to the preferred port, will let system to assign port.
NEARBY_LOGS(ERROR) << "cannot bind to any port.";
}
return false;
}
fire_and_forget WifiLanServerSocket::Listener_ConnectionReceived(
StreamSocketListener listener,
StreamSocketListenerConnectionReceivedEventArgs const& args) {
absl::MutexLock lock(&mutex_);
if (closed_) {
return fire_and_forget{};
}
pending_sockets_.push_back(args.Socket());
cond_.SignalAll();
return fire_and_forget{};
}
// Retrieves IP addresses from local machine
std::vector<std::string> WifiLanServerSocket::GetIpAddresses() {
std::vector<std::string> result{};
auto host_names = NetworkInformation::GetHostNames();
for (auto host_name : host_names) {
if (host_name.IPInformation() != nullptr &&
host_name.IPInformation().NetworkAdapter() != nullptr) {
result.push_back(wstring_to_string(host_name.ToString().c_str()));
}
}
return result;
}
} // namespace windows
} // namespace nearby
} // namespace location
+1 -31
View File
@@ -19,9 +19,7 @@ namespace location {
namespace nearby {
namespace windows {
WifiLanSocket::WifiLanSocket(api::WifiLanService* wifi_lan_service,
StreamSocket socket) {
remote_wifi_lan_service_ = wifi_lan_service;
WifiLanSocket::WifiLanSocket(StreamSocket socket) {
stream_soket_ = socket;
input_stream_ = SocketInputStream(socket.InputStream());
output_stream_ = SocketOutputStream(socket.OutputStream());
@@ -45,7 +43,6 @@ Exception WifiLanSocket::Close() {
try {
if (stream_soket_ != nullptr) {
stream_soket_.Close();
medium_->CloseConnection(*this);
}
return {Exception::kSuccess};
} catch (...) {
@@ -53,33 +50,6 @@ Exception WifiLanSocket::Close() {
}
}
api::WifiLanService* WifiLanSocket::GetRemoteWifiLanService() {
return remote_wifi_lan_service_;
}
void WifiLanSocket::SetServiceId(std::string service_id) {
service_id_ = service_id;
}
void WifiLanSocket::SetMedium(WifiLanMedium* medium) { medium_ = medium; }
std::string WifiLanSocket::GetLocalAddress() {
if (stream_soket_ == nullptr) {
return {};
}
return winrt::to_string(
stream_soket_.Information().LocalAddress().ToString());
}
int WifiLanSocket::GetLocalPort() {
if (stream_soket_ == nullptr) {
return 0;
}
return std::stoi(stream_soket_.Information().LocalPort().c_str());
}
// SocketInputStream
WifiLanSocket::SocketInputStream::SocketInputStream(IInputStream input_stream) {
input_stream_ = input_stream;