Merge branch 'master' into release

Change-Id: I8a6cfe28093bf3d60dc91bcbc4e98e641764c4c0
This commit is contained in:
Alexey Polyudov
2020-07-07 12:54:19 -07:00
29 changed files with 1034 additions and 234 deletions
+1 -1
View File
@@ -16,7 +16,7 @@
#include "absl/numeric/int128.h"
#include "absl/strings/numbers.h"
#include "smhasher/MurmurHash3.h"
#include "smhasher/src/MurmurHash3.h"
namespace location {
namespace nearby {
+1 -1
View File
@@ -16,7 +16,7 @@
#include "absl/numeric/int128.h"
#include "absl/strings/numbers.h"
#include "smhasher/MurmurHash3.h"
#include "smhasher/src/MurmurHash3.h"
namespace location {
namespace nearby {
+73 -19
View File
@@ -21,11 +21,13 @@
#include "core_v2/internal/mediums/webrtc/signaling_frames.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/listeners.h"
#include "platform_v2/public/cancelable_alarm.h"
#include "platform_v2/public/future.h"
#include "platform_v2/public/logging.h"
#include "platform_v2/public/mutex_lock.h"
#include "location/nearby/mediums/proto/web_rtc_signaling_frames.pb.h"
#include "absl/strings/str_cat.h"
#include "absl/time/time.h"
#include "webrtc/api/jsep.h"
namespace location {
@@ -36,19 +38,22 @@ namespace mediums {
namespace {
// The maximum amount of time to wait to connect to a data channel via WebRTC.
// TODO(himanshujaju): Should this be configurable per platform?
constexpr absl::Duration kDataChannelTimeout = absl::Milliseconds(5000);
// Delay between restarting signaling messenger to receive messages.
constexpr absl::Duration kRestartReceiveMessagesDuration = absl::Seconds(60);
} // namespace
WebRtc::WebRtc() = default;
WebRtc::~WebRtc() {
// This ensures that all pending callbacks are run before we reset the medium
// and we are not accepting new runnables.
restart_receive_messages_executor_.Shutdown();
single_thread_executor_.Shutdown();
{
MutexLock lock(&mutex_);
Disconnect();
}
Disconnect();
}
bool WebRtc::IsAvailable() { return medium_.IsValid(); }
@@ -84,6 +89,11 @@ bool WebRtc::StartAcceptingConnections(const PeerId& self_id,
if (!InitWebRtcFlow(Role::kOfferer, self_id)) return false;
restart_receive_messages_alarm_ = CancelableAlarm(
"restart_receiving_messages_webrtc",
std::bind(&WebRtc::RestartReceiveMessages, this),
kRestartReceiveMessagesDuration, &restart_receive_messages_executor_);
SessionDescriptionWrapper offer = connection_flow_->CreateOffer();
pending_local_offer_ = webrtc_frames::EncodeOffer(self_id, offer.GetSdp());
if (!SetLocalSessionDescription(std::move(offer))) {
@@ -103,23 +113,25 @@ bool WebRtc::StartAcceptingConnections(const PeerId& self_id,
}
WebRtcSocketWrapper WebRtc::Connect(const PeerId& peer_id) {
MutexLock lock(&mutex_);
if (!IsAvailable()) {
Disconnect();
return WebRtcSocketWrapper();
}
if (role_ != Role::kNone) {
NEARBY_LOG(WARNING,
"Cannot connect with WebRtc because we are already acting as %d",
role_);
return WebRtcSocketWrapper();
}
{
MutexLock lock(&mutex_);
if (role_ != Role::kNone) {
NEARBY_LOG(
WARNING,
"Cannot connect with WebRtc because we are already acting as %d",
role_);
return WebRtcSocketWrapper();
}
peer_id_ = peer_id;
if (!InitWebRtcFlow(Role::kAnswerer, PeerId::FromRandom())) {
return WebRtcSocketWrapper();
peer_id_ = peer_id;
if (!InitWebRtcFlow(Role::kAnswerer, PeerId::FromRandom())) {
return WebRtcSocketWrapper();
}
}
NEARBY_LOG(INFO, "Attempting to make a WebRTC connection to %s.",
@@ -130,6 +142,9 @@ WebRtcSocketWrapper WebRtc::Connect(const PeerId& peer_id) {
// The two devices have discovered each other, hence we have a timeout for
// establishing the transport channel.
// NOTE - We should not hold |mutex_| while waiting for the data channel since
// it would block incoming signaling messages from being processed, resulting
// in a timeout in creating the socket.
ExceptionOr<WebRtcSocketWrapper> result =
socket_future.Get(kDataChannelTimeout);
if (result.ok()) return result.result();
@@ -200,7 +215,8 @@ WebRtcSocketWrapper WebRtc::CreateWebRtcSocketWrapper(
}
auto socket = std::make_unique<WebRtcSocket>("WebRtcSocket", data_channel);
socket->SetOnSocketClosedListener({std::bind(&WebRtc::Disconnect, this)});
socket->SetOnSocketClosedListener(
{[this]() { OffloadFromSignalingThread([this]() { Disconnect(); }); }});
return WebRtcSocketWrapper(std::move(socket));
}
@@ -231,7 +247,7 @@ bool WebRtc::InitWebRtcFlow(Role role, const PeerId& self_id) {
if (!signaling_messenger_->IsValid() ||
!signaling_messenger_->StartReceivingMessages(
signaling_message_callback)) {
Disconnect();
DisconnectLocked();
return false;
}
@@ -407,7 +423,7 @@ void WebRtc::SendAnswerToPeer() {
void WebRtc::LogAndDisconnect(const std::string& error_message) {
NEARBY_LOG(WARNING, "Disconnecting WebRTC : %s", error_message.c_str());
Disconnect();
DisconnectLocked();
}
void WebRtc::LogAndShutdownSignaling(const std::string& error_message) {
@@ -422,6 +438,11 @@ void WebRtc::ShutdownSignaling() {
pending_local_offer_ = ByteArray();
pending_local_ice_candidates_.clear();
if (restart_receive_messages_alarm_.IsValid()) {
restart_receive_messages_alarm_.Cancel();
restart_receive_messages_alarm_ = CancelableAlarm();
}
if (signaling_messenger_) {
signaling_messenger_->StopReceivingMessages();
signaling_messenger_.reset();
@@ -431,6 +452,11 @@ void WebRtc::ShutdownSignaling() {
}
void WebRtc::Disconnect() {
MutexLock lock(&mutex_);
DisconnectLocked();
}
void WebRtc::DisconnectLocked() {
ShutdownSignaling();
ShutdownWebRtcSocket();
ShutdownIceCandidateCollection();
@@ -454,6 +480,34 @@ void WebRtc::OffloadFromSignalingThread(Runnable runnable) {
single_thread_executor_.Execute(std::move(runnable));
}
void WebRtc::RestartReceiveMessages() {
if (!IsAcceptingConnections()) {
NEARBY_LOG(INFO,
"Skipping restart since we are not accepting connections.");
return;
}
NEARBY_LOG(INFO, "Restarting listening for receiving signaling messages.");
{
MutexLock lock(&mutex_);
signaling_messenger_->StopReceivingMessages();
signaling_messenger_ = medium_.GetSignalingMessenger(self_id_.GetId());
auto signaling_message_callback = [this](ByteArray message) {
OffloadFromSignalingThread([this, message{std::move(message)}]() {
ProcessSignalingMessage(message);
});
};
if (!signaling_messenger_->IsValid() ||
!signaling_messenger_->StartReceivingMessages(
signaling_message_callback)) {
DisconnectLocked();
}
}
}
} // namespace mediums
} // namespace connections
} // namespace nearby
+14 -1
View File
@@ -24,9 +24,12 @@
#include "core_v2/internal/mediums/webrtc/peer_id.h"
#include "core_v2/internal/mediums/webrtc/webrtc_socket.h"
#include "core_v2/internal/mediums/webrtc/webrtc_socket_wrapper.h"
#include "platform_v2/public/cancelable_alarm.h"
#include "platform_v2/public/scheduled_executor.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/listeners.h"
#include "platform_v2/base/runnable.h"
#include "platform_v2/public/atomic_boolean.h"
#include "platform_v2/public/future.h"
#include "platform_v2/public/mutex.h"
#include "platform_v2/public/single_thread_executor.h"
@@ -126,8 +129,11 @@ class WebRtc {
void LogAndDisconnect(const std::string& error_message)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Runs on @MainThread.
void Disconnect() ABSL_LOCKS_EXCLUDED(mutex_);
// Runs on @MainThread and |single_thread_executor_|.
void Disconnect() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
void DisconnectLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
void LogAndShutdownSignaling(const std::string& error_message)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
@@ -143,6 +149,9 @@ class WebRtc {
void OffloadFromSignalingThread(Runnable runnable);
// Runs on |restart_receive_messages_executor_|.
void RestartReceiveMessages() ABSL_LOCKS_EXCLUDED(mutex_);
Mutex mutex_;
Role role_ ABSL_GUARDED_BY(mutex_) = Role::kNone;
@@ -159,6 +168,10 @@ class WebRtc {
WebRtcSocketWrapper socket_ ABSL_GUARDED_BY(mutex_);
SingleThreadExecutor single_thread_executor_;
// Restarts the signaling messenger for receiving messages.
ScheduledExecutor restart_receive_messages_executor_;
CancelableAlarm restart_receive_messages_alarm_;
};
} // namespace mediums
+120 -9
View File
@@ -103,28 +103,139 @@ TEST(WebRtcTest, StartAndStopAcceptingConnections) {
EXPECT_FALSE(webrtc.IsAcceptingConnections());
}
// Tests the flow when the device calls StartAcceptingConnections() after
// calling Connect() without disconnecting in between.
TEST(WebRtcTest, Connect_ThenStartAcceptingConnections) {
// TODO(himanshujaju) - Complete the test.
}
// Tests the flow when the device tries to connect to two different peers
// without disconnecting in between.
TEST(WebRtcTest, ConnectTwice) {
// TODO(himanshujaju) - Complete the test.
WebRtc receiver, sender, device_c;
WebRtcSocketWrapper receiver_socket, sender_socket;
const PeerId self_id("self_id"), other_id("other_id");
Future<bool> connected;
ByteArray message("message xyz");
receiver.StartAcceptingConnections(
self_id,
{[&receiver_socket, connected](WebRtcSocketWrapper wrapper) mutable {
receiver_socket = wrapper;
connected.Set(receiver_socket.IsValid());
}});
using MockAcceptedCallback =
testing::MockFunction<void(WebRtcSocketWrapper socket)>;
testing::StrictMock<MockAcceptedCallback> mock_accepted_callback_;
device_c.StartAcceptingConnections(other_id,
{mock_accepted_callback_.AsStdFunction()});
sender_socket = sender.Connect(self_id);
EXPECT_TRUE(sender_socket.IsValid());
ExceptionOr<bool> devices_connected = connected.Get();
ASSERT_TRUE(devices_connected.ok());
EXPECT_TRUE(devices_connected.result());
WebRtcSocketWrapper socket = sender.Connect(other_id);
EXPECT_FALSE(socket.IsValid());
EXPECT_TRUE(receiver_socket.IsValid());
EXPECT_TRUE(sender_socket.IsValid());
sender_socket.GetOutputStream().Write(message);
ExceptionOr<ByteArray> received_msg =
receiver_socket.GetInputStream().Read(/*size=*/32);
ASSERT_TRUE(received_msg.ok());
EXPECT_EQ(message, received_msg.result());
receiver_socket.Close();
}
// Tests the flow when the two devices exchange SDP messages and connect to each
// other but disconnect before being able to send/receive the actual data.
TEST(WebRtcTest, ConnectBothDevicesAndAbort) {
// TODO(himanshujaju) - Complete the test.
WebRtc receiver, sender;
WebRtcSocketWrapper receiver_socket, sender_socket;
const PeerId self_id("self_id");
Future<bool> connected;
ByteArray message("message xyz");
receiver.StartAcceptingConnections(
self_id,
{[&receiver_socket, connected](WebRtcSocketWrapper wrapper) mutable {
receiver_socket = wrapper;
connected.Set(receiver_socket.IsValid());
}});
sender_socket = sender.Connect(self_id);
EXPECT_TRUE(sender_socket.IsValid());
ExceptionOr<bool> devices_connected = connected.Get();
ASSERT_TRUE(devices_connected.ok());
EXPECT_TRUE(devices_connected.result());
receiver_socket.Close();
}
// Tests the flow when the two devices exchange SDP messages and connect to each
// other and the actual data is exchanged successfully between the devices.
TEST(WebRtcTest, ConnectBothDevicesAndSendData) {
// TODO(himanshujaju) - Complete the test.
WebRtc receiver, sender;
WebRtcSocketWrapper receiver_socket, sender_socket;
const PeerId self_id("self_id");
Future<bool> connected;
ByteArray message("message");
receiver.StartAcceptingConnections(
self_id,
{[&receiver_socket, connected](WebRtcSocketWrapper wrapper) mutable {
receiver_socket = wrapper;
connected.Set(receiver_socket.IsValid());
}});
sender_socket = sender.Connect(self_id);
EXPECT_TRUE(sender_socket.IsValid());
ExceptionOr<bool> devices_connected = connected.Get();
ASSERT_TRUE(devices_connected.ok());
EXPECT_TRUE(devices_connected.result());
sender_socket.GetOutputStream().Write(message);
ExceptionOr<ByteArray> received_msg =
receiver_socket.GetInputStream().Read(/*size=*/32);
ASSERT_TRUE(received_msg.ok());
EXPECT_EQ(message, received_msg.result());
receiver_socket.Close();
}
// Tests the flow when the two devices exchange SDP messages and connect to each
// other but the signaling channel is closed before sending the data.
TEST(WebRtcTest, ConnectBothDevices_ShutdownSignaling_SendData) {
WebRtc receiver, sender;
WebRtcSocketWrapper receiver_socket, sender_socket;
const PeerId self_id("self_id");
Future<bool> connected;
ByteArray message("message xyz");
receiver.StartAcceptingConnections(
self_id,
{[&receiver_socket, connected](WebRtcSocketWrapper wrapper) mutable {
receiver_socket = wrapper;
connected.Set(receiver_socket.IsValid());
}});
sender_socket = sender.Connect(self_id);
EXPECT_TRUE(sender_socket.IsValid());
ExceptionOr<bool> devices_connected = connected.Get();
ASSERT_TRUE(devices_connected.ok());
EXPECT_TRUE(devices_connected.result());
// Only shuts down signaling channel.
receiver.StopAcceptingConnections();
sender_socket.GetOutputStream().Write(message);
ExceptionOr<ByteArray> received_msg =
receiver_socket.GetInputStream().Read(/*size=*/32);
ASSERT_TRUE(received_msg.ok());
EXPECT_EQ(message, received_msg.result());
}
} // namespace
+22 -11
View File
@@ -57,24 +57,28 @@ bool WifiLan::StartAdvertising(const std::string& service_id,
return false;
}
NEARBY_LOG(INFO, "Turned on WifiLan advertising with service info name=%s",
wifi_lan_service_info_name.c_str());
NEARBY_LOGS(INFO) << "Turned on WifiLan advertising with service info name="
<< wifi_lan_service_info_name
<< ", service id=" << service_id;
advertising_info_.service_id = service_id;
return true;
}
void WifiLan::StopAdvertising(const std::string& service_id) {
bool WifiLan::StopAdvertising(const std::string& service_id) {
MutexLock lock(&mutex_);
if (!IsAdvertisingLocked()) {
NEARBY_LOG(INFO, "Can't turn off WifiLan advertising; it is already off");
return;
return false;
}
medium_.StopAdvertising(advertising_info_.service_id);
NEARBY_LOG(INFO, "Turned off WifiLan advertising with service id=%s",
service_id.c_str());
bool ret = medium_.StopAdvertising(advertising_info_.service_id);
// Reset our bundle of advertising state to mark that we're no longer
// advertising.
advertising_info_.Clear();
return ret;
}
bool WifiLan::IsAdvertising() {
@@ -117,23 +121,28 @@ bool WifiLan::StartDiscovery(const std::string& service_id,
return false;
}
NEARBY_LOG(INFO, "Turned on WifiLan discovering with service id=%s",
service_id.c_str());
// Mark the fact that we're currently performing a WifiLan discovering.
discovering_info_.service_id = service_id;
return true;
}
void WifiLan::StopDiscovery(const std::string& service_id) {
bool WifiLan::StopDiscovery(const std::string& service_id) {
MutexLock lock(&mutex_);
if (!IsDiscoveringLocked(service_id)) {
NEARBY_LOG(INFO,
"Can't turn off WifiLan discovering because we never started "
"discovering.");
return;
return false;
}
medium_.StopDiscovery(service_id);
NEARBY_LOG(INFO, "Turned off WifiLan discovering with service id=%s",
service_id.c_str());
bool ret = medium_.StopDiscovery(service_id);
discovering_info_.Clear();
return ret;
}
bool WifiLan::IsDiscovering(const std::string& service_id) {
@@ -183,20 +192,22 @@ bool WifiLan::StartAcceptingConnections(const std::string& service_id,
return true;
}
void WifiLan::StopAcceptingConnections(const std::string& service_id) {
bool WifiLan::StopAcceptingConnections(const std::string& service_id) {
MutexLock lock(&mutex_);
if (!IsAcceptingConnectionsLocked(service_id)) {
NEARBY_LOG(INFO,
"Can't stop accepting WifiLan connections because it was never "
"started.");
return;
return false;
}
medium_.StopAcceptingConnections(accepting_connections_info_.service_id);
bool ret =
medium_.StopAcceptingConnections(accepting_connections_info_.service_id);
// Reset our bundle of accepting connections state to mark that we're no
// longer accepting connections.
accepting_connections_info_.Clear();
return ret;
}
bool WifiLan::IsAcceptingConnections(const std::string& service_id) {
+3 -3
View File
@@ -44,7 +44,7 @@ class WifiLan {
// Disables WifiLan advertising, and restores service info name to
// what they were before the call to StartAdvertising().
void StopAdvertising(const std::string& service_id)
bool StopAdvertising(const std::string& service_id)
ABSL_LOCKS_EXCLUDED(mutex_);
bool IsAdvertising() ABSL_LOCKS_EXCLUDED(mutex_);
@@ -57,7 +57,7 @@ class WifiLan {
ABSL_LOCKS_EXCLUDED(mutex_);
// Disables WifiLan discovery mode.
void StopDiscovery(const std::string& service_id) ABSL_LOCKS_EXCLUDED(mutex_);
bool StopDiscovery(const std::string& service_id) ABSL_LOCKS_EXCLUDED(mutex_);
bool IsDiscovering(const std::string& service_id) ABSL_LOCKS_EXCLUDED(mutex_);
@@ -68,7 +68,7 @@ class WifiLan {
ABSL_LOCKS_EXCLUDED(mutex_);
// Closes socket corresponding to a service id.
void StopAcceptingConnections(const std::string& service_id)
bool StopAcceptingConnections(const std::string& service_id)
ABSL_LOCKS_EXCLUDED(mutex_);
bool IsAcceptingConnections(const std::string& service_id)
+99 -4
View File
@@ -17,6 +17,8 @@
#include <string>
#include "platform_v2/base/medium_environment.h"
#include "platform_v2/public/count_down_latch.h"
#include "platform_v2/public/logging.h"
#include "platform_v2/public/wifi_lan.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@@ -26,11 +28,11 @@ namespace nearby {
namespace connections {
namespace {
constexpr absl::Duration kWaitDuration = absl::Milliseconds(1000);
constexpr absl::string_view kServiceID{"com.google.location.nearby.apps.test"};
constexpr absl::string_view kServiceInfoName{
"Simulated WifiLan service encrypted string #1"};
// TODO(edwinwu): Continue writing more tests after medium_environment is done.
class WifiLanTest : public ::testing::Test {
protected:
using DiscoveredServiceCallback = WifiLanMedium::DiscoveredServiceCallback;
@@ -44,6 +46,8 @@ TEST_F(WifiLanTest, CanConstructValidObject) {
env_.Start();
WifiLan wifi_lan_a;
WifiLan wifi_lan_b;
std::string service_id(kServiceID);
std::string service_name{kServiceInfoName};
EXPECT_TRUE(wifi_lan_a.IsAvailable());
EXPECT_TRUE(wifi_lan_b.IsAvailable());
@@ -52,9 +56,100 @@ TEST_F(WifiLanTest, CanConstructValidObject) {
TEST_F(WifiLanTest, CanStartAdvertising) {
env_.Start();
WifiLan wifi_lan;
EXPECT_TRUE(wifi_lan.StartAdvertising(std::string(kServiceID),
std::string(kServiceInfoName)));
WifiLan wifi_lan_a;
WifiLan wifi_lan_b;
std::string service_id(kServiceID);
std::string service_name{kServiceInfoName};
CountDownLatch found_latch(1);
wifi_lan_b.StartDiscovery(
service_id, DiscoveredServiceCallback{
.service_discovered_cb =
[&found_latch](WifiLanService& service,
const std::string& service_id) {
found_latch.CountDown();
},
});
EXPECT_TRUE(wifi_lan_a.StartAdvertising(service_id, service_name));
EXPECT_TRUE(found_latch.Await(kWaitDuration).result());
EXPECT_TRUE(wifi_lan_a.StopAdvertising(service_id));
EXPECT_TRUE(wifi_lan_b.StopDiscovery(service_id));
env_.Stop();
}
TEST_F(WifiLanTest, CanStartDiscovery) {
env_.Start();
WifiLan wifi_lan_a;
WifiLan wifi_lan_b;
std::string service_id(kServiceID);
std::string service_name{kServiceInfoName};
CountDownLatch accept_latch(1);
CountDownLatch lost_latch(1);
wifi_lan_b.StartAdvertising(service_id, service_name);
EXPECT_TRUE(wifi_lan_a.StartDiscovery(
service_id, {
.service_discovered_cb =
[&accept_latch](WifiLanService& service,
const std::string& service_id) {
accept_latch.CountDown();
},
.service_lost_cb =
[&lost_latch](WifiLanService& service,
const std::string& service_id) {
lost_latch.CountDown();
},
}));
EXPECT_TRUE(accept_latch.Await(kWaitDuration).result());
wifi_lan_b.StopAdvertising(service_id);
EXPECT_TRUE(lost_latch.Await(kWaitDuration).result());
EXPECT_TRUE(wifi_lan_a.StopDiscovery(service_id));
env_.Stop();
}
TEST_F(WifiLanTest, CanStartAcceptingConnectionsAndConnect) {
env_.Start();
WifiLan wifi_lan_a;
WifiLan wifi_lan_b;
std::string service_id(kServiceID);
std::string service_name{kServiceInfoName};
CountDownLatch found_latch(1);
CountDownLatch accept_latch(1);
wifi_lan_a.StartAdvertising(service_id, service_name);
wifi_lan_a.StartAcceptingConnections(
service_id,
{
.accepted_cb = [&accept_latch](
WifiLanSocket socket,
const std::string&) { accept_latch.CountDown(); },
});
WifiLanService discovered_service;
wifi_lan_b.StartDiscovery(
service_id,
{
.service_discovered_cb =
[&found_latch, &discovered_service](
WifiLanService& service, const std::string& service_id) {
discovered_service = service;
NEARBY_LOG(INFO, "Discovered service=%p [impl=%p]", &service,
&service.GetImpl());
found_latch.CountDown();
},
});
EXPECT_TRUE(found_latch.Await(kWaitDuration).result());
ASSERT_TRUE(discovered_service.IsValid());
WifiLanSocket socket =
wifi_lan_b.Connect(discovered_service, service_id);
EXPECT_TRUE(accept_latch.Await(kWaitDuration).result());
EXPECT_TRUE(socket.IsValid());
wifi_lan_b.StopDiscovery(service_id);
wifi_lan_a.StopAdvertising(service_id);
env_.Stop();
}
@@ -1,17 +1,3 @@
// 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 "core_v2/internal/offline_service_controller.h"
#include <string>
@@ -1,17 +1,3 @@
// 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 CORE_V2_INTERNAL_OFFLINE_SERVICE_CONTROLLER_H_
#define CORE_V2_INTERNAL_OFFLINE_SERVICE_CONTROLLER_H_
@@ -1,17 +1,3 @@
// 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 "core_v2/internal/offline_service_controller.h"
#include "core_v2/internal/offline_simulation_user.h"
@@ -1,17 +1,3 @@
// 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 "core_v2/internal/offline_simulation_user.h"
#include "core_v2/listeners.h"
@@ -1,17 +1,3 @@
// 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 CORE_V2_INTERNAL_OFFLINE_SIMULATION_USER_H_
#define CORE_V2_INTERNAL_OFFLINE_SIMULATION_USER_H_
@@ -602,7 +602,7 @@ proto::connections::Medium P2pClusterPcpHandler::StartWifiLanAdvertising(
service_id.c_str());
if (!wifi_lan_medium_.StartAcceptingConnections(
service_id, {.accepted_cb = [this, client, local_endpoint_name](
WifiLanSocket& socket,
WifiLanSocket socket,
const std::string& service_id) {
if (!socket.IsValid()) {
NEARBY_LOG(ERROR, "Invalid socket in accept callback: name=%s",
+83 -33
View File
@@ -166,32 +166,19 @@ void MediumEnvironment::OnWifiLanServiceStateChanged(
WifiLanMediumContext& info, api::WifiLanService& service,
const std::string& service_id, bool enabled) {
if (!enabled_) return;
auto item = info.services.find(&service);
if (item == info.services.end()) {
NEARBY_LOG(INFO,
"G3 OnWifiLanServiceStateChanged [service impl=%p]: new service",
&service);
info.services.emplace(&service, service.GetName());
if (enabled) {
RunOnMediumEnvironmentThread([&info, &service, service_id]() {
info.discovery_callback.service_discovered_cb(service, service_id);
});
}
NEARBY_LOG(INFO,
"G3 OnWifiLanServiceStateChanged [service impl=%p]; context=%p, "
"notify=%d",
&info, &service, enable_notifications_.load());
if (!enable_notifications_) return;
if (enabled) {
RunOnMediumEnvironmentThread([&info, &service, service_id]() {
info.discovery_callback.service_discovered_cb(service, service_id);
});
} else {
NEARBY_LOG(INFO,
"G3 OnWifiLanServiceStateChanged [service impl=%p]: exisitng "
"service",
&service);
if (enabled) {
RunOnMediumEnvironmentThread([&info, &service, service_id]() {
info.discovery_callback.service_discovered_cb(service, service_id);
});
} else {
RunOnMediumEnvironmentThread([&info, &service, service_id]() {
info.discovery_callback.service_lost_cb(service, service_id);
});
info.services.erase(item);
}
RunOnMediumEnvironmentThread([&info, &service, service_id]() {
info.discovery_callback.service_lost_cb(service, service_id);
});
}
}
@@ -298,14 +285,44 @@ void MediumEnvironment::SendWebRtcSignalingMessage(absl::string_view peer_id,
});
}
void MediumEnvironment::RegisterWifiLanMedium(api::WifiLanMedium& medium) {
void MediumEnvironment::RegisterWifiLanMedium(api::WifiLanMedium& medium,
api::WifiLanService& service) {
if (!enabled_) return;
RunOnMediumEnvironmentThread([this, &medium]() {
wifi_lan_mediums_.insert({&medium, WifiLanMediumContext{}});
RunOnMediumEnvironmentThread([this, &medium, &service]() {
wifi_lan_mediums_.insert({&medium, WifiLanMediumContext{
.service = &service,
}});
NEARBY_LOG(INFO, "Registered: medium=%p", &medium);
});
}
void MediumEnvironment::UpdateWifiLanMediumForAdvertising(
api::WifiLanMedium& medium, api::WifiLanService& service,
const std::string& service_id, bool enabled) {
if (!enabled_) return;
RunOnMediumEnvironmentThread([this, &medium, &service, service_id,
enabled]() {
auto item = wifi_lan_mediums_.find(&medium);
if (item == wifi_lan_mediums_.end()) {
NEARBY_LOG(
INFO, "Update WifiLan medium failed. There is no medium registered.");
return;
}
auto& context = item->second;
context.advertising = enabled;
NEARBY_LOG(
INFO,
"Update WifiLan medium for advertising: this=%p; medium=%p; name=%s; "
"enabled=%d; advertising=%d",
this, &medium, service.GetName().c_str(), enabled, context.advertising);
for (auto& [local_medium, info] : wifi_lan_mediums_) {
// Do not send notification to the same medium.
if (local_medium == &medium) continue;
OnWifiLanServiceStateChanged(info, service, service_id, enabled);
}
});
}
void MediumEnvironment::UpdateWifiLanMediumForDiscovery(
api::WifiLanMedium& medium, api::WifiLanService& service,
const std::string& service_id, WifiLanDiscoveredServiceCallback callback,
@@ -321,16 +338,29 @@ void MediumEnvironment::UpdateWifiLanMediumForDiscovery(
}
auto& context = item->second;
context.discovery_callback = std::move(callback);
NEARBY_LOG(INFO, "Updated: this=%p; medium=%p", this, &medium);
OnWifiLanServiceStateChanged(context, service, service_id, enabled);
NEARBY_LOG(
INFO,
"Update WifiLan medium for discovery: this=%p; medium=%p; name=%s; "
"enabled=%d; advertising=%d",
this, &medium, service.GetName().c_str(), enabled, context.advertising);
for (auto& [local_medium, info] : wifi_lan_mediums_) {
// Do not send notification to the same medium.
if (local_medium == &medium) continue;
// Search advertising mediums and send notification.
if (info.advertising && enabled) {
OnWifiLanServiceStateChanged(context, *(info.service), service_id,
enabled);
}
}
});
}
void MediumEnvironment::UpdateWifiLanMediumForAcceptedConnection(
api::WifiLanMedium& medium, const std::string& service_id,
api::WifiLanMedium& medium, api::WifiLanService& service,
const std::string& service_id,
WifiLanAcceptedConnectionCallback accepted_connection_callback) {
if (!enabled_) return;
RunOnMediumEnvironmentThread([this, &medium,
RunOnMediumEnvironmentThread([this, &medium, &service, service_id,
accepted_connection_callback =
std::move(accepted_connection_callback)]() {
auto item = wifi_lan_mediums_.find(&medium);
@@ -342,7 +372,10 @@ void MediumEnvironment::UpdateWifiLanMediumForAcceptedConnection(
auto& context = item->second;
context.accepted_connection_callback =
std::move(accepted_connection_callback);
NEARBY_LOG(INFO, "Updated: this=%p; medium=%p", this, &medium);
NEARBY_LOG(INFO,
"Update WifiLan medium for accepted callback: this=%p; "
"medium=%p; name=%s; ",
this, &medium, service.GetName().c_str());
});
}
@@ -355,5 +388,22 @@ void MediumEnvironment::UnregisterWifiLanMedium(api::WifiLanMedium& medium) {
});
}
void MediumEnvironment::CallWifiLanAcceptedConnectionCallback(
api::WifiLanMedium& medium, api::WifiLanSocket& socket,
const std::string& service_id) {
if (!enabled_) return;
RunOnMediumEnvironmentThread([this, &medium, &socket, service_id]() {
auto item = wifi_lan_mediums_.find(&medium);
if (item == wifi_lan_mediums_.end()) {
NEARBY_LOG(INFO,
"Call AcceptedConnectionCallback failed.. There is no medium "
"registered.");
return;
}
auto& info = item->second;
info.accepted_connection_callback.accepted_cb(socket, service_id);
});
}
} // namespace nearby
} // namespace location
+36 -5
View File
@@ -118,17 +118,48 @@ class MediumEnvironment {
// |peer_id|.
void SendWebRtcSignalingMessage(absl::string_view peer_id,
const ByteArray& message);
// Wifi-Lan medium registration/update calls.
void RegisterWifiLanMedium(api::WifiLanMedium& medium);
// Adds medium-related info to allow for discovery/advertising to work.
// This provides acccess to this medium from other mediums, when protocol
// expects they should communicate.
void RegisterWifiLanMedium(api::WifiLanMedium& medium,
api::WifiLanService& service);
// Updates advertising info to indicate the current medium is exposing
// advertising event.
void UpdateWifiLanMediumForAdvertising(
api::WifiLanMedium& medium, api::WifiLanService& service,
const std::string& service_id, bool enabled);
// Updates discovery callback info to allow for dispatch of discovery events.
//
// Invokes callback asynchronously when any changes happen to discoverable
// devices, or if the defice is turned off, whether or not it is discoverable,
// if it was ever reported as discoverable.
//
// This should be called when discoverable state changes.
// with user-specified callback when discovery is enabled, and with default
// (empty) callback otherwise.
void UpdateWifiLanMediumForDiscovery(
api::WifiLanMedium& medium, api::WifiLanService& service,
const std::string& service_id,
WifiLanDiscoveredServiceCallback discovery_callback, bool enabled);
// Updates Accepted connection callback info to allow for dispatch of
// advertising events.
void UpdateWifiLanMediumForAcceptedConnection(
api::WifiLanMedium& medium, const std::string& service_id,
api::WifiLanMedium& medium, api::WifiLanService& service,
const std::string& service_id,
WifiLanAcceptedConnectionCallback accepted_connection_callback);
// Removes medium-related info. This should correspond to device power off.
void UnregisterWifiLanMedium(api::WifiLanMedium& medium);
// Call back when advertising has created the server socket and is ready for
// connect.
void CallWifiLanAcceptedConnectionCallback(api::WifiLanMedium& medium,
api::WifiLanSocket& socket,
const std::string& service_id);
private:
struct BluetoothMediumContext {
BluetoothDiscoveryCallback callback;
@@ -140,8 +171,8 @@ class MediumEnvironment {
struct WifiLanMediumContext {
WifiLanDiscoveredServiceCallback discovery_callback;
WifiLanAcceptedConnectionCallback accepted_connection_callback;
// discovered service vs service name map.
absl::flat_hash_map<api::WifiLanService*, std::string> services;
api::WifiLanService* service = nullptr;
bool advertising = false;
};
// This is a singleton object, for which destructor will never be called.
+252 -31
View File
@@ -14,6 +14,7 @@
#include "platform_v2/impl/g3/wifi_lan.h"
#include <iostream>
#include <memory>
#include <string>
@@ -26,20 +27,45 @@ namespace location {
namespace nearby {
namespace g3 {
InputStream& WifiLanSocket::GetInputStream() {
WifiLanSocket::~WifiLanSocket() {
absl::MutexLock lock(&mutex_);
return pipe_.GetInputStream();
DoClose();
}
void WifiLanSocket::Connect(WifiLanSocket& other) {
absl::MutexLock lock(&mutex_);
remote_socket_ = &other;
input_ = other.output_;
}
InputStream& WifiLanSocket::GetInputStream() {
auto* remote_socket = GetRemoteSocket();
CHECK(remote_socket != nullptr);
return remote_socket->GetLocalInputStream();
}
OutputStream& WifiLanSocket::GetOutputStream() {
return GetLocalOutputStream();
}
WifiLanSocket* WifiLanSocket::GetRemoteSocket() {
absl::MutexLock lock(&mutex_);
return pipe_.GetOutputStream();
return remote_socket_;
}
bool WifiLanSocket::IsConnected() const {
absl::MutexLock lock(&mutex_);
return IsConnectedLocked();
}
bool WifiLanSocket::IsClosed() const {
absl::MutexLock lock(&mutex_);
return closed_;
}
Exception WifiLanSocket::Close() {
absl::MutexLock lock(&mutex_);
pipe_.GetOutputStream().Close();
pipe_.GetInputStream().Close();
DoClose();
return {Exception::kSuccess};
}
@@ -48,45 +74,215 @@ WifiLanService* WifiLanSocket::GetRemoteWifiLanService() {
return service_;
}
void WifiLanSocket::DoClose() {
if (!closed_) {
remote_socket_ = nullptr;
output_->GetOutputStream().Close();
output_->GetInputStream().Close();
if (IsConnectedLocked()) {
input_->GetOutputStream().Close();
input_->GetInputStream().Close();
}
closed_ = true;
}
}
bool WifiLanSocket::IsConnectedLocked() const { return input_ != nullptr; }
InputStream& WifiLanSocket::GetLocalInputStream() {
absl::MutexLock lock(&mutex_);
return output_->GetInputStream();
}
OutputStream& WifiLanSocket::GetLocalOutputStream() {
absl::MutexLock lock(&mutex_);
return output_->GetOutputStream();
}
std::unique_ptr<api::WifiLanSocket> WifiLanServerSocket::Accept() {
absl::MutexLock lock(&mutex_);
if (closed_) return {};
while (pending_sockets_.empty()) {
cond_.Wait(&mutex_);
if (closed_) break;
}
if (closed_) return {};
auto* remote_socket =
pending_sockets_.extract(pending_sockets_.begin()).value();
CHECK(remote_socket);
auto local_socket = std::make_unique<WifiLanSocket>();
local_socket->Connect(*remote_socket);
remote_socket->Connect(*local_socket);
cond_.SignalAll();
return local_socket;
}
bool WifiLanServerSocket::Connect(WifiLanSocket& socket) {
absl::MutexLock lock(&mutex_);
if (closed_) return false;
if (socket.IsConnected()) {
NEARBY_LOG(ERROR,
"Failed to connect to WifiLan server socket: already connected");
return true; // already connected.
}
// add client socket to the pending list
pending_sockets_.emplace(&socket);
cond_.SignalAll();
while (!socket.IsConnected()) {
cond_.Wait(&mutex_);
if (closed_) return false;
}
return true;
}
void WifiLanServerSocket::SetCloseNotifier(std::function<void()> notifier) {
absl::MutexLock lock(&mutex_);
close_notifier_ = std::move(notifier);
}
WifiLanServerSocket::~WifiLanServerSocket() {
absl::MutexLock lock(&mutex_);
DoClose();
}
Exception WifiLanServerSocket::Close() {
absl::MutexLock lock(&mutex_);
return DoClose();
}
Exception WifiLanServerSocket::DoClose() {
bool should_notify = !closed_;
closed_ = true;
if (should_notify) {
cond_.SignalAll();
if (close_notifier_) {
auto notifier = std::move(close_notifier_);
mutex_.Unlock();
// Notifier may contain calls to public API, and may cause deadlock, if
// mutex_ is held during the call.
notifier();
mutex_.Lock();
}
}
return {Exception::kSuccess};
}
WifiLanMedium::WifiLanMedium() {
service_.SetMedium(this);
auto& env = MediumEnvironment::Instance();
env.RegisterWifiLanMedium(*this);
env.RegisterWifiLanMedium(*this, service_);
}
WifiLanMedium::~WifiLanMedium() {
service_.SetMedium(nullptr);
auto& env = MediumEnvironment::Instance();
env.UnregisterWifiLanMedium(*this);
StopAdvertising(advertising_info_.service_id);
StopDiscovery(discovering_info_.service_id);
accept_loops_runner_.Shutdown();
NEARBY_LOG(INFO,
"WifiLanMedium dtor advertising_accept_thread_running_ = %d",
acceptance_thread_running_.load());
// If acceptance thread is still running, wait to finish.
if (acceptance_thread_running_) {
while (acceptance_thread_running_) {
CountDownLatch latch(1);
close_accept_loops_runner_.Execute([&latch]() { latch.CountDown(); });
latch.Await();
}
}
}
bool WifiLanMedium::StartAdvertising(
const std::string& service_id,
const std::string& wifi_lan_service_info_name) {
// TODO(edwinwu): Integrate medium_environment.
// steps:
// 1. create wifi_lan_service as the parameter to create wifi_lan_socket
// auto service = std::make_unique<WifiLanService>();
// auto socket = std::make_unique<WifiLanSocket>(service);
// 2. callback for accepting connection; otherwise don't callback if not
// accepted connection.
// accepted_connection_callback_.accepted_cb(socket, service_id);
NEARBY_LOG(INFO,
"G3 WifiLan StartAdvertising: service_id=%s, service_name=%s",
service_id.c_str(), wifi_lan_service_info_name.c_str());
auto& env = MediumEnvironment::Instance();
env.UpdateWifiLanMediumForAdvertising(*this, service_, service_id, true);
absl::MutexLock lock(&mutex_);
if (server_socket_ != nullptr) server_socket_.release();
server_socket_ = std::make_unique<WifiLanServerSocket>();
acceptance_thread_running_.exchange(true);
accept_loops_runner_.Execute([&env, this, service_id]() mutable {
if (!accept_loops_runner_.InShutdown()) {
while (true) {
auto client_socket = server_socket_->Accept();
if (client_socket == nullptr) break;
env.CallWifiLanAcceptedConnectionCallback(*this, *client_socket,
service_id);
}
}
acceptance_thread_running_.exchange(false);
});
advertising_info_.service_id = service_id;
return true;
}
bool WifiLanMedium::StopAdvertising(const std::string& service_id) {
// TODO(edwinwu): Integrate medium_environment.
NEARBY_LOG(INFO, "G3 WifiLan StopAdvertising: service_id=%s",
service_id.c_str());
{
absl::MutexLock lock(&mutex_);
if (advertising_info_.Empty()) {
NEARBY_LOG(
INFO, "Can't stop advertising because we never started advertising.");
return false;
}
advertising_info_.Clear();
}
auto& env = MediumEnvironment::Instance();
env.UpdateWifiLanMediumForAdvertising(*this, service_, service_id, false);
accept_loops_runner_.Shutdown();
if (server_socket_ == nullptr) {
NEARBY_LOG(ERROR, "Failed to find WifiLan Server socket: service_id=%s",
service_id.c_str());
// Fall through for server socket not found.
return true;
}
if (!server_socket_->Close().Ok()) {
NEARBY_LOG(INFO, "Failed to close WifiLan server socket for %s.",
service_id.c_str());
return false;
}
return true;
}
bool WifiLanMedium::StartDiscovery(const std::string& service_id,
DiscoveredServiceCallback callback) {
NEARBY_LOG(INFO, "G3 WifiLan StartDiscovery: service_id=%s",
service_id.c_str());
auto& env = MediumEnvironment::Instance();
NEARBY_LOG(INFO, "G3 StartDiscovery: service_id=%s", service_id.c_str());
env.UpdateWifiLanMediumForDiscovery(*this, service_, service_id,
std::move(callback), true);
{
absl::MutexLock lock(&mutex_);
discovering_info_.service_id = service_id;
}
return true;
}
bool WifiLanMedium::StopDiscovery(const std::string& service_id) {
NEARBY_LOG(INFO, "G3 WifiLan StopDiscovery: service_id=%s",
service_id.c_str());
{
absl::MutexLock lock(&mutex_);
if (discovering_info_.Empty()) {
NEARBY_LOG(
INFO, "Can't stop discovering because we never started discovering.");
return false;
}
discovering_info_.Clear();
}
auto& env = MediumEnvironment::Instance();
env.UpdateWifiLanMediumForDiscovery(*this, service_, service_id, {}, false);
return true;
@@ -94,33 +290,58 @@ bool WifiLanMedium::StopDiscovery(const std::string& service_id) {
bool WifiLanMedium::StartAcceptingConnections(
const std::string& service_id, AcceptedConnectionCallback callback) {
// TODO(edwinwu): Integrate medium_environment.
// steps:
NEARBY_LOG(INFO, "G3 WifiLan StartAcceptingConnections: service_id=%s",
service_id.c_str());
auto& env = MediumEnvironment::Instance();
env.UpdateWifiLanMediumForAcceptedConnection(*this, service_id, callback);
env.UpdateWifiLanMediumForAcceptedConnection(*this, service_, service_id,
callback);
return true;
}
bool WifiLanMedium::StopAcceptingConnections(const std::string& service_id) {
// TODO(edwinwu): Integrate medium_environment.
NEARBY_LOG(INFO, "G3 WifiLan StopAcceptingConnections: service_id=%s",
service_id.c_str());
auto& env = MediumEnvironment::Instance();
env.UpdateWifiLanMediumForAcceptedConnection(*this, service_id, {});
env.UpdateWifiLanMediumForAcceptedConnection(*this, service_, service_id, {});
return true;
}
std::unique_ptr<api::WifiLanSocket> WifiLanMedium::Connect(
api::WifiLanService& service, const std::string& service_id) {
api::WifiLanService& remote_service, const std::string& service_id) {
NEARBY_LOG(INFO, "G3 WifiLan Connect: medium=%p, service=%p, service_id=%s",
this, &service_, service_id.c_str());
// First, find an instance of remote medium, that exposed this service.
auto* medium = static_cast<WifiLanService&>(remote_service).GetMedium();
if (!medium) return {}; // Can't find medium. Bail out.
WifiLanServerSocket* server_socket = nullptr;
NEARBY_LOG(INFO,
"G3 WifiLan Connect [peer]: medium=%p, service=%p, service_id=%s",
medium, &remote_service, service_id.c_str());
// Then, find our server socket context in this medium.
{
absl::MutexLock medium_lock(&medium->mutex_);
server_socket = medium->server_socket_.get();
if (server_socket == nullptr) {
NEARBY_LOG(ERROR, "Failed to find WifiLan Server socket: service_id=%s",
service_id.c_str());
return {};
}
}
auto socket = std::make_unique<WifiLanSocket>();
NEARBY_LOG(INFO, "G3 Connect: medium=%p, service_id=%s", this,
service_id.c_str());
// Finally, Request to connect to this socket.
if (!server_socket->Connect(*socket)) {
NEARBY_LOG(
ERROR,
"Failed to connect to existing WifiLan Server socket: service_id=%s",
service_id.c_str());
return {};
}
NEARBY_LOG(INFO, "G3 WifiLan Connect: connected: socket=%p", socket.get());
return socket;
// TODO(edwinwu): Integrate medium_environment.
// steps:
// Request a connection, and block until the socket is provided via the
// callback.
// 1. connection = wifi_lan_service.requestConnection_();
// 2. create wifi_lan_socket with wifi_lan_service and connection
// return wifi_lan_socket;
}
} // namespace g3
+125 -8
View File
@@ -15,20 +15,25 @@
#ifndef PLATFORM_V2_IMPL_G3_WIFI_LAN_H_
#define PLATFORM_V2_IMPL_G3_WIFI_LAN_H_
#include <memory>
#include <string>
#include "platform_v2/api/wifi_lan.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/output_stream.h"
#include "platform_v2/impl/g3/multi_thread_executor.h"
#include "platform_v2/impl/g3/pipe.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/synchronization/mutex.h"
namespace location {
namespace nearby {
namespace g3 {
class WifiLanMedium;
// Opaque wrapper over a WifiLan service which contains encoded WifiLan service
// info name.
class WifiLanService : public api::WifiLanService {
@@ -39,19 +44,23 @@ class WifiLanService : public api::WifiLanService {
void SetName(std::string name) { name_ = std::move(name); }
std::string GetName() const override { return name_; }
void SetMedium(WifiLanMedium* medium) { medium_ = medium; }
WifiLanMedium* GetMedium() { return medium_; }
private:
std::string name_;
WifiLanMedium* medium_ = nullptr;
};
class WifiLanSocket : public api::WifiLanSocket {
public:
WifiLanSocket() = default;
explicit WifiLanSocket(WifiLanService* service) : service_(service) {}
~WifiLanSocket() override = default;
~WifiLanSocket() override;
// Connect to another WifiLanSocket, to form a functional low-level channel.
// from this point on, and until Close is called, connection exists.
void ConnectTo(WifiLanSocket* other) ABSL_LOCKS_EXCLUDED(mutex_);
void Connect(WifiLanSocket& other) ABSL_LOCKS_EXCLUDED(mutex_);
// Returns the InputStream of this connected WifiLanSocket.
InputStream& GetInputStream() override ABSL_LOCKS_EXCLUDED(mutex_);
@@ -60,6 +69,15 @@ class WifiLanSocket : public api::WifiLanSocket {
// This stream is for local side to write.
OutputStream& GetOutputStream() override ABSL_LOCKS_EXCLUDED(mutex_);
// Returns address of a remote WifiLanSocket or nullptr.
WifiLanSocket* GetRemoteSocket() ABSL_LOCKS_EXCLUDED(mutex_);
// Returns true if connection exists to the (possibly closed) remote socket.
bool IsConnected() const ABSL_LOCKS_EXCLUDED(mutex_);
// Returns true if socket is closed.
bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_);
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_);
@@ -69,9 +87,75 @@ class WifiLanSocket : public api::WifiLanSocket {
ABSL_LOCKS_EXCLUDED(mutex_);
private:
Pipe pipe_;
WifiLanService* service_;
void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns true if connection exists to the (possibly closed) remote socket.
bool IsConnectedLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns InputStream of our side of a connection.
// This is what the remote side is supposed to read from.
// This is a helper for GetInputStream() method.
InputStream& GetLocalInputStream() ABSL_LOCKS_EXCLUDED(mutex_);
// Returns OutputStream of our side of a connection.
// This is what the local size is supposed to write to.
// This is a helper for GetOutputStream() method.
OutputStream& GetLocalOutputStream() ABSL_LOCKS_EXCLUDED(mutex_);
// Output pipe is initialized by constructor, it remains always valid, until
// it is closed. it represents output part of a local socket. Input part of a
// local socket comes from the peer socket, after connection.
std::shared_ptr<Pipe> output_ {new Pipe};
std::shared_ptr<Pipe> input_;
mutable absl::Mutex mutex_;
WifiLanService* service_;
WifiLanSocket* remote_socket_ ABSL_GUARDED_BY(mutex_) = nullptr;
bool closed_ ABSL_GUARDED_BY(mutex_) = false;
};
class WifiLanServerSocket {
public:
~WifiLanServerSocket();
// 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.
//
// Called by the server side of a connection.
// Returns WifiLanSocket to the server side.
// If not null, returned socket is connected to its remote (client-side) peer.
std::unique_ptr<api::WifiLanSocket> Accept() ABSL_LOCKS_EXCLUDED(mutex_);
// Blocks until either:
// - connection is available, or
// - server socket is closed, or
// - error happens.
//
// Called by the client side of a connection.
// Returns true, if socket is successfully connected.
bool Connect(WifiLanSocket& socket) ABSL_LOCKS_EXCLUDED(mutex_);
// 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)
ABSL_LOCKS_EXCLUDED(mutex_);
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
// Calls close_notifier if it was previously set, and marks socket as closed.
Exception Close() ABSL_LOCKS_EXCLUDED(mutex_);
private:
Exception DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
absl::Mutex mutex_;
absl::CondVar cond_;
absl::flat_hash_set<WifiLanSocket*> pending_sockets_ ABSL_GUARDED_BY(mutex_);
std::function<void()> close_notifier_ ABSL_GUARDED_BY(mutex_);
bool closed_ ABSL_GUARDED_BY(mutex_) = false;
};
// Container of operations that can be performed over the WifiLan medium.
@@ -105,15 +189,48 @@ class WifiLanMedium : public api::WifiLanMedium {
bool StopAcceptingConnections(const std::string& service_id) override
ABSL_LOCKS_EXCLUDED(mutex_);
// Returns a new WifiLanSocket. On Success, WifiLanSocket::IsValid()
// returns true.
// Connects to existing remote WifiLan service.
//
// On success, returns a new WifiLanSocket.
// On error, returns nullptr.
std::unique_ptr<api::WifiLanSocket> Connect(
api::WifiLanService& service, const std::string& service_id) override
ABSL_LOCKS_EXCLUDED(mutex_);
api::WifiLanService& remote_service,
const std::string& service_id) override ABSL_LOCKS_EXCLUDED(mutex_);
private:
static constexpr int kMaxConcurrentAcceptLoops = 5;
struct AdvertisingInfo {
bool Empty() const { return service_id.empty(); }
void Clear() { service_id.clear(); }
std::string service_id;
};
struct DiscoveringInfo {
bool Empty() const { return service_id.empty(); }
void Clear() { service_id.clear(); }
std::string service_id;
};
absl::Mutex mutex_;
WifiLanService service_{"wifi_lan_service_info_name"};
// A thread pool dedicated to running all the accept loops from
// StartAdvertising().
MultiThreadExecutor accept_loops_runner_{kMaxConcurrentAcceptLoops};
std::atomic_bool acceptance_thread_running_ = false;
// A thread pool dedicated to wait to complete the accept_loops_runner_.
MultiThreadExecutor close_accept_loops_runner_{kMaxConcurrentAcceptLoops};
// TODO(edwinwu): Extend it to hashmap to accept multiple sockets for multiple
// entrance.
// A server socket is established when start advertising.
std::unique_ptr<WifiLanServerSocket> server_socket_;
AdvertisingInfo advertising_info_ ABSL_GUARDED_BY(mutex_);
DiscoveringInfo discovering_info_ ABSL_GUARDED_BY(mutex_);
};
} // namespace g3
+1
View File
@@ -107,6 +107,7 @@ cc_test(
"atomic_reference_test.cc",
"bluetooth_adapter_test.cc",
"bluetooth_classic_test.cc",
"cancelable_alarm_test.cc",
"condition_variable_test.cc",
"count_down_latch_test.cc",
"crypto_test.cc",
+3 -1
View File
@@ -38,7 +38,9 @@ class Cancelable final {
explicit Cancelable(std::shared_ptr<api::Cancelable> impl)
: impl_(std::move(impl)) {}
bool Cancel() { return impl_->Cancel(); }
bool Cancel() { return impl_ ? impl_->Cancel() : false; }
bool IsValid() { return impl_ != nullptr; }
private:
std::shared_ptr<api::Cancelable> impl_;
@@ -35,6 +35,7 @@ namespace nearby {
*/
class CancelableAlarm {
public:
CancelableAlarm() = default;
CancelableAlarm(absl::string_view name, std::function<void()>&& runnable,
absl::Duration delay, ScheduledExecutor* scheduled_executor)
: name_(name),
@@ -58,6 +59,10 @@ class CancelableAlarm {
return cancelable_.Cancel();
}
bool IsValid() {
return cancelable_.IsValid();
}
private:
Mutex mutex_;
std::string name_;
@@ -0,0 +1,54 @@
#include "platform_v2/public/cancelable_alarm.h"
#include "platform_v2/public/atomic_boolean.h"
#include "platform_v2/public/scheduled_executor.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/time/time.h"
namespace location {
namespace nearby {
namespace {
TEST(CancelableAlarmTest, CanCreateDefault) { CancelableAlarm alarm; }
TEST(CancelableAlarmTest, CancelDefaultFails) {
CancelableAlarm alarm;
EXPECT_FALSE(alarm.Cancel());
}
TEST(CancelableAlarmTest, CanCreateAndFireAlarm) {
ScheduledExecutor alarm_executor;
AtomicBoolean done{false};
CancelableAlarm alarm(
"test_alarm", [&done]() { done.Set(true); }, absl::Milliseconds(100),
&alarm_executor);
SystemClock::Sleep(absl::Milliseconds(1000));
EXPECT_TRUE(done.Get());
}
TEST(CancelableAlarmTest, CanCreateAndCancelAlarm) {
ScheduledExecutor alarm_executor;
AtomicBoolean done{false};
CancelableAlarm alarm(
"test_alarm", [&done]() { done.Set(true); }, absl::Milliseconds(100),
&alarm_executor);
EXPECT_TRUE(alarm.Cancel());
SystemClock::Sleep(absl::Milliseconds(1000));
EXPECT_FALSE(done.Get());
}
TEST(CancelableAlarmTest, CancelExpiredAlarmFails) {
ScheduledExecutor alarm_executor;
AtomicBoolean done{false};
CancelableAlarm alarm(
"test_alarm", [&done]() { done.Set(true); }, absl::Milliseconds(100),
&alarm_executor);
SystemClock::Sleep(absl::Milliseconds(1000));
EXPECT_TRUE(done.Get());
EXPECT_FALSE(alarm.Cancel());
}
} // namespace
} // namespace nearby
} // namespace location
+3 -3
View File
@@ -62,6 +62,7 @@ bool WifiLanMedium::StartDiscovery(const std::string& service_id,
[this](api::WifiLanService& service,
const std::string& service_id) {
MutexLock lock(&mutex_);
if (services_.empty()) return;
auto item = services_.extract(&service);
auto& context = *item.mapped();
NEARBY_LOG(INFO, "Removing service=%p, impl=%p",
@@ -101,9 +102,8 @@ bool WifiLanMedium::StartAcceptingConnections(
if (!pair.second) {
NEARBY_LOG(INFO, "Adding (again) socket=%p, impl=%p",
&context.socket, &socket);
return;
context.socket = WifiLanSocket(&socket);
}
context.socket = WifiLanSocket(&socket);
NEARBY_LOG(INFO, "Adding socket=%p, impl=%p", &context.socket,
&socket);
accepted_connection_callback_.accepted_cb(context.socket,
@@ -120,7 +120,7 @@ bool WifiLanMedium::StopAcceptingConnections(const std::string& service_id) {
NEARBY_LOG(INFO, "WifiLan accepted connection disabled: impl=%p",
&GetImpl());
}
return impl_->StopDiscovery(service_id);
return impl_->StopAcceptingConnections(service_id);
}
WifiLanSocket WifiLanMedium::Connect(WifiLanService& service,
+2 -2
View File
@@ -116,8 +116,8 @@ class WifiLanMedium final {
};
struct AcceptedConnectionCallback {
std::function<void(WifiLanSocket& socket, const std::string& service_id)>
accepted_cb = DefaultCallback<WifiLanSocket&, const std::string&>();
std::function<void(WifiLanSocket socket, const std::string& service_id)>
accepted_cb = DefaultCallback<WifiLanSocket, const std::string&>();
};
struct AcceptedConnectionInfo {
WifiLanSocket socket;
+100 -24
View File
@@ -27,10 +27,12 @@ namespace nearby {
namespace {
constexpr absl::string_view kServiceID{"com.google.location.nearby.apps.test"};
constexpr absl::string_view kServiceName{"service name"};
class WifiLanMediumTest : public ::testing::Test {
protected:
using DiscoveredServiceCallback = WifiLanMedium::DiscoveredServiceCallback;
using AcceptedConnectionCallback = WifiLanMedium::AcceptedConnectionCallback;
WifiLanMediumTest() { env_.Stop(); }
@@ -39,75 +41,149 @@ class WifiLanMediumTest : public ::testing::Test {
TEST_F(WifiLanMediumTest, ConstructorDestructorWorks) {
env_.Start();
WifiLanMedium medium_a;
WifiLanMedium medium_b;
WifiLanMedium wifi_a;
WifiLanMedium wifi_b;
// Make sure we can create functional mediums.
ASSERT_TRUE(medium_a.IsValid());
ASSERT_TRUE(medium_b.IsValid());
ASSERT_TRUE(wifi_a.IsValid());
ASSERT_TRUE(wifi_b.IsValid());
// Make sure we can create 2 distinct mediums.
EXPECT_NE(&medium_a.GetImpl(), &medium_b.GetImpl());
EXPECT_NE(&wifi_a.GetImpl(), &wifi_b.GetImpl());
env_.Stop();
}
TEST_F(WifiLanMediumTest, CanStartDiscoveryAndServiceIndeedDiscovered) {
TEST_F(WifiLanMediumTest, CanStartAdvertising) {
env_.Start();
WifiLanMedium medium;
WifiLanMedium wifi_a;
WifiLanMedium wifi_b;
std::string service_id(kServiceID);
std::string service_name{kServiceName};
CountDownLatch found_latch(1);
wifi_a.StartAdvertising(service_id, service_name);
EXPECT_TRUE(wifi_b.StartDiscovery(
service_id, DiscoveredServiceCallback{
.service_discovered_cb =
[&found_latch](WifiLanService& service,
const std::string& service_id) {
found_latch.CountDown();
},
}));
EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result());
EXPECT_TRUE(wifi_a.StopAdvertising(service_id));
EXPECT_TRUE(wifi_b.StopDiscovery(service_id));
env_.Stop();
}
TEST_F(WifiLanMediumTest, CanStartDiscovery) {
env_.Start();
WifiLanMedium wifi_a;
WifiLanMedium wifi_b;
std::string service_id(kServiceID);
std::string service_name{kServiceName};
CountDownLatch found_latch(1);
CountDownLatch lost_latch(1);
medium.StartDiscovery(std::string(kServiceID),
wifi_a.StartDiscovery(service_id,
DiscoveredServiceCallback{
.service_discovered_cb =
[&found_latch](WifiLanService& service,
const std::string& service_id) {
NEARBY_LOG(INFO, "Service discovered: %s",
service.GetName().c_str());
EXPECT_EQ(kServiceID, service_id);
found_latch.CountDown();
},
.service_lost_cb =
[&lost_latch](WifiLanService& service,
const std::string& service_id) {
NEARBY_LOG(INFO, "Service lost: %s",
service.GetName().c_str());
EXPECT_EQ(kServiceID, service_id);
lost_latch.CountDown();
},
});
EXPECT_TRUE(wifi_b.StartAdvertising(service_id, service_name));
EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result());
EXPECT_TRUE(wifi_b.StopAdvertising(service_id));
EXPECT_TRUE(lost_latch.Await(absl::Milliseconds(1000)).result());
EXPECT_TRUE(wifi_a.StopDiscovery(service_id));
env_.Stop();
}
TEST_F(WifiLanMediumTest, CanStopDiscovery) {
env_.Start();
WifiLanMedium medium;
WifiLanMedium wifi_a;
WifiLanMedium wifi_b;
std::string service_id(kServiceID);
std::string service_name{kServiceName};
CountDownLatch found_latch(1);
CountDownLatch lost_latch(1);
medium.StartDiscovery(std::string(kServiceID),
wifi_a.StartDiscovery(service_id,
DiscoveredServiceCallback{
.service_discovered_cb =
[&found_latch](WifiLanService& service,
const std::string& service_id) {
NEARBY_LOG(INFO, "Service discovered: %s",
service.GetName().c_str());
EXPECT_EQ(kServiceID, service_id);
found_latch.CountDown();
},
.service_lost_cb =
[&lost_latch](WifiLanService& service,
const std::string& service_id) {
NEARBY_LOG(INFO, "Service lost: %s",
service.GetName().c_str());
EXPECT_EQ(kServiceID, service_id);
lost_latch.CountDown();
},
});
EXPECT_TRUE(wifi_b.StartAdvertising(service_id, service_name));
EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result());
bool stop = medium.StopDiscovery(std::string(kServiceID));
EXPECT_TRUE(stop);
EXPECT_TRUE(wifi_a.StopDiscovery(service_id));
EXPECT_TRUE(wifi_b.StopAdvertising(service_id));
EXPECT_FALSE(lost_latch.Await(absl::Milliseconds(1000)).result());
env_.Stop();
}
TEST_F(WifiLanMediumTest, CanStartAcceptingConnectionsAndConnect) {
env_.Start();
WifiLanMedium wifi_a;
WifiLanMedium wifi_b;
std::string service_id(kServiceID);
std::string service_name{kServiceName};
CountDownLatch found_latch(1);
CountDownLatch accepted_latch(1);
WifiLanService* discovered_service = nullptr;
wifi_a.StartDiscovery(
service_id,
DiscoveredServiceCallback{
.service_discovered_cb =
[&found_latch, &discovered_service](
WifiLanService& service, const std::string& service_id) {
NEARBY_LOG(INFO, "Service discovered: %s, %p",
service.GetName().c_str(), &service);
discovered_service = &service;
found_latch.CountDown();
},
});
wifi_b.StartAdvertising(service_id, service_name);
wifi_b.StartAcceptingConnections(
service_id,
AcceptedConnectionCallback{
.accepted_cb = [&accepted_latch](WifiLanSocket socket,
const std::string& service_id) {
NEARBY_LOG(INFO, "Connection accepted: socket=%p, service_id=%s",
&socket, service_id.c_str());
accepted_latch.CountDown();
}});
EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result());
WifiLanSocket socket_a;
EXPECT_FALSE(socket_a.IsValid());
{
SingleThreadExecutor client_executor;
client_executor.Execute(
[&wifi_a, &socket_a, discovered_service, &service_id]() {
socket_a = wifi_a.Connect(*discovered_service, service_id);
});
}
EXPECT_TRUE(accepted_latch.Await(absl::Milliseconds(1000)).result());
EXPECT_TRUE(socket_a.IsValid());
wifi_b.StopAdvertising(service_id);
wifi_a.StopDiscovery(service_id);
env_.Stop();
}