PiperOrigin-RevId: 925564968
This commit is contained in:
Francis Tsui
2026-06-02 14:02:58 -07:00
committed by Copybara-Service
parent ec9759d88a
commit 8bc556c4e7
34 changed files with 9 additions and 5757 deletions
+1 -11
View File
@@ -345,7 +345,6 @@ let package = Package(
"internal/platform/implementation/apple/Mediums/WiFiCommon/BUILD",
"internal/platform/implementation/BUILD",
"internal/platform/BUILD",
"internal/analytics/BUILD",
"internal/flags/BUILD",
"internal/network/BUILD",
"internal/rpc/BUILD",
@@ -369,7 +368,6 @@ let package = Package(
"connections/implementation/offline_frames_validator_test.cc",
"connections/implementation/service_controller_router_test.cc",
"connections/implementation/analytics/analytics_recorder_impl_test.cc",
"connections/implementation/analytics/throughput_recorder_test.cc",
"connections/implementation/mediums/advertisements/data_element_test.cc",
"connections/implementation/mediums/advertisements/dct_advertisement_test.cc",
"connections/implementation/mediums/advertisements/advertisement_util_test.cc",
@@ -434,7 +432,6 @@ let package = Package(
"internal/encoding/base85_test.cc",
"internal/data/leveldb_data_set_test.cc",
"internal/flags/nearby_flags_test.cc",
"internal/proto/analytics/connections_log_test.cc",
"internal/platform/feature_flags_test.cc",
"internal/platform/file_test.cc",
"internal/platform/cancelable_alarm_test.cc",
@@ -499,7 +496,6 @@ let package = Package(
"internal/network/http_client_impl_test.cc",
"internal/network/http_status_code_test.cc",
"internal/test/fake_clock_test.cc",
"internal/test/fake_webrtc.cc",
"internal/test/fake_timer_test.cc",
"internal/test/fake_device_info_test.cc",
"internal/test/fake_task_runner_test.cc",
@@ -521,15 +517,9 @@ let package = Package(
"proto",
"internal/data/leveldb_data_set_test.proto",
// webrtc
"connections/implementation/webrtc_bwu_handler.cc",
"connections/implementation/webrtc_endpoint_channel.cc",
"connections/implementation/mediums/webrtc.cc",
"connections/implementation/mediums/webrtc",
"connections/implementation/mediums/webrtc/tachyon_express_signaling_messenger.cc",
"connections/implementation/mediums/webrtc/tachyon_express_signaling_messenger.h",
"internal/platform/implementation/apple/webrtc.h",
"internal/platform/implementation/apple/webrtc.mm",
// This breaks the build, but seems to work fine without it?
// Only used in tests
"internal/platform/medium_environment.cc",
],
sources: [
@@ -1,268 +0,0 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
# 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.
licenses(["notice"])
cc_library(
name = "webrtc",
hdrs = [
"data_channel_listener.h",
"local_ice_candidate_listener.h",
"session_description_wrapper.h",
],
deps = [
"//connections/implementation/mediums:webrtc_socket",
"//internal/platform:base",
"//third_party/webrtc/files/stable/webrtc/api:jsep",
"@com_google_absl//absl/functional:any_invocable",
],
)
cc_library(
name = "connection_flow",
srcs = ["connection_flow.cc"],
hdrs = ["connection_flow.h"],
deps = [
":webrtc",
":webrtc_medium",
":webrtc_socket_impl",
"//connections/implementation/mediums:webrtc_socket",
"//internal/platform:base",
"//internal/platform:comm",
"//internal/platform:logging",
"//internal/platform:types",
"//third_party/webrtc/files/stable/webrtc/api:data_channel_interface",
"//third_party/webrtc/files/stable/webrtc/api:jsep",
"//third_party/webrtc/files/stable/webrtc/api:libjingle_peerconnection_api",
"//third_party/webrtc/files/stable/webrtc/api:rtc_error",
"//third_party/webrtc/files/stable/webrtc/api:scoped_refptr",
"//third_party/webrtc/files/stable/webrtc/rtc_base:network_constants",
"//third_party/webrtc/files/stable/webrtc/rtc_base:refcount",
"//third_party/webrtc/files/stable/webrtc/rtc_base:threading",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/functional:any_invocable",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/time",
],
)
cc_library(
name = "signaling_frames",
srcs = ["signaling_frames.cc"],
hdrs = ["signaling_frames.h"],
deps = [
"//connections/implementation/mediums:webrtc_peer_id",
"//internal/platform:base",
"//proto/mediums:web_rtc_signaling_frames_cc_proto",
"//third_party/webrtc/files/stable/webrtc/api:libjingle_peerconnection_api",
],
)
cc_library(
name = "webrtc_socket_impl",
srcs = ["webrtc_socket_impl.cc"],
hdrs = ["webrtc_socket_impl.h"],
deps = [
"//connections/implementation/mediums:webrtc_socket",
"//internal/platform:base",
"//internal/platform:logging",
"//internal/platform:types",
"//third_party/webrtc/files/stable/webrtc/api:data_channel_interface",
"//third_party/webrtc/files/stable/webrtc/api:scoped_refptr",
"@com_google_absl//absl/functional:any_invocable",
"@com_google_absl//absl/strings:string_view",
],
)
cc_library(
name = "webrtc_medium",
hdrs = ["webrtc.h"],
deps = [
"//internal/platform:base",
"//internal/platform/implementation:webrtc_platform",
"//third_party/webrtc/files/stable/webrtc/api:libjingle_peerconnection_api",
"//third_party/webrtc/files/stable/webrtc/rtc_base:network_constants",
"@com_google_absl//absl/strings:string_view",
],
)
cc_library(
name = "webrtc_medium_impl",
srcs = ["webrtc_medium_impl.cc"],
hdrs = ["webrtc_medium_impl.h"],
visibility = [
"//internal/platform/implementation:__subpackages__",
],
deps = [
":tachyon_express_signaling_messenger",
"//internal/platform/implementation:webrtc_platform",
"//third_party/webrtc/files/stable/webrtc/api:create_modular_peer_connection_factory",
"//third_party/webrtc/files/stable/webrtc/api:libjingle_peerconnection_api",
"//third_party/webrtc/files/stable/webrtc/api:rtc_error",
"//third_party/webrtc/files/stable/webrtc/api:scoped_refptr",
"//third_party/webrtc/files/stable/webrtc/rtc_base:threading",
"@com_google_absl//absl/strings:string_view",
],
)
cc_library(
name = "webrtc_impl",
srcs = [
"webrtc_bwu_handler.cc",
"webrtc_endpoint_channel.cc",
"webrtc_impl.cc",
],
hdrs = [
"webrtc_bwu_handler.h",
"webrtc_endpoint_channel.h",
"webrtc_impl.h",
],
visibility = [
"//connections/implementation/mediums:__pkg__",
],
deps = [
":connection_flow",
":signaling_frames",
":webrtc",
":webrtc_medium",
"//connections:core_types",
"//connections/implementation:bwu_handler",
"//connections/implementation:client_proxy",
"//connections/implementation:endpoint_channel",
"//connections/implementation:offline_frames",
"//connections/implementation/mediums:webrtc",
"//connections/implementation/mediums:webrtc_peer_id",
"//connections/implementation/mediums:webrtc_socket",
"//connections/implementation/proto:offline_wire_formats_cc_proto",
"//internal/platform:base",
"//internal/platform:cancellation_flag",
"//internal/platform:logging",
"//internal/platform:types",
"//internal/platform/implementation:webrtc_platform",
"//proto/mediums:web_rtc_signaling_frames_cc_proto",
"//third_party/webrtc/files/stable/webrtc/api:jsep",
"//third_party/webrtc/files/stable/webrtc/rtc_base:network_constants",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/base:nullability",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/functional:bind_front",
"@com_google_absl//absl/time",
],
)
cc_library(
name = "tachyon_express_signaling_messenger",
srcs = ["tachyon_express_signaling_messenger.cc"],
hdrs = ["tachyon_express_signaling_messenger.h"],
deps = [
"//internal/account",
"//internal/platform:base",
"//internal/platform:logging",
"//internal/platform:types",
"//internal/platform/implementation:webrtc_platform",
"//internal/proto:messaging_cc_grpc_proto",
"//internal/proto:tachyon_cc_proto",
"//internal/rpc:utils",
"//location/nearby/sharing/lib/account:account_manager",
"//third_party/gloop/util/random:mt_random",
"//third_party/grpc:gpr",
"//third_party/grpc:grpc++",
"//util/random:util",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/functional:any_invocable",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/time",
],
)
cc_library(
name = "fake_webrtc",
testonly = True,
srcs = ["fake_webrtc.cc"],
hdrs = ["fake_webrtc.h"],
deps = [
":webrtc_medium",
"//internal/platform:cancellation_flag",
"@com_google_absl//absl/strings:string_view",
],
)
cc_test(
name = "webrtc_test",
timeout = "short",
srcs = [
"connection_flow_test.cc",
"signaling_frames_test.cc",
"webrtc_bwu_handler_test.cc",
"webrtc_impl_test.cc",
"webrtc_socket_impl_test.cc",
],
shard_count = 16,
tags = [
"requires-net:external",
],
deps = [
":connection_flow",
":fake_webrtc",
":signaling_frames",
":webrtc",
":webrtc_impl",
":webrtc_medium",
":webrtc_socket_impl",
"//connections/implementation:bwu_handler",
"//connections/implementation:client_proxy",
"//connections/implementation:endpoint_channel",
"//connections/implementation:offline_frames",
"//connections/implementation/mediums:webrtc",
"//connections/implementation/mediums:webrtc_peer_id",
"//connections/implementation/mediums:webrtc_socket",
"//internal/platform:base",
"//internal/platform:cancellation_flag",
"//internal/platform:logging",
"//internal/platform:test_util",
"//internal/platform:types",
"//internal/platform/implementation:platform_impl",
"//third_party/webrtc/files/stable/webrtc/api:data_channel_interface",
"//third_party/webrtc/files/stable/webrtc/api:jsep",
"//third_party/webrtc/files/stable/webrtc/api:scoped_refptr",
"//third_party/webrtc/files/stable/webrtc/rtc_base:network_constants",
"//third_party/webrtc/files/stable/webrtc/rtc_base:refcount",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/strings:string_view",
"@com_google_absl//absl/time",
"@com_google_googletest//:gtest_main",
"@com_google_protobuf//:protobuf",
],
)
cc_test(
name = "webrtc_medium_impl_test",
size = "small",
srcs = ["webrtc_medium_impl_test.cc"],
deps = [
":webrtc_medium_impl",
"//internal/platform/implementation:platform_impl",
"//internal/platform/implementation:webrtc_platform",
"//third_party/webrtc/files/stable/webrtc/api:data_channel_interface",
"//third_party/webrtc/files/stable/webrtc/api:jsep",
"//third_party/webrtc/files/stable/webrtc/api:peer_connection_interface",
"//third_party/webrtc/files/stable/webrtc/api:scoped_refptr",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_googletest//:gtest_main",
],
)
@@ -1,17 +0,0 @@
# WebRtc support for Nearby Connections
This directory contains the implementation to support WebRtc in the Nearby
Connection library.
All dependencies on webrtc MUST be limited to targets in this directory.
## To Enable WebRtc support
To enabled WebRtc support undefine the ```NO_WEBRTC``` preprocessor symbol in
the file **connections/implementation/mediums/mediums.cc**.
When building using bazel, pass the build flag
```--//:enable_webrtc=true``` to set the correct symbol.
Make sure the binary is linked with an implementation of the
```WebRtcImplementationPlatform```.
@@ -1,585 +0,0 @@
// 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/webrtc/connection_flow.h"
#include <iterator>
#include <memory>
#include <utility>
#include <vector>
#include "absl/memory/memory.h"
#include "absl/time/time.h"
#include "connections/implementation/mediums/webrtc/data_channel_listener.h"
#include "connections/implementation/mediums/webrtc/local_ice_candidate_listener.h"
#include "connections/implementation/mediums/webrtc/session_description_wrapper.h"
#include "connections/implementation/mediums/webrtc/webrtc.h"
#include "connections/implementation/mediums/webrtc/webrtc_socket_impl.h"
#include "internal/platform/exception.h"
#include "internal/platform/future.h"
#include "internal/platform/logging.h"
#include "internal/platform/mutex_lock.h"
#include "internal/platform/runnable.h"
#include "webrtc/api/data_channel_interface.h"
#include "webrtc/api/jsep.h"
#include "webrtc/api/peer_connection_interface.h"
#include "webrtc/api/rtc_error.h"
#include "webrtc/api/scoped_refptr.h"
#include "webrtc/api/set_local_description_observer_interface.h"
#include "webrtc/api/set_remote_description_observer_interface.h"
#include "webrtc/rtc_base/ref_counted_object.h"
#include "webrtc/rtc_base/thread.h"
namespace nearby {
namespace connections {
namespace mediums {
constexpr absl::Duration ConnectionFlow::kTimeout;
constexpr absl::Duration ConnectionFlow::kPeerConnectionTimeout;
// This is the same as the nearby data channel name.
constexpr char kDataChannelName[] = "dataChannel";
class CreateSessionDescriptionObserverImpl
: public webrtc::CreateSessionDescriptionObserver {
public:
CreateSessionDescriptionObserverImpl(
ConnectionFlow* connection_flow,
Future<SessionDescriptionWrapper> settable_future,
ConnectionFlow::State expected_entry_state,
ConnectionFlow::State exit_state)
: connection_flow_{connection_flow},
settable_future_{settable_future},
expected_entry_state_{expected_entry_state},
exit_state_{exit_state} {}
// webrtc::CreateSessionDescriptionObserver
void OnSuccess(webrtc::SessionDescriptionInterface* desc) override {
if (connection_flow_->TransitionState(expected_entry_state_, exit_state_)) {
settable_future_.Set(SessionDescriptionWrapper{desc});
} else {
settable_future_.SetException({Exception::kFailed});
}
}
void OnFailure(webrtc::RTCError error) override {
LOG(ERROR) << "Error when creating session description: "
<< error.message();
settable_future_.SetException({Exception::kFailed});
}
private:
ConnectionFlow* connection_flow_;
Future<SessionDescriptionWrapper> settable_future_;
ConnectionFlow::State expected_entry_state_;
ConnectionFlow::State exit_state_;
};
class SetDescriptionObserverBase {
public:
ExceptionOr<bool> GetResult(absl::Duration timeout) {
return settable_future_.Get(timeout);
}
protected:
void OnSetDescriptionComplete(webrtc::RTCError error) {
// On success, |error.ok()| is true.
if (error.ok()) {
settable_future_.Set(true);
return;
}
settable_future_.SetException({Exception::kFailed});
}
private:
Future<bool> settable_future_;
};
class SetLocalDescriptionObserver
: public webrtc::SetLocalDescriptionObserverInterface,
public SetDescriptionObserverBase {
public:
void OnSetLocalDescriptionComplete(webrtc::RTCError error) override {
OnSetDescriptionComplete(error);
}
};
class SetRemoteDescriptionObserver
: public webrtc::SetRemoteDescriptionObserverInterface,
public SetDescriptionObserverBase {
public:
void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override {
OnSetDescriptionComplete(error);
}
};
using PeerConnectionState =
webrtc::PeerConnectionInterface::PeerConnectionState;
std::unique_ptr<ConnectionFlow> ConnectionFlow::Create(
LocalIceCandidateListener local_ice_candidate_listener,
DataChannelListener data_channel_listener,
AdapterTypeListener adapter_type_listener, WebRtcMedium& webrtc_medium) {
auto connection_flow = absl::WrapUnique(new ConnectionFlow(
std::move(local_ice_candidate_listener), std::move(data_channel_listener),
std::move(adapter_type_listener)));
if (connection_flow->InitPeerConnection(webrtc_medium)) {
return connection_flow;
}
return nullptr;
}
ConnectionFlow::ConnectionFlow(
LocalIceCandidateListener local_ice_candidate_listener,
DataChannelListener data_channel_listener,
AdapterTypeListener adapter_type_listener)
: data_channel_listener_(std::move(data_channel_listener)),
local_ice_candidate_listener_(std::move(local_ice_candidate_listener)),
adapter_type_listener_(std::move(adapter_type_listener)) {}
ConnectionFlow::~ConnectionFlow() {
LOG(INFO) << "~ConnectionFlow";
RunOnSignalingThread([this] { CloseOnSignalingThread(); });
shutdown_latch_.Await();
LOG(INFO) << "~ConnectionFlow done";
}
SessionDescriptionWrapper ConnectionFlow::CreateOffer() {
CHECK(!IsRunningOnSignalingThread());
Future<SessionDescriptionWrapper> success_future;
if (!RunOnSignalingThread([this, success_future] {
CreateOfferOnSignalingThread(success_future);
})) {
LOG(ERROR) << "Failed to create offer";
return SessionDescriptionWrapper();
}
ExceptionOr<SessionDescriptionWrapper> result = success_future.Get(kTimeout);
if (result.ok()) {
return std::move(result.result());
}
LOG(ERROR) << "Failed to create offer: " << result.exception();
return SessionDescriptionWrapper();
}
void ConnectionFlow::CreateOfferOnSignalingThread(
Future<SessionDescriptionWrapper> success_future) {
if (!TransitionState(State::kInitialized, State::kCreatingOffer)) {
success_future.SetException({Exception::kFailed});
return;
}
webrtc::DataChannelInit data_channel_init;
data_channel_init.reliable = true;
auto pc = GetPeerConnection();
auto result =
pc->CreateDataChannelOrError(kDataChannelName, &data_channel_init);
if (!result.ok()) {
success_future.SetException({Exception::kFailed});
return;
}
CreateSocketFromDataChannel(result.MoveValue());
webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options;
webrtc::scoped_refptr<CreateSessionDescriptionObserverImpl> observer(
new webrtc::RefCountedObject<CreateSessionDescriptionObserverImpl>(
this, success_future, State::kCreatingOffer,
State::kWaitingForAnswer));
pc->CreateOffer(observer.get(), options);
}
SessionDescriptionWrapper ConnectionFlow::CreateAnswer() {
CHECK(!IsRunningOnSignalingThread());
Future<SessionDescriptionWrapper> success_future;
if (!RunOnSignalingThread([this, success_future] {
CreateAnswerOnSignalingThread(success_future);
})) {
LOG(ERROR) << "Failed to create answer";
return SessionDescriptionWrapper();
}
ExceptionOr<SessionDescriptionWrapper> result = success_future.Get(kTimeout);
if (result.ok()) {
return std::move(result.result());
}
LOG(ERROR) << "Failed to create answer: " << result.exception();
return SessionDescriptionWrapper();
}
void ConnectionFlow::CreateAnswerOnSignalingThread(
Future<SessionDescriptionWrapper> success_future) {
if (!TransitionState(State::kReceivedOffer, State::kCreatingAnswer)) {
success_future.SetException({Exception::kFailed});
return;
}
webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options;
webrtc::scoped_refptr<CreateSessionDescriptionObserverImpl> observer(
new webrtc::RefCountedObject<CreateSessionDescriptionObserverImpl>(
this, success_future, State::kCreatingAnswer,
State::kWaitingToConnect));
auto pc = GetPeerConnection();
pc->CreateAnswer(observer.get(), options);
}
bool ConnectionFlow::SetLocalSessionDescription(SessionDescriptionWrapper sdp) {
CHECK(!IsRunningOnSignalingThread());
if (!sdp.IsValid()) return false;
webrtc::scoped_refptr<SetLocalDescriptionObserver> observer(
new webrtc::RefCountedObject<SetLocalDescriptionObserver>());
if (!RunOnSignalingThread([this, observer, sdp = std::move(sdp)]() mutable {
if (state_ == State::kEnded) {
observer->OnSetLocalDescriptionComplete(
webrtc::RTCError(webrtc::RTCErrorType::INVALID_STATE));
return;
}
auto pc = GetPeerConnection();
pc->SetLocalDescription(
std::unique_ptr<webrtc::SessionDescriptionInterface>(sdp.Release()),
observer);
})) {
return false;
}
ExceptionOr<bool> result = observer->GetResult(kTimeout);
bool success = result.ok() && result.result();
if (!success) {
LOG(ERROR) << "Failed to set local session description: "
<< result.exception();
}
return success;
}
bool ConnectionFlow::SetRemoteSessionDescription(SessionDescriptionWrapper sdp,
State expected_entry_state,
State exit_state) {
if (!sdp.IsValid()) return false;
webrtc::scoped_refptr<SetRemoteDescriptionObserver> observer(
new webrtc::RefCountedObject<SetRemoteDescriptionObserver>());
if (!RunOnSignalingThread([this, observer, sdp = std::move(sdp),
expected_entry_state, exit_state]() mutable {
if (!TransitionState(expected_entry_state, exit_state)) {
observer->OnSetRemoteDescriptionComplete(
webrtc::RTCError(webrtc::RTCErrorType::INVALID_STATE));
return;
}
auto pc = GetPeerConnection();
pc->SetRemoteDescription(
std::unique_ptr<webrtc::SessionDescriptionInterface>(sdp.Release()),
observer);
})) {
return false;
}
ExceptionOr<bool> result = observer->GetResult(kTimeout);
bool success = result.ok() && result.result();
if (!success) {
LOG(ERROR) << "Failed to set remote description: " << result.exception();
}
return success;
}
bool ConnectionFlow::OnOfferReceived(SessionDescriptionWrapper offer) {
CHECK(!IsRunningOnSignalingThread());
return SetRemoteSessionDescription(std::move(offer), State::kInitialized,
State::kReceivedOffer);
}
bool ConnectionFlow::OnAnswerReceived(SessionDescriptionWrapper answer) {
CHECK(!IsRunningOnSignalingThread());
return SetRemoteSessionDescription(
std::move(answer), State::kWaitingForAnswer, State::kWaitingToConnect);
}
bool ConnectionFlow::OnRemoteIceCandidatesReceived(
std::vector<std::unique_ptr<webrtc::IceCandidate>> ice_candidates) {
CHECK(!IsRunningOnSignalingThread());
// We can't call RunOnSignalingThread because C++ wants to copy ice_candidates
// if we try. unique_ptr is not CopyConstructible and compilation fails.
auto pc = GetPeerConnection();
if (!pc) {
return false;
}
pc->signaling_thread()->PostTask(
[this, can_run_tasks = std::weak_ptr<void>(can_run_tasks_),
candidates = std::move(ice_candidates)]() mutable {
// Don't run the task if the weak_ptr is no longer valid.
if (!can_run_tasks.lock()) {
return;
}
AddIceCandidatesOnSignalingThread(std::move(candidates));
});
return true;
}
void ConnectionFlow::AddIceCandidatesOnSignalingThread(
std::vector<std::unique_ptr<webrtc::IceCandidate>> ice_candidates) {
CHECK(IsRunningOnSignalingThread());
if (state_ == State::kEnded) {
LOG(WARNING) << "You cannot add ice candidates to a disconnected session.";
return;
}
if (state_ != State::kWaitingToConnect && state_ != State::kConnected) {
cached_remote_ice_candidates_.insert(
cached_remote_ice_candidates_.end(),
std::make_move_iterator(ice_candidates.begin()),
std::make_move_iterator(ice_candidates.end()));
return;
}
auto pc = GetPeerConnection();
for (auto&& ice_candidate : ice_candidates) {
if (!pc->AddIceCandidate(ice_candidate.get())) {
LOG(WARNING) << "Unable to add remote ice candidate.";
}
}
}
bool ConnectionFlow::CloseIfNotConnected() {
CHECK(!IsRunningOnSignalingThread());
Future<bool> closed;
if (RunOnSignalingThread([this, closed]() mutable {
if (state_ == State::kConnected) {
closed.Set(false);
} else {
CloseOnSignalingThread();
closed.Set(true);
}
})) {
auto result = closed.Get();
return result.ok() && result.result();
}
return true;
}
bool ConnectionFlow::InitPeerConnection(WebRtcMedium& webrtc_medium) {
Future<bool> success_future;
// CreatePeerConnection callback may be invoked after ConnectionFlow lifetime
// has ended, in case of a timeout. Future is captured by value, and is safe
// to access, but it is not safe to access ConnectionFlow member variables
// unless the Future::Set() returns true.
webrtc_medium.CreatePeerConnection(
this, [this, success_future](
webrtc::scoped_refptr<webrtc::PeerConnectionInterface>
peer_connection) mutable {
if (!peer_connection) {
success_future.Set(false);
return;
}
// If this fails, means we have already assigned something to
// success_future; it is either:
// 1) this is the 2nd call of this callback (and this is a bug), or
// 2) Get(timeout) has set the future value as exception already.
if (success_future.IsSet()) return;
MutexLock lock(&mutex_);
peer_connection_ = peer_connection;
signaling_thread_for_dcheck_only_ =
peer_connection_->signaling_thread();
success_future.Set(true);
});
ExceptionOr<bool> result = success_future.Get(kPeerConnectionTimeout);
bool success = result.ok() && result.result();
if (!success) {
shutdown_latch_.CountDown();
LOG(ERROR) << "Failed to create peer connection: " << result.exception();
}
return success;
}
void ConnectionFlow::OnSignalingStable() {
if (state_ != State::kWaitingToConnect && state_ != State::kConnected) return;
auto pc = GetPeerConnection();
for (auto&& ice_candidate : cached_remote_ice_candidates_) {
if (!pc->AddIceCandidate(ice_candidate.get())) {
LOG(WARNING) << "Unable to add remote ice candidate.";
}
}
cached_remote_ice_candidates_.clear();
}
void ConnectionFlow::CreateSocketFromDataChannel(
webrtc::scoped_refptr<webrtc::DataChannelInterface> data_channel) {
LOG(INFO) << "Creating data channel socket";
auto socket = std::make_shared<WebRtcSocketImpl>("WebRtcSocket",
std::move(data_channel));
socket_ = socket;
socket->SetSocketListener({
.socket_ready_cb = {[this](WebRtcSocketImpl* socket) {
CHECK(IsRunningOnSignalingThread());
if (!TransitionState(State::kWaitingToConnect, State::kConnected)) {
LOG(ERROR) << "Data channel socket is open but connection "
"flow was not in the required state";
socket->Close();
return;
}
// Pass socket wrapper by copy on purpose
data_channel_listener_.data_channel_open_cb(socket_);
}},
.socket_closed_cb =
[this](WebRtcSocketImpl*) {
data_channel_listener_.data_channel_closed_cb();
},
});
}
void ConnectionFlow::OnIceCandidate(const webrtc::IceCandidate* candidate) {
CHECK(IsRunningOnSignalingThread());
local_ice_candidate_listener_.local_ice_candidate_found_cb(candidate);
}
void ConnectionFlow::OnSignalingChange(
webrtc::PeerConnectionInterface::SignalingState new_state) {
LOG(INFO) << "OnSignalingChange: " << new_state;
CHECK(IsRunningOnSignalingThread());
if (new_state == webrtc::PeerConnectionInterface::SignalingState::kStable) {
OnSignalingStable();
}
}
void ConnectionFlow::OnDataChannel(
webrtc::scoped_refptr<webrtc::DataChannelInterface> data_channel) {
LOG(INFO) << "OnDataChannel";
CHECK(IsRunningOnSignalingThread());
CreateSocketFromDataChannel(std::move(data_channel));
}
void ConnectionFlow::OnIceGatheringChange(
webrtc::PeerConnectionInterface::IceGatheringState new_state) {
LOG(INFO) << "OnIceGatheringChange: " << new_state;
CHECK(IsRunningOnSignalingThread());
}
void ConnectionFlow::OnConnectionChange(
webrtc::PeerConnectionInterface::PeerConnectionState new_state) {
LOG(INFO) << "OnConnectionChange: " << static_cast<int>(new_state);
CHECK(IsRunningOnSignalingThread());
if (new_state == PeerConnectionState::kClosed ||
new_state == PeerConnectionState::kFailed ||
new_state == PeerConnectionState::kDisconnected) {
LOG(INFO) << "Closing due to peer connection state change: "
<< static_cast<int>(new_state);
CloseOnSignalingThread();
}
}
void ConnectionFlow::OnRenegotiationNeeded() {
LOG(INFO) << "OnRenegotiationNeeded";
CHECK(IsRunningOnSignalingThread());
}
void ConnectionFlow::OnIceSelectedCandidatePairChanged(
const webrtc::CandidatePairChangeEvent& event) {
LOG(INFO) << "OnIceSelectedCandidatePairChanged";
CHECK(IsRunningOnSignalingThread());
// TODO(edwinwu) - Implement the unit test for this. We should be able to get
// the adapter type from the PeerConnection.
adapter_type_listener_.adapter_type_changed_cb(
event.selected_candidate_pair.local_candidate().network_type());
}
bool ConnectionFlow::TransitionState(State current_state, State new_state) {
CHECK(IsRunningOnSignalingThread());
if (current_state != state_) {
LOG(WARNING) << "Invalid state transition to "
<< static_cast<int>(new_state) << ": current state is "
<< static_cast<int>(state_) << " but expected "
<< static_cast<int>(current_state);
return false;
}
LOG(INFO) << "Transition: " << static_cast<int>(state_) << "->"
<< static_cast<int>(new_state);
state_ = new_state;
return true;
}
bool ConnectionFlow::CloseOnSignalingThread() {
if (state_ == State::kEnded) {
return false;
}
state_ = State::kEnded;
// Close the socket wrapper before terminating the PeerConnection
// since the teardown process of the PC may close threads that are
// otherwise depended upon by objects kept alive by the socket_wrapper.
if (socket_ && socket_->IsValid()) socket_->Close();
// This prevents other tasks from queuing on the signaling thread for this
// object.
auto pc = GetAndResetPeerConnection();
LOG(INFO) << "Closing WebRTC peer connection.";
// NOTE: Closing the peer connection will close the data channel and thus the
// socket implicitly.
if (pc) pc->Close();
LOG(INFO) << "Closed WebRTC peer connection.";
// Prevent any already queued tasks from running on the signaling thread
can_run_tasks_.reset();
// If anyone was waiting for shutdown to be done let them know.
shutdown_latch_.CountDown();
return true;
}
bool ConnectionFlow::RunOnSignalingThread(Runnable&& runnable) {
CHECK(!IsRunningOnSignalingThread());
auto pc = GetPeerConnection();
if (!pc) {
LOG(WARNING) << "Peer connection not available. Cannot schedule tasks.";
return false;
}
// We are off signaling thread, so we can't use peer connection's methods
// but we can access the signaling thread handle.
pc->signaling_thread()->PostTask(
[can_run_tasks = std::weak_ptr<void>(can_run_tasks_),
task = std::move(runnable)]() mutable {
// Don't run the task if the weak_ptr is no longer valid.
// shared_ptr |can_run_tasks_| is destroyed on the same thread
// (signaling thread). This guarantees that if the weak_ptr is valid
// when this task starts, it will stay valid until the task ends.
if (!can_run_tasks.lock()) {
LOG(INFO) << "Peer connection already closed. Cannot run tasks.";
return;
}
task();
});
return true;
}
bool ConnectionFlow::IsRunningOnSignalingThread() {
return signaling_thread_for_dcheck_only_ != nullptr &&
signaling_thread_for_dcheck_only_ == webrtc::Thread::Current();
}
webrtc::scoped_refptr<webrtc::PeerConnectionInterface>
ConnectionFlow::GetPeerConnection() {
// We must use a mutex to ensure that peer connection is
// fully initialized.
// We increase the peer_connection_'s refcount to keep it
// alive while we use it.
MutexLock lock(&mutex_);
return peer_connection_;
}
webrtc::scoped_refptr<webrtc::PeerConnectionInterface>
ConnectionFlow::GetAndResetPeerConnection() {
MutexLock lock(&mutex_);
return std::move(peer_connection_);
}
} // namespace mediums
} // namespace connections
} // namespace nearby
@@ -1,250 +0,0 @@
// 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_INTERNAL_MEDIUMS_WEBRTC_CONNECTION_FLOW_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_CONNECTION_FLOW_H_
#include <memory>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/functional/any_invocable.h"
#include "absl/time/time.h"
#include "connections/implementation/mediums/webrtc/data_channel_listener.h"
#include "connections/implementation/mediums/webrtc/local_ice_candidate_listener.h"
#include "connections/implementation/mediums/webrtc/session_description_wrapper.h"
#include "connections/implementation/mediums/webrtc/webrtc.h"
#include "connections/implementation/mediums/webrtc_socket.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/future.h"
#include "internal/platform/listeners.h"
#include "internal/platform/mutex.h"
#include "internal/platform/runnable.h"
#include "webrtc/api/data_channel_interface.h"
#include "webrtc/api/jsep.h"
#include "webrtc/api/peer_connection_interface.h"
#include "webrtc/api/scoped_refptr.h"
#include "webrtc/rtc_base/network_constants.h"
namespace nearby {
namespace connections {
namespace mediums {
/**
* Flow for an offerer:
*
* <ul>
* <li>INITIALIZED: After construction.
* <li>CREATING_OFFER: After CreateOffer(). Local ice candidate collection
* begins.
* <li>WAITING_FOR_ANSWER: Until the remote peer sends their answer.
* <li>WAITING_TO_CONNECT: Until the data channel actually connects. Remote
* ice candidates should be added with OnRemoteIceCandidatesReceived as they are
* gathered.
* <li>CONNECTED: We successfully connected to the remote data
* channel.
* <li>ENDED: The final state that can occur from any of the previous
* states if we disconnect at any point in the flow.
* </ul>
*
* <p>Flow for an answerer:
*
* <ul>
* <li>INITIALIZED: After construction.
* <li>RECEIVED_OFFER: After onOfferReceived().
* <li>CREATING_ANSWER: After CreateAnswer(). Local ice candidate collection
* begins.
* <li>WAITING_TO_CONNECT: Until the data channel actually connects.
* Remote ice candidates should be added with OnRemoteIceCandidatesReceived as
* they are gathered.
* <li>CONNECTED: We successfully connected to the remote
* data channel.
* <li>ENDED: The final state that can occur from any of the
* previous states if we disconnect at any point in the flow.
* </ul>
*/
class ConnectionFlow : public webrtc::PeerConnectionObserver {
public:
enum class State {
kInitialized,
kCreatingOffer,
kWaitingForAnswer,
kReceivedOffer,
kCreatingAnswer,
kWaitingToConnect,
kConnected,
kEnded,
};
struct AdapterTypeListener {
absl::AnyInvocable<void(webrtc::AdapterType adapter_type)>
adapter_type_changed_cb = DefaultCallback<webrtc::AdapterType>();
};
// This method blocks on the creation of the peer connection object.
// Can be called on any thread but never called on signaling thread.
static std::unique_ptr<ConnectionFlow> Create(
LocalIceCandidateListener local_ice_candidate_listener,
DataChannelListener data_channel_listener,
AdapterTypeListener adapter_type_listener, WebRtcMedium& webrtc_medium);
~ConnectionFlow() override;
// Create the offer that will be sent to the remote. Mirrors the behaviour of
// PeerConnectionInterface::CreateOffer.
// Can be called on any thread but never called on signaling thread.
SessionDescriptionWrapper CreateOffer() ABSL_LOCKS_EXCLUDED(mutex_);
// Create the answer that will be sent to the remote. Mirrors the behaviour of
// PeerConnectionInterface::CreateAnswer.
// Can be called on any thread but never called on signaling thread.
SessionDescriptionWrapper CreateAnswer() ABSL_LOCKS_EXCLUDED(mutex_);
// Set the local session description. |sdp| was created via CreateOffer()
// or CreateAnswer().
// Can be called on any thread but never called on signaling thread.
bool SetLocalSessionDescription(SessionDescriptionWrapper sdp)
ABSL_LOCKS_EXCLUDED(mutex_);
// Invoked when an offer was received from a remote; this will set the remote
// session description on the peer connection. Returns true if the offer was
// successfully set as remote session description.
// Can be called on any thread but never called on signaling thread.
bool OnOfferReceived(SessionDescriptionWrapper offer)
ABSL_LOCKS_EXCLUDED(mutex_);
// Invoked when an answer was received from a remote; this will set the remote
// session description on the peer connection. Returns true if the offer was
// successfully set as remote session description.
// Can be called on any thread but never called on signaling thread.
bool OnAnswerReceived(SessionDescriptionWrapper answer)
ABSL_LOCKS_EXCLUDED(mutex_);
// Invoked when an ice candidate was received from a remote; this will add the
// ice candidate to the peer connection if ready or cache it otherwise.
// Can be called on any thread but never called on signaling thread.
bool OnRemoteIceCandidatesReceived(
std::vector<std::unique_ptr<webrtc::IceCandidate>> ice_candidates)
ABSL_LOCKS_EXCLUDED(mutex_);
// Close the peer connection and data channel if not connected.
// Can be called on any thread but never called on signaling thread.
bool CloseIfNotConnected() ABSL_LOCKS_EXCLUDED(mutex_);
// webrtc::PeerConnectionObserver:
// All methods called only on signaling thread.
void OnIceCandidate(const webrtc::IceCandidate* candidate) override;
void OnSignalingChange(
webrtc::PeerConnectionInterface::SignalingState new_state) override;
void OnDataChannel(webrtc::scoped_refptr<webrtc::DataChannelInterface>
data_channel) override;
void OnIceGatheringChange(
webrtc::PeerConnectionInterface::IceGatheringState new_state) override;
void OnConnectionChange(
webrtc::PeerConnectionInterface::PeerConnectionState new_state) override;
void OnRenegotiationNeeded() override;
void OnIceSelectedCandidatePairChanged(
const webrtc::CandidatePairChangeEvent& event) override;
// Public because it's used in tests too.
webrtc::scoped_refptr<webrtc::PeerConnectionInterface> GetPeerConnection();
private:
ConnectionFlow(LocalIceCandidateListener local_ice_candidate_listener,
DataChannelListener data_channel_listener,
AdapterTypeListener adapter_type_listener);
// Resets peer connection reference. Returns old value.
webrtc::scoped_refptr<webrtc::PeerConnectionInterface>
GetAndResetPeerConnection();
void CreateOfferOnSignalingThread(
Future<SessionDescriptionWrapper> success_future);
void CreateAnswerOnSignalingThread(
Future<SessionDescriptionWrapper> success_future);
void AddIceCandidatesOnSignalingThread(
std::vector<std::unique_ptr<webrtc::IceCandidate>> ice_candidates);
// Invoked when the peer connection indicates that signaling is stable.
void OnSignalingStable() ABSL_LOCKS_EXCLUDED(mutex_);
void CreateSocketFromDataChannel(
webrtc::scoped_refptr<webrtc::DataChannelInterface> data_channel);
// TODO(bfranz): Consider whether this needs to be configurable per platform
static constexpr absl::Duration kTimeout = absl::Milliseconds(250);
static constexpr absl::Duration kPeerConnectionTimeout =
absl::Milliseconds(2500);
bool InitPeerConnection(WebRtcMedium& webrtc_medium);
bool TransitionState(State current_state, State new_state);
bool SetRemoteSessionDescription(SessionDescriptionWrapper sdp,
State expected_entry_state,
State exit_state);
bool CloseOnSignalingThread() ABSL_LOCKS_EXCLUDED(mutex_);
bool RunOnSignalingThread(Runnable&& runnable);
bool IsRunningOnSignalingThread();
Mutex mutex_;
// Used to prevent the destructor from returning while the signaling thread is
// still running CloseOnSignalingThread()
CountDownLatch shutdown_latch_{1};
// State is used on signaling thread only.
State state_ = State::kInitialized;
// Used to communicate data channel events back to the caller of Create()
DataChannelListener data_channel_listener_;
LocalIceCandidateListener local_ice_candidate_listener_;
// Peer connection can be used only on signaling thread. The only exception
// is accessing the signaling thread handle. Tasks posted on the
// signaling thread may outlive both |peer_connection_| and |this| objects.
// A mutex is required to access peer connection reference because peer
// connection object and the reference can be initialized on different
// threads - the reference could be initialized before peer connection's
// constructor has finished.
// |peer_connection_| is actually implemented by PeerConnectionProxy, which
// runs the real PeerConnection's methods on the correct thread (signaling or
// worker). If a proxy method is called on the correct thread, then the real
// method is called directly. Otherwise, a task is posted on the correct
// thread and the current thread is blocked until that task finishes. We
// choose to explicitly use |peer_connection_| on the signaling thread,
// because it allows us to do state management on the signaling thread too,
// simplifies locking, and we don't have to block the current thread for every
// peer connection call.
webrtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_
ABSL_GUARDED_BY(mutex_);
// Used to hold a reference to the WebRtcSocket while the data channel is
// connecting.
std::shared_ptr<WebRtcSocket> socket_;
std::vector<std::unique_ptr<webrtc::IceCandidate>>
cached_remote_ice_candidates_;
// This pointer is only for DCHECK() assertions.
// It allows us to check if we are running on signaling thread even
// after destroying |peer_connection_|.
const void* signaling_thread_for_dcheck_only_ = nullptr;
// This shared_ptr is reset on the signaling thread when ConnectionFlow is
// closed. This prevents us from running tasks on the signaling thread when
// peer connection is closed. The value stored in |can_run_tasks_| is not
// used. We are using std::shared_ptr instead of webrtc::WeakPtrFactory
// because the former is thread-safe.
std::shared_ptr<void> can_run_tasks_ = std::make_shared<int>();
AdapterTypeListener adapter_type_listener_;
friend class CreateSessionDescriptionObserverImpl;
};
} // namespace mediums
} // namespace connections
} // namespace nearby
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_CONNECTION_FLOW_H_
@@ -1,445 +0,0 @@
// 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/webrtc/connection_flow.h"
#include <memory>
#include <utility>
#include <vector>
#include "gtest/gtest.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "connections/implementation/mediums/webrtc/data_channel_listener.h"
#include "connections/implementation/mediums/webrtc/local_ice_candidate_listener.h"
#include "connections/implementation/mediums/webrtc/session_description_wrapper.h"
#include "connections/implementation/mediums/webrtc/webrtc.h"
#include "connections/implementation/mediums/webrtc_socket.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/exception.h"
#include "internal/platform/future.h"
#include "internal/platform/medium_environment.h"
#include "webrtc/api/jsep.h"
#include "webrtc/api/scoped_refptr.h"
#include "webrtc/rtc_base/network_constants.h"
namespace nearby {
namespace connections {
namespace mediums {
namespace {
class ConnectionFlowTest : public ::testing::Test {
protected:
ConnectionFlowTest() {
MediumEnvironment::Instance().Start({.webrtc_enabled = true});
}
~ConnectionFlowTest() override { MediumEnvironment::Instance().Stop(); }
};
std::unique_ptr<webrtc::IceCandidate> CopyCandidate(
const webrtc::IceCandidate* candidate) {
return webrtc::CreateIceCandidate(candidate->sdp_mid(),
candidate->sdp_mline_index(),
candidate->candidate());
}
// TODO(bfranz) - Add test that deterministically sends answerer_ice_candidates
// before answer is sent.
TEST_F(ConnectionFlowTest, SuccessfulOfferAnswerFlow) {
WebRtcMedium webrtc_medium_offerer, webrtc_medium_answerer;
Future<ByteArray> message_received_future;
Future<std::shared_ptr<WebRtcSocket>> offerer_socket_future,
answerer_socket_future;
std::unique_ptr<ConnectionFlow> offerer, answerer;
// Send Ice Candidates immediately when you retrieve them
offerer = ConnectionFlow::Create(
{.local_ice_candidate_found_cb =
[&answerer](const webrtc::IceCandidate* candidate) {
std::vector<std::unique_ptr<webrtc::IceCandidate>> vec;
vec.push_back(CopyCandidate(candidate));
// The callback might be alive while the objects in test are
// destroyed.
if (answerer)
answerer->OnRemoteIceCandidatesReceived(std::move(vec));
}},
{.data_channel_open_cb =
[&offerer_socket_future](std::shared_ptr<WebRtcSocket> socket) {
offerer_socket_future.Set(std::move(socket));
}},
{.adapter_type_changed_cb =
[](webrtc::AdapterType adapter_type) {
// Do nothing
}},
webrtc_medium_offerer);
ASSERT_NE(offerer, nullptr);
answerer = ConnectionFlow::Create(
{.local_ice_candidate_found_cb =
[&offerer](const webrtc::IceCandidate* candidate) {
std::vector<std::unique_ptr<webrtc::IceCandidate>> vec;
vec.push_back(CopyCandidate(candidate));
// The callback might be alive while the objects in test are
// destroyed.
if (offerer)
offerer->OnRemoteIceCandidatesReceived(std::move(vec));
}},
{.data_channel_open_cb =
[&answerer_socket_future](std::shared_ptr<WebRtcSocket> socket) {
answerer_socket_future.Set(std::move(socket));
}},
{.adapter_type_changed_cb =
[](webrtc::AdapterType adapter_type) {
// Do nothing
}},
webrtc_medium_answerer);
ASSERT_NE(answerer, nullptr);
// Create and send offer
SessionDescriptionWrapper offer = offerer->CreateOffer();
ASSERT_TRUE(offer.IsValid());
EXPECT_EQ(offer.GetType(), webrtc::SdpType::kOffer);
EXPECT_TRUE(answerer->OnOfferReceived(offer));
EXPECT_TRUE(offerer->SetLocalSessionDescription(std::move(offer)));
// Create and send answer
SessionDescriptionWrapper answer = answerer->CreateAnswer();
ASSERT_TRUE(answer.IsValid());
EXPECT_EQ(answer.GetType(), webrtc::SdpType::kAnswer);
EXPECT_TRUE(offerer->OnAnswerReceived(answer));
EXPECT_TRUE(answerer->SetLocalSessionDescription(std::move(answer)));
// Retrieve Data Channels
ExceptionOr<std::shared_ptr<WebRtcSocket>> offerer_socket =
offerer_socket_future.Get(absl::Seconds(1));
EXPECT_TRUE(offerer_socket.ok());
ExceptionOr<std::shared_ptr<WebRtcSocket>> answerer_socket =
answerer_socket_future.Get(absl::Seconds(1));
EXPECT_TRUE(answerer_socket.ok());
// Send message on data channel
absl::string_view message = "Test";
offerer_socket.result()->GetOutputStream().Write(message);
ExceptionOr<ByteArray> received_message =
answerer_socket.result()->GetInputStream().Read(4);
EXPECT_TRUE(received_message.ok());
EXPECT_EQ(received_message.result(), ByteArray{message.data()});
}
TEST_F(ConnectionFlowTest, CreateAnswerBeforeOfferReceived) {
WebRtcMedium webrtc_medium;
std::unique_ptr<ConnectionFlow> answerer = ConnectionFlow::Create(
LocalIceCandidateListener(), DataChannelListener(),
ConnectionFlow::AdapterTypeListener(), webrtc_medium);
ASSERT_NE(answerer, nullptr);
SessionDescriptionWrapper answer = answerer->CreateAnswer();
EXPECT_FALSE(answer.IsValid());
}
TEST_F(ConnectionFlowTest, SetAnswerBeforeOffer) {
WebRtcMedium webrtc_medium_offerer, webrtc_medium_answerer;
std::unique_ptr<ConnectionFlow> offerer = ConnectionFlow::Create(
LocalIceCandidateListener(), DataChannelListener(),
ConnectionFlow::AdapterTypeListener(), webrtc_medium_offerer);
ASSERT_NE(offerer, nullptr);
std::unique_ptr<ConnectionFlow> answerer = ConnectionFlow::Create(
LocalIceCandidateListener(), DataChannelListener(),
ConnectionFlow::AdapterTypeListener(), webrtc_medium_answerer);
ASSERT_NE(answerer, nullptr);
SessionDescriptionWrapper offer = offerer->CreateOffer();
ASSERT_TRUE(offer.IsValid());
EXPECT_EQ(offer.GetType(), webrtc::SdpType::kOffer);
// Did not set offer as local session description
EXPECT_TRUE(answerer->OnOfferReceived(offer));
SessionDescriptionWrapper answer = answerer->CreateAnswer();
ASSERT_TRUE(answer.IsValid());
EXPECT_EQ(answer.GetType(), webrtc::SdpType::kAnswer);
EXPECT_FALSE(offerer->OnAnswerReceived(answer));
}
TEST_F(ConnectionFlowTest, CannotCreateOfferAfterClose) {
WebRtcMedium webrtc_medium;
std::unique_ptr<ConnectionFlow> offerer = ConnectionFlow::Create(
LocalIceCandidateListener(), DataChannelListener(),
ConnectionFlow::AdapterTypeListener(), webrtc_medium);
ASSERT_NE(offerer, nullptr);
EXPECT_TRUE(offerer->CloseIfNotConnected());
EXPECT_FALSE(offerer->CreateOffer().IsValid());
}
TEST_F(ConnectionFlowTest, CannotSetSessionDescriptionAfterClose) {
WebRtcMedium webrtc_medium;
std::unique_ptr<ConnectionFlow> offerer = ConnectionFlow::Create(
LocalIceCandidateListener(), DataChannelListener(),
ConnectionFlow::AdapterTypeListener(), webrtc_medium);
ASSERT_NE(offerer, nullptr);
SessionDescriptionWrapper offer = offerer->CreateOffer();
ASSERT_TRUE(offer.IsValid());
EXPECT_EQ(offer.GetType(), webrtc::SdpType::kOffer);
EXPECT_TRUE(offerer->CloseIfNotConnected());
EXPECT_FALSE(offerer->SetLocalSessionDescription(offer));
}
TEST_F(ConnectionFlowTest, CannotReceiveOfferAfterClose) {
WebRtcMedium webrtc_medium_offerer, webrtc_medium_answerer;
std::unique_ptr<ConnectionFlow> offerer = ConnectionFlow::Create(
LocalIceCandidateListener(), DataChannelListener(),
ConnectionFlow::AdapterTypeListener(), webrtc_medium_offerer);
ASSERT_NE(offerer, nullptr);
std::unique_ptr<ConnectionFlow> answerer = ConnectionFlow::Create(
LocalIceCandidateListener(), DataChannelListener(),
ConnectionFlow::AdapterTypeListener(), webrtc_medium_answerer);
ASSERT_NE(answerer, nullptr);
EXPECT_TRUE(answerer->CloseIfNotConnected());
SessionDescriptionWrapper offer = offerer->CreateOffer();
ASSERT_TRUE(offer.IsValid());
EXPECT_EQ(offer.GetType(), webrtc::SdpType::kOffer);
EXPECT_FALSE(answerer->OnOfferReceived(offer));
}
TEST_F(ConnectionFlowTest, NullPeerConnection) {
MediumEnvironment::Instance().SetUseValidPeerConnection(
/*use_valid_peer_connection=*/false);
WebRtcMedium medium;
std::unique_ptr<ConnectionFlow> answerer =
ConnectionFlow::Create(LocalIceCandidateListener(), DataChannelListener(),
ConnectionFlow::AdapterTypeListener(), medium);
EXPECT_EQ(answerer, nullptr);
}
TEST_F(ConnectionFlowTest, PeerConnectionTimeout) {
MediumEnvironment::Instance().SetUseValidPeerConnection(
/*use_valid_peer_connection=*/true);
WebRtcMedium medium1;
std::unique_ptr<ConnectionFlow> flow1 =
ConnectionFlow::Create(LocalIceCandidateListener(), DataChannelListener(),
ConnectionFlow::AdapterTypeListener(), medium1);
EXPECT_NE(flow1, nullptr);
// Attempt to trigger the 2.5s peer connection timeout.
MediumEnvironment::Instance().SetPeerConnectionLatency(absl::Seconds(5));
WebRtcMedium medium2;
std::unique_ptr<ConnectionFlow> flow2 =
ConnectionFlow::Create(LocalIceCandidateListener(), DataChannelListener(),
ConnectionFlow::AdapterTypeListener(), medium2);
EXPECT_EQ(flow2, nullptr);
}
TEST_F(ConnectionFlowTest, TerminateAnswerer) {
WebRtcMedium webrtc_medium_offerer, webrtc_medium_answerer;
Future<ByteArray> message_received_future;
Future<std::shared_ptr<WebRtcSocket>> offerer_socket_future,
answerer_socket_future;
std::unique_ptr<ConnectionFlow> offerer, answerer;
// Send Ice Candidates immediately when you retrieve them
offerer = ConnectionFlow::Create(
{.local_ice_candidate_found_cb =
[&answerer](const webrtc::IceCandidate* candidate) {
std::vector<std::unique_ptr<webrtc::IceCandidate>> vec;
vec.push_back(CopyCandidate(candidate));
// The callback might be alive while the objects in test are
// destroyed.
if (answerer)
answerer->OnRemoteIceCandidatesReceived(std::move(vec));
}},
{.data_channel_open_cb =
[&offerer_socket_future](std::shared_ptr<WebRtcSocket> socket) {
offerer_socket_future.Set(std::move(socket));
}},
{.adapter_type_changed_cb =
[](webrtc::AdapterType adapter_type) {
// Do nothing
}},
webrtc_medium_offerer);
ASSERT_NE(offerer, nullptr);
answerer = ConnectionFlow::Create(
{.local_ice_candidate_found_cb =
[&offerer](const webrtc::IceCandidate* candidate) {
std::vector<std::unique_ptr<webrtc::IceCandidate>> vec;
vec.push_back(CopyCandidate(candidate));
// The callback might be alive while the objects in test are
// destroyed.
if (offerer)
offerer->OnRemoteIceCandidatesReceived(std::move(vec));
}},
{.data_channel_open_cb =
[&answerer_socket_future](std::shared_ptr<WebRtcSocket> wrapper) {
answerer_socket_future.Set(std::move(wrapper));
}},
{.adapter_type_changed_cb =
[](webrtc::AdapterType adapter_type) {
EXPECT_GE(adapter_type, webrtc::ADAPTER_TYPE_UNKNOWN);
EXPECT_LE(adapter_type, webrtc::ADAPTER_TYPE_CELLULAR_5G);
}},
webrtc_medium_answerer);
ASSERT_NE(answerer, nullptr);
// Create and send offer
SessionDescriptionWrapper offer = offerer->CreateOffer();
ASSERT_TRUE(offer.IsValid());
EXPECT_EQ(offer.GetType(), webrtc::SdpType::kOffer);
EXPECT_TRUE(answerer->OnOfferReceived(offer));
EXPECT_TRUE(offerer->SetLocalSessionDescription(std::move(offer)));
// Create and send answer
SessionDescriptionWrapper answer = answerer->CreateAnswer();
ASSERT_TRUE(answer.IsValid());
EXPECT_EQ(answer.GetType(), webrtc::SdpType::kAnswer);
EXPECT_TRUE(offerer->OnAnswerReceived(answer));
EXPECT_TRUE(answerer->SetLocalSessionDescription(std::move(answer)));
// Retrieve Data Channels
ExceptionOr<std::shared_ptr<WebRtcSocket>> offerer_socket =
offerer_socket_future.Get(absl::Seconds(1));
EXPECT_TRUE(offerer_socket.ok());
ExceptionOr<std::shared_ptr<WebRtcSocket>> answerer_socket =
answerer_socket_future.Get(absl::Seconds(1));
EXPECT_TRUE(offerer_socket.ok());
CountDownLatch latch(1);
auto pc = answerer->GetPeerConnection();
pc->signaling_thread()->PostTask([pc, latch]() mutable {
pc->Close();
latch.CountDown();
});
latch.Await();
// Send message on data channel
absl::string_view message = "Test";
offerer_socket.result()->GetOutputStream().Write(message);
ExceptionOr<ByteArray> received_message =
answerer_socket.result()->GetInputStream().Read(4);
EXPECT_TRUE(received_message.GetResult().Empty());
}
TEST_F(ConnectionFlowTest, TerminateOfferer) {
WebRtcMedium webrtc_medium_offerer, webrtc_medium_answerer;
Future<ByteArray> message_received_future;
Future<std::shared_ptr<WebRtcSocket>> offerer_socket_future,
answerer_socket_future;
std::unique_ptr<ConnectionFlow> offerer, answerer;
// Send Ice Candidates immediately when you retrieve them
offerer = ConnectionFlow::Create(
{.local_ice_candidate_found_cb =
[&answerer](const webrtc::IceCandidate* candidate) {
std::vector<std::unique_ptr<webrtc::IceCandidate>> vec;
vec.push_back(CopyCandidate(candidate));
// The callback might be alive while the objects in test are
// destroyed.
if (answerer)
answerer->OnRemoteIceCandidatesReceived(std::move(vec));
}},
{.data_channel_open_cb =
[&offerer_socket_future](std::shared_ptr<WebRtcSocket> socket) {
offerer_socket_future.Set(std::move(socket));
}},
{.adapter_type_changed_cb =
[](webrtc::AdapterType adapter_type) {
EXPECT_GE(adapter_type, webrtc::ADAPTER_TYPE_UNKNOWN);
EXPECT_LE(adapter_type, webrtc::ADAPTER_TYPE_CELLULAR_5G);
}},
webrtc_medium_offerer);
ASSERT_NE(offerer, nullptr);
answerer = ConnectionFlow::Create(
{.local_ice_candidate_found_cb =
[&offerer](const webrtc::IceCandidate* candidate) {
std::vector<std::unique_ptr<webrtc::IceCandidate>> vec;
vec.push_back(CopyCandidate(candidate));
// The callback might be alive while the objects in test are
// destroyed.
if (offerer)
offerer->OnRemoteIceCandidatesReceived(std::move(vec));
}},
{.data_channel_open_cb =
[&answerer_socket_future](std::shared_ptr<WebRtcSocket> wrapper) {
answerer_socket_future.Set(std::move(wrapper));
}},
{.adapter_type_changed_cb =
[](webrtc::AdapterType adapter_type) {
EXPECT_GE(adapter_type, webrtc::ADAPTER_TYPE_UNKNOWN);
EXPECT_LE(adapter_type, webrtc::ADAPTER_TYPE_CELLULAR_5G);
}},
webrtc_medium_answerer);
ASSERT_NE(answerer, nullptr);
// Create and send offer
SessionDescriptionWrapper offer = offerer->CreateOffer();
ASSERT_TRUE(offer.IsValid());
EXPECT_EQ(offer.GetType(), webrtc::SdpType::kOffer);
EXPECT_TRUE(answerer->OnOfferReceived(offer));
EXPECT_TRUE(offerer->SetLocalSessionDescription(std::move(offer)));
// Create and send answer
SessionDescriptionWrapper answer = answerer->CreateAnswer();
ASSERT_TRUE(answer.IsValid());
EXPECT_EQ(answer.GetType(), webrtc::SdpType::kAnswer);
EXPECT_TRUE(offerer->OnAnswerReceived(answer));
EXPECT_TRUE(answerer->SetLocalSessionDescription(std::move(answer)));
// Retrieve Data Channels
ExceptionOr<std::shared_ptr<WebRtcSocket>> offerer_socket =
offerer_socket_future.Get(absl::Seconds(1));
EXPECT_TRUE(offerer_socket.ok());
ExceptionOr<std::shared_ptr<WebRtcSocket>> answerer_socket =
answerer_socket_future.Get(absl::Seconds(1));
EXPECT_TRUE(offerer_socket.ok());
CountDownLatch latch(1);
auto pc = offerer->GetPeerConnection();
pc->signaling_thread()->PostTask([pc, latch]() mutable {
pc->Close();
latch.CountDown();
});
latch.Await();
// Send message on data channel
absl::string_view message = "Test";
offerer_socket.result()->GetOutputStream().Write(message);
ExceptionOr<ByteArray> received_message =
answerer_socket.result()->GetInputStream().Read(4);
EXPECT_TRUE(received_message.GetResult().Empty());
}
} // namespace
} // namespace mediums
} // namespace connections
} // namespace nearby
@@ -1,42 +0,0 @@
// 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_INTERNAL_MEDIUMS_WEBRTC_DATA_CHANNEL_LISTENER_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_DATA_CHANNEL_LISTENER_H_
#include <memory>
#include "absl/functional/any_invocable.h"
#include "connections/implementation/mediums/webrtc_socket.h"
namespace nearby {
namespace connections {
namespace mediums {
// Callbacks from the data channel.
struct DataChannelListener {
// Called when the data channel is open and the socket wrapper is ready to
// read and write.
absl::AnyInvocable<void(std::shared_ptr<WebRtcSocket>)> data_channel_open_cb =
[](std::shared_ptr<WebRtcSocket>) {};
// Called when the data channel is closed.
absl::AnyInvocable<void()> data_channel_closed_cb = []() {};
};
} // namespace mediums
} // namespace connections
} // namespace nearby
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_DATA_CHANNEL_LISTENER_H_
@@ -1,41 +0,0 @@
// Copyright 2023 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/webrtc/fake_webrtc.h"
#include <memory>
#include "absl/strings/string_view.h"
#include "connections/implementation/mediums/webrtc/webrtc.h"
#include "internal/platform/cancellation_flag.h"
namespace nearby::connections::mediums {
FakeWebRtcMedium::FakeWebRtcMedium(CancellationFlag* flag)
: WebRtcMedium(), flag_(flag) {}
FakeWebRtcMedium::~FakeWebRtcMedium() = default;
std::unique_ptr<WebRtcSignalingMessenger>
FakeWebRtcMedium::GetSignalingMessenger(
absl::string_view self_id,
const location::nearby::connections::LocationHint& location_hint) {
if (cancel_during_get_signaling_messenger_) {
flag_->Cancel();
}
return WebRtcMedium::GetSignalingMessenger(self_id, location_hint);
}
} // namespace nearby::connections::mediums
@@ -1,55 +0,0 @@
// Copyright 2023 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_WEBRTC_FAKE_WEBRTC_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_FAKE_WEBRTC_H_
#include <memory>
#include "absl/strings/string_view.h"
#include "connections/implementation/mediums/webrtc/webrtc.h"
#include "internal/platform/cancellation_flag.h"
namespace nearby::connections::mediums {
class FakeWebRtcMedium : public WebRtcMedium {
public:
explicit FakeWebRtcMedium(CancellationFlag* flag);
FakeWebRtcMedium(FakeWebRtcMedium&&) = delete;
FakeWebRtcMedium& operator=(FakeWebRtcMedium&&) = delete;
~FakeWebRtcMedium() override;
// WebRtcMedium:
bool IsValid() const override { return is_valid_; }
std::unique_ptr<WebRtcSignalingMessenger> GetSignalingMessenger(
absl::string_view self_id,
const location::nearby::connections::LocationHint& location_hint)
override;
void TriggerCancellationDuringGetSignalingMessenger() {
cancel_during_get_signaling_messenger_ = true;
}
void SetIsValid(bool is_valid) { is_valid_ = is_valid; }
private:
CancellationFlag* flag_ = nullptr;
bool is_valid_ = true;
bool cancel_during_get_signaling_messenger_ = false;
};
} // namespace nearby::connections::mediums
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_FAKE_WEBRTC_H_
@@ -1,38 +0,0 @@
// 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_INTERNAL_MEDIUMS_WEBRTC_LOCAL_ICE_CANDIDATE_LISTENER_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_LOCAL_ICE_CANDIDATE_LISTENER_H_
#include "absl/functional/any_invocable.h"
#include "internal/platform/listeners.h"
#include "webrtc/api/jsep.h"
namespace nearby {
namespace connections {
namespace mediums {
// Callbacks from local ice candidate collection.
struct LocalIceCandidateListener {
// Called when a new local ice candidate has been found.
absl::AnyInvocable<void(const webrtc::IceCandidate*)>
local_ice_candidate_found_cb =
nearby::DefaultCallback<const webrtc::IceCandidate*>();
};
} // namespace mediums
} // namespace connections
} // namespace nearby
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_LOCAL_ICE_CANDIDATE_LISTENER_H_
@@ -1,66 +0,0 @@
// 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_INTERNAL_MEDIUMS_WEBRTC_SESSION_DESCRIPTION_WRAPPER_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_SESSION_DESCRIPTION_WRAPPER_H_
#include <memory>
#include <string>
#include "webrtc/api/jsep.h"
// Wrapper object around SessionDescriptionInterface*.
// This object owns the SessionDescriptionInterface* unless Release() has been
// called.
class SessionDescriptionWrapper {
public:
SessionDescriptionWrapper() = default;
explicit SessionDescriptionWrapper(webrtc::SessionDescriptionInterface* sdp)
: impl_(sdp) {}
// Copy constructor that performs a deep copy, i.e. creates a new
// SessionDescriptionInterface.
SessionDescriptionWrapper(const SessionDescriptionWrapper& sdp) {
if (sdp.IsValid()) {
impl_ = webrtc::CreateSessionDescription(sdp.GetType(), sdp.ToString());
}
}
SessionDescriptionWrapper(SessionDescriptionWrapper&&) = default;
SessionDescriptionWrapper& operator=(SessionDescriptionWrapper&&) = default;
// Release the ownership of the SessionDescriptionInterface*.
webrtc::SessionDescriptionInterface* Release() { return impl_.release(); }
// Returns a string representation of the sdp. Only call this, if IsValid() is
// true.
std::string ToString() const {
std::string str;
impl_->ToString(&str);
return str;
}
// Returns the SdpType of the SessionDescriptionInterface. Only call this, if
// IsValid() is true.
webrtc::SdpType GetType() const { return impl_->GetType(); }
const webrtc::SessionDescriptionInterface& GetSdp() { return *impl_; }
// Return whether this object currently holds a SessionDescriptionInterface.
bool IsValid() const { return impl_ != nullptr; }
private:
std::unique_ptr<webrtc::SessionDescriptionInterface> impl_;
};
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_SESSION_DESCRIPTION_WRAPPER_H_
@@ -1,139 +0,0 @@
// 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 <memory>
#include <string>
#include <utility>
#include <vector>
#include "connections/implementation/mediums/webrtc/signaling_frames.h"
#include "connections/implementation/mediums/webrtc_peer_id.h"
#include "internal/platform/byte_array.h"
#include "webrtc/api/jsep.h"
namespace nearby {
namespace connections {
namespace mediums {
namespace webrtc_frames {
using WebRtcSignalingFrame = ::location::nearby::mediums::WebRtcSignalingFrame;
namespace {
ByteArray FrameToByteArray(const WebRtcSignalingFrame& signaling_frame) {
std::string message;
signaling_frame.SerializeToString(&message);
return ByteArray(message.c_str(), message.size());
}
void SetSenderId(const WebrtcPeerId& sender_id, WebRtcSignalingFrame& frame) {
frame.mutable_sender_id()->set_id(sender_id.GetId());
}
std::unique_ptr<webrtc::IceCandidate> DecodeIceCandidate(
location::nearby::mediums::IceCandidate ice_candidate_proto) {
webrtc::SdpParseError error;
return std::unique_ptr<webrtc::IceCandidate>(webrtc::CreateIceCandidate(
ice_candidate_proto.sdp_mid(), ice_candidate_proto.sdp_m_line_index(),
ice_candidate_proto.sdp(), &error));
}
} // namespace
ByteArray EncodeReadyForSignalingPoke(const WebrtcPeerId& sender_id) {
WebRtcSignalingFrame signaling_frame;
signaling_frame.set_type(WebRtcSignalingFrame::READY_FOR_SIGNALING_POKE_TYPE);
SetSenderId(sender_id, signaling_frame);
signaling_frame.set_allocated_ready_for_signaling_poke(
new location::nearby::mediums::ReadyForSignalingPoke());
return FrameToByteArray(std::move(signaling_frame));
}
ByteArray EncodeOffer(const WebrtcPeerId& sender_id,
const webrtc::SessionDescriptionInterface& offer) {
WebRtcSignalingFrame signaling_frame;
signaling_frame.set_type(WebRtcSignalingFrame::OFFER_TYPE);
SetSenderId(sender_id, signaling_frame);
std::string offer_str;
offer.ToString(&offer_str);
signaling_frame.mutable_offer()
->mutable_session_description()
->set_description(offer_str);
return FrameToByteArray(std::move(signaling_frame));
}
ByteArray EncodeAnswer(const WebrtcPeerId& sender_id,
const webrtc::SessionDescriptionInterface& answer) {
WebRtcSignalingFrame signaling_frame;
signaling_frame.set_type(WebRtcSignalingFrame::ANSWER_TYPE);
SetSenderId(sender_id, signaling_frame);
std::string answer_str;
answer.ToString(&answer_str);
signaling_frame.mutable_answer()
->mutable_session_description()
->set_description(answer_str);
return FrameToByteArray(std::move(signaling_frame));
}
ByteArray EncodeIceCandidates(
const WebrtcPeerId& sender_id,
const std::vector<location::nearby::mediums::IceCandidate>&
ice_candidates) {
WebRtcSignalingFrame signaling_frame;
signaling_frame.set_type(WebRtcSignalingFrame::ICE_CANDIDATES_TYPE);
SetSenderId(sender_id, signaling_frame);
for (const auto& ice_candidate : ice_candidates) {
*signaling_frame.mutable_ice_candidates()->add_ice_candidates() =
ice_candidate;
}
return FrameToByteArray(std::move(signaling_frame));
}
std::unique_ptr<webrtc::SessionDescriptionInterface> DecodeOffer(
const WebRtcSignalingFrame& frame) {
return webrtc::CreateSessionDescription(
webrtc::SdpType::kOffer,
frame.offer().session_description().description());
}
std::unique_ptr<webrtc::SessionDescriptionInterface> DecodeAnswer(
const WebRtcSignalingFrame& frame) {
return webrtc::CreateSessionDescription(
webrtc::SdpType::kAnswer,
frame.answer().session_description().description());
}
std::vector<std::unique_ptr<webrtc::IceCandidate>> DecodeIceCandidates(
const WebRtcSignalingFrame& frame) {
std::vector<std::unique_ptr<webrtc::IceCandidate>> ice_candidates;
for (const auto& candidate : frame.ice_candidates().ice_candidates()) {
ice_candidates.push_back(DecodeIceCandidate(candidate));
}
return ice_candidates;
}
location::nearby::mediums::IceCandidate EncodeIceCandidate(
const webrtc::IceCandidate& ice_candidate) {
std::string sdp;
ice_candidate.ToString(&sdp);
location::nearby::mediums::IceCandidate ice_candidate_proto;
ice_candidate_proto.set_sdp(sdp);
ice_candidate_proto.set_sdp_mid(ice_candidate.sdp_mid());
ice_candidate_proto.set_sdp_m_line_index(ice_candidate.sdp_mline_index());
return ice_candidate_proto;
}
} // namespace webrtc_frames
} // namespace mediums
} // namespace connections
} // namespace nearby
@@ -1,57 +0,0 @@
// 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_INTERNAL_MEDIUMS_WEBRTC_SIGNALING_FRAMES_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_SIGNALING_FRAMES_H_
#include <memory>
#include <vector>
#include "connections/implementation/mediums/webrtc_peer_id.h"
#include "internal/platform/byte_array.h"
#include "proto/mediums/web_rtc_signaling_frames.pb.h"
#include "webrtc/api/jsep.h"
namespace nearby {
namespace connections {
namespace mediums {
namespace webrtc_frames {
ByteArray EncodeReadyForSignalingPoke(const WebrtcPeerId& sender_id);
ByteArray EncodeOffer(const WebrtcPeerId& sender_id,
const webrtc::SessionDescriptionInterface& offer);
ByteArray EncodeAnswer(const WebrtcPeerId& sender_id,
const webrtc::SessionDescriptionInterface& answer);
ByteArray EncodeIceCandidates(
const WebrtcPeerId& sender_id,
const std::vector<location::nearby::mediums::IceCandidate>& ice_candidates);
location::nearby::mediums::IceCandidate EncodeIceCandidate(
const webrtc::IceCandidate& ice_candidate);
std::unique_ptr<webrtc::SessionDescriptionInterface> DecodeOffer(
const location::nearby::mediums::WebRtcSignalingFrame& frame);
std::unique_ptr<webrtc::SessionDescriptionInterface> DecodeAnswer(
const location::nearby::mediums::WebRtcSignalingFrame& frame);
std::vector<std::unique_ptr<webrtc::IceCandidate>> DecodeIceCandidates(
const location::nearby::mediums::WebRtcSignalingFrame& frame);
} // namespace webrtc_frames
} // namespace mediums
} // namespace connections
} // namespace nearby
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_SIGNALING_FRAMES_H_
@@ -1,198 +0,0 @@
// 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/webrtc/signaling_frames.h"
#include <memory>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "connections/implementation/mediums/webrtc_peer_id.h"
#include "google/protobuf/text_format.h"
#include "webrtc/api/jsep.h"
namespace nearby {
namespace connections {
namespace mediums {
namespace webrtc_frames {
namespace {
using ::location::nearby::mediums::IceCandidate;
using ::location::nearby::mediums::WebRtcSignalingFrame;
const char kSampleSdp[] =
"v=0\r\no=- 7859371131 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 "
"0\r\na=msid-semantic: WMS\r\n";
const char kIceCandidateSdp1[] =
"a=candidate:1 1 UDP 2130706431 10.0.1.1 8998 typ host";
const char kIceCandidateSdp2[] =
"a=candidate:2 1 UDP 1694498815 192.0.2.3 45664 typ srflx raddr";
const char kIceSdpMid[] = "data";
const int kIceSdpMLineIndex = 0;
const char kOfferProto[] = R"(
sender_id { id: "abc" }
type: OFFER_TYPE
offer {
session_description {
description: "v=0\r\no=- 7859371131 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=msid-semantic: WMS\r\n"
}
}
)";
const char kAnswerProto[] = R"(
sender_id { id: "abc" }
type: ANSWER_TYPE
answer {
session_description {
description: "v=0\r\no=- 7859371131 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=msid-semantic: WMS\r\n"
}
}
)";
const char kIceCandidatesProto[] = R"(
sender_id { id: "abc" }
type: ICE_CANDIDATES_TYPE
ice_candidates {
ice_candidates {
sdp: "candidate:1 1 udp 2130706431 10.0.1.1 8998 typ host generation 0"
sdp_mid: "data"
sdp_m_line_index: 0
}
ice_candidates {
sdp: "candidate:2 1 udp 1694498815 192.0.2.3 45664 typ srflx generation 0"
sdp_mid: "data"
sdp_m_line_index: 0
}
}
)";
} // namespace
TEST(SignalingFramesTest, SignalingPoke) {
WebrtcPeerId sender_id("abc");
ByteArray encoded_poke = EncodeReadyForSignalingPoke(sender_id);
WebRtcSignalingFrame frame;
frame.ParseFromString(std::string(encoded_poke.data(), encoded_poke.size()));
EXPECT_THAT(frame, protobuf_matchers::EqualsProto(R"pb(
sender_id { id: "abc" }
type: READY_FOR_SIGNALING_POKE_TYPE
ready_for_signaling_poke {}
)pb"));
}
TEST(SignalingFramesTest, EncodeValidOffer) {
WebrtcPeerId sender_id("abc");
std::unique_ptr<webrtc::SessionDescriptionInterface> offer =
webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, kSampleSdp);
ByteArray encoded_offer = EncodeOffer(sender_id, *offer);
WebRtcSignalingFrame frame;
frame.ParseFromString(
std::string(encoded_offer.data(), encoded_offer.size()));
EXPECT_THAT(frame, protobuf_matchers::EqualsProto(kOfferProto));
}
TEST(SignaingFramesTest, DecodeValidOffer) {
WebRtcSignalingFrame frame;
proto2::TextFormat::ParseFromString(kOfferProto, &frame);
std::unique_ptr<webrtc::SessionDescriptionInterface> decoded_offer =
DecodeOffer(frame);
EXPECT_EQ(webrtc::SdpType::kOffer, decoded_offer->GetType());
std::string description;
decoded_offer->ToString(&description);
EXPECT_EQ(kSampleSdp, description);
}
TEST(SignalingFramesTest, EncodeValidAnswer) {
WebrtcPeerId sender_id("abc");
std::unique_ptr<webrtc::SessionDescriptionInterface> answer(
webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, kSampleSdp));
ByteArray encoded_answer = EncodeAnswer(sender_id, *answer);
WebRtcSignalingFrame frame;
frame.ParseFromString(
std::string(encoded_answer.data(), encoded_answer.size()));
EXPECT_THAT(frame, protobuf_matchers::EqualsProto(kAnswerProto));
}
TEST(SignalingFramesTest, DecodeValidAnswer) {
WebRtcSignalingFrame frame;
proto2::TextFormat::ParseFromString(kAnswerProto, &frame);
std::unique_ptr<webrtc::SessionDescriptionInterface> decoded_answer =
DecodeAnswer(frame);
EXPECT_EQ(webrtc::SdpType::kAnswer, decoded_answer->GetType());
std::string description;
decoded_answer->ToString(&description);
EXPECT_EQ(kSampleSdp, description);
}
TEST(SignalingFramesTest, EncodeValidIceCandidates) {
WebrtcPeerId sender_id("abc");
webrtc::SdpParseError error;
std::vector<std::unique_ptr<webrtc::IceCandidate>> ice_candidates;
ice_candidates.emplace_back(webrtc::CreateIceCandidate(
kIceSdpMid, kIceSdpMLineIndex, kIceCandidateSdp1, &error));
ice_candidates.emplace_back(webrtc::CreateIceCandidate(
kIceSdpMid, kIceSdpMLineIndex, kIceCandidateSdp2, &error));
std::vector<IceCandidate> encoded_candidates_vec;
for (const auto& ice_candidate : ice_candidates) {
encoded_candidates_vec.push_back(EncodeIceCandidate(*ice_candidate));
}
ByteArray encoded_candidates =
EncodeIceCandidates(sender_id, encoded_candidates_vec);
WebRtcSignalingFrame frame;
frame.ParseFromString(
std::string(encoded_candidates.data(), encoded_candidates.size()));
EXPECT_THAT(frame, protobuf_matchers::EqualsProto(kIceCandidatesProto));
}
TEST(SignalingFramesTest, DecodeValidIceCandidates) {
webrtc::SdpParseError error;
std::vector<std::unique_ptr<webrtc::IceCandidate>> ice_candidates;
ice_candidates.emplace_back(webrtc::CreateIceCandidate(
kIceSdpMid, kIceSdpMLineIndex, kIceCandidateSdp1, &error));
ice_candidates.emplace_back(webrtc::CreateIceCandidate(
kIceSdpMid, kIceSdpMLineIndex, kIceCandidateSdp2, &error));
WebRtcSignalingFrame frame;
proto2::TextFormat::ParseFromString(kIceCandidatesProto, &frame);
std::vector<std::unique_ptr<webrtc::IceCandidate>> decoded_candidates =
DecodeIceCandidates(frame);
ASSERT_EQ(2u, decoded_candidates.size());
for (int i = 0; i < static_cast<int>(decoded_candidates.size()); i++) {
EXPECT_TRUE(ice_candidates[i]->candidate().IsEquivalent(
decoded_candidates[i]->candidate()));
EXPECT_EQ(ice_candidates[i]->sdp_mid(), decoded_candidates[i]->sdp_mid());
EXPECT_EQ(ice_candidates[i]->sdp_mline_index(),
decoded_candidates[i]->sdp_mline_index());
}
}
} // namespace webrtc_frames
} // namespace mediums
} // namespace connections
} // namespace nearby
@@ -1,340 +0,0 @@
// Copyright 2025 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/webrtc/tachyon_express_signaling_messenger.h"
#include <ctime>
#include <memory>
#include <string>
#include <utility>
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "third_party/gloop/util/random/mt_random.h"
#include "third_party/grpc/include/grpc/support/time.h"
#include "third_party/grpc/include/grpcpp/channel.h"
#include "third_party/grpc/include/grpcpp/client_context.h"
#include "third_party/grpc/include/grpcpp/create_channel.h"
#include "third_party/grpc/include/grpcpp/security/credentials.h"
#include "third_party/grpc/include/grpcpp/support/client_callback.h"
#include "third_party/grpc/include/grpcpp/support/status.h"
#include "internal/account/account_manager_impl.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/logging.h"
#include "internal/proto/messaging.grpc.pb.h"
#include "internal/proto/tachyon.proto.h"
#include "internal/proto/tachyon_common.proto.h"
#include "internal/proto/tachyon_enums.proto.h"
#include "internal/rpc/utils.h"
#include "util/random/util.h"
namespace nearby::connections::mediums {
namespace {
using ::google::internal::communications::instantmessaging::v1::ClientInfo;
using ::google::internal::communications::instantmessaging::v1::Id;
using ::google::internal::communications::instantmessaging::v1::
LocationStandard;
using ::google::internal::communications::instantmessaging::v1::
ReceiveMessagesResponse;
using ::google::internal::communications::instantmessaging::v1::RequestHeader;
using ::google::internal::communications::instantmessaging::v1::
SendMessageExpressRequest;
using ::google::internal::communications::instantmessaging::v1::
SendMessageExpressResponse;
constexpr absl::string_view kApp = "Nearby";
constexpr absl::string_view kTachyonAddress =
"instantmessaging-pa.googleapis.com:443";
// It is unclear to me where these magic numbers are from but they are used
// across both the Android and CrOS implementations.
// See:
// https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/nearby_sharing/tachyon_ice_config_fetcher.cc;l=53
constexpr int kMajorVersion = 1;
constexpr int kMinorVersion = 24;
constexpr int kPointVersion = 0;
void InitId(Id& id, absl::string_view id_str,
const location::nearby::connections::LocationHint& location_hint) {
id.set_id(id_str);
id.set_app(kApp);
id.set_type(google::internal::communications::instantmessaging::v1::IdType::
NEARBY_ID);
auto* request_location_hint = id.mutable_location_hint();
request_location_hint->set_location(location_hint.location());
if (location_hint.format() ==
location::nearby::connections::LocationStandard::E164_CALLING) {
request_location_hint->set_format(LocationStandard::E164_CALLING);
} else if (location_hint.format() ==
location::nearby::connections::LocationStandard::
ISO_3166_1_ALPHA_2) {
request_location_hint->set_format(LocationStandard::ISO_3166_1_ALPHA_2);
} else {
request_location_hint->set_format(LocationStandard::UNKNOWN);
}
}
void InitRequestHeader(
RequestHeader& header, absl::string_view self_id,
const location::nearby::connections::LocationHint& location_hint) {
ClientInfo* client_info = header.mutable_client_info();
client_info->set_platform_type(google::internal::communications::
instantmessaging::v1::Platform::DESKTOP);
client_info->set_major(kMajorVersion);
client_info->set_minor(kMinorVersion);
client_info->set_point(kPointVersion);
client_info->set_api_version(
google::internal::communications::instantmessaging::v1::ApiVersion::V4);
// Generate a random message identifier.
MTRandom rand;
header.set_request_id(util_random::RandomString(&rand, /*length=*/13,
util::random::kWebsafe64));
header.set_app(kApp);
InitId(*header.mutable_requester_id(), self_id, location_hint);
}
} // namespace
bool TachyonExpressSignalingMessenger::ReceiveMessagesReader::Start(
google::internal::communications::instantmessaging::v1::grpc::Messaging::
StubInterface* stub,
absl::string_view self_id,
const location::nearby::connections::LocationHint& location_hint,
absl::string_view access_token,
absl::AnyInvocable<void()> on_fast_path_ready_callback,
absl::AnyInvocable<void(const ByteArray&)> on_inbox_message_callback,
absl::AnyInvocable<void(bool)> on_complete_callback) {
{
absl::MutexLock lock(mutex_);
if (is_receiving_messages_) {
return false;
}
is_receiving_messages_ = true;
}
on_fast_path_ready_callback_ = std::move(on_fast_path_ready_callback);
on_inbox_message_callback_ = std::move(on_inbox_message_callback);
on_complete_callback_ = std::move(on_complete_callback);
const std::shared_ptr<grpc::CallCredentials> call_creds =
grpc::AccessTokenCredentials(std::string(access_token));
context_.set_credentials(call_creds);
gpr_timespec deadline = gpr_now(GPR_CLOCK_MONOTONIC);
timespec timespec = absl::ToTimespec(absl::Seconds(30));
deadline.tv_sec += timespec.tv_sec;
deadline.tv_nsec += timespec.tv_nsec;
context_.set_deadline(deadline);
InitRequestHeader(*request_.mutable_header(), self_id, location_hint);
stub->async()->ReceiveMessagesExpress(&context_, &request_, this);
StartRead(&response_);
StartCall();
return true;
}
void TachyonExpressSignalingMessenger::ReceiveMessagesReader::OnReadDone(
bool ok) {
{
absl::MutexLock lock(mutex_);
if (!is_receiving_messages_) {
return;
}
}
if (ok) {
switch (response_.body_case()) {
case ReceiveMessagesResponse::kFastPathReady:
on_fast_path_ready_callback_();
break;
case ReceiveMessagesResponse::kInboxMessage:
on_inbox_message_callback_(
ByteArray(response_.inbox_message().message()));
break;
default:
break;
}
StartRead(&response_);
}
}
void TachyonExpressSignalingMessenger::ReceiveMessagesReader::OnDone(
const grpc::Status& s) {
{
absl::MutexLock lock(mutex_);
if (!is_receiving_messages_) {
return;
}
}
if (!s.ok()) {
LOG(ERROR) << "ReceiveMessagesExpress failed: "
<< rpc::GrpcStatusToAbslStatus(s);
}
on_complete_callback_(s.ok());
}
void TachyonExpressSignalingMessenger::ReceiveMessagesReader::TryCancel() {
{
absl::MutexLock lock(mutex_);
if (!is_receiving_messages_) {
return;
}
is_receiving_messages_ = false;
}
context_.TryCancel();
}
TachyonExpressSignalingMessenger::TachyonExpressSignalingMessenger(
absl::string_view self_id,
const location::nearby::connections::LocationHint& location_hint)
: self_id_(self_id),
location_hint_(location_hint),
account_manager_(AccountManagerImpl::Factory::instance()) {
std::shared_ptr<grpc::Channel> channel =
grpc::CreateChannel(std::string(kTachyonAddress),
grpc::SslCredentials(grpc::SslCredentialsOptions()));
messaging_stub_ = google::internal::communications::instantmessaging::v1::
grpc::Messaging::NewStub(channel);
}
struct StartState {
CountDownLatch latch{1};
bool success = false;
};
bool TachyonExpressSignalingMessenger::StartReceivingMessages(
OnSignalingMessageCallback on_message_callback,
OnSignalingCompleteCallback on_complete_callback) {
auto state = std::make_shared<StartState>();
account_manager_->GetAccessToken(
[this, state, on_message_callback = std::move(on_message_callback),
on_complete_callback = std::move(on_complete_callback)](
absl::StatusOr<std::string> token) mutable {
if (!token.ok()) {
state->success = false;
state->latch.CountDown();
return;
}
auto reader = std::make_shared<ReceiveMessagesReader>();
reader_ = reader;
std::weak_ptr<StartState> weak_state = state;
bool started = reader->Start(
messaging_stub_.get(), self_id_, location_hint_, token.value(),
/*on_fast_path_ready_callback=*/
[state] {
LOG(INFO) << "Received fast path ready message from tachyon.";
state->success = true;
state->latch.CountDown();
},
std::move(on_message_callback),
[reader, weak_state,
cb = std::move(on_complete_callback)](bool s) mutable {
LOG(INFO) << "Finished receiving messages from tachyon.";
cb(s);
if (auto state = weak_state.lock()) {
state->success = false;
state->latch.CountDown();
}
});
if (!started) {
state->success = false;
state->latch.CountDown();
}
});
state->latch.Await();
if (state->success) {
LOG(INFO) << "Receiving messages from tachyon.";
} else {
LOG(ERROR) << "Failed to start receiving messages from tachyon.";
reader_.reset();
}
return state->success;
}
void TachyonExpressSignalingMessenger::StopReceivingMessages() {
if (reader_) {
reader_->TryCancel();
reader_.reset();
}
}
bool TachyonExpressSignalingMessenger::SendMessage(absl::string_view peer_id,
const ByteArray& message) {
auto rpc_state =
std::make_shared<rpc::AsyncRpcArgs<SendMessageExpressRequest,
SendMessageExpressResponse>>();
InitRequestHeader(*rpc_state->request.mutable_header(), self_id_,
location_hint_);
InitId(*rpc_state->request.mutable_dest_id(), peer_id, location_hint_);
auto* request_message = rpc_state->request.mutable_message();
request_message->set_message(message.string_data());
request_message->set_message_type(
google::internal::communications::instantmessaging::v1::InboxMessage::
BASIC);
request_message->set_message_class(
google::internal::communications::instantmessaging::v1::InboxMessage::
EPHEMERAL);
MTRandom rand;
request_message->set_message_id(util_random::RandomString(
&rand, /*length=*/13, util::random::kWebsafe64));
CountDownLatch latch(1);
bool success = false;
account_manager_->GetAccessToken(
[this, &latch, &success, rpc_state](absl::StatusOr<std::string> token) {
if (!token.ok()) {
success = false;
latch.CountDown();
return;
}
const std::shared_ptr<grpc::CallCredentials> call_creds =
grpc::AccessTokenCredentials(token.value());
rpc_state->context.set_credentials(call_creds);
gpr_timespec deadline = gpr_now(GPR_CLOCK_MONOTONIC);
timespec timespec = absl::ToTimespec(absl::Seconds(30));
deadline.tv_sec += timespec.tv_sec;
deadline.tv_nsec += timespec.tv_nsec;
rpc_state->context.set_deadline(deadline);
// `rpc_state` is captured to ensure its lifetime is valid until the
// callback is executed.
messaging_stub_->async()->SendMessageExpress(
&rpc_state->context, &rpc_state->request, &rpc_state->response,
[&success, &latch, rpc_state](grpc::Status status) {
if (!status.ok()) {
LOG(ERROR) << "SendMessageExpress failed: "
<< rpc::GrpcStatusToAbslStatus(status);
}
success = status.ok();
latch.CountDown();
});
});
latch.Await();
return success;
}
} // namespace nearby::connections::mediums
@@ -1,99 +0,0 @@
// Copyright 2025 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_WEBRTC_TACHYON_MESSAGING_CLIENT_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_TACHYON_MESSAGING_CLIENT_H_
#include <memory>
#include <string>
#include "location/nearby/sharing/lib/account/account_manager.h"
#include "absl/base/thread_annotations.h"
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "third_party/grpc/include/grpcpp/client_context.h"
#include "third_party/grpc/include/grpcpp/support/client_callback.h"
#include "third_party/grpc/include/grpcpp/support/status.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/implementation/webrtc.h"
#include "internal/proto/messaging.grpc.pb.h"
namespace nearby::connections::mediums {
// Interface for the messaging Tachyon service. See
// third_party/nearby/internal/proto/messaging.proto
class TachyonExpressSignalingMessenger : public api::WebRtcSignalingMessenger {
public:
explicit TachyonExpressSignalingMessenger(
absl::string_view self_id,
const location::nearby::connections::LocationHint& location_hint);
class ReceiveMessagesReader
: public grpc::ClientReadReactor<
google::internal::communications::instantmessaging::v1::
ReceiveMessagesResponse> {
public:
ReceiveMessagesReader() = default;
void OnReadDone(bool ok) override;
void OnDone(const grpc::Status& s) override;
bool Start(
google::internal::communications::instantmessaging::v1::grpc::
Messaging::StubInterface* stub,
absl::string_view self_id,
const location::nearby::connections::LocationHint& location_hint,
absl::string_view access_token,
absl::AnyInvocable<void()> on_fast_path_ready_callback,
absl::AnyInvocable<void(const ByteArray&)> on_inbox_message_callback,
absl::AnyInvocable<void(bool)> on_complete_callback);
void TryCancel();
private:
grpc::ClientContext context_;
google::internal::communications::instantmessaging::v1::
ReceiveMessagesExpressRequest request_;
google::internal::communications::instantmessaging::v1::
ReceiveMessagesResponse response_;
absl::AnyInvocable<void()> on_fast_path_ready_callback_;
absl::AnyInvocable<void(const ByteArray&)> on_inbox_message_callback_;
absl::AnyInvocable<void(bool)> on_complete_callback_;
absl::Mutex mutex_;
bool is_receiving_messages_ ABSL_GUARDED_BY(mutex_) = false;
};
bool SendMessage(absl::string_view peer_id,
const ByteArray& message) override;
bool StartReceivingMessages(
OnSignalingMessageCallback on_message_callback,
OnSignalingCompleteCallback on_complete_callback) override;
void StopReceivingMessages() override;
private:
std::string self_id_;
location::nearby::connections::LocationHint location_hint_;
std::unique_ptr<google::internal::communications::instantmessaging::v1::grpc::
Messaging::StubInterface>
messaging_stub_;
nearby::sharing::AccountManager* const account_manager_;
std::shared_ptr<ReceiveMessagesReader> reader_ = nullptr;
};
} // namespace nearby::connections::mediums
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_TACHYON_MESSAGING_CLIENT_H_
@@ -1,110 +0,0 @@
// 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_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_H_
#include <memory>
#include <optional>
#include <utility>
#include "absl/strings/string_view.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/feature_flags.h"
#include "internal/platform/implementation/webrtc.h"
#include "internal/platform/implementation/webrtc_platform.h"
#include "webrtc/api/peer_connection_interface.h"
#include "webrtc/rtc_base/network_constants.h"
namespace nearby::connections::mediums {
class WebRtcSignalingMessenger {
public:
using OnSignalingMessageCallback =
api::WebRtcSignalingMessenger::OnSignalingMessageCallback;
using OnSignalingCompleteCallback =
api::WebRtcSignalingMessenger::OnSignalingCompleteCallback;
explicit WebRtcSignalingMessenger(
std::unique_ptr<api::WebRtcSignalingMessenger> messenger)
: impl_(std::move(messenger)) {}
virtual ~WebRtcSignalingMessenger() = default;
WebRtcSignalingMessenger(WebRtcSignalingMessenger&&) = default;
WebRtcSignalingMessenger operator=(WebRtcSignalingMessenger&&) = delete;
virtual bool SendMessage(absl::string_view peer_id,
const ByteArray& message) {
return impl_->SendMessage(peer_id, message);
}
virtual bool StartReceivingMessages(
OnSignalingMessageCallback on_message_callback,
OnSignalingCompleteCallback on_complete_callback) {
return impl_->StartReceivingMessages(std::move(on_message_callback),
std::move(on_complete_callback));
}
virtual void StopReceivingMessages() { impl_->StopReceivingMessages(); }
virtual bool IsValid() const { return impl_ != nullptr; }
private:
std::unique_ptr<api::WebRtcSignalingMessenger> impl_;
};
class WebRtcMedium {
public:
WebRtcMedium()
: impl_(api::WebRtcImplementationPlatform::CreateWebRtcMedium()) {}
virtual ~WebRtcMedium() = default;
WebRtcMedium(WebRtcMedium&&) = default;
WebRtcMedium& operator=(WebRtcMedium&&) = delete;
void SetNonCellular(bool non_cellular) { non_cellular_ = non_cellular; }
// Creates and returns a new webrtc::PeerConnectionInterface object via
// |callback|.
void CreatePeerConnection(
webrtc::PeerConnectionObserver* observer,
api::WebRtcMedium::PeerConnectionCallback callback) {
if (FeatureFlags::GetInstance()
.GetFlags()
.support_web_rtc_non_cellular_medium &&
non_cellular_) {
std::optional<webrtc::PeerConnectionFactoryInterface::Options> options;
options->network_ignore_mask |= webrtc::ADAPTER_TYPE_CELLULAR;
impl_->CreatePeerConnection(options, observer, std::move(callback));
} else {
impl_->CreatePeerConnection(observer, std::move(callback));
}
}
// Returns a signaling messenger for sending WebRTC signaling messages.
virtual std::unique_ptr<WebRtcSignalingMessenger> GetSignalingMessenger(
absl::string_view self_id,
const location::nearby::connections::LocationHint& location_hint) {
return std::make_unique<WebRtcSignalingMessenger>(
impl_->GetSignalingMessenger(self_id, location_hint));
}
virtual bool IsValid() const { return impl_ != nullptr; }
private:
std::unique_ptr<api::WebRtcMedium> impl_;
bool non_cellular_ = false;
};
} // namespace nearby::connections::mediums
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_H_
@@ -1,184 +0,0 @@
// 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/webrtc/webrtc_bwu_handler.h"
#include <memory>
#include <string>
#include <utility>
#include "absl/base/nullability.h"
#include "absl/functional/bind_front.h"
#include "connections/implementation/base_bwu_handler.h"
#include "connections/implementation/client_proxy.h"
#include "connections/implementation/endpoint_channel.h"
#include "connections/implementation/mediums/webrtc.h"
#include "connections/implementation/mediums/webrtc/webrtc_endpoint_channel.h"
#include "connections/implementation/mediums/webrtc_peer_id.h"
#include "connections/implementation/mediums/webrtc_socket.h"
#include "connections/implementation/offline_frames.h"
#include "connections/implementation/proto/offline_wire_formats.pb.h"
#include "internal/platform/cancellation_flag.h"
#include "internal/platform/expected.h"
#include "internal/platform/implementation/webrtc_platform.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace connections {
namespace {
using ::location::nearby::connections::BandwidthUpgradeNegotiationFrame;
using ::location::nearby::connections::LocationHint;
using ::location::nearby::connections::LocationStandard;
using ::location::nearby::proto::connections::OperationResultCode;
LocationHint BuildLocationHint(const std::string& location) {
LocationHint location_hint;
location_hint.set_format(LocationStandard::UNKNOWN);
if (!location.empty()) {
location_hint.set_location(location);
if (location.at(0) == '+') {
location_hint.set_format(LocationStandard::E164_CALLING);
} else {
location_hint.set_format(LocationStandard::ISO_3166_1_ALPHA_2);
}
}
return location_hint;
}
} // namespace
WebrtcBwuHandler::WebrtcIncomingSocket::WebrtcIncomingSocket(
const std::string& name, std::shared_ptr<mediums::WebRtcSocket> socket)
: name_(name), socket_(std::move(socket)) {}
void WebrtcBwuHandler::WebrtcIncomingSocket::Close() { socket_->Close(); }
std::string WebrtcBwuHandler::WebrtcIncomingSocket::ToString() { return name_; }
WebrtcBwuHandler::WebrtcBwuHandler(
mediums::WebRtc* absl_nonnull webrtc_medium,
IncomingConnectionCallback incoming_connection_callback)
: BaseBwuHandler(std::move(incoming_connection_callback)),
webrtc_(*webrtc_medium) {}
// Called by BWU target. Retrieves a new medium info from incoming message,
// and establishes connection over WebRTC using this info.
ErrorOr<std::unique_ptr<EndpointChannel>>
WebrtcBwuHandler::CreateUpgradedEndpointChannel(
ClientProxy* client, const std::string& service_id,
const std::string& endpoint_id,
const BandwidthUpgradeNegotiationFrame::UpgradePathInfo&
upgrade_path_info) {
const BandwidthUpgradeNegotiationFrame::UpgradePathInfo::WebRtcCredentials&
web_rtc_credentials = upgrade_path_info.web_rtc_credentials();
mediums::WebrtcPeerId peer_id(web_rtc_credentials.peer_id());
LocationHint location_hint;
location_hint.set_format(LocationStandard::UNKNOWN);
if (web_rtc_credentials.has_location_hint()) {
location_hint = web_rtc_credentials.location_hint();
}
LOG(INFO) << "WebRtcBwuHandler is attempting to connect to remote peer "
<< peer_id.GetId() << ", location hint "
<< location_hint.location();
std::shared_ptr<CancellationFlag> cancellation_flag =
client->GetCancellationFlag(endpoint_id);
ErrorOr<std::shared_ptr<mediums::WebRtcSocket>> socket_result =
webrtc_.Connect(service_id, peer_id, location_hint,
cancellation_flag.get(), client->GetWebRtcNonCellular());
if (socket_result.has_error()) {
LOG(ERROR) << "WebRtcBwuHandler failed to connect to remote peer ("
<< peer_id.GetId() << ") on endpoint " << endpoint_id
<< ", aborting upgrade.";
return {Error(socket_result.error().operation_result_code().value())};
}
LOG(INFO) << "WebRtcBwuHandler successfully connected to remote "
"peer ("
<< peer_id.GetId() << ") while upgrading endpoint " << endpoint_id;
// Create a new WebRtcEndpointChannel.
auto channel = std::make_unique<WebRtcEndpointChannel>(
service_id, /*channel_name=*/service_id, socket_result.value());
if (channel == nullptr) {
socket_result.value()->Close();
LOG(ERROR) << "WebRtcBwuHandler failed to create new EndpointChannel for "
"outgoing socket, aborting upgrade.";
return {Error(
OperationResultCode::NEARBY_WEB_RTC_ENDPOINT_CHANNEL_CREATION_FAILURE)};
}
return {std::move(channel)};
}
void WebrtcBwuHandler::HandleRevertInitiatorStateForService(
const std::string& upgrade_service_id) {
webrtc_.StopAcceptingConnections(upgrade_service_id);
LOG(INFO) << "WebrtcBwuHandler successfully reverted state for service "
<< upgrade_service_id;
}
// Called by BWU initiator. Set up WebRTC upgraded medium for this endpoint,
// and returns a upgrade path info (PeerId, LocationHint) for remote party to
// perform discovery.
std::string WebrtcBwuHandler::HandleInitializeUpgradedMediumForEndpoint(
ClientProxy* client, const std::string& upgrade_service_id,
const std::string& endpoint_id) {
LocationHint location_hint = BuildLocationHint(
api::WebRtcImplementationPlatform::GetDefaultCountryCode());
mediums::WebrtcPeerId self_id{mediums::WebrtcPeerId::FromRandom()};
if (!webrtc_.IsAcceptingConnections(upgrade_service_id)) {
if (!webrtc_.StartAcceptingConnections(
upgrade_service_id, self_id, location_hint,
absl::bind_front(&WebrtcBwuHandler::OnIncomingWebrtcConnection,
this, client),
client->GetWebRtcNonCellular())) {
LOG(ERROR) << "WebRtcBwuHandler couldn't initiate the WEB_RTC "
"upgrade for endpoint "
<< endpoint_id
<< " because it failed to start listening for "
"incoming WebRTC connections.";
return {};
}
LOG(INFO) << "WebRtcBwuHandler successfully started listening for "
"incoming WebRTC connections while upgrading endpoint "
<< endpoint_id;
}
return parser::ForBwuWebrtcPathAvailable(self_id.GetId(), location_hint);
}
// Accept Connection Callback.
// Notifies that the remote party called WebRtc::Connect()
// for this socket.
void WebrtcBwuHandler::OnIncomingWebrtcConnection(
ClientProxy* client, const std::string& upgrade_service_id,
std::shared_ptr<mediums::WebRtcSocket> socket) {
auto channel = std::make_unique<WebRtcEndpointChannel>(
upgrade_service_id, /*channel_name=*/upgrade_service_id, socket);
auto webrtc_socket = std::make_unique<WebrtcIncomingSocket>(
upgrade_service_id, std::move(socket));
std::unique_ptr<IncomingSocketConnection> connection(
new IncomingSocketConnection{std::move(webrtc_socket),
std::move(channel)});
NotifyOnIncomingConnection(client, std::move(connection));
}
} // namespace connections
} // namespace nearby
@@ -1,85 +0,0 @@
// 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_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_BWU_HANDLER_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_BWU_HANDLER_H_
#include <memory>
#include <string>
#include "absl/base/nullability.h"
#include "connections/implementation/base_bwu_handler.h"
#include "connections/implementation/bwu_handler.h"
#include "connections/implementation/client_proxy.h"
#include "connections/implementation/endpoint_channel.h"
#include "connections/implementation/mediums/webrtc.h"
#include "connections/implementation/mediums/webrtc_socket.h"
#include "connections/medium_selector.h"
#include "internal/platform/expected.h"
namespace nearby {
namespace connections {
// Defines the set of methods that need to be implemented to handle the
// per-Medium-specific operations needed to upgrade an EndpointChannel.
class WebrtcBwuHandler : public BaseBwuHandler {
public:
WebrtcBwuHandler(
mediums::WebRtc* absl_nonnull webrtc_medium,
IncomingConnectionCallback incoming_connection_callback);
private:
class WebrtcIncomingSocket : public BwuHandler::IncomingSocket {
public:
explicit WebrtcIncomingSocket(
const std::string& name, std::shared_ptr<mediums::WebRtcSocket> socket);
std::string ToString() override;
void Close() override;
private:
std::string name_;
std::shared_ptr<mediums::WebRtcSocket> socket_;
};
// BwuHandler implementation:
ErrorOr<std::unique_ptr<EndpointChannel>> CreateUpgradedEndpointChannel(
ClientProxy* client, const std::string& service_id,
const std::string& endpoint_id,
const location::nearby::connections::BandwidthUpgradeNegotiationFrame::
UpgradePathInfo& upgrade_path_info) final;
location::nearby::proto::connections::Medium GetUpgradeMedium() const final {
return Medium::WEB_RTC;
}
void OnEndpointDisconnect(ClientProxy* client,
const std::string& endpoint_id) final {}
// BaseBwuHandler implementation:
std::string HandleInitializeUpgradedMediumForEndpoint(
ClientProxy* client, const std::string& upgrade_service_id,
const std::string& endpoint_id) final;
void HandleRevertInitiatorStateForService(
const std::string& upgrade_service_id) final;
void OnIncomingWebrtcConnection(
ClientProxy* client, const std::string& upgrade_service_id,
std::shared_ptr<mediums::WebRtcSocket> socket);
mediums::WebRtc& webrtc_;
};
} // namespace connections
} // namespace nearby
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_BWU_HANDLER_H_
@@ -1,139 +0,0 @@
// Copyright 2026 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/webrtc/webrtc_bwu_handler.h"
#include <memory>
#include <string>
#include <utility>
#include "gtest/gtest.h"
#include "absl/time/time.h"
#include "connections/implementation/bwu_handler.h"
#include "connections/implementation/client_proxy.h"
#include "connections/implementation/endpoint_channel.h"
#include "connections/implementation/mediums/webrtc/webrtc_impl.h"
#include "connections/implementation/offline_frames.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/exception.h"
#include "internal/platform/expected.h"
#include "internal/platform/feature_flags.h"
#include "internal/platform/logging.h"
#include "internal/platform/medium_environment.h"
#include "internal/platform/single_thread_executor.h"
namespace nearby {
namespace connections {
namespace {
using ::location::nearby::connections::OfflineFrame;
using ::location::nearby::proto::connections::OperationResultCode;
constexpr absl::Duration kWaitDuration = absl::Milliseconds(5000);
class WebrtcBwuTest : public ::testing::Test {
protected:
WebrtcBwuTest() {
original_flags_ = FeatureFlags::GetInstance().GetFlags();
env_.Start({.webrtc_enabled = true});
}
~WebrtcBwuTest() override {
FeatureFlags::GetMutableInstanceForTesting().SetFlags(original_flags_);
env_.Stop();
}
void RunCreateEndpointChannelTest(bool enable_cancellation);
MediumEnvironment& env_{MediumEnvironment::Instance()};
FeatureFlags::Flags original_flags_;
};
void WebrtcBwuTest::RunCreateEndpointChannelTest(bool enable_cancellation) {
FeatureFlags::Flags flags = original_flags_;
flags.enable_cancellation_flag = enable_cancellation;
FeatureFlags::GetMutableInstanceForTesting().SetFlags(flags);
CountDownLatch start_latch(1);
CountDownLatch accept_latch(1);
CountDownLatch end_latch(1);
ClientProxy client_1, client_2;
auto webrtc_1 = std::make_unique<mediums::WebRtcImpl>();
auto webrtc_2 = std::make_unique<mediums::WebRtcImpl>();
ExceptionOr<OfflineFrame> upgrade_frame;
std::unique_ptr<BwuHandler> handler_1 = std::make_unique<WebrtcBwuHandler>(
webrtc_1.get(),
[&](ClientProxy* client,
std::unique_ptr<BwuHandler::IncomingSocketConnection> connection) {
LOG(INFO) << "Handler 1 callback triggered";
accept_latch.CountDown();
});
std::unique_ptr<BwuHandler> handler_2 = std::make_unique<WebrtcBwuHandler>(
webrtc_2.get(),
[&](ClientProxy* client,
std::unique_ptr<BwuHandler::IncomingSocketConnection> connection) {
LOG(INFO) << "Handler 2 callback triggered";
});
// Server starts advertising.
SingleThreadExecutor server_executor;
server_executor.Execute([&]() {
std::string upgrade_frame_bytes =
handler_1->InitializeUpgradedMediumForEndpoint(
&client_1, /*upgrade_service_id=*/"A", /*endpoint_id=*/"1");
EXPECT_FALSE(upgrade_frame_bytes.empty());
upgrade_frame = parser::FromBytes(upgrade_frame_bytes);
start_latch.CountDown();
});
// Client connects.
EXPECT_TRUE(start_latch.Await(kWaitDuration).result());
if (enable_cancellation) {
client_2.AddCancellationFlag(/*endpoint_id=*/"1");
client_2.GetCancellationFlag(/*endpoint_id=*/"1")->Cancel();
}
SingleThreadExecutor client_executor;
client_executor.Execute([&]() {
auto bwu_frame =
upgrade_frame.result().v1().bandwidth_upgrade_negotiation();
auto result = handler_2->CreateUpgradedEndpointChannel(
&client_2, /*service_id=*/"A",
/*endpoint_id=*/"1", bwu_frame.upgrade_path_info());
if (!enable_cancellation) {
ASSERT_TRUE(result.has_value());
std::unique_ptr<EndpointChannel> new_channel = std::move(result.value());
EXPECT_TRUE(accept_latch.Await(kWaitDuration).result());
EXPECT_EQ(new_channel->GetMedium(),
location::nearby::proto::connections::Medium::WEB_RTC);
} else {
EXPECT_FALSE(result.has_value());
EXPECT_TRUE(result.has_error());
EXPECT_EQ(result.error().operation_result_code(),
OperationResultCode::
CLIENT_CANCELLATION_CANCEL_WEB_RTC_OUTGOING_CONNECTION);
accept_latch.CountDown();
}
handler_1->RevertResponderState(/*service_id=*/"A");
end_latch.CountDown();
});
EXPECT_TRUE(accept_latch.Await(kWaitDuration).result());
EXPECT_TRUE(end_latch.Await(kWaitDuration).result());
}
TEST_F(WebrtcBwuTest, CanCreateBwuHandler) {
auto webrtc = std::make_unique<mediums::WebRtcImpl>();
std::unique_ptr<BwuHandler> handler = std::make_unique<WebrtcBwuHandler>(
webrtc.get(),
[](ClientProxy* client,
std::unique_ptr<BwuHandler::IncomingSocketConnection> connection) {});
EXPECT_EQ(handler->GetUpgradeMedium(),
location::nearby::proto::connections::Medium::WEB_RTC);
}
TEST_F(WebrtcBwuTest, CreateEndpointChannel_WithCancellation) {
RunCreateEndpointChannelTest(true);
}
TEST_F(WebrtcBwuTest, CreateEndpointChannel_NoCancellation) {
RunCreateEndpointChannelTest(false);
}
} // namespace
} // namespace connections
} // namespace nearby
@@ -1,42 +0,0 @@
// 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/webrtc/webrtc_endpoint_channel.h"
#include <memory>
#include <string>
#include <utility>
#include "connections/implementation/base_endpoint_channel.h"
#include "connections/implementation/mediums/webrtc_socket.h"
namespace nearby {
namespace connections {
WebRtcEndpointChannel::WebRtcEndpointChannel(
const std::string& service_id, const std::string& channel_name,
std::shared_ptr<mediums::WebRtcSocket> socket)
: BaseEndpointChannel(service_id, channel_name, &socket->GetInputStream(),
&socket->GetOutputStream()),
webrtc_socket_(std::move(socket)) {}
location::nearby::proto::connections::Medium WebRtcEndpointChannel::GetMedium()
const {
return location::nearby::proto::connections::Medium::WEB_RTC;
}
void WebRtcEndpointChannel::CloseImpl() { webrtc_socket_->Close(); }
} // namespace connections
} // namespace nearby
@@ -1,44 +0,0 @@
// 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_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_ENDPOINT_CHANNEL_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_ENDPOINT_CHANNEL_H_
#include <memory>
#include <string>
#include "connections/implementation/base_endpoint_channel.h"
#include "connections/implementation/mediums/webrtc_socket.h"
namespace nearby {
namespace connections {
class WebRtcEndpointChannel final : public BaseEndpointChannel {
public:
WebRtcEndpointChannel(const std::string& service_id,
const std::string& channel_name,
std::shared_ptr<mediums::WebRtcSocket> socket);
location::nearby::proto::connections::Medium GetMedium() const override;
private:
void CloseImpl() override;
std::shared_ptr<mediums::WebRtcSocket> webrtc_socket_;
};
} // namespace connections
} // namespace nearby
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_ENDPOINT_CHANNEL_H_
@@ -1,793 +0,0 @@
// 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/webrtc/webrtc_impl.h"
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_set.h"
#include "absl/functional/bind_front.h"
#include "absl/time/time.h"
#include "connections/implementation/bwu_handler.h"
#include "connections/implementation/mediums/webrtc/connection_flow.h"
#include "connections/implementation/mediums/webrtc/session_description_wrapper.h"
#include "connections/implementation/mediums/webrtc/signaling_frames.h"
#include "connections/implementation/mediums/webrtc/webrtc.h"
#include "connections/implementation/mediums/webrtc/webrtc_bwu_handler.h"
#include "connections/implementation/mediums/webrtc_peer_id.h"
#include "connections/implementation/mediums/webrtc_socket.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/cancelable_alarm.h"
#include "internal/platform/cancellation_flag.h"
#include "internal/platform/cancellation_flag_listener.h"
#include "internal/platform/exception.h"
#include "internal/platform/expected.h"
#include "internal/platform/feature_flags.h"
#include "internal/platform/future.h"
#include "internal/platform/logging.h"
#include "internal/platform/mutex_lock.h"
#include "internal/platform/runnable.h"
#include "webrtc/api/jsep.h"
#include "webrtc/rtc_base/network_constants.h"
namespace nearby {
namespace connections {
namespace mediums {
namespace {
using ::location::nearby::connections::LocationHint;
using ::location::nearby::proto::connections::OperationResultCode;
// The maximum amount of time to wait to connect to a data channel via WebRTC.
constexpr absl::Duration kDataChannelTimeout = absl::Seconds(10);
// Delay between restarting signaling messenger to receive messages.
constexpr absl::Duration kRestartReceiveMessagesDuration = absl::Seconds(60);
} // namespace
WebRtcImpl::WebRtcImpl() : WebRtcImpl(std::make_unique<WebRtcMedium>()) {}
WebRtcImpl::WebRtcImpl(std::unique_ptr<WebRtcMedium> medium)
: medium_(std::move(medium)) {}
WebRtcImpl::~WebRtcImpl() {
// This ensures that all pending callbacks are run before we reset the medium
// and we are not accepting new runnables.
single_thread_executor_.Shutdown();
// Stop accepting all connections
absl::flat_hash_set<std::string> service_ids;
for (auto& item : accepting_connections_info_) {
service_ids.emplace(item.first);
}
for (const auto& service_id : service_ids) {
StopAcceptingConnections(service_id);
}
}
bool WebRtcImpl::IsAvailable() { return medium_->IsValid(); }
bool WebRtcImpl::IsAcceptingConnections(const std::string& service_id) {
MutexLock lock(&mutex_);
return IsAcceptingConnectionsLocked(service_id);
}
bool WebRtcImpl::IsAcceptingConnectionsLocked(const std::string& service_id) {
return accepting_connections_info_.contains(service_id);
}
bool WebRtcImpl::StartAcceptingConnections(const std::string& service_id,
const WebrtcPeerId& self_peer_id,
const LocationHint& location_hint,
AcceptedConnectionCallback callback,
bool non_cellular) {
MutexLock lock(&mutex_);
if (!IsAvailable()) {
LOG(WARNING) << "Cannot start accepting WebRTC connections because "
"WebRTC is not available.";
return false;
}
if (IsAcceptingConnectionsLocked(service_id)) {
LOG(WARNING) << "Cannot start accepting WebRTC connections because service "
<< service_id << "is already accepting WebRTC connections.";
return false;
}
// We'll track our state here, so that we're separated from the other services
// who may be also using WebRTC.
AcceptingConnectionsInfo info = AcceptingConnectionsInfo();
info.self_peer_id = self_peer_id;
info.accepted_connection_callback = std::move(callback);
medium_->SetNonCellular(non_cellular);
// Create a new SignalingMessenger so that we can communicate w/ Tachyon.
info.signaling_messenger =
medium_->GetSignalingMessenger(self_peer_id.GetId(), location_hint);
if (!info.signaling_messenger->IsValid()) {
return false;
}
// This registers ourselves w/ Tachyon, creating a room from the PeerId.
// This allows a remote device to message us over Tachyon.
if (!info.signaling_messenger->StartReceivingMessages(
absl::bind_front(&WebRtcImpl::OnSignalingMessage, this, service_id),
absl::bind_front(&WebRtcImpl::OnSignalingComplete, this,
service_id))) {
info.signaling_messenger.reset();
return false;
}
// We'll automatically disconnect from Tachyon after 60sec. When this alarm
// fires, we'll recreate our room so we continue to receive messages.
info.restart_tachyon_receive_messages_alarm =
std::make_unique<CancelableAlarm>(
"restart_receiving_messages_webrtc",
std::bind(&WebRtcImpl::ProcessRestartTachyonReceiveMessages, this,
service_id),
kRestartReceiveMessagesDuration, &single_thread_executor_);
// Now that we're set up to receive messages, we'll save our state and return
// a successful result.
accepting_connections_info_.emplace(service_id, std::move(info));
LOG(INFO) << "Started listening for WebRTC connections as "
<< self_peer_id.GetId() << " on service " << service_id;
return true;
}
void WebRtcImpl::StopAcceptingConnections(const std::string& service_id) {
MutexLock lock(&mutex_);
if (!IsAcceptingConnectionsLocked(service_id)) {
LOG(WARNING) << "Cannot stop accepting WebRTC connections because service "
<< service_id << "is not accepting WebRTC connections.";
return;
}
// Grab our info from the map.
auto& info = accepting_connections_info_.find(service_id)->second;
// Stop receiving messages from Tachyon.
info.signaling_messenger->StopReceivingMessages();
info.signaling_messenger.reset();
// Cancel the scheduled alarm.
if (info.restart_tachyon_receive_messages_alarm &&
info.restart_tachyon_receive_messages_alarm->IsValid()) {
info.restart_tachyon_receive_messages_alarm->Cancel();
info.restart_tachyon_receive_messages_alarm.reset();
}
// If we had any in-progress connections that haven't materialized into full
// DataChannels yet, it's time to shut them down since they can't reach us
// anymore.
absl::flat_hash_set<std::string> peer_ids;
for (auto& item : connection_flows_) {
peer_ids.emplace(item.first);
}
for (const auto& peer_id : peer_ids) {
const auto& entry = connection_flows_.find(peer_id);
// Skip outgoing connections in this step. Start/StopAcceptingConnections
// only deals with incoming connections.
if (requesting_connections_info_.contains(peer_id)) {
continue;
}
// Skip fully connected connections in this step. If the connection was
// formed while we were accepting connections, then it will stay alive until
// it's explicitly closed.
if (!entry->second->CloseIfNotConnected()) {
continue;
}
connection_flows_.erase(peer_id);
}
// Clean up our state. We're now no longer listening for connections.
accepting_connections_info_.erase(service_id);
LOG(INFO) << "Stopped listening for WebRTC connections for service "
<< service_id;
}
ErrorOr<std::shared_ptr<WebRtcSocket>> WebRtcImpl::Connect(
const std::string& service_id, const WebrtcPeerId& remote_peer_id,
const LocationHint& location_hint, CancellationFlag* cancellation_flag,
bool non_cellular) {
service_id_to_connect_attempts_count_map_[service_id] = 1;
medium_->SetNonCellular(non_cellular);
ErrorOr<std::shared_ptr<WebRtcSocket>> wrapper_result = {
Error(OperationResultCode::DETAIL_UNKNOWN)};
while (service_id_to_connect_attempts_count_map_[service_id] <=
kConnectAttemptsLimit) {
if (cancellation_flag->Cancelled()) {
LOG(WARNING) << "Attempt #"
<< service_id_to_connect_attempts_count_map_[service_id]
<< ": Cannot Connect with WebRtc due to cancel.";
return {
Error(OperationResultCode::
CLIENT_CANCELLATION_CANCEL_WEB_RTC_OUTGOING_CONNECTION)};
}
LOG(INFO) << "Attempt #"
<< service_id_to_connect_attempts_count_map_[service_id]
<< ": Beginning connection.";
wrapper_result = AttemptToConnect(service_id, remote_peer_id, location_hint,
cancellation_flag);
if (wrapper_result.has_value()) {
return std::move(wrapper_result.value());
}
service_id_to_connect_attempts_count_map_[service_id]++;
}
LOG(WARNING) << "Giving up after " << kConnectAttemptsLimit << " attempts";
return {Error(wrapper_result.error().operation_result_code().value())};
}
ErrorOr<std::shared_ptr<WebRtcSocket>> WebRtcImpl::AttemptToConnect(
const std::string& service_id, const WebrtcPeerId& remote_peer_id,
const LocationHint& location_hint, CancellationFlag* cancellation_flag) {
ConnectionRequestInfo info = ConnectionRequestInfo();
info.self_peer_id = WebrtcPeerId::FromRandom();
Future<std::shared_ptr<WebRtcSocket>> socket_future = info.socket_future;
// `listener` will go out of scope at the end of `AttemptToConnect`, and this
// is expected. This `listener` is tied to `socket_future` which we block on
// within this stack call, and will not go out of scope until the attempt
// is complete.
CancellationFlagListener listener(
cancellation_flag, [this, &service_id, &socket_future]() {
LOG(WARNING) << "Attempt # "
<< service_id_to_connect_attempts_count_map_[service_id]
<< " to connect with WebRtc stopped due to cancel.";
socket_future.SetException({Exception::kFailed});
});
{
MutexLock lock(&mutex_);
if (!IsAvailable()) {
LOG(WARNING) << "Cannot connect to WebRTC peer " << remote_peer_id.GetId()
<< " because WebRTC is not available.";
return {
Error(OperationResultCode::MEDIUM_UNAVAILABLE_WEB_RTC_NOT_AVAILABLE)};
}
// Create a new ConnectionFlow for this connection attempt.
std::unique_ptr<ConnectionFlow> connection_flow =
CreateConnectionFlow(service_id, remote_peer_id);
if (!connection_flow) {
LOG(INFO) << "Cannot connect to WebRTC peer " << remote_peer_id.GetId()
<< " because we failed to create a ConnectionFlow.";
return {Error(OperationResultCode::NEARBY_WEB_RTC_CONNECTION_FLOW_NULL)};
}
// Create a new SignalingMessenger so that we can communicate over Tachyon.
info.signaling_messenger = medium_->GetSignalingMessenger(
info.self_peer_id.GetId(), location_hint);
if (!info.signaling_messenger->IsValid()) {
LOG(INFO) << "Cannot connect to WebRTC peer " << remote_peer_id.GetId()
<< " because we failed to create a SignalingMessenger.";
return {
Error(OperationResultCode::
MISCELLEANEOUS_WEB_RTC_TACHYON_SIGNALING_MESSENGER_NULL)};
}
// This registers ourselves w/ Tachyon, creating a room from the PeerId.
// This allows a remote device to message us over Tachyon.
auto signaling_complete_callback = [socket_future](bool success) mutable {
if (!success) {
socket_future.SetException({Exception::kFailed});
}
};
if (!info.signaling_messenger->StartReceivingMessages(
absl::bind_front(&WebRtcImpl::OnSignalingMessage, this, service_id),
signaling_complete_callback)) {
LOG(INFO)
<< "Cannot connect to WebRTC peer " << remote_peer_id.GetId()
<< " because we failed to start receiving messages over Tachyon.";
info.signaling_messenger.reset();
return {Error(OperationResultCode::
MISCELLEANEOUS_WEB_RTC_FAILED_TO_RECEIVE_MESSAGE)};
}
// Poke the remote device. This will cause them to send us an Offer.
if (!info.signaling_messenger->SendMessage(
remote_peer_id.GetId(),
webrtc_frames::EncodeReadyForSignalingPoke(info.self_peer_id))) {
LOG(INFO) << "Cannot connect to WebRTC peer " << remote_peer_id.GetId()
<< " because we failed to poke the peer over Tachyon.";
info.signaling_messenger.reset();
return {Error(OperationResultCode::
CONNECTIVITY_WEB_RTC_CONNECT_TO_TACHYON_FAILURE)};
}
// Create a new ConnectionRequest entry. This map will be used later to look
// up state as we negotiate the connection over Tachyon.
requesting_connections_info_.emplace(remote_peer_id.GetId(),
std::move(info));
connection_flows_.emplace(remote_peer_id.GetId(),
std::move(connection_flow));
}
// Wait for the connection to go through. Don't hold the mutex here so that
// we're not blocking necessary operations.
ExceptionOr<std::shared_ptr<WebRtcSocket>> socket_result =
socket_future.Get(kDataChannelTimeout);
{
MutexLock lock(&mutex_);
// Reclaim our info, since we had released ownership while talking to
// Tachyon.
auto& info =
requesting_connections_info_.find(remote_peer_id.GetId())->second;
// Verify that the connection went through.
if (!socket_result.ok()) {
LOG(INFO) << "Failed to connect to WebRTC peer "
<< remote_peer_id.GetId();
RemoveConnectionFlow(remote_peer_id);
info.signaling_messenger.reset();
requesting_connections_info_.erase(remote_peer_id.GetId());
return {Error(OperationResultCode::
CONNECTIVITY_WEB_RTC_CLIENT_SOCKET_CREATION_FAILURE)};
}
// Clean up our ConnectionRequest.
info.signaling_messenger.reset();
requesting_connections_info_.erase(remote_peer_id.GetId());
// Return the result.
return socket_result.GetResult();
}
}
void WebRtcImpl::ProcessLocalIceCandidate(
const std::string& service_id, const WebrtcPeerId& remote_peer_id,
const location::nearby::mediums::IceCandidate ice_candidate) {
MutexLock lock(&mutex_);
// Check first if we have an outgoing request w/ this peer. As this request is
// tied to a specific peer, it takes precedence.
const auto& connection_request_entry =
requesting_connections_info_.find(remote_peer_id.GetId());
if (connection_request_entry != requesting_connections_info_.end()) {
// Pass the ice candidate to the remote side.
if (!connection_request_entry->second.signaling_messenger->SendMessage(
remote_peer_id.GetId(),
webrtc_frames::EncodeIceCandidates(
connection_request_entry->second.self_peer_id,
{ice_candidate}))) {
LOG(INFO) << "Failed to send ice candidate to " << remote_peer_id.GetId();
}
LOG(INFO) << "Sent ice candidate to " << remote_peer_id.GetId();
return;
}
// Check next if we're expecting incoming connection requests.
const auto& accepting_connection_entry =
accepting_connections_info_.find(service_id);
if (accepting_connection_entry != accepting_connections_info_.end()) {
// Pass the ice candidate to the remote side.
// TODO(xlythe) Consider not blocking here, since this can eat into the
// connection time
if (!accepting_connection_entry->second.signaling_messenger->SendMessage(
remote_peer_id.GetId(),
webrtc_frames::EncodeIceCandidates(
accepting_connection_entry->second.self_peer_id,
{ice_candidate}))) {
LOG(INFO) << "Failed to send ice candidate to " << remote_peer_id.GetId();
}
LOG(INFO) << "Sent ice candidate to " << remote_peer_id.GetId();
return;
}
LOG(INFO) << "Skipping restart listening for tachyon inbox messages "
"since we are not accepting connections for service "
<< service_id;
}
void WebRtcImpl::OnSignalingMessage(const std::string& service_id,
const ByteArray& message) {
OffloadFromThread("rtc-on-signaling-message", [this, service_id, message]() {
ProcessTachyonInboxMessage(service_id, message);
});
}
void WebRtcImpl::OnSignalingComplete(const std::string& service_id,
bool success) {
LOG(INFO) << "Signaling completed with status: " << success;
if (success) {
return;
}
OffloadFromThread("rtc-on-signaling-complete", [this, service_id]() {
MutexLock lock(&mutex_);
const auto& info_entry = accepting_connections_info_.find(service_id);
if (info_entry == accepting_connections_info_.end()) {
return;
}
if (info_entry->second.restart_accept_connections_count <
kRestartAcceptConnectionsLimit) {
++info_entry->second.restart_accept_connections_count;
} else {
return;
}
RestartTachyonReceiveMessages(service_id);
});
}
void WebRtcImpl::ProcessTachyonInboxMessage(const std::string& service_id,
const ByteArray& message) {
MutexLock lock(&mutex_);
// Attempt to parse the incoming message as a WebRtcSignalingFrame.
location::nearby::mediums::WebRtcSignalingFrame frame;
if (!frame.ParseFromString(std::string(message))) {
LOG(WARNING) << "Failed to parse signaling message.";
return;
}
// Ensure that the frame is valid (no missing fields).
if (!frame.has_sender_id()) {
LOG(WARNING) << "Invalid WebRTC frame: Sender ID is missing.";
return;
}
WebrtcPeerId remote_peer_id = WebrtcPeerId(frame.sender_id().id());
// Depending on the message type, we'll respond as appropriate.
if (requesting_connections_info_.contains(remote_peer_id.GetId())) {
// This is from a peer we have an outgoing connection request with, so we'll
// only process the Answer path.
if (frame.has_offer()) {
ReceiveOffer(remote_peer_id,
SessionDescriptionWrapper(
webrtc_frames::DecodeOffer(frame).release()));
SendAnswer(remote_peer_id);
} else if (frame.has_ice_candidates()) {
ReceiveIceCandidates(remote_peer_id,
webrtc_frames::DecodeIceCandidates(frame));
} else {
LOG(INFO) << "Received unknown WebRTC frame: ignoring.";
}
} else if (IsAcceptingConnectionsLocked(service_id)) {
// We don't have an outgoing connection request with this peer, but we are
// accepting incoming requests so we'll only process the Offer path.
if (frame.has_ready_for_signaling_poke()) {
SendOffer(service_id, remote_peer_id);
} else if (frame.has_answer()) {
ReceiveAnswer(remote_peer_id,
SessionDescriptionWrapper(
webrtc_frames::DecodeAnswer(frame).release()));
} else if (frame.has_ice_candidates()) {
ReceiveIceCandidates(remote_peer_id,
webrtc_frames::DecodeIceCandidates(frame));
} else {
LOG(INFO) << "Received unknown WebRTC frame: ignoring.";
}
} else {
LOG(INFO)
<< "Ignoring Tachyon message since we are not accepting connections.";
}
}
void WebRtcImpl::SendOffer(const std::string& service_id,
const WebrtcPeerId& remote_peer_id) {
std::unique_ptr<ConnectionFlow> connection_flow =
CreateConnectionFlow(service_id, remote_peer_id);
if (!connection_flow) {
LOG(INFO) << "Unable to send offer. Failed to create a ConnectionFlow.";
return;
}
SessionDescriptionWrapper offer = connection_flow->CreateOffer();
if (!offer.IsValid()) {
LOG(INFO) << "Unable to send offer. Failed to create our offer locally.";
RemoveConnectionFlow(remote_peer_id);
return;
}
const webrtc::SessionDescriptionInterface& sdp = offer.GetSdp();
if (!connection_flow->SetLocalSessionDescription(offer)) {
LOG(INFO) << "Unable to send offer. Failed to register our offer locally.";
RemoveConnectionFlow(remote_peer_id);
return;
}
// Grab our info from the map.
auto& info = accepting_connections_info_.find(service_id)->second;
// Pass the offer to the remote side.
if (!info.signaling_messenger->SendMessage(
remote_peer_id.GetId(),
webrtc_frames::EncodeOffer(info.self_peer_id, sdp))) {
LOG(INFO)
<< "Unable to send offer. Failed to write the offer to the remote peer "
<< remote_peer_id.GetId();
RemoveConnectionFlow(remote_peer_id);
return;
}
// Store the ConnectionFlow so that other methods can use it later.
connection_flows_.emplace(remote_peer_id.GetId(), std::move(connection_flow));
LOG(INFO) << "Sent offer to " << remote_peer_id.GetId();
}
void WebRtcImpl::ReceiveOffer(const WebrtcPeerId& remote_peer_id,
SessionDescriptionWrapper offer) {
const auto& entry = connection_flows_.find(remote_peer_id.GetId());
if (entry == connection_flows_.end()) {
LOG(INFO) << "Unable to receive offer. Failed to create a ConnectionFlow.";
return;
}
if (!entry->second->OnOfferReceived(offer)) {
LOG(INFO) << "Unable to receive offer. Failed to process the offer.";
RemoveConnectionFlow(remote_peer_id);
}
}
void WebRtcImpl::SendAnswer(const WebrtcPeerId& remote_peer_id) {
const auto& entry = connection_flows_.find(remote_peer_id.GetId());
if (entry == connection_flows_.end()) {
LOG(INFO) << "Unable to send answer. Failed to create a ConnectionFlow.";
return;
}
SessionDescriptionWrapper answer = entry->second->CreateAnswer();
if (!answer.IsValid()) {
LOG(INFO) << "Unable to send answer. Failed to create our answer locally.";
RemoveConnectionFlow(remote_peer_id);
return;
}
const webrtc::SessionDescriptionInterface& sdp = answer.GetSdp();
if (!entry->second->SetLocalSessionDescription(answer)) {
LOG(INFO)
<< "Unable to send answer. Failed to register our answer locally.";
RemoveConnectionFlow(remote_peer_id);
return;
}
// Grab our info from the map.
const auto& connection_request_entry =
requesting_connections_info_.find(remote_peer_id.GetId());
if (connection_request_entry == requesting_connections_info_.end()) {
LOG(INFO) << "Unable to send answer. Failed to find an outgoing "
"connection request.";
RemoveConnectionFlow(remote_peer_id);
return;
}
// Pass the answer to the remote side.
if (!connection_request_entry->second.signaling_messenger->SendMessage(
remote_peer_id.GetId(),
webrtc_frames::EncodeAnswer(
connection_request_entry->second.self_peer_id, sdp))) {
LOG(INFO)
<< "Unable to send answer. Failed to write the answer to the remote "
"peer "
<< remote_peer_id.GetId();
RemoveConnectionFlow(remote_peer_id);
return;
}
LOG(INFO) << "Sent answer to " << remote_peer_id.GetId();
}
void WebRtcImpl::ReceiveAnswer(const WebrtcPeerId& remote_peer_id,
SessionDescriptionWrapper answer) {
const auto& entry = connection_flows_.find(remote_peer_id.GetId());
if (entry == connection_flows_.end()) {
LOG(INFO) << "Unable to receive answer. Failed to create a ConnectionFlow.";
return;
}
if (!entry->second->OnAnswerReceived(answer)) {
LOG(INFO) << "Unable to receive answer. Failed to process the answer.";
RemoveConnectionFlow(remote_peer_id);
}
}
void WebRtcImpl::ReceiveIceCandidates(
const WebrtcPeerId& remote_peer_id,
std::vector<std::unique_ptr<webrtc::IceCandidate>> ice_candidates) {
const auto& entry = connection_flows_.find(remote_peer_id.GetId());
if (entry == connection_flows_.end()) {
LOG(INFO) << "Unable to receive ice candidates. Failed to create a "
"ConnectionFlow.";
return;
}
entry->second->OnRemoteIceCandidatesReceived(std::move(ice_candidates));
}
void WebRtcImpl::ProcessRestartTachyonReceiveMessages(
const std::string& service_id) {
MutexLock lock(&mutex_);
RestartTachyonReceiveMessages(service_id);
}
void WebRtcImpl::RestartTachyonReceiveMessages(const std::string& service_id) {
if (!IsAcceptingConnectionsLocked(service_id)) {
LOG(INFO)
<< "Skipping restart listening for tachyon inbox messages since we are "
"not accepting connections for service "
<< service_id;
return;
}
// Grab our info from the map.
auto& info = accepting_connections_info_.find(service_id)->second;
// Ensure we've disconnected from Tachyon.
info.signaling_messenger->StopReceivingMessages();
// Attempt to re-register.
if (!info.signaling_messenger->StartReceivingMessages(
absl::bind_front(&WebRtcImpl::OnSignalingMessage, this, service_id),
absl::bind_front(&WebRtcImpl::OnSignalingComplete, this,
service_id))) {
LOG(WARNING)
<< "Failed to restart listening for tachyon inbox messages for "
"service "
<< service_id << " since we failed to reach Tachyon.";
return;
}
LOG(INFO) << "Successfully restarted listening for tachyon inbox "
"messages on service "
<< service_id;
}
void WebRtcImpl::ProcessDataChannelOpen(
const std::string& service_id, const WebrtcPeerId& remote_peer_id,
std::shared_ptr<WebRtcSocket> socket_wrapper) {
MutexLock lock(&mutex_);
// Notify the client of the newly formed socket.
const auto& connection_request_entry =
requesting_connections_info_.find(remote_peer_id.GetId());
if (connection_request_entry != requesting_connections_info_.end()) {
connection_request_entry->second.socket_future.Set(socket_wrapper);
return;
}
const auto& accepting_connection_entry =
accepting_connections_info_.find(service_id);
if (accepting_connection_entry != accepting_connections_info_.end() &&
accepting_connection_entry->second.accepted_connection_callback) {
accepting_connection_entry->second.accepted_connection_callback(
service_id, socket_wrapper);
return;
}
// No one to handle the newly created DataChannel, so we'll just close it.
socket_wrapper->Close();
LOG(INFO) << "Ignoring new DataChannel because we are not accepting "
"connections for service "
<< service_id;
}
void WebRtcImpl::ProcessDataChannelClosed(const WebrtcPeerId& remote_peer_id) {
MutexLock lock(&mutex_);
LOG(INFO) << "Data channel has closed, removing connection flow for peer "
<< remote_peer_id.GetId();
RemoveConnectionFlow(remote_peer_id);
}
std::unique_ptr<ConnectionFlow> WebRtcImpl::CreateConnectionFlow(
const std::string& service_id, const WebrtcPeerId& remote_peer_id) {
RemoveConnectionFlow(remote_peer_id);
return ConnectionFlow::Create(
{.local_ice_candidate_found_cb =
{[this, service_id,
remote_peer_id](const webrtc::IceCandidate* ice_candidate) {
// Note: We need to encode the ice candidate here, before we jump
// off the thread. Otherwise, it gets destroyed and we can't read
// it later.
location::nearby::mediums::IceCandidate encoded_ice_candidate =
webrtc_frames::EncodeIceCandidate(*ice_candidate);
OffloadFromThread(
"rtc-ice-candidates",
[this, service_id, remote_peer_id, encoded_ice_candidate]() {
ProcessLocalIceCandidate(service_id, remote_peer_id,
encoded_ice_candidate);
});
}}},
{
.data_channel_open_cb = {[this, service_id, remote_peer_id](
std::shared_ptr<WebRtcSocket>
socket_wrapper) {
OffloadFromThread(
"rtc-channel-created",
[this, service_id, remote_peer_id, socket_wrapper]() {
ProcessDataChannelOpen(service_id, remote_peer_id,
socket_wrapper);
});
}},
.data_channel_closed_cb = {[this, remote_peer_id]() {
OffloadFromThread("rtc-channel-closed", [this, remote_peer_id]() {
ProcessDataChannelClosed(remote_peer_id);
});
}},
},
{
.adapter_type_changed_cb = {[this](webrtc::AdapterType adapter_type) {
OffloadFromThread("rtc-adapter-type-changed",
[this, adapter_type]() {
if (FeatureFlags::GetInstance()
.GetFlags()
.support_web_rtc_non_cellular_medium) {
AdapterTypeChangedHandler(adapter_type);
}
});
}},
},
*medium_);
}
void WebRtcImpl::AdapterTypeChangedHandler(webrtc::AdapterType adapter_type) {
MutexLock lock(&mutex_);
is_using_cellular_ = adapter_type == webrtc::ADAPTER_TYPE_CELLULAR ||
adapter_type == webrtc::ADAPTER_TYPE_CELLULAR_2G ||
adapter_type == webrtc::ADAPTER_TYPE_CELLULAR_3G ||
adapter_type == webrtc::ADAPTER_TYPE_CELLULAR_4G ||
adapter_type == webrtc::ADAPTER_TYPE_CELLULAR_5G;
}
void WebRtcImpl::RemoveConnectionFlow(const WebrtcPeerId& remote_peer_id) {
if (!connection_flows_.erase(remote_peer_id.GetId())) {
return;
}
// If we had an outgoing connection request w/ this peer, report the failure
// to the future that's being waited on.
const auto& connection_request_entry =
requesting_connections_info_.find(remote_peer_id.GetId());
if (connection_request_entry != requesting_connections_info_.end()) {
connection_request_entry->second.socket_future.SetException(
{Exception::kFailed});
}
}
void WebRtcImpl::OffloadFromThread(const std::string& name, Runnable runnable) {
single_thread_executor_.Execute(name, std::move(runnable));
}
bool WebRtcImpl::IsUsingCellular() {
MutexLock lock(&mutex_);
return is_using_cellular_;
}
std::unique_ptr<BwuHandler> WebRtcImpl::CreateBwuHandler(
BwuHandler::IncomingConnectionCallback incoming_connection_callback) {
return std::make_unique<WebrtcBwuHandler>(
this, std::move(incoming_connection_callback));
}
} // namespace mediums
} // namespace connections
} // namespace nearby
@@ -1,253 +0,0 @@
// 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_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_IMPL_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_IMPL_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "connections/implementation/bwu_handler.h"
#include "connections/implementation/mediums/webrtc.h"
#include "connections/implementation/mediums/webrtc/connection_flow.h"
#include "connections/implementation/mediums/webrtc/session_description_wrapper.h"
#include "connections/implementation/mediums/webrtc/webrtc.h"
#include "connections/implementation/mediums/webrtc_peer_id.h"
#include "connections/implementation/mediums/webrtc_socket.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/cancelable_alarm.h"
#include "internal/platform/cancellation_flag.h"
#include "internal/platform/expected.h"
#include "internal/platform/future.h"
#include "internal/platform/mutex.h"
#include "internal/platform/runnable.h"
#include "internal/platform/scheduled_executor.h"
#include "proto/mediums/web_rtc_signaling_frames.pb.h"
#include "webrtc/api/jsep.h"
#include "webrtc/rtc_base/network_constants.h"
namespace nearby {
namespace connections {
namespace mediums {
// Entry point for connecting a data channel between two devices via WebRtc.
class WebRtcImpl : public WebRtc {
public:
WebRtcImpl();
~WebRtcImpl() override;
// Overrides for WebRtc:
bool IsAvailable() override;
bool IsAcceptingConnections(const std::string& service_id) override
ABSL_LOCKS_EXCLUDED(mutex_);
bool StartAcceptingConnections(
const std::string& service_id, const WebrtcPeerId& self_peer_id,
const location::nearby::connections::LocationHint& location_hint,
AcceptedConnectionCallback callback, bool non_cellular) override
ABSL_LOCKS_EXCLUDED(mutex_);
void StopAcceptingConnections(const std::string& service_id) override
ABSL_LOCKS_EXCLUDED(mutex_);
ErrorOr<std::shared_ptr<WebRtcSocket>> Connect(
const std::string& service_id, const WebrtcPeerId& peer_id,
const location::nearby::connections::LocationHint& location_hint,
CancellationFlag* cancellation_flag, bool non_cellular) override
ABSL_LOCKS_EXCLUDED(mutex_);
bool IsUsingCellular() override ABSL_LOCKS_EXCLUDED(mutex_);
std::unique_ptr<BwuHandler> CreateBwuHandler(
BwuHandler::IncomingConnectionCallback incoming_connection_callback)
override;
protected:
// Use for unit tests only to inject a WebRtcMedium.
explicit WebRtcImpl(std::unique_ptr<WebRtcMedium> medium);
// Used in unit tests to determine how many calls to `AttemptToConnect`
// occured during a call to `Connect`, per service id.
std::map<std::string, int> service_id_to_connect_attempts_count_map_;
private:
static constexpr int kConnectAttemptsLimit = 3;
static constexpr int kRestartAcceptConnectionsLimit = 3;
enum class Role {
kNone = 0,
kOfferer = 1,
kAnswerer = 2,
};
struct AcceptingConnectionsInfo {
// The self_peer_id is generated from the BT/WiFi advertisements and allows
// the scanner to message us over Tachyon.
WebrtcPeerId self_peer_id;
// The registered callback. When there's an incoming connection, this
// callback is notified.
AcceptedConnectionCallback accepted_connection_callback;
// Allows us to communicate with the Tachyon web server.
std::unique_ptr<WebRtcSignalingMessenger> signaling_messenger;
// Restarts the tachyon inbox receives messages streaming rpc if the
// streaming rpc times out. The streaming rpc times out after 60s while
// advertising. Non-null when listening for WebRTC connections as an
// offerer.
std::unique_ptr<CancelableAlarm> restart_tachyon_receive_messages_alarm;
// Tracks the number of times we've restarted receiving messages after a
// failure. We limit the number to prevent endless restarts if we are
// repeatedly unable to communicate with Tachyon.
int restart_accept_connections_count = 0;
};
struct ConnectionRequestInfo {
// The self_peer_id is randomly generated and allows the advertiser to
// message us over Tachyon.
WebrtcPeerId self_peer_id;
// Allows us to communicate with the Tachyon web server.
std::unique_ptr<WebRtcSignalingMessenger> signaling_messenger;
// The pending DataChannel future. Our client will be blocked on this while
// they wait for us to set up the channel over Tachyon.
Future<std::shared_ptr<WebRtcSocket>> socket_future;
};
// Attempt to initiates a WebRtc connection with peer device identified by
// |peer_id|.
// Runs on @MainThread.
ErrorOr<std::shared_ptr<WebRtcSocket>> AttemptToConnect(
const std::string& service_id, const WebrtcPeerId& peer_id,
const location::nearby::connections::LocationHint& location_hint,
CancellationFlag* cancellation_flag) ABSL_LOCKS_EXCLUDED(mutex_);
// Returns if the device is accepting connection with specific service id.
// Runs on @MainThread.
bool IsAcceptingConnectionsLocked(const std::string& service_id)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Receives a message from the signaling messenger.
void OnSignalingMessage(const std::string& service_id,
const ByteArray& message);
// Decides whether to restart receiving messages.
void OnSignalingComplete(const std::string& service_id, bool success);
// Runs on |single_thread_executor_|.
void ProcessTachyonInboxMessage(const std::string& service_id,
const ByteArray& message)
ABSL_LOCKS_EXCLUDED(mutex_);
// Runs on |single_thread_executor_|.
void SendOffer(const std::string& service_id,
const WebrtcPeerId& remote_peer_id)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Runs on |single_thread_executor_|.
void ReceiveOffer(const WebrtcPeerId& remote_peer_id,
SessionDescriptionWrapper offer)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Runs on |single_thread_executor_|.
void SendAnswer(const WebrtcPeerId& remote_peer_id)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Runs on |single_thread_executor_|.
void ReceiveAnswer(const WebrtcPeerId& remote_peer_id,
SessionDescriptionWrapper answer)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Runs on |single_thread_executor_|.
void ReceiveIceCandidates(
const WebrtcPeerId& remote_peer_id,
std::vector<std::unique_ptr<webrtc::IceCandidate>> ice_candidates)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Runs on |single_thread_executor_|.
std::unique_ptr<ConnectionFlow> CreateConnectionFlow(
const std::string& service_id, const WebrtcPeerId& remote_peer_id)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Runs on |single_thread_executor_|.
std::unique_ptr<ConnectionFlow> GetConnectionFlow(
const WebrtcPeerId& remote_peer_id) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Runs on |single_thread_executor_|.
void RemoveConnectionFlow(const WebrtcPeerId& remote_peer_id)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Runs on |single_thread_executor_|.
void ProcessDataChannelOpen(const std::string& service_id,
const WebrtcPeerId& remote_peer_id,
std::shared_ptr<WebRtcSocket> socket_wrapper)
ABSL_LOCKS_EXCLUDED(mutex_);
// Runs on |single_thread_executor_|.
void ProcessDataChannelClosed(const WebrtcPeerId& remote_peer_id)
ABSL_LOCKS_EXCLUDED(mutex_);
// Runs on |single_thread_executor_|.
void ProcessLocalIceCandidate(
const std::string& service_id, const WebrtcPeerId& remote_peer_id,
location::nearby::mediums::IceCandidate ice_candidate)
ABSL_LOCKS_EXCLUDED(mutex_);
// Runs on |single_thread_executor_|.
void ProcessRestartTachyonReceiveMessages(const std::string& service_id)
ABSL_LOCKS_EXCLUDED(mutex_);
// Runs on |single_thread_executor_|.
void RestartTachyonReceiveMessages(const std::string& service_id)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Runs on |single_thread_executor_|.
void AdapterTypeChangedHandler(webrtc::AdapterType adapter_type)
ABSL_LOCKS_EXCLUDED(mutex_);
void OffloadFromThread(const std::string& name, Runnable runnable);
Mutex mutex_;
std::unique_ptr<WebRtcMedium> medium_;
// The single thread we throw the potentially blocking work on to.
ScheduledExecutor single_thread_executor_;
// A map of ServiceID -> State for all services that are listening for
// incoming connections.
absl::flat_hash_map<std::string, AcceptingConnectionsInfo>
accepting_connections_info_ ABSL_GUARDED_BY(mutex_);
// A map of a remote PeerId -> State for pending connection requests. As
// messages from Tachyon come in, this lets us look up the connection request
// info to handle the interaction.
absl::flat_hash_map<std::string, ConnectionRequestInfo>
requesting_connections_info_ ABSL_GUARDED_BY(mutex_);
// A map of a remote PeerId -> ConnectionFlow. For each connection, we create
// a unique ConnectionFlow.
absl::flat_hash_map<std::string, std::unique_ptr<ConnectionFlow>>
connection_flows_ ABSL_GUARDED_BY(mutex_);
bool is_using_cellular_ ABSL_GUARDED_BY(mutex_) = true;
};
} // namespace mediums
} // namespace connections
} // namespace nearby
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_IMPL_H_
@@ -1,651 +0,0 @@
// 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/webrtc/webrtc_impl.h"
#include <memory>
#include <string>
#include <utility>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/strings/string_view.h"
#include "connections/implementation/mediums/webrtc.h"
#include "connections/implementation/mediums/webrtc/fake_webrtc.h"
#include "connections/implementation/mediums/webrtc/webrtc.h"
#include "connections/implementation/mediums/webrtc_peer_id.h"
#include "connections/implementation/mediums/webrtc_socket.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/cancellation_flag.h"
#include "internal/platform/exception.h"
#include "internal/platform/expected.h"
#include "internal/platform/feature_flags.h"
#include "internal/platform/future.h"
#include "internal/platform/medium_environment.h"
namespace nearby {
namespace connections {
namespace mediums {
namespace {
using FeatureFlags = FeatureFlags::Flags;
using ::location::nearby::connections::LocationHint;
struct WebRtcTestParams {
FeatureFlags feature_flags;
bool non_cellular;
};
class TestWebRtc : public WebRtcImpl {
public:
explicit TestWebRtc(std::unique_ptr<WebRtcMedium> medium)
: WebRtcImpl(std::move(medium)) {}
int connect_attempts_count(std::string service_id) {
return service_id_to_connect_attempts_count_map_[service_id];
}
};
class WebRtcTest : public ::testing::TestWithParam<WebRtcTestParams> {
protected:
using MockAcceptedCallback = testing::MockFunction<void(
const std::string& service_id, std::shared_ptr<WebRtcSocket> socket)>;
MediumEnvironment& env_{MediumEnvironment::Instance()};
};
// 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_P(WebRtcTest, ConnectBothDevices_ShutdownSignaling_SendData) {
env_.Start({.webrtc_enabled = true});
WebRtcTestParams params = GetParam();
env_.SetFeatureFlags(params.feature_flags);
WebRtcImpl receiver, sender;
std::shared_ptr<WebRtcSocket> receiver_socket;
const WebrtcPeerId self_id("self_id");
const std::string service_id("NearbySharing");
LocationHint location_hint;
Future<bool> connected;
absl::string_view message("message xyz");
receiver.StartAcceptingConnections(
service_id, self_id, location_hint,
[&receiver_socket, connected](
const std::string& service_id,
std::shared_ptr<WebRtcSocket> wrapper) mutable {
receiver_socket = wrapper;
connected.Set(receiver_socket->IsValid());
},
params.non_cellular);
CancellationFlag flag;
ErrorOr<std::shared_ptr<WebRtcSocket>> sender_socket_result = sender.Connect(
service_id, self_id, location_hint, &flag, params.non_cellular);
ASSERT_TRUE(sender_socket_result.has_value());
EXPECT_TRUE(sender_socket_result.value()->IsValid());
ExceptionOr<bool> devices_connected = connected.Get();
ASSERT_TRUE(devices_connected.ok());
EXPECT_TRUE(devices_connected.result());
// Only shuts down signaling channel.
receiver.StopAcceptingConnections(service_id);
sender_socket_result.value()->GetOutputStream().Write(message);
ExceptionOr<ByteArray> received_msg =
receiver_socket->GetInputStream().Read(/*size=*/32);
ASSERT_TRUE(received_msg.ok());
EXPECT_EQ(message, received_msg.result().AsStringView());
env_.Stop();
}
TEST_P(WebRtcTest, CanCancelConnect) {
env_.Start({.webrtc_enabled = true});
WebRtcTestParams params = GetParam();
env_.SetFeatureFlags(params.feature_flags);
WebRtcImpl receiver, sender;
std::shared_ptr<WebRtcSocket> receiver_socket;
const WebrtcPeerId self_id("self_id");
const std::string service_id("NearbySharing");
LocationHint location_hint;
Future<bool> connected;
absl::string_view message("message");
receiver.StartAcceptingConnections(
service_id, self_id, location_hint,
[&receiver_socket, connected](
const std::string& service_id,
std::shared_ptr<WebRtcSocket> wrapper) mutable {
receiver_socket = wrapper;
connected.Set(receiver_socket->IsValid());
},
params.non_cellular);
CancellationFlag flag(true);
ErrorOr<std::shared_ptr<WebRtcSocket>> sender_socket_result = sender.Connect(
service_id, self_id, location_hint, &flag, params.non_cellular);
// If FeatureFlag is disabled, Cancelled is false as no-op.
if (!params.feature_flags.enable_cancellation_flag) {
ASSERT_TRUE(sender_socket_result.has_value());
EXPECT_TRUE(sender_socket_result.value()->IsValid());
ExceptionOr<bool> devices_connected = connected.Get();
ASSERT_TRUE(devices_connected.ok());
EXPECT_TRUE(devices_connected.result());
sender_socket_result.value()->GetOutputStream().Write(message);
ExceptionOr<ByteArray> received_msg =
receiver_socket->GetInputStream().Read(/*size=*/32);
ASSERT_TRUE(received_msg.ok());
EXPECT_EQ(message, received_msg.result().AsStringView());
receiver_socket->Close();
} else {
EXPECT_TRUE(sender_socket_result.has_error());
}
env_.Stop();
}
// Basic test to check that device is accepting connections when initialized.
TEST_P(WebRtcTest, NotAcceptingConnections) {
env_.Start({.webrtc_enabled = true});
WebRtcImpl webrtc;
ASSERT_TRUE(webrtc.IsAvailable());
EXPECT_FALSE(webrtc.IsAcceptingConnections(std::string{}));
env_.Stop();
}
// Tests the flow when the device tries to accept connections twice. In this
// case, only the first call is successful and subsequent calls fail.
TEST_P(WebRtcTest, StartAcceptingConnectionTwice) {
env_.Start({.webrtc_enabled = true});
WebRtcTestParams params = GetParam();
testing::StrictMock<MockAcceptedCallback> mock_accepted_callback_;
WebRtcImpl webrtc;
WebrtcPeerId self_id("peer_id");
const std::string service_id("NearbySharing");
LocationHint location_hint{};
ASSERT_TRUE(webrtc.IsAvailable());
ASSERT_TRUE(webrtc.StartAcceptingConnections(
service_id, self_id, location_hint,
mock_accepted_callback_.AsStdFunction(), params.non_cellular));
EXPECT_FALSE(webrtc.StartAcceptingConnections(
service_id, self_id, location_hint,
mock_accepted_callback_.AsStdFunction(), params.non_cellular));
EXPECT_TRUE(webrtc.IsAcceptingConnections(service_id));
EXPECT_FALSE(webrtc.IsAcceptingConnections(std::string{}));
env_.Stop();
}
// Tests the flow when the device tries to connect but there is no peer
// accepting connections at the given peer ID.
TEST_P(WebRtcTest, Connect_NoPeer) {
env_.Start({.webrtc_enabled = true});
WebRtcTestParams params = GetParam();
WebRtcImpl webrtc;
WebrtcPeerId peer_id("peer_id");
const std::string service_id("NearbySharing");
LocationHint location_hint;
ASSERT_TRUE(webrtc.IsAvailable());
CancellationFlag flag;
ErrorOr<std::shared_ptr<WebRtcSocket>> wrapper_1_result = webrtc.Connect(
service_id, peer_id, location_hint, &flag, params.non_cellular);
EXPECT_TRUE(wrapper_1_result.has_error());
EXPECT_TRUE(webrtc.StartAcceptingConnections(
service_id, peer_id, location_hint, nullptr, params.non_cellular));
env_.Stop();
}
// Tests the flow when the device calls Connect() after calling
// StartAcceptingConnections() without StopAcceptingConnections().
TEST_P(WebRtcTest, StartAcceptingConnection_ThenConnect) {
env_.Start({.webrtc_enabled = true});
testing::StrictMock<MockAcceptedCallback> mock_accepted_callback_;
WebRtcTestParams params = GetParam();
WebRtcImpl webrtc;
WebrtcPeerId self_id("peer_id");
const std::string service_id("NearbySharing");
LocationHint location_hint;
ASSERT_TRUE(webrtc.IsAvailable());
ASSERT_TRUE(webrtc.StartAcceptingConnections(
service_id, self_id, location_hint,
mock_accepted_callback_.AsStdFunction(), params.non_cellular));
CancellationFlag flag;
ErrorOr<std::shared_ptr<WebRtcSocket>> wrapper_result =
webrtc.Connect(service_id, WebrtcPeerId("random_peer_id"), location_hint,
&flag, params.non_cellular);
EXPECT_TRUE(webrtc.IsAcceptingConnections(service_id));
EXPECT_TRUE(wrapper_result.has_error());
EXPECT_FALSE(webrtc.StartAcceptingConnections(
service_id, self_id, location_hint,
mock_accepted_callback_.AsStdFunction(), params.non_cellular));
env_.Stop();
}
// Tests the flow when the device calls StartAcceptingConnections but the medium
// is closed before a peer device can connect to it.
TEST_P(WebRtcTest, StartAndStopAcceptingConnections) {
env_.Start({.webrtc_enabled = true});
testing::StrictMock<MockAcceptedCallback> mock_accepted_callback_;
WebRtcTestParams params = GetParam();
WebRtcImpl webrtc;
WebrtcPeerId self_id("peer_id");
const std::string service_id("NearbySharing");
LocationHint location_hint;
ASSERT_TRUE(webrtc.IsAvailable());
ASSERT_TRUE(webrtc.StartAcceptingConnections(
service_id, self_id, location_hint,
mock_accepted_callback_.AsStdFunction(), params.non_cellular));
EXPECT_TRUE(webrtc.IsAcceptingConnections(service_id));
webrtc.StopAcceptingConnections(service_id);
EXPECT_FALSE(webrtc.IsAcceptingConnections(service_id));
env_.Stop();
}
// Tests the flow when the device tries to connect to two different peers
// without disconnecting in between.
TEST_P(WebRtcTest, ConnectTwice) {
env_.Start({.webrtc_enabled = true});
WebRtcImpl receiver, sender, device_c;
std::shared_ptr<WebRtcSocket> receiver_socket;
WebRtcTestParams params = GetParam();
const WebrtcPeerId self_id("self_id"), other_id("other_id");
const std::string service_id("NearbySharing");
LocationHint location_hint;
Future<bool> connected;
absl::string_view message("message xyz");
receiver.StartAcceptingConnections(
service_id, self_id, location_hint,
[&receiver_socket, connected](
const std::string& service_id,
std::shared_ptr<WebRtcSocket> wrapper) mutable {
receiver_socket = wrapper;
connected.Set(receiver_socket->IsValid());
},
params.non_cellular);
device_c.StartAcceptingConnections(
service_id, other_id, location_hint,
[](const std::string& service_id, std::shared_ptr<WebRtcSocket> wrapper) {
},
params.non_cellular);
CancellationFlag flag;
ErrorOr<std::shared_ptr<WebRtcSocket>> sender_socket_result = sender.Connect(
service_id, self_id, location_hint, &flag, params.non_cellular);
ASSERT_TRUE(sender_socket_result.has_value());
EXPECT_TRUE(sender_socket_result.value()->IsValid());
ExceptionOr<bool> devices_connected = connected.Get();
ASSERT_TRUE(devices_connected.ok());
EXPECT_TRUE(devices_connected.result());
ErrorOr<std::shared_ptr<WebRtcSocket>> socket_result = sender.Connect(
service_id, other_id, location_hint, &flag, params.non_cellular);
EXPECT_TRUE(socket_result.has_value());
EXPECT_TRUE(socket_result.value()->IsValid());
socket_result.value()->Close();
EXPECT_TRUE(receiver_socket->IsValid());
ASSERT_TRUE(sender_socket_result.has_value());
EXPECT_TRUE(sender_socket_result.value()->IsValid());
sender_socket_result.value()->GetOutputStream().Write(message);
ExceptionOr<ByteArray> received_msg =
receiver_socket->GetInputStream().Read(/*size=*/32);
ASSERT_TRUE(received_msg.ok());
EXPECT_EQ(message, received_msg.result().AsStringView());
receiver_socket->Close();
env_.Stop();
}
// 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_P(WebRtcTest, ConnectBothDevicesAndAbort) {
env_.Start({.webrtc_enabled = true});
WebRtcImpl receiver, sender;
std::shared_ptr<WebRtcSocket> receiver_socket, sender_socket;
WebRtcTestParams params = GetParam();
const WebrtcPeerId self_id("self_id");
const std::string service_id("NearbySharing");
LocationHint location_hint;
Future<bool> connected;
receiver.StartAcceptingConnections(
service_id, self_id, location_hint,
[&receiver_socket, connected](
const std::string& service_id,
std::shared_ptr<WebRtcSocket> wrapper) mutable {
receiver_socket = wrapper;
connected.Set(receiver_socket->IsValid());
},
params.non_cellular);
CancellationFlag flag;
ErrorOr<std::shared_ptr<WebRtcSocket>> sender_socket_result = sender.Connect(
service_id, self_id, location_hint, &flag, params.non_cellular);
ASSERT_TRUE(sender_socket_result.has_value());
EXPECT_TRUE(sender_socket_result.value()->IsValid());
ExceptionOr<bool> devices_connected = connected.Get();
ASSERT_TRUE(devices_connected.ok());
EXPECT_TRUE(devices_connected.result());
receiver_socket->Close();
env_.Stop();
}
// 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_P(WebRtcTest, ConnectBothDevicesAndSendData) {
env_.Start({.webrtc_enabled = true});
WebRtcImpl receiver, sender;
std::shared_ptr<WebRtcSocket> receiver_socket;
WebRtcTestParams params = GetParam();
const WebrtcPeerId self_id("self_id");
const std::string service_id("NearbySharing");
LocationHint location_hint;
Future<bool> connected;
absl::string_view message("message");
receiver.StartAcceptingConnections(
service_id, self_id, location_hint,
[&receiver_socket, connected](
const std::string& service_id,
std::shared_ptr<WebRtcSocket> wrapper) mutable {
receiver_socket = wrapper;
connected.Set(receiver_socket->IsValid());
},
params.non_cellular);
CancellationFlag flag;
ErrorOr<std::shared_ptr<WebRtcSocket>> sender_socket_result = sender.Connect(
service_id, self_id, location_hint, &flag, params.non_cellular);
ASSERT_TRUE(sender_socket_result.has_value());
EXPECT_TRUE(sender_socket_result.value()->IsValid());
ExceptionOr<bool> devices_connected = connected.Get();
ASSERT_TRUE(devices_connected.ok());
EXPECT_TRUE(devices_connected.result());
sender_socket_result.value()->GetOutputStream().Write(message);
ExceptionOr<ByteArray> received_msg =
receiver_socket->GetInputStream().Read(/*size=*/32);
ASSERT_TRUE(received_msg.ok());
EXPECT_EQ(message, received_msg.result().AsStringView());
receiver_socket->Close();
env_.Stop();
}
TEST_P(WebRtcTest, Connect_NullPeerConnection) {
env_.Start({.webrtc_enabled = true});
WebRtcTestParams params = GetParam();
testing::StrictMock<MockAcceptedCallback> mock_accepted_callback_;
env_.SetUseValidPeerConnection(
/*use_valid_peer_connection=*/false);
WebRtcImpl webrtc;
const std::string service_id("NearbySharing");
WebrtcPeerId self_id("peer_id");
LocationHint location_hint;
ASSERT_TRUE(webrtc.IsAvailable());
CancellationFlag flag;
ErrorOr<std::shared_ptr<WebRtcSocket>> wrapper_result =
webrtc.Connect(service_id, WebrtcPeerId("random_peer_id"), location_hint,
&flag, params.non_cellular);
EXPECT_TRUE(wrapper_result.has_error());
env_.Stop();
}
// Tests the flow when the device calls StartAcceptingConnections and the
// receive messages stream fails.
TEST_P(WebRtcTest, ContinueAcceptingConnectionsOnComplete) {
env_.Start({.webrtc_enabled = true});
testing::StrictMock<MockAcceptedCallback> mock_accepted_callback_;
WebRtcTestParams params = GetParam();
WebRtcImpl webrtc;
WebrtcPeerId self_id("peer_id");
const std::string service_id("NearbySharing");
LocationHint location_hint;
ASSERT_TRUE(webrtc.IsAvailable());
ASSERT_TRUE(webrtc.StartAcceptingConnections(
service_id, self_id, location_hint,
mock_accepted_callback_.AsStdFunction(), params.non_cellular));
EXPECT_TRUE(webrtc.IsAcceptingConnections(service_id));
// Simulate a failure in receiving messages stream, WebRtc should restart
// accepting connections.
env_.SendWebRtcSignalingComplete(self_id.GetId(),
/*success=*/false);
EXPECT_TRUE(webrtc.IsAcceptingConnections(service_id));
// And a "success" message should not cause accepting connections to stop.
env_.SendWebRtcSignalingComplete(self_id.GetId(),
/*success=*/true);
EXPECT_TRUE(webrtc.IsAcceptingConnections(service_id));
webrtc.StopAcceptingConnections(service_id);
EXPECT_FALSE(webrtc.IsAcceptingConnections(service_id));
env_.Stop();
}
// Tests when a CancellationFlag is cancelled during an attempt to
// `WebRtc::AttemptToConnect` triggered by `WebRtc::Connect`.
TEST_P(WebRtcTest, CancelDuringConnect) {
env_.Start({.webrtc_enabled = true});
WebRtcTestParams params = GetParam();
// Enable cancellation flags.
env_.SetFeatureFlags(FeatureFlags{
.enable_cancellation_flag = true,
});
std::shared_ptr<WebRtcSocket> receiver_socket, sender_socket;
const WebrtcPeerId self_id("self_id");
const std::string service_id("NearbySharing");
LocationHint location_hint;
Future<bool> connected;
CancellationFlag receiver_flag;
std::unique_ptr<WebRtc> receiver = std::make_unique<TestWebRtc>(
std::make_unique<FakeWebRtcMedium>(&receiver_flag));
CancellationFlag sender_flag;
std::unique_ptr<WebRtcMedium> sender_medium =
std::make_unique<FakeWebRtcMedium>(&sender_flag);
FakeWebRtcMedium* fake_sender_medium =
static_cast<FakeWebRtcMedium*>(sender_medium.get());
auto sender = std::make_unique<TestWebRtc>(std::move(sender_medium));
// Calls `CancellationFlag::Cancel` during a call to `GetSignalingMessenger`
// to simulate the cancellation occuring during an `AttemptToConnect`.
fake_sender_medium->TriggerCancellationDuringGetSignalingMessenger();
receiver->StartAcceptingConnections(
service_id, self_id, location_hint,
[&receiver_socket, connected](
const std::string& service_id,
std::shared_ptr<WebRtcSocket> wrapper) mutable {
receiver_socket = wrapper;
connected.Set(receiver_socket->IsValid());
},
params.non_cellular);
ErrorOr<std::shared_ptr<WebRtcSocket>> sender_socket_result = sender->Connect(
service_id, self_id, location_hint, &sender_flag, params.non_cellular);
// Since the flag was cancelled during the initial `AttemptToConnect`, except
// only one attempt instead of the usual three, because the cancellation flag
// should short-circuit the lengthy connection attempts during shutdown.
// Because of the way the iteration happens, the check for is cancelled
// happens after the counter has already been incremented, but before the
// attempt actually occurs.
EXPECT_TRUE(sender_socket_result.has_error());
EXPECT_EQ(2, sender->connect_attempts_count(service_id));
env_.Stop();
}
// Tests when a CancellationFlag is cancelled before `WebRtc::Connect` is
// called.
TEST_P(WebRtcTest, CancelBeforeConnect) {
env_.Start({.webrtc_enabled = true});
WebRtcTestParams params = GetParam();
// Enable cancellation flags.
env_.SetFeatureFlags(FeatureFlags{
.enable_cancellation_flag = true,
});
std::shared_ptr<WebRtcSocket> receiver_socket;
const WebrtcPeerId self_id("self_id");
const std::string service_id("NearbySharing");
LocationHint location_hint;
Future<bool> connected;
CancellationFlag receiver_flag;
std::unique_ptr<WebRtc> receiver = std::make_unique<TestWebRtc>(
std::make_unique<FakeWebRtcMedium>(&receiver_flag));
CancellationFlag sender_flag(true);
auto sender = std::make_unique<TestWebRtc>(
std::make_unique<FakeWebRtcMedium>(&sender_flag));
receiver->StartAcceptingConnections(
service_id, self_id, location_hint,
[&receiver_socket, connected](
const std::string& service_id,
std::shared_ptr<WebRtcSocket> wrapper) mutable {
receiver_socket = wrapper;
connected.Set(receiver_socket->IsValid());
},
params.non_cellular);
ErrorOr<std::shared_ptr<WebRtcSocket>> sender_socket_result = sender->Connect(
service_id, self_id, location_hint, &sender_flag, params.non_cellular);
// Expect an invalid socket from stopping during the first attempt to connect,
// because `Connect` returned immediatley when it checked for cancellation.
EXPECT_TRUE(sender_socket_result.has_error());
EXPECT_EQ(1, sender->connect_attempts_count(service_id));
env_.Stop();
}
// Tests when a CancellationFlag is cancelled during an attempt to
// `WebRtc::AttemptToConnect` triggered by `WebRtc::Connect` when multiple
// `WebRTC::Connect` calls are in flight for multiple service ids.
TEST_P(WebRtcTest, CancelDuringConnect_MultipleConnect) {
env_.Start({.webrtc_enabled = true});
WebRtcTestParams params = GetParam();
// Enable cancellation flags.
env_.SetFeatureFlags(FeatureFlags{
.enable_cancellation_flag = true,
});
std::shared_ptr<WebRtcSocket> receiver_socket;
const WebrtcPeerId self_id("self_id");
const std::string ns_service_id("NearbySharing");
const std::string ph_service_id("PhoneHub");
LocationHint location_hint;
Future<bool> connected;
CancellationFlag receiver_flag;
std::unique_ptr<WebRtc> receiver = std::make_unique<TestWebRtc>(
std::make_unique<FakeWebRtcMedium>(&receiver_flag));
CancellationFlag flag;
auto sender_medium = std::make_unique<FakeWebRtcMedium>(&flag);
FakeWebRtcMedium* fake_sender_medium = sender_medium.get();
auto sender = std::make_unique<TestWebRtc>(std::move(sender_medium));
receiver->StartAcceptingConnections(
ns_service_id, self_id, location_hint,
[&receiver_socket, connected](
const std::string& ns_service_id,
std::shared_ptr<WebRtcSocket> wrapper) mutable {
receiver_socket = wrapper;
connected.Set(receiver_socket->IsValid());
},
params.non_cellular);
// Simulate a successful connect for the endpoint of NearbySharing.
ErrorOr<std::shared_ptr<WebRtcSocket>> sender_socket_result = sender->Connect(
ns_service_id, self_id, location_hint, &flag, params.non_cellular);
ASSERT_TRUE(sender_socket_result.has_value());
EXPECT_TRUE(sender_socket_result.value()->IsValid());
// Calls `CancellationFlag::Cancel` during a call to `GetSignalingMessenger`
// to simulate the cancellation occuring during an `AttemptToConnect` for the
// endpoint of Phone Hub.
fake_sender_medium->TriggerCancellationDuringGetSignalingMessenger();
sender_socket_result = sender->Connect(ph_service_id, self_id, location_hint,
&flag, params.non_cellular);
EXPECT_TRUE(sender_socket_result.has_error());
// Since the flag was cancelled during the initial `AttemptToConnect`, except
// only one attempt instead of the usual three, because the cancellation flag
// should short-circuit the lengthy connection attempts during shutdown.
// Because of the way the iteration happens, the check for is cancelled
// happens after the counter has already been incremented, but before the
// attempt actually occurs. For the successful connect, expect only one
// attempt.
EXPECT_EQ(1, sender->connect_attempts_count(ns_service_id));
EXPECT_EQ(2, sender->connect_attempts_count(ph_service_id));
env_.Stop();
}
INSTANTIATE_TEST_SUITE_P(ParametrisedWebRtcTest, WebRtcTest,
testing::ValuesIn<WebRtcTestParams>({
{.feature_flags =
FeatureFlags{
.enable_cancellation_flag = true,
},
.non_cellular = true},
{.feature_flags =
FeatureFlags{
.enable_cancellation_flag = true,
},
.non_cellular = false},
{.feature_flags =
FeatureFlags{
.enable_cancellation_flag = false,
},
.non_cellular = true},
{.feature_flags =
FeatureFlags{
.enable_cancellation_flag = false,
},
.non_cellular = false},
}));
} // namespace
} // namespace mediums
} // namespace connections
} // namespace nearby
@@ -1,87 +0,0 @@
// Copyright 2023 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/webrtc/webrtc_medium_impl.h"
#include <memory>
#include <optional>
#include <utility>
#include "absl/strings/string_view.h"
#include "connections/implementation/mediums/webrtc/tachyon_express_signaling_messenger.h"
#include "internal/platform/implementation/webrtc.h"
#include "webrtc/api/create_modular_peer_connection_factory.h"
#include "webrtc/api/peer_connection_interface.h"
#include "webrtc/api/rtc_error.h"
#include "webrtc/api/scoped_refptr.h"
#include "webrtc/rtc_base/thread.h"
namespace nearby::connections::mediums {
void WebRtcMediumImpl::CreatePeerConnection(
webrtc::PeerConnectionObserver* observer, PeerConnectionCallback callback) {
CreatePeerConnection(std::nullopt, observer, std::move(callback));
}
void WebRtcMediumImpl::CreatePeerConnection(
std::optional<webrtc::PeerConnectionFactoryInterface::Options> options,
webrtc::PeerConnectionObserver* observer, PeerConnectionCallback callback) {
webrtc::PeerConnectionInterface::RTCConfiguration rtc_config;
rtc_config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
// TODO: b/261663238 - Add the TURN servers and go beyond the default servers.
webrtc::PeerConnectionInterface::IceServer ice_server;
ice_server.urls.emplace_back("stun:stun.l.google.com:19302");
ice_server.urls.emplace_back("stun:stun1.l.google.com:19302");
ice_server.urls.emplace_back("stun:stun2.l.google.com:19302");
ice_server.urls.emplace_back("stun:stun3.l.google.com:19302");
ice_server.urls.emplace_back("stun:stun4.l.google.com:19302");
rtc_config.servers.push_back(ice_server);
std::unique_ptr<webrtc::Thread> signaling_thread = webrtc::Thread::Create();
signaling_thread->SetName("signaling_thread", nullptr);
if (!signaling_thread->Start()) {
callback(/*peer_connection=*/nullptr);
return;
}
webrtc::PeerConnectionDependencies dependencies(observer);
webrtc::PeerConnectionFactoryDependencies factory_dependencies;
factory_dependencies.signaling_thread = signaling_thread.release();
webrtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface>
peer_connection_factory = webrtc::CreateModularPeerConnectionFactory(
std::move(factory_dependencies));
if (options.has_value()) {
peer_connection_factory->SetOptions(options.value());
}
webrtc::RTCErrorOr<webrtc::scoped_refptr<webrtc::PeerConnectionInterface>>
peer_connection_or_error =
peer_connection_factory->CreatePeerConnectionOrError(
rtc_config, std::move(dependencies));
if (peer_connection_or_error.ok()) {
callback(peer_connection_or_error.MoveValue());
} else {
callback(/*peer_connection=*/nullptr);
}
}
std::unique_ptr<api::WebRtcSignalingMessenger>
WebRtcMediumImpl::GetSignalingMessenger(
absl::string_view self_id,
const location::nearby::connections::LocationHint& location_hint) {
return std::make_unique<TachyonExpressSignalingMessenger>(self_id,
location_hint);
}
} // namespace nearby::connections::mediums
@@ -1,52 +0,0 @@
// 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_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_MEDIUM_IMPL_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_MEDIUM_IMPL_H_
#include <memory>
#include <optional>
#include "absl/strings/string_view.h"
#include "internal/platform/implementation/webrtc.h"
#include "webrtc/api/peer_connection_interface.h"
namespace nearby::connections::mediums {
class WebRtcMediumImpl : public api::WebRtcMedium {
public:
~WebRtcMediumImpl() override = default;
// Creates and returns a new webrtc::PeerConnectionInterface object via
// |callback|.
void CreatePeerConnection(webrtc::PeerConnectionObserver* observer,
PeerConnectionCallback callback) override;
// Creates and returns a new webrtc::PeerConnectionInterface object via
// |callback| with |PeerConnectionFactoryInterface::Options|.
void CreatePeerConnection(
std::optional<webrtc::PeerConnectionFactoryInterface::Options> options,
webrtc::PeerConnectionObserver* observer,
PeerConnectionCallback callback) override;
// Returns a signaling messenger for sending WebRTC signaling messages.
std::unique_ptr<api::WebRtcSignalingMessenger> GetSignalingMessenger(
absl::string_view self_id,
const location::nearby::connections::LocationHint& location_hint)
override;
};
} // namespace nearby::connections::mediums
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_MEDIUM_IMPL_H_
@@ -1,74 +0,0 @@
// Copyright 2023 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/webrtc/webrtc_medium_impl.h"
#include <memory>
#include <optional>
#include <string>
#include "gtest/gtest.h"
#include "internal/platform/implementation/webrtc.h"
#include "webrtc/api/data_channel_interface.h"
#include "webrtc/api/jsep.h"
#include "webrtc/api/peer_connection_interface.h"
#include "webrtc/api/scoped_refptr.h"
namespace nearby::connections::mediums {
class MockPeerConnectionObserver : public webrtc::PeerConnectionObserver {
public:
void OnSignalingChange(
webrtc::PeerConnectionInterface::SignalingState new_state) override {}
void OnDataChannel(webrtc::scoped_refptr<webrtc::DataChannelInterface>
data_channel) override {}
void OnIceGatheringChange(
webrtc::PeerConnectionInterface::IceGatheringState new_state) override {}
void OnIceCandidate(const webrtc::IceCandidate* candidate) override {}
};
location::nearby::connections::LocationHint GetCountryCodeLocationHint(
const std::string& country_code) {
auto location_hint = location::nearby::connections::LocationHint();
location_hint.set_location(country_code);
location_hint.set_format(
location::nearby::connections::LocationStandard::ISO_3166_1_ALPHA_2);
return location_hint;
}
TEST(WebrtcMediumImplTest, CreatePeerConnectionSucceeds) {
auto observer = std::make_unique<MockPeerConnectionObserver>();
WebRtcMediumImpl medium;
medium.CreatePeerConnection(
std::nullopt, observer.get(),
[](webrtc::scoped_refptr<webrtc::PeerConnectionInterface>
peer_connection) mutable {
if (!peer_connection) {
FAIL() << "Peer connection should have been non-null";
return;
}
});
}
TEST(WebrtcMediumImplTest, GetSignalingMessengerSucceeds) {
WebRtcMediumImpl medium;
std::unique_ptr<api::WebRtcSignalingMessenger> messenger =
medium.GetSignalingMessenger("US", GetCountryCodeLocationHint("US"));
EXPECT_TRUE(messenger);
}
} // namespace nearby::connections::mediums
@@ -1,203 +0,0 @@
// 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/webrtc/webrtc_socket_impl.h"
#include <cstdint>
#include <string>
#include <tuple>
#include <utility>
#include "absl/strings/string_view.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "internal/platform/input_stream.h"
#include "internal/platform/logging.h"
#include "internal/platform/mutex_lock.h"
#include "internal/platform/output_stream.h"
#include "internal/platform/pipe.h"
#include "internal/platform/runnable.h"
#include "webrtc/api/data_channel_interface.h"
#include "webrtc/api/scoped_refptr.h"
namespace nearby {
namespace connections {
namespace mediums {
// OutputStreamImpl
Exception WebRtcSocketImpl::OutputStreamImpl::Write(absl::string_view data) {
if (data.size() > kMaxDataSize) {
LOG(WARNING) << "Sending data larger than 1MB";
return {Exception::kIo};
}
socket_->BlockUntilSufficientSpaceInBuffer(data.size());
if (socket_->IsClosed()) {
LOG(WARNING) << "Tried sending message while socket is closed";
return {Exception::kIo};
}
if (!socket_->SendMessage(ByteArray::FromStringView(data))) {
LOG(INFO) << "Unable to write data to socket.";
return {Exception::kIo};
}
return {Exception::kSuccess};
}
Exception WebRtcSocketImpl::OutputStreamImpl::Flush() {
// Java implementation is empty.
return {Exception::kSuccess};
}
Exception WebRtcSocketImpl::OutputStreamImpl::Close() {
socket_->Close();
return {Exception::kSuccess};
}
// WebRtcSocket
WebRtcSocketImpl::WebRtcSocketImpl(
const std::string& name,
webrtc::scoped_refptr<webrtc::DataChannelInterface> data_channel)
: name_(name), data_channel_(std::move(data_channel)) {
LOG(INFO) << "WebRtcSocket::WebRtcSocket(" << name_ << ") this: " << this;
std::tie(pipe_input_, pipe_output_) = CreatePipe();
data_channel_->RegisterObserver(this);
}
WebRtcSocketImpl::~WebRtcSocketImpl() {
LOG(INFO) << "WebRtcSocket::~WebRtcSocket(" << name_ << ") this: " << this;
if (!IsClosed()) {
data_channel_->UnregisterObserver();
Close();
}
LOG(INFO) << "WebRtcSocket::~WebRtcSocket(" << name_ << ") this: " << this
<< " done";
}
InputStream& WebRtcSocketImpl::GetInputStream() { return *pipe_input_; }
OutputStream& WebRtcSocketImpl::GetOutputStream() { return output_stream_; }
Exception WebRtcSocketImpl::Close() {
LOG(INFO) << "WebRtcSocket::Close(" << name_ << ") this: " << this;
if (closed_.Set(true)) return {Exception::kSuccess};
ClosePipe();
// NOTE: This call blocks and triggers a state change on the signaling thread
// to 'closing' but does not block until 'closed' is sent so the data channel
// is not fully closed when this call is done.
data_channel_->Close();
LOG(INFO) << "WebRtcSocket::Close(" << name_ << ") this: " << this << " done";
return {Exception::kSuccess};
}
void WebRtcSocketImpl::OnStateChange() {
// Running on the signaling thread right now.
LOG(ERROR) << "WebRtcSocket::OnStateChange() webrtc data channel state: "
<< webrtc::DataChannelInterface::DataStateString(
data_channel_->state());
switch (data_channel_->state()) {
case webrtc::DataChannelInterface::DataState::kConnecting:
break;
case webrtc::DataChannelInterface::DataState::kOpen:
// We implicitly depend on the |socket_listener_| to offload from
// the signaling thread so it does not get blocked.
socket_listener_.socket_ready_cb(this);
break;
case webrtc::DataChannelInterface::DataState::kClosing:
break;
case webrtc::DataChannelInterface::DataState::kClosed:
LOG(ERROR) << "WebRtcSocket::OnStateChange() unregistering data "
"channel observer.";
// This will trigger a destruction of the owning connection flow
// We implicitly depend on the |socket_listener_| to offload from
// the signaling thread so it does not get blocked.
socket_listener_.socket_closed_cb(this);
if (!closed_.Set(true)) {
OffloadFromSignalingThread([this] { ClosePipe(); });
}
break;
}
}
void WebRtcSocketImpl::OnMessage(const webrtc::DataBuffer& buffer) {
// This is a data channel callback on the signaling thread, lets off load so
// we don't block signaling.
OffloadFromSignalingThread(
[this, buffer = ByteArray(buffer.data.data<char>(), buffer.size())] {
if (!pipe_output_->Write(buffer.AsStringView()).Ok()) {
Close();
return;
}
if (!pipe_output_->Flush().Ok()) {
Close();
}
});
}
void WebRtcSocketImpl::OnBufferedAmountChange(uint64_t sent_data_size) {
// This is a data channel callback on the signaling thread, lets off load so
// we don't block signaling.
OffloadFromSignalingThread([this] { WakeUpWriter(); });
}
bool WebRtcSocketImpl::SendMessage(const ByteArray& data) {
return data_channel_->Send(
webrtc::DataBuffer(std::string(data.data(), data.size())));
}
bool WebRtcSocketImpl::IsClosed() { return closed_.Get(); }
void WebRtcSocketImpl::ClosePipe() {
LOG(INFO) << "WebRtcSocket::ClosePipe(" << name_ << ") this: " << this;
// This is thread-safe to close these sockets even if a read or write is in
// process on another thread, Close will wait for the exclusive mutex before
// setting state.
pipe_input_->Close();
pipe_output_->Close();
WakeUpWriter();
LOG(INFO) << "WebRtcSocket::ClosePipe(" << name_ << ") this: " << this
<< " done";
}
// Must not be called on signalling thread.
void WebRtcSocketImpl::WakeUpWriter() {
MutexLock lock(&backpressure_mutex_);
buffer_variable_.Notify();
}
void WebRtcSocketImpl::SetSocketListener(SocketListener&& listener) {
socket_listener_ = std::move(listener);
}
void WebRtcSocketImpl::BlockUntilSufficientSpaceInBuffer(int length) {
MutexLock lock(&backpressure_mutex_);
while (!IsClosed() &&
(data_channel_->buffered_amount() + length > kMaxDataSize)) {
// TODO(himanshujaju): Add wait with timeout.
buffer_variable_.Wait();
}
}
void WebRtcSocketImpl::OffloadFromSignalingThread(Runnable runnable) {
single_thread_executor_.Execute(std::move(runnable));
}
} // namespace mediums
} // namespace connections
} // namespace nearby
@@ -1,132 +0,0 @@
// 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_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_SOCKET_IMPL_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_SOCKET_IMPL_H_
#include <cstdint>
#include <memory>
#include <string>
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "connections/implementation/mediums/webrtc_socket.h"
#include "internal/platform/atomic_boolean.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/condition_variable.h"
#include "internal/platform/exception.h"
#include "internal/platform/input_stream.h"
#include "internal/platform/listeners.h"
#include "internal/platform/mutex.h"
#include "internal/platform/output_stream.h"
#include "internal/platform/runnable.h"
#include "internal/platform/single_thread_executor.h"
#include "webrtc/api/data_channel_interface.h"
#include "webrtc/api/scoped_refptr.h"
namespace nearby {
namespace connections {
namespace mediums {
// Maximum data size: 1 MB
constexpr int kMaxDataSize = 1 * 1024 * 1024;
// Defines the Socket implementation specific to WebRTC, which uses the WebRTC
// data channel to send and receive messages.
//
// Messages are buffered here to prevent the data channel from overflowing,
// which could lead to data loss.
class WebRtcSocketImpl : public WebRtcSocket,
public webrtc::DataChannelObserver {
public:
WebRtcSocketImpl(
const std::string& name,
webrtc::scoped_refptr<webrtc::DataChannelInterface> data_channel);
~WebRtcSocketImpl() override;
WebRtcSocketImpl(const WebRtcSocketImpl& other) = delete;
WebRtcSocketImpl& operator=(const WebRtcSocketImpl& other) = delete;
// Overrides for WebRtcSocket:
InputStream& GetInputStream() override;
OutputStream& GetOutputStream() override;
Exception Close() override;
bool IsValid() const override { return true; }
// webrtc::DataChannelObserver:
void OnStateChange() override;
void OnMessage(const webrtc::DataBuffer& buffer) override;
void OnBufferedAmountChange(uint64_t sent_data_size) override;
// Listener class the gets called when the socket is ready or closed
struct SocketListener {
absl::AnyInvocable<void(WebRtcSocketImpl*)> socket_ready_cb =
DefaultCallback<WebRtcSocketImpl*>();
absl::AnyInvocable<void(WebRtcSocketImpl*)> socket_closed_cb =
DefaultCallback<WebRtcSocketImpl*>();
};
void SetSocketListener(SocketListener&& listener);
private:
class OutputStreamImpl : public OutputStream {
public:
explicit OutputStreamImpl(WebRtcSocketImpl* const socket)
: socket_(socket) {}
~OutputStreamImpl() override = default;
OutputStreamImpl(const OutputStreamImpl& other) = delete;
OutputStreamImpl& operator=(const OutputStreamImpl& other) = delete;
// OutputStream:
Exception Write(absl::string_view data) override;
Exception Flush() override;
Exception Close() override;
private:
// |this| OutputStreamImpl is owned by |socket_|.
WebRtcSocketImpl* const socket_;
};
void WakeUpWriter();
bool IsClosed();
void ClosePipe();
bool SendMessage(const ByteArray& data);
void BlockUntilSufficientSpaceInBuffer(int length);
void OffloadFromSignalingThread(Runnable runnable);
std::string name_;
webrtc::scoped_refptr<webrtc::DataChannelInterface> data_channel_;
std::unique_ptr<InputStream> pipe_input_;
std::unique_ptr<OutputStream> pipe_output_;
OutputStreamImpl output_stream_{this};
AtomicBoolean closed_{false};
SocketListener socket_listener_;
mutable Mutex backpressure_mutex_;
ConditionVariable buffer_variable_{&backpressure_mutex_};
// This should be destroyed first to ensure any remaining tasks flushed on
// shutdown get run while the other members are still alive.
SingleThreadExecutor single_thread_executor_;
};
} // namespace mediums
} // namespace connections
} // namespace nearby
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_SOCKET_IMPL_H_
@@ -1,249 +0,0 @@
// 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/webrtc/webrtc_socket_impl.h"
#include <cstdint>
#include <string>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/strings/string_view.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "webrtc/api/data_channel_interface.h"
#include "webrtc/api/scoped_refptr.h"
#include "webrtc/rtc_base/ref_counted_object.h"
namespace nearby {
namespace connections {
namespace mediums {
namespace {
// using TestPlatform = platform::ImplementationPlatform;
const char kSocketName[] = "TestSocket";
class MockDataChannel
: public webrtc::RefCountedObject<webrtc::DataChannelInterface> {
public:
MOCK_METHOD(void, RegisterObserver, (webrtc::DataChannelObserver*));
MOCK_METHOD(void, UnregisterObserver, ());
MOCK_METHOD(std::string, label, (), (const));
MOCK_METHOD(bool, reliable, (), (const));
MOCK_METHOD(int, id, (), (const));
MOCK_METHOD(DataState, state, (), (const));
MOCK_METHOD(uint32_t, messages_sent, (), (const));
MOCK_METHOD(uint64_t, bytes_sent, (), (const));
MOCK_METHOD(uint32_t, messages_received, (), (const));
MOCK_METHOD(uint64_t, bytes_received, (), (const));
MOCK_METHOD(uint64_t, buffered_amount, (), (const));
MOCK_METHOD(void, Close, ());
MOCK_METHOD(bool, Send, (const webrtc::DataBuffer&));
};
} // namespace
TEST(WebRtcSocketTest, ReadFromSocket) {
const char* message = "message";
webrtc::scoped_refptr<MockDataChannel> mock_data_channel(
new MockDataChannel());
WebRtcSocketImpl webrtc_socket(kSocketName, mock_data_channel);
webrtc_socket.OnMessage(webrtc::DataBuffer{message});
ExceptionOr<ByteArray> result = webrtc_socket.GetInputStream().Read(7);
EXPECT_TRUE(result.ok());
EXPECT_EQ(result.result(), ByteArray{message});
}
TEST(WebRtcSocketTest, ReadMultipleMessages) {
webrtc::scoped_refptr<MockDataChannel> mock_data_channel(
new MockDataChannel());
WebRtcSocketImpl webrtc_socket(kSocketName, mock_data_channel);
webrtc_socket.OnMessage(webrtc::DataBuffer{"Me"});
webrtc_socket.OnMessage(webrtc::DataBuffer{"ssa"});
webrtc_socket.OnMessage(webrtc::DataBuffer{"ge"});
ExceptionOr<ByteArray> result;
// This behaviour is different from the Java code
result = webrtc_socket.GetInputStream().Read(7);
EXPECT_TRUE(result.ok());
EXPECT_EQ(result.result(), ByteArray{"Me"});
result = webrtc_socket.GetInputStream().Read(7);
EXPECT_TRUE(result.ok());
EXPECT_EQ(result.result(), ByteArray{"ssa"});
result = webrtc_socket.GetInputStream().Read(7);
EXPECT_TRUE(result.ok());
EXPECT_EQ(result.result(), ByteArray{"ge"});
}
TEST(WebRtcSocketTest, WriteToSocket) {
absl::string_view kMessage{"Message"};
webrtc::scoped_refptr<MockDataChannel> mock_data_channel(
new MockDataChannel());
WebRtcSocketImpl webrtc_socket(kSocketName, mock_data_channel);
EXPECT_CALL(*mock_data_channel, Send(testing::_))
.WillRepeatedly(testing::Return(true));
EXPECT_TRUE(webrtc_socket.GetOutputStream().Write(kMessage).Ok());
}
TEST(WebRtcSocketTest, SendDataBiggerThanMax) {
std::string kMessage(kMaxDataSize + 1, '0');
webrtc::scoped_refptr<MockDataChannel> mock_data_channel(
new MockDataChannel());
WebRtcSocketImpl webrtc_socket(kSocketName, mock_data_channel);
EXPECT_CALL(*mock_data_channel, Send(testing::_)).Times(0);
EXPECT_EQ(webrtc_socket.GetOutputStream().Write(kMessage),
Exception{Exception::kIo});
}
TEST(WebRtcSocketTest, WriteToDataChannelFails) {
absl::string_view kMessage{"Message"};
webrtc::scoped_refptr<MockDataChannel> mock_data_channel(
new MockDataChannel());
WebRtcSocketImpl webrtc_socket(kSocketName, mock_data_channel);
ON_CALL(*mock_data_channel, Send(testing::_))
.WillByDefault(testing::Return(false));
EXPECT_EQ(webrtc_socket.GetOutputStream().Write(kMessage),
Exception{Exception::kIo});
}
TEST(WebRtcSocketTest, Close) {
webrtc::scoped_refptr<MockDataChannel> mock_data_channel(
new MockDataChannel());
WebRtcSocketImpl webrtc_socket(kSocketName, mock_data_channel);
EXPECT_CALL(*mock_data_channel, Close());
int socket_closed_cb_called = 0;
webrtc_socket.SetSocketListener(
{.socket_closed_cb = [&](WebRtcSocketImpl* socket) {
socket_closed_cb_called++;
}});
webrtc_socket.Close();
// We have to fake the close event to get the callback to run.
ON_CALL(*mock_data_channel, state())
.WillByDefault(
testing::Return(webrtc::DataChannelInterface::DataState::kClosed));
webrtc_socket.OnStateChange();
EXPECT_EQ(socket_closed_cb_called, 1);
}
TEST(WebRtcSocketTest, WriteOnClosedChannel) {
absl::string_view kMessage{"Message"};
webrtc::scoped_refptr<MockDataChannel> mock_data_channel(
new MockDataChannel());
WebRtcSocketImpl webrtc_socket(kSocketName, mock_data_channel);
webrtc_socket.Close();
EXPECT_CALL(*mock_data_channel, Send(testing::_)).Times(0);
EXPECT_EQ(webrtc_socket.GetOutputStream().Write(kMessage),
Exception{Exception::kIo});
}
TEST(WebRtcSocketTest, ReadFromClosedChannel) {
absl::string_view kMessage{"Message"};
webrtc::scoped_refptr<MockDataChannel> mock_data_channel(
new MockDataChannel());
WebRtcSocketImpl webrtc_socket(kSocketName, mock_data_channel);
ON_CALL(*mock_data_channel, Send(testing::_))
.WillByDefault(testing::Return(true));
webrtc_socket.GetOutputStream().Write(kMessage);
webrtc_socket.Close();
EXPECT_TRUE(webrtc_socket.GetInputStream().Read(7).GetResult().Empty());
}
TEST(WebRtcSocketTest, DataChannelCloseEventCleansUp) {
webrtc::scoped_refptr<MockDataChannel> mock_data_channel(
new MockDataChannel());
WebRtcSocketImpl webrtc_socket(kSocketName, mock_data_channel);
ON_CALL(*mock_data_channel, state())
.WillByDefault(
testing::Return(webrtc::DataChannelInterface::DataState::kClosed));
webrtc_socket.OnStateChange();
EXPECT_TRUE(webrtc_socket.GetInputStream().Read(7).GetResult().Empty());
// Calling Close again should be safe even if the channel is already shut
// down.
webrtc_socket.Close();
}
TEST(WebRtcSocketTest, OpenStateTriggersCallback) {
webrtc::scoped_refptr<MockDataChannel> mock_data_channel(
new MockDataChannel());
WebRtcSocketImpl webrtc_socket(kSocketName, mock_data_channel);
int socket_ready_cb_called = 0;
webrtc_socket.SetSocketListener(
{.socket_ready_cb = [&](WebRtcSocketImpl* socket) {
socket_ready_cb_called++;
}});
ON_CALL(*mock_data_channel, state())
.WillByDefault(
testing::Return(webrtc::DataChannelInterface::DataState::kOpen));
webrtc_socket.OnStateChange();
EXPECT_EQ(socket_ready_cb_called, 1);
}
TEST(WebRtcSocketTest, CloseStateTriggersCallback) {
webrtc::scoped_refptr<MockDataChannel> mock_data_channel(
new MockDataChannel());
WebRtcSocketImpl webrtc_socket(kSocketName, mock_data_channel);
int socket_closed_cb_called = 0;
webrtc_socket.SetSocketListener(
{.socket_closed_cb = [&](WebRtcSocketImpl* socket) {
socket_closed_cb_called++;
}});
ON_CALL(*mock_data_channel, state())
.WillByDefault(
testing::Return(webrtc::DataChannelInterface::DataState::kClosed));
webrtc_socket.OnStateChange();
EXPECT_EQ(socket_closed_cb_called, 1);
}
} // namespace mediums
} // namespace connections
} // namespace nearby
@@ -24,11 +24,11 @@
#include "internal/platform/byte_array.h"
#include "internal/platform/implementation/webrtc.h"
#include "internal/platform/medium_environment.h"
#include "webrtc/api/create_modular_peer_connection_factory.h"
#include "webrtc/api/peer_connection_interface.h"
#include "webrtc/api/scoped_refptr.h"
#include "webrtc/rtc_base/checks.h"
#include "webrtc/rtc_base/thread.h"
#include "third_party/webrtc/files/stable/webrtc/api/create_modular_peer_connection_factory.h"
#include "third_party/webrtc/files/stable/webrtc/api/peer_connection_interface.h"
#include "third_party/webrtc/files/stable/webrtc/api/scoped_refptr.h"
#include "third_party/webrtc/files/stable/webrtc/rtc_base/checks.h"
#include "third_party/webrtc/files/stable/webrtc/rtc_base/thread.h"
namespace nearby {
namespace g3 {
+1 -1
View File
@@ -23,7 +23,7 @@
#include "internal/platform/byte_array.h"
#include "internal/platform/implementation/webrtc.h"
#include "internal/platform/implementation/g3/single_thread_executor.h"
#include "webrtc/api/peer_connection_interface.h"
#include "third_party/webrtc/files/stable/webrtc/api/peer_connection_interface.h"
namespace nearby {
namespace g3 {
+2 -2
View File
@@ -22,8 +22,8 @@
#include "absl/strings/string_view.h"
#include "connections/implementation/proto/offline_wire_formats.pb.h"
#include "internal/platform/byte_array.h"
#include "webrtc/api/peer_connection_interface.h"
#include "webrtc/api/scoped_refptr.h"
#include "third_party/webrtc/files/stable/webrtc/api/peer_connection_interface.h"
#include "third_party/webrtc/files/stable/webrtc/api/scoped_refptr.h"
namespace nearby {
namespace api {