mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46:12 -04:00
internal
PiperOrigin-RevId: 925564968
This commit is contained in:
committed by
Copybara-Service
parent
ec9759d88a
commit
8bc556c4e7
+1
-11
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user