WIFI Direct implementation (2)

Part 2: Implementation g3 medium/Environment/platform layer unit test

PiperOrigin-RevId: 485417100
This commit is contained in:
hai007
2022-11-01 14:23:14 -07:00
committed by Copybara-Service
parent 5385dd4d6b
commit 03b1c773fe
5 changed files with 448 additions and 18 deletions
@@ -23,42 +23,168 @@
#include "absl/strings/str_format.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/cancellation_flag_listener.h"
#include "internal/platform/implementation/g3/wifi_hotspot.h"
#include "internal/platform/implementation/wifi_direct.h"
#include "internal/platform/implementation/wifi_hotspot.h"
#include "internal/platform/logging.h"
#include "internal/platform/medium_environment.h"
namespace location {
namespace nearby {
namespace g3 {
// Code for WifiDirectMedium
WifiDirectMedium::WifiDirectMedium() {
auto& env = MediumEnvironment::Instance();
env.RegisterWifiDirectMedium(*this);
}
WifiDirectMedium::~WifiDirectMedium() {
auto& env = MediumEnvironment::Instance();
env.UnregisterWifiDirectMedium(*this);
}
bool WifiDirectMedium::StartWifiDirect(
HotspotCredentials* wifi_direct_credentials) {
absl::MutexLock lock(&mutex_);
std::string ssid = absl::StrCat("DIRECT-", Prng().NextUint32());
wifi_direct_credentials->SetSSID(ssid);
std::string password = absl::StrFormat("%08x", Prng().NextUint32());
wifi_direct_credentials->SetPassword(password);
NEARBY_LOGS(INFO) << "G3 StartWifiDirect GO: ssid=" << ssid
<< ", password:" << password;
auto& env = MediumEnvironment::Instance();
env.UpdateWifiDirectMediumForStartOrConnect(*this, wifi_direct_credentials,
/*is_go=*/true, /*enabled=*/true);
return true;
}
bool WifiDirectMedium::StopWifiDirect() {
absl::MutexLock lock(&mutex_);
NEARBY_LOGS(INFO) << "G3 StopWifiDirect GO";
auto& env = MediumEnvironment::Instance();
env.UpdateWifiDirectMediumForStartOrConnect(*this, /*credentials*/ nullptr,
/*is_go=*/true,
/*enabled=*/false);
return true;
}
bool WifiDirectMedium::ConnectWifiDirect(
HotspotCredentials* wifi_direct_credentials) {
absl::MutexLock lock(&mutex_);
NEARBY_LOGS(INFO) << "G3 ConnectWifiDirect : ssid="
<< wifi_direct_credentials->GetSSID()
<< ", password:" << wifi_direct_credentials->GetPassword();
auto& env = MediumEnvironment::Instance();
auto* remote_medium = static_cast<WifiDirectMedium*>(
env.GetWifiDirectMedium(wifi_direct_credentials->GetSSID(), {}));
if (!remote_medium) {
env.UpdateWifiDirectMediumForStartOrConnect(*this, wifi_direct_credentials,
/*is_go=*/false,
/*enabled=*/false);
return false;
}
env.UpdateWifiDirectMediumForStartOrConnect(*this, wifi_direct_credentials,
/*is_go=*/false,
/*enabled=*/true);
return true;
}
bool WifiDirectMedium::DisconnectWifiDirect() { return true; }
bool WifiDirectMedium::DisconnectWifiDirect() {
absl::MutexLock lock(&mutex_);
NEARBY_LOGS(INFO) << "G3 DisconnectWifiDirect";
auto& env = MediumEnvironment::Instance();
env.UpdateWifiDirectMediumForStartOrConnect(*this, /*credentials*/ nullptr,
/*is_go=*/false,
/*enabled=*/false);
return true;
}
std::unique_ptr<api::WifiHotspotSocket> WifiDirectMedium::ConnectToService(
absl::string_view ip_address, int port,
CancellationFlag* cancellation_flag) {
return nullptr;
std::string socket_name = WifiHotspotServerSocket::GetName(ip_address, port);
NEARBY_LOGS(INFO) << "G3 WifiDirect ConnectToService [self]: medium=" << this
<< ", ip address + port=" << socket_name;
// First, find an instance of remote medium, that exposed this service.
auto& env = MediumEnvironment::Instance();
auto* remote_medium =
static_cast<WifiDirectMedium*>(env.GetWifiDirectMedium({}, ip_address));
if (remote_medium == nullptr) {
return nullptr;
}
WifiHotspotServerSocket* server_socket = nullptr;
NEARBY_LOGS(INFO) << "G3 WifiDirect ConnectToService [peer]: medium="
<< remote_medium
<< ", remote ip address + port=" << socket_name;
// Then, find our server socket context in this medium.
{
absl::MutexLock medium_lock(&remote_medium->mutex_);
auto item = remote_medium->server_sockets_.find(socket_name);
server_socket = item != server_sockets_.end() ? item->second : nullptr;
if (server_socket == nullptr) {
NEARBY_LOGS(ERROR) << "G3 WifiDirect Failed to find WifiDirect Server "
"socket: socket_name="
<< socket_name;
return nullptr;
}
}
if (cancellation_flag->Cancelled()) {
NEARBY_LOGS(ERROR)
<< "G3 WifiDirect Connect: Has been cancelled: socket_name="
<< socket_name;
return nullptr;
}
auto socket = std::make_unique<WifiHotspotSocket>();
// Finally, Request to connect to this socket.
server_socket->Connect(*socket);
NEARBY_LOGS(INFO) << "G3 WifiHotspot GC ConnectToService: connected: socket="
<< socket.get();
return socket;
}
std::unique_ptr<api::WifiHotspotServerSocket>
WifiDirectMedium::ListenForService(int port) {
return nullptr;
auto& env = MediumEnvironment::Instance();
auto server_socket = std::make_unique<WifiHotspotServerSocket>();
std::string dot_decimal_ip;
std::string ip_address = env.GetFakeIPAddress();
for (auto byte : ip_address) {
absl::StrAppend(&dot_decimal_ip, absl::StrFormat("%d", byte), ".");
}
dot_decimal_ip.pop_back();
server_socket->SetIPAddress(dot_decimal_ip);
server_socket->SetPort(port == 0 ? env.GetFakePort() : port);
std::string socket_name = WifiHotspotServerSocket::GetName(
server_socket->GetIPAddress(), server_socket->GetPort());
server_socket->SetCloseNotifier([this, socket_name]() {
absl::MutexLock lock(&mutex_);
server_sockets_.erase(socket_name);
});
NEARBY_LOGS(INFO) << "G3 WifiDirect GO Adding server socket: medium=" << this
<< ", socket_name=" << socket_name;
absl::MutexLock lock(&mutex_);
server_sockets_.insert({socket_name, server_socket.get()});
return server_socket;
}
} // namespace g3
} // namespace nearby
} // namespace location
@@ -40,8 +40,8 @@ class WifiDirectMedium;
// Container of operations that can be performed over the WifiDirect medium.
class WifiDirectMedium : public api::WifiDirectMedium {
public:
WifiDirectMedium() = default;
~WifiDirectMedium() override = default;
WifiDirectMedium();
~WifiDirectMedium() override;
WifiDirectMedium(const WifiDirectMedium&) = delete;
WifiDirectMedium(WifiDirectMedium&&) = delete;
+91
View File
@@ -71,6 +71,7 @@ void MediumEnvironment::Reset() {
wifi_lan_mediums_.clear();
{
MutexLock lock(&mutex_);
wifi_direct_mediums_.clear();
wifi_hotspot_mediums_.clear();
}
use_valid_peer_connection_ = true;
@@ -927,6 +928,96 @@ api::WifiLanMedium* MediumEnvironment::GetWifiLanMedium(
return nullptr;
}
void MediumEnvironment::RegisterWifiDirectMedium(
api::WifiDirectMedium& medium) {
if (!enabled_) return;
RunOnMediumEnvironmentThread([this, &medium]() {
MutexLock lock(&mutex_);
wifi_direct_mediums_.insert({&medium, WifiDirectMediumContext{}});
NEARBY_LOG(INFO, "Registered: medium=%p", &medium);
});
}
api::WifiDirectMedium* MediumEnvironment::GetWifiDirectMedium(
absl::string_view ssid, absl::string_view ip_address) {
MutexLock lock(&mutex_);
for (auto& medium_info : wifi_direct_mediums_) {
auto* medium_found = medium_info.first;
auto& info = medium_info.second;
if (info.is_go && info.is_active) {
if ((info.wifi_direct_credentials->GetSSID() == ssid) ||
(!ip_address.empty() &&
(info.wifi_direct_credentials->GetIPAddress() == ip_address))) {
NEARBY_LOGS(INFO) << "Found Remote WifiDirect medium=" << medium_found;
return medium_found;
}
}
}
NEARBY_LOGS(INFO) << "Can't find WifiDirect medium!";
return nullptr;
}
void MediumEnvironment::UpdateWifiDirectMediumForStartOrConnect(
api::WifiDirectMedium& medium,
const HotspotCredentials* wifi_direct_credentials, bool is_go,
bool enabled) {
if (!enabled_) return;
CountDownLatch latch(1);
RunOnMediumEnvironmentThread(
[this, &medium, wifi_direct_credentials = wifi_direct_credentials, is_go,
enabled, &latch]() {
std::string role_status = absl::StrFormat(
"; %s is %s", is_go ? "Group Owner" : "Group Client",
is_go ? (enabled ? "Started" : "Stopped")
: (enabled ? "Connected" : "Disconneced"));
if (wifi_direct_credentials) {
NEARBY_LOGS(INFO)
<< "Update WifiDirect medium for GO: this=" << this
<< "; medium=" << &medium << role_status
<< "; ssid=" << wifi_direct_credentials->GetSSID()
<< "; password=" << wifi_direct_credentials->GetPassword();
} else {
NEARBY_LOGS(INFO) << "Reset WifiDirect medium for GO: this=" << this
<< "; medium=" << &medium << role_status;
}
MutexLock lock(&mutex_);
for (auto& medium_info : wifi_direct_mediums_) {
auto& local_medium = medium_info.first;
auto& info = medium_info.second;
if (local_medium == &medium) {
NEARBY_LOGS(INFO) << "Found WifiDirect medium=" << &medium;
info.is_active = enabled;
info.is_go = is_go;
if (enabled) {
info.wifi_direct_credentials = wifi_direct_credentials;
}
continue;
}
}
latch.CountDown();
});
latch.Await();
}
bool MediumEnvironment::IsWifiDirectMediumsEmpty() {
MutexLock lock(&mutex_);
return wifi_direct_mediums_.empty();
}
void MediumEnvironment::UnregisterWifiDirectMedium(
api::WifiDirectMedium& medium) {
if (!enabled_) return;
RunOnMediumEnvironmentThread([this, &medium]() {
MutexLock lock(&mutex_);
wifi_direct_mediums_.extract(&medium);
NEARBY_LOGS(INFO) << "Unregistered WifiDirect medium";
});
}
void MediumEnvironment::RegisterWifiHotspotMedium(
api::WifiHotspotMedium& medium) {
if (!enabled_) return;
+35
View File
@@ -20,6 +20,7 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
@@ -35,6 +36,7 @@
#endif
#include "internal/platform/byte_array.h"
#include "internal/platform/feature_flags.h"
#include "internal/platform/implementation/wifi_direct.h"
#include "internal/platform/implementation/wifi_hotspot.h"
#include "internal/platform/implementation/wifi_lan.h"
#include "internal/platform/mutex.h"
@@ -309,6 +311,28 @@ class MediumEnvironment {
// port, or nullptr.
api::WifiLanMedium* GetWifiLanMedium(const std::string& ip_address, int port);
// Adds medium-related info to allow for start/connect WifiDirect to work.
void RegisterWifiDirectMedium(api::WifiDirectMedium& medium);
// Returns WifiDirect medium that matches ssid or IP address with the role of
// the Medium. Returns nullptr if not found.
api::WifiDirectMedium* GetWifiDirectMedium(absl::string_view ssid,
absl::string_view ip_address);
// Updates credential and Medium role(GO or GC) to indicate the current
// medium is exposing Start WifiDirect event.
void UpdateWifiDirectMediumForStartOrConnect(
api::WifiDirectMedium& medium,
const HotspotCredentials* wifi_direct_credentials, bool is_go,
bool enabled);
// For unit test only
bool IsWifiDirectMediumsEmpty();
// Removes medium-related info. This should correspond to device being stopped
// or disconnected.
void UnregisterWifiDirectMedium(api::WifiDirectMedium& medium);
// Adds medium-related info to allow for start/connect Hotspot to work.
// This provides access to this medium from other mediums, when protocol
// expects they should communicate.
@@ -367,6 +391,14 @@ class MediumEnvironment {
absl::flat_hash_map<std::string, NsdServiceInfo> discovered_services;
};
struct WifiDirectMediumContext {
// Set to "true" for Medium act as WifiDirect GO role; "false" for GC role
bool is_go = false;
// Set "true" when GO is started or GC is connected
bool is_active = false;
const HotspotCredentials* wifi_direct_credentials;
};
struct WifiHotspotMediumContext {
// Set to "true" for Medium act as SoftAP role; "false" for STA role
bool is_ap = true;
@@ -441,6 +473,9 @@ class MediumEnvironment {
wifi_lan_mediums_;
Mutex mutex_;
absl::flat_hash_map<api::WifiDirectMedium*, WifiDirectMediumContext>
wifi_direct_mediums_ ABSL_GUARDED_BY(mutex_);
absl::flat_hash_map<api::WifiHotspotMedium*, WifiHotspotMediumContext>
wifi_hotspot_mediums_ ABSL_GUARDED_BY(mutex_);
+190 -12
View File
@@ -18,23 +18,57 @@
#include <string>
#include "gtest/gtest.h"
#include "absl/strings/match.h"
#include "internal/platform/medium_environment.h"
#include "internal/platform/wifi_hotspot.h"
#include "internal/platform/wifi_hotspot_credential.h"
namespace location {
namespace nearby {
namespace {
using FeatureFlags = FeatureFlags::Flags;
constexpr FeatureFlags kTestCases[] = {
FeatureFlags{
.enable_cancellation_flag = true,
},
FeatureFlags{
.enable_cancellation_flag = false,
},
};
constexpr absl::string_view kSsid = "Direct-357a2d8c";
constexpr absl::string_view kPassword = "b592f7d3";
constexpr absl::string_view kIp = "123.234.23.1";
constexpr const size_t kPort = 20;
constexpr absl::string_view kData = "ABCD";
constexpr const size_t kChunkSize = 10;
constexpr absl::Duration kWaitDuration = absl::Milliseconds(100);
class WifiDirectMediumTest : public ::testing::Test {};
class WifiDirectMediumTest : public testing::TestWithParam<FeatureFlags> {
protected:
WifiDirectMediumTest() {
env_.Stop();
env_.Start();
}
~WifiDirectMediumTest() override {
absl::SleepFor(kWaitDuration);
EXPECT_TRUE(env_.IsWifiDirectMediumsEmpty());
env_.Stop();
}
MediumEnvironment& env_{MediumEnvironment::Instance()};
};
INSTANTIATE_TEST_SUITE_P(ParametrisedWifiDirectMediumTest, WifiDirectMediumTest,
testing::ValuesIn(kTestCases));
TEST_F(WifiDirectMediumTest, ConstructorDestructorWorks) {
auto wifi_direct_a = WifiDirectMedium();
auto wifi_direct_b = WifiDirectMedium();
WifiDirectMedium wifi_direct_a;
WifiDirectMedium wifi_direct_b;
// Make sure we can create functional mediums.
ASSERT_TRUE(wifi_direct_a.IsInterfaceValid());
ASSERT_TRUE(wifi_direct_b.IsInterfaceValid());
@@ -43,29 +77,173 @@ TEST_F(WifiDirectMediumTest, ConstructorDestructorWorks) {
}
TEST_F(WifiDirectMediumTest, CanStartStopDirect) {
auto wifi_direct_a = WifiDirectMedium();
WifiDirectMedium wifi_direct_a;
ASSERT_TRUE(wifi_direct_a.IsInterfaceValid());
EXPECT_TRUE(wifi_direct_a.StartWifiDirect());
EXPECT_EQ(wifi_direct_a.GetDynamicPortRange(), std::nullopt);
WifiHotspotServerSocket server_socket = wifi_direct_a.ListenForService();
EXPECT_FALSE(server_socket.IsValid());
EXPECT_TRUE(server_socket.IsValid());
server_socket.Close();
EXPECT_TRUE(wifi_direct_a.StopWifiDirect());
}
TEST_F(WifiDirectMediumTest, CanConnectDisconnectDirect) {
auto wifi_direct_a = WifiDirectMedium();
WifiDirectMedium wifi_direct_a;
ASSERT_TRUE(wifi_direct_a.IsInterfaceValid());
EXPECT_TRUE(wifi_direct_a.ConnectWifiDirect(kSsid, kPassword));
EXPECT_FALSE(wifi_direct_a.ConnectWifiDirect(kSsid, kPassword));
EXPECT_TRUE(wifi_direct_a.DisconnectWifiDirect());
}
TEST_F(WifiDirectMediumTest, CanConnectToService) {
auto wifi_direct = WifiDirectMedium();
CancellationFlag flag;
EXPECT_FALSE(wifi_direct.ConnectToService(kIp, kPort, &flag).IsValid());
TEST_P(WifiDirectMediumTest, CanStartDirectGOThatOtherCanConnect) {
FeatureFlags feature_flags = GetParam();
env_.SetFeatureFlags(feature_flags);
WifiDirectMedium wifi_direct_a;
WifiDirectMedium wifi_direct_b;
ASSERT_TRUE(wifi_direct_a.IsInterfaceValid());
ASSERT_TRUE(wifi_direct_b.IsInterfaceValid());
EXPECT_TRUE(wifi_direct_a.StartWifiDirect());
HotspotCredentials* wifi_direct_credentials = wifi_direct_a.GetCredential();
auto* medium_a =
env_.GetWifiDirectMedium(wifi_direct_credentials->GetSSID(), {});
EXPECT_NE(medium_a, nullptr);
EXPECT_TRUE(
wifi_direct_b.ConnectWifiDirect(wifi_direct_credentials->GetSSID(),
wifi_direct_credentials->GetPassword()));
WifiHotspotServerSocket server_socket = wifi_direct_a.ListenForService();
EXPECT_TRUE(server_socket.IsValid());
auto ip_addr = server_socket.GetIPAddress();
EXPECT_FALSE(absl::EndsWith(ip_addr, "."));
wifi_direct_credentials->SetIPAddress(ip_addr);
WifiHotspotSocket socket_a;
WifiHotspotSocket socket_b;
EXPECT_FALSE(socket_a.IsValid());
EXPECT_FALSE(socket_b.IsValid());
{
CancellationFlag flag;
SingleThreadExecutor server_executor;
SingleThreadExecutor client_executor;
client_executor.Execute(
[&wifi_direct_b, &socket_b, &server_socket, &flag]() {
socket_b = wifi_direct_b.ConnectToService(kIp, kPort, &flag);
EXPECT_FALSE(socket_b.IsValid());
socket_b = wifi_direct_b.ConnectToService(
server_socket.GetIPAddress(), kPort, &flag);
EXPECT_FALSE(socket_b.IsValid());
socket_b = wifi_direct_b.ConnectToService(
server_socket.GetIPAddress(), server_socket.GetPort(), &flag);
if (!socket_b.IsValid()) {
server_socket.Close();
}
});
server_executor.Execute([&socket_a, &server_socket]() {
socket_a = server_socket.Accept();
if (!socket_a.IsValid()) {
server_socket.Close();
}
});
}
EXPECT_TRUE(socket_a.IsValid());
EXPECT_TRUE(socket_b.IsValid());
InputStream& in_stream = socket_a.GetInputStream();
OutputStream& out_stream = socket_b.GetOutputStream();
std::string data(kData);
EXPECT_TRUE(out_stream.Write(ByteArray(data)).Ok());
ExceptionOr<ByteArray> read_data = in_stream.Read(kChunkSize);
EXPECT_TRUE(read_data.ok());
EXPECT_EQ(std::string(read_data.result()), data);
socket_a.Close();
socket_b.Close();
EXPECT_FALSE(out_stream.Write(ByteArray(data)).Ok());
read_data = in_stream.Read(kChunkSize);
EXPECT_FALSE(read_data.ok());
server_socket.Close();
EXPECT_TRUE(wifi_direct_b.DisconnectWifiDirect());
EXPECT_TRUE(wifi_direct_a.StopWifiDirect());
auto* medium_b =
env_.GetWifiDirectMedium(wifi_direct_credentials->GetSSID(), {});
EXPECT_EQ(medium_b, nullptr);
}
TEST_P(WifiDirectMediumTest, CanStartDirectGOThatOtherCanCancelConnect) {
FeatureFlags feature_flags = GetParam();
env_.SetFeatureFlags(feature_flags);
WifiDirectMedium wifi_direct_a;
WifiDirectMedium wifi_direct_b;
ASSERT_TRUE(wifi_direct_a.IsInterfaceValid());
ASSERT_TRUE(wifi_direct_b.IsInterfaceValid());
EXPECT_TRUE(wifi_direct_a.StartWifiDirect());
HotspotCredentials* wifi_direct_credentials = wifi_direct_a.GetCredential();
EXPECT_TRUE(
wifi_direct_b.ConnectWifiDirect(wifi_direct_credentials->GetSSID(),
wifi_direct_credentials->GetPassword()));
WifiHotspotServerSocket server_socket = wifi_direct_a.ListenForService();
EXPECT_TRUE(server_socket.IsValid());
wifi_direct_credentials->SetIPAddress(server_socket.GetIPAddress());
WifiHotspotSocket socket_a;
WifiHotspotSocket socket_b;
EXPECT_FALSE(socket_a.IsValid());
EXPECT_FALSE(socket_b.IsValid());
{
CancellationFlag flag(true);
SingleThreadExecutor server_executor;
SingleThreadExecutor client_executor;
client_executor.Execute(
[&wifi_direct_b, &socket_b, &server_socket, &flag]() {
socket_b = wifi_direct_b.ConnectToService(kIp, kPort, &flag);
EXPECT_FALSE(socket_b.IsValid());
socket_b = wifi_direct_b.ConnectToService(
server_socket.GetIPAddress(), server_socket.GetPort(), &flag);
if (!socket_b.IsValid()) {
server_socket.Close();
}
});
server_executor.Execute([&socket_a, &server_socket]() {
socket_a = server_socket.Accept();
if (!socket_a.IsValid()) {
server_socket.Close();
}
});
}
if (!feature_flags.enable_cancellation_flag) {
EXPECT_TRUE(socket_a.IsValid());
EXPECT_TRUE(socket_b.IsValid());
} else {
EXPECT_FALSE(socket_a.IsValid());
EXPECT_FALSE(socket_b.IsValid());
}
server_socket.Close();
EXPECT_TRUE(wifi_direct_b.DisconnectWifiDirect());
EXPECT_TRUE(wifi_direct_a.StopWifiDirect());
}
TEST_F(WifiDirectMediumTest, CanStartDirectGOThatOtherFailConnect) {
WifiDirectMedium wifi_direct_a;
WifiDirectMedium wifi_direct_b;
ASSERT_TRUE(wifi_direct_a.IsInterfaceValid());
ASSERT_TRUE(wifi_direct_b.IsInterfaceValid());
EXPECT_TRUE(wifi_direct_a.StartWifiDirect());
EXPECT_FALSE(wifi_direct_b.ConnectWifiDirect(kSsid, kPassword));
EXPECT_TRUE(wifi_direct_b.DisconnectWifiDirect());
EXPECT_TRUE(wifi_direct_a.StopWifiDirect());
}
} // namespace
} // namespace nearby
} // namespace location