WIFI Direct implementation (2)

Part 2: Connection common code for WIFI Direct

PiperOrigin-RevId: 493153750
This commit is contained in:
hai007
2022-12-05 17:14:26 -08:00
committed by Copybara-Service
parent 87c57534a4
commit fa7c8a2ec8
7 changed files with 609 additions and 0 deletions
+1
View File
@@ -449,6 +449,7 @@ let package = Package(
"connections/implementation/mediums/webrtc_test.cc",
"connections/implementation/mediums/lost_entity_tracker_test.cc",
"connections/implementation/mediums/bluetooth_radio_test.cc",
"connections/implementation/mediums/wifi_direct_test.cc",
"connections/implementation/mediums/wifi_hotspot_test.cc",
"connections/implementation/mediums/wifi_test.cc",
"connections/implementation/endpoint_channel_manager_test.cc",
+3
View File
@@ -22,6 +22,7 @@ cc_library(
"bluetooth_radio.cc",
"mediums.cc",
"webrtc_stub.cc",
"wifi_direct.cc",
"wifi_hotspot.cc",
"wifi_lan.cc",
],
@@ -33,6 +34,7 @@ cc_library(
"mediums.h",
"webrtc_stub.h",
"wifi.h",
"wifi_direct.h",
"wifi_hotspot.h",
"wifi_lan.h",
],
@@ -99,6 +101,7 @@ cc_test(
"bluetooth_classic_test.cc",
"bluetooth_radio_test.cc",
"lost_entity_tracker_test.cc",
"wifi_direct_test.cc",
"wifi_hotspot_test.cc",
"wifi_lan_test.cc",
"wifi_test.cc",
@@ -32,6 +32,8 @@ WifiLan& Mediums::GetWifiLan() { return wifi_lan_; }
WifiHotspot& Mediums::GetWifiHotspot() { return wifi_hotspot_; }
WifiDirect& Mediums::GetWifiDirect() { return wifi_direct_; }
mediums::WebRtc& Mediums::GetWebRtc() { return webrtc_; }
} // namespace connections
@@ -26,6 +26,7 @@
#endif
#include "connections/implementation/mediums/wifi.h"
#include "connections/implementation/mediums/wifi_hotspot.h"
#include "connections/implementation/mediums/wifi_direct.h"
#include "connections/implementation/mediums/wifi_lan.h"
namespace location {
@@ -59,6 +60,9 @@ class Mediums {
// Returns a handle to the Wifi-Hotspot medium.
WifiHotspot& GetWifiHotspot();
// Returns a handle to the Wifi-Direct medium.
WifiDirect& GetWifiDirect();
// Returns a handle to the WebRtc medium.
mediums::WebRtc& GetWebRtc();
@@ -78,6 +82,7 @@ class Mediums {
Wifi wifi_;
WifiLan wifi_lan_;
WifiHotspot wifi_hotspot_;
WifiDirect wifi_direct_;
mediums::WebRtc webrtc_;
};
@@ -0,0 +1,282 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "connections/implementation/mediums/wifi_direct.h"
#include <string>
#include <utility>
#include "internal/platform/logging.h"
namespace location {
namespace nearby {
namespace connections {
WifiDirect::~WifiDirect() {
while (!server_sockets_.empty()) {
StopAcceptingConnections(server_sockets_.begin()->first);
}
if (is_go_started_) StopWifiDirect();
if (is_connected_to_go_) DisconnectWifiDirect();
// All the AcceptLoopRunnable objects in here should already have gotten an
// opportunity to shut themselves down cleanly in the calls to
// StopAcceptingConnections() above.
accept_loops_runner_.Shutdown();
}
bool WifiDirect::IsGOAvailable() const {
MutexLock lock(&mutex_);
return IsGOAvailableLocked();
}
bool WifiDirect::IsGOAvailableLocked() const {
if (medium_.IsValid()) return medium_.IsInterfaceValid();
return false;
}
bool WifiDirect::IsGCAvailable() const {
MutexLock lock(&mutex_);
return IsGCAvailableLocked();
}
bool WifiDirect::IsGCAvailableLocked() const { return medium_.IsValid(); }
bool WifiDirect::IsGOStarted() {
MutexLock lock(&mutex_);
return is_go_started_;
}
// Call the Medium to start a softAP and send beacon for Client to scan and
// connect
bool WifiDirect::StartWifiDirect() {
MutexLock lock(&mutex_);
if (is_go_started_) {
NEARBY_LOGS(INFO) << "No need to start GO because it is already started.";
return true;
}
is_go_started_ = medium_.StartWifiDirect();
return is_go_started_;
}
bool WifiDirect::StopWifiDirect() {
MutexLock lock(&mutex_);
if (!is_go_started_) {
NEARBY_LOGS(INFO) << "No need to stop GO because it is not started.";
return true;
}
is_go_started_ = false;
medium_.StopWifiDirect();
return true;
}
bool WifiDirect::IsConnectedToGO() {
MutexLock lock(&mutex_);
return is_connected_to_go_;
}
bool WifiDirect::ConnectWifiDirect(const std::string& ssid,
const std::string& password) {
MutexLock lock(&mutex_);
if (is_connected_to_go_) {
NEARBY_LOGS(INFO)
<< "No need to connect to GO because it is already connected.";
return true;
}
is_connected_to_go_ = medium_.ConnectWifiDirect(ssid, password);
return is_connected_to_go_;
}
bool WifiDirect::DisconnectWifiDirect() {
MutexLock lock(&mutex_);
if (!is_connected_to_go_) {
NEARBY_LOGS(INFO)
<< "No need to disconnect to GO because it is not connected.";
return true;
}
is_connected_to_go_ = false;
return medium_.DisconnectWifiDirect();
}
HotspotCredentials* WifiDirect::GetCredentials(absl::string_view service_id) {
MutexLock lock(&mutex_);
HotspotCredentials* crendential = medium_.GetCredential();
CHECK(crendential);
const auto& it = server_sockets_.find(service_id);
if (it == server_sockets_.end()) {
NEARBY_LOGS(INFO) << "No server socket found for service_id:" << service_id
<< ". Use default credentials";
return crendential;
}
crendential->SetGateway(it->second.GetIPAddress());
crendential->SetIPAddress(it->second.GetIPAddress());
crendential->SetPort(it->second.GetPort());
return crendential;
}
bool WifiDirect::StartAcceptingConnections(
const std::string& service_id, AcceptedConnectionCallback callback) {
MutexLock lock(&mutex_);
if (service_id.empty()) {
NEARBY_LOGS(INFO)
<< "Can not to start accepting WifiDirect GC's connections; "
"service_id is empty.";
return false;
}
if (!IsGOAvailableLocked()) {
NEARBY_LOGS(INFO)
<< "Can't start accepting WifiDirect GC's connections [service_id="
<< service_id << "]; WifiDirct GO is not available.";
return false;
}
if (IsAcceptingConnectionsLocked(service_id)) {
NEARBY_LOGS(INFO)
<< "Refusing to start accepting WifiDirect GC's connections [service="
<< service_id
<< "]; WifiDirect GO server is already in-progress with the same name.";
return false;
}
// "port=0" to let the platform to select an available port for the socket
WifiHotspotServerSocket server_socket = medium_.ListenForService(/*port=*/0);
if (!server_socket.IsValid()) {
NEARBY_LOGS(INFO)
<< "Failed to start to listen on WifiDirect GO server for service_id="
<< service_id;
return false;
}
// Mark the fact that there's an in-progress WifiDirect server accepting
// connections.
auto owned_server_socket =
server_sockets_.insert({service_id, std::move(server_socket)})
.first->second;
// Start the accept loop on a dedicated thread - this stays alive and
// listening for new incoming connections until StopAcceptingConnections() is
// invoked.
accept_loops_runner_.Execute(
"wifi-direct-accept",
[callback = std::move(callback),
server_socket = std::move(owned_server_socket), service_id]() mutable {
while (true) {
WifiHotspotSocket client_socket = server_socket.Accept();
if (!client_socket.IsValid()) {
server_socket.Close();
break;
}
callback.accepted_cb(service_id, std::move(client_socket));
}
});
return true;
}
bool WifiDirect::StopAcceptingConnections(const std::string& service_id) {
MutexLock lock(&mutex_);
if (service_id.empty()) {
NEARBY_LOGS(INFO)
<< "Unable to stop accepting WifiDirect GC's connections because "
"the service_id is empty.";
return false;
}
const auto& it = server_sockets_.find(service_id);
if (it == server_sockets_.end()) {
NEARBY_LOGS(INFO) << "Can't stop accepting WifiDirect GC's connections for "
<< service_id << " because it was never started.";
return false;
}
// Closing the WifiDirectServerSocket will kick off the suicide of the thread
// in accept_loops_thread_pool_ that block on WifiDirectServerSocket.accept()
// That may take some time to complete, but there's no particular reason to
// wait around for it.
auto item = server_sockets_.extract(it);
// Store a handle to the WifiDirectServerSocket, so we can use it after
// removing the entry from server_sockets_; making it scoped
// is a bonus that takes care of deallocation before we leave this method.
WifiHotspotServerSocket& listening_socket = item.mapped();
// Regardless of whether or not we fail to close the existing
// WifiDirectServerSocket, remove it from server_sockets_ so that it
// frees up this service for another round.
// Finally, close the WifiDirectServerSocket.
if (!listening_socket.Close().Ok()) {
NEARBY_LOGS(INFO)
<< "Failed to close WifiDirect server socket for service_id:"
<< service_id;
return false;
}
return true;
}
bool WifiDirect::IsAcceptingConnections(const std::string& service_id) {
MutexLock lock(&mutex_);
return IsAcceptingConnectionsLocked(service_id);
}
bool WifiDirect::IsAcceptingConnectionsLocked(const std::string& service_id) {
return server_sockets_.find(service_id) != server_sockets_.end();
}
WifiHotspotSocket WifiDirect::Connect(const std::string& service_id,
const std::string& ip_address, int port,
CancellationFlag* cancellation_flag) {
MutexLock lock(&mutex_);
// Socket to return. To allow for NRVO to work, it has to be a single object.
WifiHotspotSocket socket;
if (service_id.empty()) {
NEARBY_LOGS(INFO) << "Refusing to create client WifiDirect socket because "
"service_id is empty.";
return socket;
}
if (!IsGCAvailableLocked()) {
NEARBY_LOGS(INFO) << "Can't create WifiDirect client socket [service_id="
<< service_id << "]; WifiDirect GC isn't available.";
return socket;
}
if (cancellation_flag->Cancelled()) {
NEARBY_LOGS(INFO) << "Can't create WifiDirect client socket due to cancel";
return socket;
}
socket = medium_.ConnectToService(ip_address, port, cancellation_flag);
if (!socket.IsValid()) {
NEARBY_LOGS(INFO) << "Failed to Connect via WifiDirect Server [service_id="
<< service_id << "]";
}
return socket;
}
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,127 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_INTERNAL_MEDIUMS_WIFI_DIRECT_H_
#define CORE_INTERNAL_MEDIUMS_WIFI_DIRECT_H_
#include <cstdint>
#include <functional>
#include <string>
#include "internal/platform/multi_thread_executor.h"
#include "internal/platform/mutex.h"
#include "internal/platform/wifi_direct.h"
#include "internal/platform/wifi_hotspot.h"
namespace location {
namespace nearby {
namespace connections {
class WifiDirect {
public:
// Callback that is invoked when a new connection is accepted.
struct AcceptedConnectionCallback {
std::function<void(const std::string& service_id, WifiHotspotSocket socket)>
accepted_cb = DefaultCallback<const std::string&, WifiHotspotSocket>();
};
WifiDirect() : is_go_started_(false), is_connected_to_go_(false) {}
~WifiDirect();
// Not copyable or movable
WifiDirect(const WifiDirect&) = delete;
WifiDirect& operator=(const WifiDirect&) = delete;
WifiDirect(WifiDirect&&) = delete;
WifiDirect& operator=(WifiDirect&&) = delete;
// Returns true, if WifiDirect Group Owner is supported by a platform.
bool IsGOAvailable() const ABSL_LOCKS_EXCLUDED(mutex_);
// Returns true, if WifiDirect Group Client is supported by a platform.
bool IsGCAvailable() const ABSL_LOCKS_EXCLUDED(mutex_);
// If WifiDirect Group Owner started
bool IsGOStarted() ABSL_LOCKS_EXCLUDED(mutex_);
// Start WifiDirect Group Owner. Returns true if AutoGO is successfully
// started.
bool StartWifiDirect() ABSL_LOCKS_EXCLUDED(mutex_);
// Stop WifiDirect Group Owner
bool StopWifiDirect() ABSL_LOCKS_EXCLUDED(mutex_);
// If WifiDirect Group Client connects to Group Owner
bool IsConnectedToGO() ABSL_LOCKS_EXCLUDED(mutex_);
// WifiDirect Group Client request to connect to the Group Owner
bool ConnectWifiDirect(const std::string& ssid, const std::string& password)
ABSL_LOCKS_EXCLUDED(mutex_);
// WifiDirect Group Client request to disconnect from the Group Owner
bool DisconnectWifiDirect() ABSL_LOCKS_EXCLUDED(mutex_);
// Starts a worker thread, creates a WifiDirect socket, associates it with a
// service id.
bool StartAcceptingConnections(const std::string& service_id,
AcceptedConnectionCallback callback)
ABSL_LOCKS_EXCLUDED(mutex_);
// Closes socket corresponding to a service id.
bool StopAcceptingConnections(const std::string& service_id)
ABSL_LOCKS_EXCLUDED(mutex_);
bool IsAcceptingConnections(const std::string& service_id)
ABSL_LOCKS_EXCLUDED(mutex_);
// Establishes connection to WifiDirect service by ip address and port for
// bandwidth upgradation.
// Returns socket instance. On success, WifiDirectSocket.IsValid() return
// true.
WifiHotspotSocket Connect(const std::string& service_id,
const std::string& ip_address, int port,
CancellationFlag* cancellation_flag)
ABSL_LOCKS_EXCLUDED(mutex_);
// Gets SoftAP ssid + password + ip address + gateway + port etc for remote
// services on the network to identify and connect to this service.
// Credential is for the currently-hosted WiFi SoftAP ServerSocket (if any).
HotspotCredentials* GetCredentials(absl::string_view service_id)
ABSL_LOCKS_EXCLUDED(mutex_);
private:
mutable Mutex mutex_;
static constexpr int kMaxConcurrentAcceptLoops = 5;
// Same as IsAvailable(), but must be called with mutex_ held.
bool IsGOAvailableLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
bool IsGCAvailableLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Same as IsAcceptingConnections(), but must be called with mutex_ held.
bool IsAcceptingConnectionsLocked(const std::string& service_id)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
bool is_go_started_ ABSL_GUARDED_BY(mutex_);
bool is_connected_to_go_ ABSL_GUARDED_BY(mutex_);
WifiDirectMedium medium_ ABSL_GUARDED_BY(mutex_);
// A thread pool dedicated to running all the accept loops from
// StartAcceptingConnections().
MultiThreadExecutor accept_loops_runner_{kMaxConcurrentAcceptLoops};
// A map of service_id -> ServerSocket. If map is non-empty, we
// are currently listening for incoming connections.
// WifiDirectServerSocket instances are used from accept_loops_runner_,
// and thus require pointer stability.
absl::flat_hash_map<std::string, WifiHotspotServerSocket> server_sockets_
ABSL_GUARDED_BY(mutex_);
};
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_INTERNAL_MEDIUMS_WIFI_DIRECT_H_
@@ -0,0 +1,189 @@
// 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.
#include "connections/implementation/mediums/wifi_direct.h"
#include <string>
#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "internal/platform/medium_environment.h"
#include "internal/platform/wifi_direct.h"
#include "internal/platform/wifi_hotspot_credential.h"
namespace location {
namespace nearby {
namespace connections {
namespace {
using FeatureFlags = FeatureFlags::Flags;
constexpr FeatureFlags kTestCases[] = {
FeatureFlags{
.enable_cancellation_flag = true,
},
FeatureFlags{
.enable_cancellation_flag = false,
},
};
constexpr absl::string_view kServiceID{"com.google.location.nearby.apps.test"};
constexpr absl::string_view kSsid{"Direct-357a2d8c"};
constexpr absl::string_view kPassword{"12345678"};
constexpr absl::string_view kIp = "123.234.23.1";
constexpr const size_t kPort = 20;
class WifiDirectTest : public testing::TestWithParam<FeatureFlags> {
protected:
WifiDirectTest() {
env_.Stop();
env_.Start();
}
~WifiDirectTest() override { env_.Stop(); }
MediumEnvironment& env_{MediumEnvironment::Instance()};
};
INSTANTIATE_TEST_SUITE_P(ParametrisedWifiDirectMediumTest, WifiDirectTest,
testing::ValuesIn(kTestCases));
TEST_F(WifiDirectTest, ConstructorDestructorWorks) {
WifiDirect wifi_direct_a, wifi_direct_b;
EXPECT_NE(&wifi_direct_a, &wifi_direct_b);
EXPECT_TRUE(wifi_direct_a.IsGCAvailable());
EXPECT_TRUE(wifi_direct_b.IsGCAvailable());
}
TEST_F(WifiDirectTest, CanStartStopGO) {
std::string service_id(kServiceID);
WifiDirect wifi_direct_a;
if (wifi_direct_a.IsGOAvailable()) {
EXPECT_FALSE(wifi_direct_a.IsGOStarted());
EXPECT_TRUE(wifi_direct_a.StartWifiDirect());
EXPECT_TRUE(wifi_direct_a.StartAcceptingConnections(service_id, {}));
EXPECT_TRUE(wifi_direct_a.IsGOStarted());
EXPECT_TRUE(wifi_direct_a.StopWifiDirect());
EXPECT_FALSE(wifi_direct_a.IsGOStarted());
} else {
EXPECT_FALSE(wifi_direct_a.StartWifiDirect());
}
}
TEST_F(WifiDirectTest, GCCanConnectDisconnectGO) {
std::string ssid(kSsid);
std::string password(kPassword);
WifiDirect wifi_direct_a;
EXPECT_FALSE(wifi_direct_a.ConnectWifiDirect(ssid, password));
EXPECT_TRUE(wifi_direct_a.DisconnectWifiDirect());
}
TEST_P(WifiDirectTest, CanStartGOThatOtherConnect) {
FeatureFlags feature_flags = GetParam();
env_.SetFeatureFlags(feature_flags);
std::string service_id(kServiceID);
std::string ip(kIp);
WifiDirect wifi_direct_a, wifi_direct_b;
EXPECT_FALSE(wifi_direct_b.IsConnectedToGO());
EXPECT_TRUE(wifi_direct_a.StartWifiDirect());
if (!wifi_direct_a.IsAcceptingConnections(service_id)) {
EXPECT_TRUE(wifi_direct_a.StartAcceptingConnections(service_id, {}));
}
HotspotCredentials* wifi_direct_credentials =
wifi_direct_a.GetCredentials(service_id);
EXPECT_TRUE(
wifi_direct_b.ConnectWifiDirect(wifi_direct_credentials->GetSSID(),
wifi_direct_credentials->GetPassword()));
EXPECT_TRUE(wifi_direct_b.IsConnectedToGO());
WifiHotspotSocket socket_client;
EXPECT_FALSE(socket_client.IsValid());
CancellationFlag flag;
socket_client = wifi_direct_b.Connect(service_id, ip, kPort, &flag);
EXPECT_FALSE(socket_client.IsValid());
socket_client =
wifi_direct_b.Connect(service_id, wifi_direct_credentials->GetGateway(),
wifi_direct_credentials->GetPort(), &flag);
EXPECT_TRUE(socket_client.IsValid());
EXPECT_TRUE(wifi_direct_b.DisconnectWifiDirect());
EXPECT_FALSE(wifi_direct_b.IsConnectedToGO());
EXPECT_TRUE(wifi_direct_a.StopWifiDirect());
}
TEST_P(WifiDirectTest, CanStartGOThatOtherCanCancelConnect) {
FeatureFlags feature_flags = GetParam();
env_.SetFeatureFlags(feature_flags);
std::string service_id(kServiceID);
std::string ip(kIp);
WifiDirect wifi_direct_a, wifi_direct_b;
EXPECT_TRUE(wifi_direct_a.StartWifiDirect());
if (!wifi_direct_a.IsAcceptingConnections(service_id)) {
EXPECT_TRUE(wifi_direct_a.StartAcceptingConnections(service_id, {}));
}
HotspotCredentials* wifi_direct_credentials =
wifi_direct_a.GetCredentials(service_id);
EXPECT_TRUE(
wifi_direct_b.ConnectWifiDirect(wifi_direct_credentials->GetSSID(),
wifi_direct_credentials->GetPassword()));
WifiHotspotSocket socket_client;
EXPECT_FALSE(socket_client.IsValid());
CancellationFlag flag(true);
socket_client =
wifi_direct_b.Connect(service_id, wifi_direct_credentials->GetGateway(),
wifi_direct_credentials->GetPort(), &flag);
// If FeatureFlag is disabled, Cancelled is false as no-op.
if (!feature_flags.enable_cancellation_flag) {
EXPECT_TRUE(socket_client.IsValid());
EXPECT_TRUE(wifi_direct_b.DisconnectWifiDirect());
EXPECT_TRUE(wifi_direct_a.StopWifiDirect());
} else {
EXPECT_FALSE(socket_client.IsValid());
EXPECT_TRUE(wifi_direct_b.DisconnectWifiDirect());
EXPECT_TRUE(wifi_direct_a.StopWifiDirect());
}
}
TEST_F(WifiDirectTest, CanStartGOTheOtherFailConnect) {
WifiDirect wifi_direct_a, wifi_direct_b;
EXPECT_TRUE(wifi_direct_a.StartWifiDirect());
std::string ssid(kSsid);
std::string password(kPassword);
EXPECT_FALSE(wifi_direct_b.ConnectWifiDirect(ssid, password));
EXPECT_TRUE(wifi_direct_b.DisconnectWifiDirect());
EXPECT_TRUE(wifi_direct_a.StopWifiDirect());
}
} // namespace
} // namespace connections
} // namespace nearby
} // namespace location