WIFI Hotspot implementation (3)

Part 3: WIFI Hotspot platform code for Windows

PiperOrigin-RevId: 441826092
This commit is contained in:
hai007
2022-04-14 12:31:07 -07:00
committed by Copybara-Service
parent bc01739580
commit 93f203dc0b
10 changed files with 1059 additions and 4 deletions
+17 -1
View File
@@ -64,13 +64,22 @@ cc_library(
"thread_pool.h",
"webrtc.h",
"wifi.h",
"wifi_hotspot.h",
"wifi_lan.h",
],
visibility = ["//visibility:private"],
deps = [
"//internal/platform:base",
"//internal/platform:types",
"//internal/platform/implementation:comm",
"//internal/platform/implementation:types",
"//internal/platform/implementation/windows/generated:types",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/types:optional",
],
)
@@ -103,6 +112,9 @@ cc_library(
"system_clock.cc",
"thread_pool.cc",
"utils.cc",
"wifi_hotspot_medium.cc",
"wifi_hotspot_server_socket.cc",
"wifi_hotspot_socket.cc",
"wifi_lan_medium.cc",
"wifi_lan_server_socket.cc",
"wifi_lan_socket.cc",
@@ -117,12 +129,16 @@ cc_library(
":comm",
":crypto", # build_cleaner: keep
":types",
"//internal/platform:base",
"//internal/platform:cancellation_flag",
"//internal/platform:types",
"//internal/platform/implementation:comm",
"//internal/platform/implementation:platform",
"//internal/platform/implementation:types",
"//internal/platform/implementation/shared:count_down_latch",
"//internal/platform/implementation/shared:file",
"//internal/platform/implementation/windows/generated:types",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
],
)
@@ -19,6 +19,7 @@
#include <windows.h>
#include <xstring>
#include <string>
#include <sstream>
#include "internal/platform/implementation/shared/count_down_latch.h"
@@ -42,6 +43,7 @@
#include "internal/platform/implementation/windows/webrtc.h"
#include "internal/platform/implementation/windows/wifi.h"
#include "internal/platform/implementation/windows/wifi_lan.h"
#include "internal/platform/implementation/windows/wifi_hotspot.h"
namespace location {
namespace nearby {
@@ -234,7 +236,7 @@ std::unique_ptr<WifiLanMedium> ImplementationPlatform::CreateWifiLanMedium() {
std::unique_ptr<WifiHotspotMedium>
ImplementationPlatform::CreateWifiHotspotMedium() {
return nullptr;
return std::make_unique<windows::WifiHotspotMedium>();
}
// TODO(b/184975123): replace with real implementation.
@@ -15,7 +15,9 @@
#include "internal/platform/implementation/windows/utils.h"
// Windows headers
#include <inaddr.h>
#include <strsafe.h>
#include <winsock.h>
// Standard C/C++ headers
#include <codecvt>
@@ -43,6 +45,34 @@ std::string uint64_to_mac_address_string(uint64_t bluetoothAddress) {
return absl::AsciiStrToUpper(buffer);
}
std::string ipaddr_4bytes_to_dotdecimal_string(
absl::string_view ipaddr_4bytes) {
in_addr address;
address.S_un.S_un_b.s_b1 = ipaddr_4bytes[0];
address.S_un.S_un_b.s_b2 = ipaddr_4bytes[1];
address.S_un.S_un_b.s_b3 = ipaddr_4bytes[2];
address.S_un.S_un_b.s_b4 = ipaddr_4bytes[3];
char* ipv4_address = inet_ntoa(address);
if (ipv4_address == nullptr) {
return {};
}
return std::string(ipv4_address);
}
std::string ipaddr_dotdecimal_to_4bytes_string(std::string ipv4_s) {
in_addr address;
address.S_un.S_addr = inet_addr(ipv4_s.c_str());
char ipv4_b[5];
ipv4_b[0] = address.S_un.S_un_b.s_b1;
ipv4_b[1] = address.S_un.S_un_b.s_b2;
ipv4_b[2] = address.S_un.S_un_b.s_b3;
ipv4_b[3] = address.S_un.S_un_b.s_b4;
ipv4_b[4] = 0;
return std::string(ipv4_b, 4);
}
std::wstring string_to_wstring(std::string str) {
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.from_bytes(str);
@@ -33,6 +33,9 @@ using winrt::Windows::Foundation::IInspectable;
std::string uint64_to_mac_address_string(uint64_t bluetoothAddress);
std::string ipaddr_4bytes_to_dotdecimal_string(absl::string_view ipaddr_4bytes);
std::string ipaddr_dotdecimal_to_4bytes_string(std::string ipv4_s);
// Helpers to windows platform
std::wstring string_to_wstring(std::string str);
std::string wstring_to_string(std::wstring wstr);
@@ -14,9 +14,15 @@
#include "internal/platform/implementation/windows/utils.h"
#include <windows.h>
#include <string>
#include "gtest/gtest.h"
using location::nearby::windows::uint64_to_mac_address_string;
// using location::nearby::windows::uint64_to_mac_address_string;
namespace location {
namespace nearby {
namespace windows {
TEST(UtilsTests, MacAddressToString) {
// Arrange
@@ -29,3 +35,24 @@ TEST(UtilsTests, MacAddressToString) {
// Assert
EXPECT_EQ(result, expected);
}
constexpr absl::string_view kIpDotdecimal{"192.168.1.37"};
constexpr char kIp4Bytes[] = {192, 168, 1, 37};
TEST(UtilsTests, Ip4BytesToDotdecimal) {
std::string result =
ipaddr_4bytes_to_dotdecimal_string(absl::string_view(kIp4Bytes));
EXPECT_EQ(result, kIpDotdecimal);
}
TEST(UtilsTests, IpDotdecimalTo4Bytes) {
std::string result =
ipaddr_dotdecimal_to_4bytes_string(std::string(kIpDotdecimal));
EXPECT_EQ(result, std::string(kIp4Bytes));
}
} // namespace windows
} // namespace nearby
} // namespace location
@@ -0,0 +1,285 @@
// Copyright 2020 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_WINDOWS_WIFI_HOTSPOT_H_
#define PLATFORM_IMPL_WINDOWS_WIFI_HOTSPOT_H_
// Nearby connections headers
#include "internal/platform/implementation/wifi_hotspot.h"
// WinRT headers
#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Enumeration.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.WiFi.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.WiFiDirect.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Collections.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Connectivity.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Sockets.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Security.Credentials.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Security.Cryptography.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Storage.Streams.h"
#include "internal/platform/implementation/windows/generated/winrt/base.h"
namespace location {
namespace nearby {
namespace windows {
using ::winrt::fire_and_forget;
using ::winrt::Windows::Devices::WiFiDirect::
WiFiDirectAdvertisementListenStateDiscoverability;
using ::winrt::Windows::Devices::WiFiDirect::WiFiDirectAdvertisementPublisher;
using ::winrt::Windows::Devices::WiFiDirect::
WiFiDirectAdvertisementPublisherStatus;
using ::winrt::Windows::Devices::WiFiDirect::
WiFiDirectAdvertisementPublisherStatusChangedEventArgs;
using ::winrt::Windows::Devices::WiFiDirect::WiFiDirectConnectionListener;
using ::winrt::Windows::Devices::WiFiDirect::WiFiDirectConnectionRequest;
using ::winrt::Windows::Devices::WiFiDirect::
WiFiDirectConnectionRequestedEventArgs;
using ::winrt::Windows::Devices::WiFi::WiFiAccessStatus;
using ::winrt::Windows::Devices::WiFi::WiFiAdapter;
using ::winrt::Windows::Devices::WiFi::WiFiAvailableNetwork;
using ::winrt::Windows::Devices::WiFi::WiFiConnectionStatus;
using ::winrt::Windows::Devices::WiFi::WiFiReconnectionKind;
using ::winrt::Windows::Devices::Enumeration::DeviceInformation;
using ::winrt::Windows::Devices::Enumeration::DeviceInformationPairing;
using ::winrt::Windows::Storage::Streams::Buffer;
using ::winrt::Windows::Storage::Streams::IInputStream;
using ::winrt::Windows::Storage::Streams::InputStreamOptions;
using ::winrt::Windows::Storage::Streams::IOutputStream;
using ::winrt::Windows::Security::Credentials::PasswordCredential;
using ::winrt::Windows::Security::Cryptography::CryptographicBuffer;
using ::winrt::Windows::Networking::HostName;
using ::winrt::Windows::Networking::HostNameType;
using ::winrt::Windows::Networking::Connectivity::NetworkInformation;
using ::winrt::Windows::Networking::Sockets::StreamSocket;
using ::winrt::Windows::Networking::Sockets::StreamSocketListener;
using ::winrt::Windows::Networking::Sockets::
StreamSocketListenerConnectionReceivedEventArgs;
// WifiHotspotSocket wraps the socket functions to read and write stream.
// In WiFi HOTSPOT, A WifiHotspotSocket will be passed to
// StartAcceptingConnections's call back when StreamSocketListener got connect.
// When call API to connect to remote WiFi Hotspot service, also will return a
// WifiHotspotSocket to caller.
class WifiHotspotSocket : public api::WifiHotspotSocket {
public:
explicit WifiHotspotSocket(StreamSocket socket);
WifiHotspotSocket(const WifiHotspotSocket&) = default;
WifiHotspotSocket(WifiHotspotSocket&&) = default;
~WifiHotspotSocket() override;
WifiHotspotSocket& operator=(const WifiHotspotSocket&) = default;
WifiHotspotSocket& operator=(WifiHotspotSocket&&) = default;
// Returns the InputStream of the WifiHotspotSocket.
// On error, returned stream will report Exception::kIo on any operation.
//
// The returned object is not owned by the caller, and can be invalidated once
// the WifiHotspotSocket object is destroyed.
InputStream& GetInputStream() override;
// Returns the OutputStream of the WifiHotspotSocket.
// On error, returned stream will report Exception::kIo on any operation.
//
// The returned object is not owned by the caller, and can be invalidated once
// the WifiHotspotSocket object is destroyed.
OutputStream& GetOutputStream() override;
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
Exception Close() override;
private:
// A simple wrapper to handle input stream of socket
class SocketInputStream : public InputStream {
public:
SocketInputStream(IInputStream input_stream);
~SocketInputStream() override = default;
ExceptionOr<ByteArray> Read(std::int64_t size) override;
ExceptionOr<size_t> Skip(size_t offset) override;
Exception Close() override;
private:
IInputStream input_stream_{nullptr};
};
// A simple wrapper to handle output stream of socket
class SocketOutputStream : public OutputStream {
public:
SocketOutputStream(IOutputStream output_stream);
~SocketOutputStream() override = default;
Exception Write(const ByteArray& data) override;
Exception Flush() override;
Exception Close() override;
private:
IOutputStream output_stream_{nullptr};
};
// Internal properties
StreamSocket stream_soket_{nullptr};
SocketInputStream input_stream_{nullptr};
SocketOutputStream output_stream_{nullptr};
};
// WifiHotspotServerSocket provides the support to server socket, this server
// socket accepts connection from clients.
class WifiHotspotServerSocket : public api::WifiHotspotServerSocket {
public:
explicit WifiHotspotServerSocket(int port = 0);
WifiHotspotServerSocket(const WifiHotspotServerSocket&) = default;
WifiHotspotServerSocket(WifiHotspotServerSocket&&) = default;
~WifiHotspotServerSocket() override;
WifiHotspotServerSocket& operator=(const WifiHotspotServerSocket&) = default;
WifiHotspotServerSocket& operator=(WifiHotspotServerSocket&&) = default;
std::string GetIPAddress() const override;
int GetPort() const override;
void SetPort(int port) { port_ = port; }
StreamSocketListener GetSocketListener() const {
return stream_socket_listener_;
}
// 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::WifiHotspotSocket> Accept() override;
// Called by the server side of a connection before passing ownership of
// WifiHotspotServerSocker 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() const;
std::string GetHotspotIpAddresses() const;
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
std::string hotspot_ipaddr_ = {};
int port_ = 0;
bool closed_ = false;
};
// Container of operations that can be performed over the WifiHotspot medium.
class WifiHotspotMedium : public api::WifiHotspotMedium {
public:
~WifiHotspotMedium() override;
// Discoverer connects to server socket
std::unique_ptr<api::WifiHotspotSocket> ConnectToService(
absl::string_view ip_address, int port,
CancellationFlag* cancellation_flag) override;
// Advertiser starts to listen on server socket
std::unique_ptr<api::WifiHotspotServerSocket> ListenForService(
int port) override;
// Advertiser start WIFI Hotspot with specific Crendentials
bool StartWifiHotspot(HotspotCredentials* hotspot_credentials_) override;
// Advertiser stop the current WIFI Hotspot
bool StopWifiHotspot() override;
// Discoverer connects to the Hotspot
bool ConnectWifiHotspot(HotspotCredentials* hotspot_credentials_) override;
// Discoverer disconnects from the Hotspot
bool DisconnectWifiHotspot() override;
absl::optional<std::pair<std::int32_t, std::int32_t>> GetDynamicPortRange()
override {
return absl::nullopt;
}
private:
enum Value:char {
kMediumStatusIdle = 0,
kMediumStatusAccepting = (1 << 0),
kMediumStatusBeaconing = (1 << 1),
kMediumStatusConnected = (1 << 2),
};
bool IsIdle() { return medium_status_ == kMediumStatusIdle; }
// Advertiser is accepting connection on server socket
bool IsAccepting() { return (medium_status_ & kMediumStatusAccepting) != 0; }
// Advertiser started Hotspot and sending beacon
bool IsBeaconing() { return (medium_status_ & kMediumStatusBeaconing) != 0; }
// Discoverer is connected with the Hotspot
bool IsConnected() { return (medium_status_ & kMediumStatusConnected) != 0; }
WiFiDirectAdvertisementPublisher publisher_{nullptr};
WiFiDirectConnectionListener listener_{nullptr};
fire_and_forget OnStatusChanged(
WiFiDirectAdvertisementPublisher sender,
WiFiDirectAdvertisementPublisherStatusChangedEventArgs event);
winrt::event_token publisher_status_changed_token_;
fire_and_forget OnConnectionRequested(
WiFiDirectConnectionListener const& sender,
WiFiDirectConnectionRequestedEventArgs const& event);
winrt::event_token connection_requested_token_;
WiFiAdapter wifi_adapter_{nullptr};
// Gets error message from exception pointer
std::string GetErrorMessage(std::exception_ptr eptr);
// Protects to access some members
absl::Mutex mutex_;
// Medium Status
int medium_status_ = kMediumStatusIdle;
// Keep the server socket listener pointer
WifiHotspotServerSocket* server_socket_ptr_ ABSL_GUARDED_BY(mutex_) = nullptr;
};
} // namespace windows
} // namespace nearby
} // namespace location
#endif // PLATFORM_IMPL_WINDOWS_WIFI_HOTSPOT_H_
@@ -0,0 +1,342 @@
// 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 "internal/platform/implementation/windows/wifi_hotspot.h"
// Nearby connections headers
#include "internal/platform/cancellation_flag_listener.h"
#include "internal/platform/logging.h"
#include "internal/platform/implementation/windows/utils.h"
namespace location {
namespace nearby {
namespace windows {
namespace {
constexpr int kMaxRetries = 3;
constexpr int kRetryIntervalMilliSeconds = 300;
constexpr int kMaxScans = 3;
} // namespace
WifiHotspotMedium::~WifiHotspotMedium() {
StopWifiHotspot();
DisconnectWifiHotspot();
}
std::unique_ptr<api::WifiHotspotSocket> WifiHotspotMedium::ConnectToService(
absl::string_view ip_address, int port,
CancellationFlag* cancellation_flag) {
if (ip_address.empty() || port == 0) {
NEARBY_LOGS(ERROR) << "no valid service address and port to connect: "
<< "ip_address = " << ip_address << ", port = " << port;
return nullptr;
}
std::string ipv4_address;
if (ip_address.length() == 4) {
ipv4_address = ipaddr_4bytes_to_dotdecimal_string(ip_address);
} else {
ipv4_address = std::string(ip_address);
}
if (ipv4_address.empty()) {
NEARBY_LOGS(ERROR) << "Invalid IP address parameter.";
return nullptr;
}
HostName host_name{winrt::to_hstring(ipv4_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 "
<< ipv4_address << ":" << port;
return nullptr;
}
location::nearby::CancellationFlagListener cancellationFlagListener(
cancellation_flag, [socket]() { socket.CancelIOAsync().get(); });
}
// Try connecting to the service up to kMaxRetries. Because it may fail fisrt
// time if DHCP procedure is not finished yet.
for (int i = 0; i < kMaxRetries; i++) {
try {
socket.ConnectAsync(host_name, service_name).get();
auto wifi_hotspot_socket = std::make_unique<WifiHotspotSocket>(socket);
NEARBY_LOGS(INFO) << "connected to remote service " << ipv4_address << ":"
<< port;
return wifi_hotspot_socket;
} catch (...) {
NEARBY_LOGS(ERROR) << "failed to connect remote service " << ipv4_address
<< ":" << port << " for the " << i+1 << " time";
Sleep(kRetryIntervalMilliSeconds);
}
}
return nullptr;
}
std::unique_ptr<api::WifiHotspotServerSocket>
WifiHotspotMedium::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;
}
auto server_socket = std::make_unique<WifiHotspotServerSocket>(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_ &= (~kMediumStatusAccepting);
server_socket_ptr_ = nullptr;
});
if (server_socket->listen()) {
medium_status_ |= kMediumStatusAccepting;
NEARBY_LOGS(INFO) << "started to listen serive on port "
<< server_socket_ptr_->GetPort();
return server_socket;
}
NEARBY_LOGS(ERROR) << "Failed to listen service on port " << port;
return nullptr;
}
bool WifiHotspotMedium::StartWifiHotspot(
HotspotCredentials* hotspot_credentials_) {
absl::MutexLock lock(&mutex_);
if (IsBeaconing()) {
NEARBY_LOGS(WARNING) << "cannot create SoftAP again when it is running.";
return true;
}
publisher_ = WiFiDirectAdvertisementPublisher();
publisher_status_changed_token_ =
publisher_.StatusChanged({this, &WifiHotspotMedium::OnStatusChanged});
listener_ = WiFiDirectConnectionListener();
try {
listener_.ConnectionRequested(
{this, &WifiHotspotMedium::OnConnectionRequested});
} catch (winrt::hresult_error const& ex) {
NEARBY_LOGS(ERROR) << __func__ << ": winrt exception: " << ex.code() << ": "
<< winrt::to_string(ex.message());
}
// Normal mode: The device is highly discoverable so long as the app is in the
// foreground.
publisher_.Advertisement().ListenStateDiscoverability(
WiFiDirectAdvertisementListenStateDiscoverability::Normal);
// Enbale Autonomous GO mode
publisher_.Advertisement().IsAutonomousGroupOwnerEnabled(true);
// Using WIFIDirect legacy mode to create a softAP. AP means "access point".
publisher_.Advertisement().LegacySettings().IsEnabled(true);
std::string password = absl::StrFormat("%08x", Prng().NextUint32());
hotspot_credentials_->SetPassword(password);
PasswordCredential creds;
creds.Password(winrt::to_hstring(password));
publisher_.Advertisement().LegacySettings().Passphrase(creds);
std::string ssid = "DIRECT-" + std::to_string(Prng().NextUint32());
hotspot_credentials_->SetSSID(ssid);
publisher_.Advertisement().LegacySettings().Ssid(winrt::to_hstring(ssid));
publisher_.Start();
if (publisher_.Status() == WiFiDirectAdvertisementPublisherStatus::Started) {
NEARBY_LOGS(INFO) << "Windows WIFI Hotspot started";
medium_status_ |= kMediumStatusBeaconing;
return true;
}
// Clean up when fail
NEARBY_LOGS(ERROR) << "Windows WIFI Hotspot fails to start";
listener_ = nullptr;
publisher_ = nullptr;
return false;
}
bool WifiHotspotMedium::StopWifiHotspot() {
// Need to use Win32 API to deregister the Dnssd instance
absl::MutexLock lock(&mutex_);
if (!IsBeaconing()) {
NEARBY_LOGS(WARNING) << "Cannot stop SoftAP because no SoftAP is started.";
return true;
}
if (publisher_) {
publisher_.Stop();
publisher_.StatusChanged(publisher_status_changed_token_);
listener_.ConnectionRequested(connection_requested_token_);
NEARBY_LOGS(INFO) << "succeeded to stop WIFI Hotspot";
}
medium_status_ &= (~kMediumStatusBeaconing);
return true;
}
fire_and_forget WifiHotspotMedium::OnStatusChanged(
WiFiDirectAdvertisementPublisher sender,
WiFiDirectAdvertisementPublisherStatusChangedEventArgs event) {
if (event.Status() == WiFiDirectAdvertisementPublisherStatus::Started) {
if (sender.Advertisement().LegacySettings().IsEnabled()) {
NEARBY_LOGS(INFO)
<< "WIFI SoftAP SSID: "
<< winrt::to_string(
publisher_.Advertisement().LegacySettings().Ssid());
NEARBY_LOGS(INFO) << "WIFI SoftAP PW: "
<< winrt::to_string(publisher_.Advertisement()
.LegacySettings()
.Passphrase()
.Password());
}
}
if (event.Status() == WiFiDirectAdvertisementPublisherStatus::Stopped) {
NEARBY_LOGS(INFO) << "Receive WIFI direct/SoftAP stop event";
}
return winrt::fire_and_forget();
}
fire_and_forget WifiHotspotMedium::OnConnectionRequested(
WiFiDirectConnectionListener const& sender,
WiFiDirectConnectionRequestedEventArgs const& event) {
WiFiDirectConnectionRequest connection_request = event.GetConnectionRequest();
winrt::hstring device_name = connection_request.DeviceInformation().Name();
NEARBY_LOGS(INFO) << "Receive connection request from: "
<< winrt::to_string(device_name);
DeviceInformationPairing pairing =
connection_request.DeviceInformation().Pairing();
if (pairing.IsPaired())
NEARBY_LOGS(INFO) << "Paired";
else
NEARBY_LOGS(INFO) << "Not Paired";
return winrt::fire_and_forget();
}
bool WifiHotspotMedium::ConnectWifiHotspot(
HotspotCredentials* hotspot_credentials_) {
absl::MutexLock lock(&mutex_);
if (IsConnected()) {
NEARBY_LOGS(WARNING) << "Already connected to AP, disconnect first.";
DisconnectWifiHotspot();
}
auto access = WiFiAdapter::RequestAccessAsync().get();
if (access != WiFiAccessStatus::Allowed) {
NEARBY_LOGS(WARNING) << "Access Denied with reason: "
<< static_cast<int>(access);
return false;
}
auto adaptors = WiFiAdapter::FindAllAdaptersAsync().get();
if (adaptors.Size() < 1) {
NEARBY_LOGS(WARNING) << "No WIFI Adaptor found.";
return false;
}
wifi_adapter_ = adaptors.GetAt(0);
// SoftAP is an abbreviation for "software enabled access point".
WiFiAvailableNetwork nearby_softap{nullptr};
NEARBY_LOGS(INFO) << "Scanning for Nearby Hotspot SSID: "
<< hotspot_credentials_->GetSSID();
// First time scan may not find our target hotspot, try 3 times can almost
// guarantee to find the Hotspot
for (int i = 0; i < kMaxScans; i++) {
for (const auto& network :
wifi_adapter_.NetworkReport().AvailableNetworks()) {
if (winrt::to_string(network.Ssid()) == hotspot_credentials_->GetSSID()) {
NEARBY_LOGS(INFO) << "Found Nearby SSID: "
<< winrt::to_string(network.Ssid());
nearby_softap = network;
break;
}
}
if (nearby_softap) break;
NEARBY_LOGS(INFO) << "Scan ... ";
wifi_adapter_.ScanAsync().get();
}
if (!nearby_softap) {
NEARBY_LOGS(INFO) << "Hotspot is not found";
return false;
}
PasswordCredential creds;
creds.Password(winrt::to_hstring(hotspot_credentials_->GetPassword()));
auto connect_result =
wifi_adapter_
.ConnectAsync(nearby_softap, WiFiReconnectionKind::Automatic, creds)
.get();
if (connect_result.ConnectionStatus() != WiFiConnectionStatus::Success) {
NEARBY_LOGS(INFO) << "Connectiong failed with reason: "
<< static_cast<int>(connect_result.ConnectionStatus());
return false;
}
NEARBY_LOGS(INFO) << "Connected to: " << hotspot_credentials_->GetSSID();
medium_status_ |= kMediumStatusConnected;
return true;
}
bool WifiHotspotMedium::DisconnectWifiHotspot() {
absl::MutexLock lock(&mutex_);
if (!IsConnected()) {
NEARBY_LOGS(WARNING)
<< "Cannot diconnect SoftAP because it is not connected.";
}
if (wifi_adapter_) {
wifi_adapter_.Disconnect();
NEARBY_LOGS(INFO) << "Disconnect softAP.";
wifi_adapter_ = {nullptr};
}
medium_status_ &= (~kMediumStatusConnected);
return true;
}
std::string WifiHotspotMedium::GetErrorMessage(std::exception_ptr eptr) {
try {
if (eptr) {
std::rethrow_exception(eptr);
} else {
return "";
}
} catch (const std::exception& e) {
return e.what();
}
}
} // namespace windows
} // namespace nearby
} // namespace location
@@ -0,0 +1,207 @@
// 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 "internal/platform/implementation/windows/utils.h"
#include "internal/platform/implementation/windows/wifi_hotspot.h"
#include "internal/platform/logging.h"
namespace location {
namespace nearby {
namespace windows {
WifiHotspotServerSocket::WifiHotspotServerSocket(int port) : port_(port) {}
WifiHotspotServerSocket::~WifiHotspotServerSocket() { Close(); }
std::string WifiHotspotServerSocket::GetIPAddress() const {
if (stream_socket_listener_ == nullptr) {
return {};
}
if (ip_addresses_.empty()) {
auto ip_addr = GetIpAddresses();
if (ip_addr.empty()) {
return {};
}
return ip_addr.front();
}
return ip_addresses_.front();
}
int WifiHotspotServerSocket::GetPort() const {
if (stream_socket_listener_ == nullptr) {
return 0;
}
return std::stoi(stream_socket_listener_.Information().LocalPort().c_str());
}
std::unique_ptr<api::WifiHotspotSocket> WifiHotspotServerSocket::Accept() {
absl::MutexLock lock(&mutex_);
while (!closed_ && pending_sockets_.empty()) {
cond_.Wait(&mutex_);
}
if (closed_) return {};
StreamSocket wifi_hotspot_socket = pending_sockets_.front();
pending_sockets_.pop_front();
return std::make_unique<WifiHotspotSocket>(wifi_hotspot_socket);
}
void WifiHotspotServerSocket::SetCloseNotifier(std::function<void()> notifier) {
close_notifier_ = std::move(notifier);
}
Exception WifiHotspotServerSocket::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 WifiHotspotServerSocket::listen() {
// Check IP address
hotspot_ipaddr_ = GetHotspotIpAddresses();
if (hotspot_ipaddr_.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, &WifiHotspotServerSocket::Listener_ConnectionReceived});
try {
HostName host_name{winrt::to_hstring(hotspot_ipaddr_)};
stream_socket_listener_
.BindEndpointAsync(host_name, 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());
NEARBY_LOGS(INFO) << "Server Socket port: " << port_;
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 WifiHotspotServerSocket::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{};
}
bool HasEnding(std::string const &full_string, std::string const &ending) {
if (full_string.length() >= ending.length()) {
return (0 == full_string.compare(full_string.length() - ending.length(),
ending.length(), ending));
} else {
return false;
}
}
std::vector<std::string> WifiHotspotServerSocket::GetIpAddresses() const {
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 &&
host_name.Type() == HostNameType::Ipv4) {
std::string ipv4_s = winrt::to_string(host_name.ToString());
if (HasEnding(ipv4_s, ".1")) {
// std::string ipv4_b_s = ipaddr_dotdecimal_to_4bytes_string(ipv4_s);
result.push_back(ipv4_s);
}
}
}
return result;
}
std::string WifiHotspotServerSocket::GetHotspotIpAddresses() const {
auto host_names = NetworkInformation::GetHostNames();
for (auto host_name : host_names) {
if (host_name.IPInformation() != nullptr &&
host_name.IPInformation().NetworkAdapter() != nullptr &&
host_name.Type() == HostNameType::Ipv4) {
std::string ipv4_s = winrt::to_string(host_name.ToString());
if (HasEnding(ipv4_s, ".1")) {
// TODO(b/228541380): replace when we find a better way to identifying
// the hotspot address
NEARBY_LOGS(INFO) << "Found Hotspot IP: " << ipv4_s;
return ipv4_s;
}
}
}
return {};
}
} // namespace windows
} // namespace nearby
} // namespace location
@@ -0,0 +1,142 @@
// 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 "internal/platform/implementation/windows/wifi_hotspot.h"
#include "internal/platform/logging.h"
namespace location {
namespace nearby {
namespace windows {
WifiHotspotSocket::WifiHotspotSocket(StreamSocket socket) {
stream_soket_ = socket;
input_stream_ = SocketInputStream(socket.InputStream());
output_stream_ = SocketOutputStream(socket.OutputStream());
}
WifiHotspotSocket::~WifiHotspotSocket() {
if (stream_soket_ != nullptr) {
try {
Close();
} catch (...) {
NEARBY_LOGS(ERROR) << "Failed to destructor class WifiHotspotSocket.";
}
}
}
InputStream& WifiHotspotSocket::GetInputStream() { return input_stream_; }
OutputStream& WifiHotspotSocket::GetOutputStream() { return output_stream_; }
Exception WifiHotspotSocket::Close() {
try {
if (stream_soket_ != nullptr) {
stream_soket_.Close();
}
return {Exception::kSuccess};
} catch (...) {
return {Exception::kIo};
}
}
WifiHotspotSocket::SocketInputStream::SocketInputStream(
IInputStream input_stream) {
input_stream_ = input_stream;
}
ExceptionOr<ByteArray> WifiHotspotSocket::SocketInputStream::Read(
std::int64_t size) {
try {
Buffer buffer = Buffer(size);
auto ibuffer =
input_stream_.ReadAsync(buffer, size, InputStreamOptions::None).get();
if (ibuffer.Length() != size) {
NEARBY_LOGS(WARNING) << "Only got part of data of needed.";
}
ByteArray data((char*)ibuffer.data(), ibuffer.Length());
return ExceptionOr(data);
} catch (...) {
return Exception{Exception::kIo};
}
}
ExceptionOr<size_t> WifiHotspotSocket::SocketInputStream::Skip(size_t offset) {
try {
Buffer buffer = Buffer(offset);
auto ibuffer =
input_stream_.ReadAsync(buffer, offset, InputStreamOptions::None).get();
return ExceptionOr((size_t)ibuffer.Length());
} catch (...) {
return Exception{Exception::kIo};
}
}
Exception WifiHotspotSocket::SocketInputStream::Close() {
try {
input_stream_.Close();
} catch (...) {
return {Exception::kIo};
}
return {Exception::kSuccess};
}
// SocketOutputStream
WifiHotspotSocket::SocketOutputStream::SocketOutputStream(
IOutputStream output_stream) {
output_stream_ = output_stream;
}
Exception WifiHotspotSocket::SocketOutputStream::Write(const ByteArray& data) {
Buffer buffer = Buffer(data.size());
std::memcpy(buffer.data(), data.data(), data.size());
buffer.Length(data.size());
try {
output_stream_.WriteAsync(buffer).get();
} catch (...) {
return {Exception::kIo};
}
return {Exception::kSuccess};
}
Exception WifiHotspotSocket::SocketOutputStream::Flush() {
try {
output_stream_.FlushAsync().get();
} catch (...) {
return {Exception::kIo};
}
return {Exception::kSuccess};
}
Exception WifiHotspotSocket::SocketOutputStream::Close() {
try {
output_stream_.Close();
} catch (...) {
return {Exception::kIo};
}
return {Exception::kSuccess};
}
} // namespace windows
} // namespace nearby
} // namespace location
@@ -344,7 +344,8 @@ std::unique_ptr<api::WifiLanServerSocket> WifiLanMedium::ListenForService(
if (server_socket->listen()) {
medium_status_ |= MEDIUM_STATUS_ACCEPTING;
NEARBY_LOGS(INFO) << "started to listen serive on port " << port;
NEARBY_LOGS(INFO) << "started to listen serive on port "
<< server_socket_ptr_->GetPort();
return server_socket;
}