mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-15 07:06:11 -04:00
CancellationFlags will be used to prevent crashes during the shutdown of Nearby Connections from pending tasks taking too long during the shutdown period. WebRTC using 3 x 10s retries to connect, which means we are potentially waiting for 30 seconds of retries to execute during shutdown (which is longer than the 10s duration alloted for Core shutdown). By using CancellationFlags, we can prevent the retries occuring during the Shutdown by short-circuiting an in flight AttemptToConnect, and checking for Cancellation before retries. PiperOrigin-RevId: 539690403
777 lines
28 KiB
C++
777 lines
28 KiB
C++
// 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 NO_WEBRTC
|
|
|
|
#include "connections/implementation/mediums/webrtc.h"
|
|
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "absl/functional/bind_front.h"
|
|
#include "absl/time/time.h"
|
|
#include "connections/implementation/mediums/webrtc/session_description_wrapper.h"
|
|
#include "connections/implementation/mediums/webrtc/signaling_frames.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_listener.h"
|
|
#include "internal/platform/future.h"
|
|
#include "internal/platform/logging.h"
|
|
#include "internal/platform/mutex_lock.h"
|
|
#include "webrtc/api/jsep.h"
|
|
|
|
namespace nearby {
|
|
namespace connections {
|
|
namespace mediums {
|
|
|
|
namespace {
|
|
using ::location::nearby::connections::LocationHint;
|
|
|
|
// 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
|
|
|
|
WebRtc::WebRtc() : WebRtc(std::make_unique<WebRtcMedium>()) {}
|
|
|
|
WebRtc::WebRtc(std::unique_ptr<WebRtcMedium> medium)
|
|
: medium_(std::move(medium)) {}
|
|
|
|
WebRtc::~WebRtc() {
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
const std::string WebRtc::GetDefaultCountryCode() {
|
|
return medium_->GetDefaultCountryCode();
|
|
}
|
|
|
|
bool WebRtc::IsAvailable() { return medium_->IsValid(); }
|
|
|
|
bool WebRtc::IsAcceptingConnections(const std::string& service_id) {
|
|
MutexLock lock(&mutex_);
|
|
return IsAcceptingConnectionsLocked(service_id);
|
|
}
|
|
|
|
bool WebRtc::IsAcceptingConnectionsLocked(const std::string& service_id) {
|
|
return accepting_connections_info_.contains(service_id);
|
|
}
|
|
|
|
bool WebRtc::StartAcceptingConnections(const std::string& service_id,
|
|
const WebrtcPeerId& self_peer_id,
|
|
const LocationHint& location_hint,
|
|
AcceptedConnectionCallback callback) {
|
|
MutexLock lock(&mutex_);
|
|
if (!IsAvailable()) {
|
|
NEARBY_LOG(WARNING,
|
|
"Cannot start accepting WebRTC connections because WebRTC is "
|
|
"not available.");
|
|
return false;
|
|
}
|
|
|
|
if (IsAcceptingConnectionsLocked(service_id)) {
|
|
NEARBY_LOG(WARNING,
|
|
"Cannot start accepting WebRTC connections because service %s "
|
|
"is already accepting WebRTC connections.",
|
|
service_id.c_str());
|
|
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 = callback;
|
|
|
|
// 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(&WebRtc::OnSignalingMessage, this, service_id),
|
|
absl::bind_front(&WebRtc::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(&WebRtc::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));
|
|
NEARBY_LOG(INFO,
|
|
"Started listening for WebRTC connections as %s on service %s",
|
|
self_peer_id.GetId().c_str(), service_id.c_str());
|
|
return true;
|
|
}
|
|
|
|
void WebRtc::StopAcceptingConnections(const std::string& service_id) {
|
|
MutexLock lock(&mutex_);
|
|
if (!IsAcceptingConnectionsLocked(service_id)) {
|
|
NEARBY_LOG(WARNING,
|
|
"Cannot stop accepting WebRTC connections because service %s "
|
|
"is not accepting WebRTC connections.",
|
|
service_id.c_str());
|
|
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);
|
|
NEARBY_LOG(INFO, "Stopped listening for WebRTC connections for service %s",
|
|
service_id.c_str());
|
|
}
|
|
|
|
WebRtcSocketWrapper WebRtc::Connect(const std::string& service_id,
|
|
const WebrtcPeerId& remote_peer_id,
|
|
const LocationHint& location_hint,
|
|
CancellationFlag* cancellation_flag) {
|
|
service_id_to_connect_attempts_count_map_[service_id] = 1;
|
|
while (service_id_to_connect_attempts_count_map_[service_id] <=
|
|
kConnectAttemptsLimit) {
|
|
if (cancellation_flag->Cancelled()) {
|
|
NEARBY_LOGS(WARNING)
|
|
<< "Attempt #"
|
|
<< service_id_to_connect_attempts_count_map_[service_id]
|
|
<< ": Cannot Connect with WebRtc due to cancel.";
|
|
return WebRtcSocketWrapper();
|
|
}
|
|
|
|
NEARBY_LOGS(INFO) << "Attempt #"
|
|
<< service_id_to_connect_attempts_count_map_[service_id]
|
|
<< ": Beginning connection.";
|
|
auto wrapper_result = AttemptToConnect(service_id, remote_peer_id,
|
|
location_hint, cancellation_flag);
|
|
if (wrapper_result.IsValid()) {
|
|
return wrapper_result;
|
|
}
|
|
|
|
service_id_to_connect_attempts_count_map_[service_id]++;
|
|
}
|
|
|
|
NEARBY_LOGS(WARNING) << "Giving up after " << kConnectAttemptsLimit
|
|
<< " attempts";
|
|
return WebRtcSocketWrapper();
|
|
}
|
|
|
|
WebRtcSocketWrapper WebRtc::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<WebRtcSocketWrapper> 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]() {
|
|
NEARBY_LOGS(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()) {
|
|
NEARBY_LOG(
|
|
WARNING,
|
|
"Cannot connect to WebRTC peer %s because WebRTC is not available.",
|
|
remote_peer_id.GetId().c_str());
|
|
return WebRtcSocketWrapper();
|
|
}
|
|
|
|
// Create a new ConnectionFlow for this connection attempt.
|
|
std::unique_ptr<ConnectionFlow> connection_flow =
|
|
CreateConnectionFlow(service_id, remote_peer_id);
|
|
if (!connection_flow) {
|
|
NEARBY_LOG(
|
|
INFO,
|
|
"Cannot connect to WebRTC peer %s because we failed to create a "
|
|
"ConnectionFlow.",
|
|
remote_peer_id.GetId().c_str());
|
|
return WebRtcSocketWrapper();
|
|
}
|
|
|
|
// 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()) {
|
|
NEARBY_LOG(
|
|
INFO,
|
|
"Cannot connect to WebRTC peer %s because we failed to create a "
|
|
"SignalingMessenger.",
|
|
remote_peer_id.GetId().c_str());
|
|
return WebRtcSocketWrapper();
|
|
}
|
|
|
|
// 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(&WebRtc::OnSignalingMessage, this, service_id),
|
|
signaling_complete_callback)) {
|
|
NEARBY_LOG(INFO,
|
|
"Cannot connect to WebRTC peer %s because we failed to start "
|
|
"receiving messages over Tachyon.",
|
|
remote_peer_id.GetId().c_str());
|
|
info.signaling_messenger.reset();
|
|
return WebRtcSocketWrapper();
|
|
}
|
|
|
|
// 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))) {
|
|
NEARBY_LOG(INFO,
|
|
"Cannot connect to WebRTC peer %s because we failed to poke "
|
|
"the peer over Tachyon.",
|
|
remote_peer_id.GetId().c_str());
|
|
info.signaling_messenger.reset();
|
|
return WebRtcSocketWrapper();
|
|
}
|
|
|
|
// 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<WebRtcSocketWrapper> 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()) {
|
|
NEARBY_LOG(INFO, "Failed to connect to WebRTC peer %s.",
|
|
remote_peer_id.GetId().c_str());
|
|
RemoveConnectionFlow(remote_peer_id);
|
|
info.signaling_messenger.reset();
|
|
requesting_connections_info_.erase(remote_peer_id.GetId());
|
|
return WebRtcSocketWrapper();
|
|
}
|
|
|
|
// Clean up our ConnectionRequest.
|
|
info.signaling_messenger.reset();
|
|
requesting_connections_info_.erase(remote_peer_id.GetId());
|
|
|
|
// Return the result.
|
|
return socket_result.GetResult();
|
|
}
|
|
}
|
|
|
|
void WebRtc::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}))) {
|
|
NEARBY_LOG(INFO, "Failed to send ice candidate to %s.",
|
|
remote_peer_id.GetId().c_str());
|
|
}
|
|
|
|
NEARBY_LOG(INFO, "Sent ice candidate to %s.",
|
|
remote_peer_id.GetId().c_str());
|
|
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}))) {
|
|
NEARBY_LOG(INFO, "Failed to send ice candidate to %s.",
|
|
remote_peer_id.GetId().c_str());
|
|
}
|
|
|
|
NEARBY_LOG(INFO, "Sent ice candidate to %s.",
|
|
remote_peer_id.GetId().c_str());
|
|
return;
|
|
}
|
|
|
|
NEARBY_LOG(INFO,
|
|
"Skipping restart listening for tachyon inbox messages since we "
|
|
"are not accepting connections for service %s.",
|
|
service_id.c_str());
|
|
}
|
|
|
|
void WebRtc::OnSignalingMessage(const std::string& service_id,
|
|
const ByteArray& message) {
|
|
OffloadFromThread("rtc-on-signaling-message", [this, service_id, message]() {
|
|
ProcessTachyonInboxMessage(service_id, message);
|
|
});
|
|
}
|
|
|
|
void WebRtc::OnSignalingComplete(const std::string& service_id, bool success) {
|
|
NEARBY_LOG(INFO, "Signaling completed with status: %d.", 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 WebRtc::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))) {
|
|
NEARBY_LOG(WARNING, "Failed to parse signaling message.");
|
|
return;
|
|
}
|
|
|
|
// Ensure that the frame is valid (no missing fields).
|
|
if (!frame.has_sender_id()) {
|
|
NEARBY_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 {
|
|
NEARBY_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 {
|
|
NEARBY_LOG(INFO, "Received unknown WebRTC frame: ignoring.");
|
|
}
|
|
} else {
|
|
NEARBY_LOG(
|
|
INFO,
|
|
"Ignoring Tachyon message since we are not accepting connections.");
|
|
}
|
|
}
|
|
|
|
void WebRtc::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) {
|
|
NEARBY_LOG(INFO,
|
|
"Unable to send offer. Failed to create a ConnectionFlow.");
|
|
return;
|
|
}
|
|
|
|
SessionDescriptionWrapper offer = connection_flow->CreateOffer();
|
|
if (!offer.IsValid()) {
|
|
NEARBY_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)) {
|
|
NEARBY_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))) {
|
|
NEARBY_LOG(INFO,
|
|
"Unable to send offer. Failed to write the offer to the remote "
|
|
"peer %s.",
|
|
remote_peer_id.GetId().c_str());
|
|
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));
|
|
NEARBY_LOG(INFO, "Sent offer to %s.", remote_peer_id.GetId().c_str());
|
|
}
|
|
|
|
void WebRtc::ReceiveOffer(const WebrtcPeerId& remote_peer_id,
|
|
SessionDescriptionWrapper offer) {
|
|
const auto& entry = connection_flows_.find(remote_peer_id.GetId());
|
|
if (entry == connection_flows_.end()) {
|
|
NEARBY_LOG(INFO,
|
|
"Unable to receive offer. Failed to create a ConnectionFlow.");
|
|
return;
|
|
}
|
|
|
|
if (!entry->second->OnOfferReceived(offer)) {
|
|
NEARBY_LOG(INFO, "Unable to receive offer. Failed to process the offer.");
|
|
RemoveConnectionFlow(remote_peer_id);
|
|
}
|
|
}
|
|
|
|
void WebRtc::SendAnswer(const WebrtcPeerId& remote_peer_id) {
|
|
const auto& entry = connection_flows_.find(remote_peer_id.GetId());
|
|
if (entry == connection_flows_.end()) {
|
|
NEARBY_LOG(INFO,
|
|
"Unable to send answer. Failed to create a ConnectionFlow.");
|
|
return;
|
|
}
|
|
|
|
SessionDescriptionWrapper answer = entry->second->CreateAnswer();
|
|
if (!answer.IsValid()) {
|
|
NEARBY_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)) {
|
|
NEARBY_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()) {
|
|
NEARBY_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))) {
|
|
NEARBY_LOG(
|
|
INFO,
|
|
"Unable to send answer. Failed to write the answer to the remote "
|
|
"peer %s.",
|
|
remote_peer_id.GetId().c_str());
|
|
RemoveConnectionFlow(remote_peer_id);
|
|
return;
|
|
}
|
|
|
|
NEARBY_LOG(INFO, "Sent answer to %s.", remote_peer_id.GetId().c_str());
|
|
}
|
|
|
|
void WebRtc::ReceiveAnswer(const WebrtcPeerId& remote_peer_id,
|
|
SessionDescriptionWrapper answer) {
|
|
const auto& entry = connection_flows_.find(remote_peer_id.GetId());
|
|
if (entry == connection_flows_.end()) {
|
|
NEARBY_LOG(INFO,
|
|
"Unable to receive answer. Failed to create a ConnectionFlow.");
|
|
return;
|
|
}
|
|
|
|
if (!entry->second->OnAnswerReceived(answer)) {
|
|
NEARBY_LOG(INFO, "Unable to receive answer. Failed to process the answer.");
|
|
RemoveConnectionFlow(remote_peer_id);
|
|
}
|
|
}
|
|
|
|
void WebRtc::ReceiveIceCandidates(
|
|
const WebrtcPeerId& remote_peer_id,
|
|
std::vector<std::unique_ptr<webrtc::IceCandidateInterface>>
|
|
ice_candidates) {
|
|
const auto& entry = connection_flows_.find(remote_peer_id.GetId());
|
|
if (entry == connection_flows_.end()) {
|
|
NEARBY_LOG(
|
|
INFO,
|
|
"Unable to receive ice candidates. Failed to create a ConnectionFlow.");
|
|
return;
|
|
}
|
|
|
|
entry->second->OnRemoteIceCandidatesReceived(std::move(ice_candidates));
|
|
}
|
|
|
|
void WebRtc::ProcessRestartTachyonReceiveMessages(
|
|
const std::string& service_id) {
|
|
MutexLock lock(&mutex_);
|
|
RestartTachyonReceiveMessages(service_id);
|
|
}
|
|
|
|
void WebRtc::RestartTachyonReceiveMessages(const std::string& service_id) {
|
|
if (!IsAcceptingConnectionsLocked(service_id)) {
|
|
NEARBY_LOG(INFO,
|
|
"Skipping restart listening for tachyon inbox messages since we "
|
|
"are not accepting connections for service %s.",
|
|
service_id.c_str());
|
|
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(&WebRtc::OnSignalingMessage, this, service_id),
|
|
absl::bind_front(&WebRtc::OnSignalingComplete, this, service_id))) {
|
|
NEARBY_LOG(WARNING,
|
|
"Failed to restart listening for tachyon inbox messages for "
|
|
"service %s since we failed to reach Tachyon.",
|
|
service_id.c_str());
|
|
return;
|
|
}
|
|
|
|
NEARBY_LOG(INFO,
|
|
"Successfully restarted listening for tachyon inbox messages on "
|
|
"service %s.",
|
|
service_id.c_str());
|
|
}
|
|
|
|
void WebRtc::ProcessDataChannelOpen(const std::string& service_id,
|
|
const WebrtcPeerId& remote_peer_id,
|
|
WebRtcSocketWrapper 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.accepted_cb(
|
|
service_id, socket_wrapper);
|
|
return;
|
|
}
|
|
|
|
// No one to handle the newly created DataChannel, so we'll just close it.
|
|
socket_wrapper.Close();
|
|
NEARBY_LOG(INFO,
|
|
"Ignoring new DataChannel because we "
|
|
"are not accepting connections for service %s.",
|
|
service_id.c_str());
|
|
}
|
|
|
|
void WebRtc::ProcessDataChannelClosed(const WebrtcPeerId& remote_peer_id) {
|
|
MutexLock lock(&mutex_);
|
|
NEARBY_LOG(INFO,
|
|
"Data channel has closed, removing connection flow for peer %s.",
|
|
remote_peer_id.GetId().c_str());
|
|
|
|
RemoveConnectionFlow(remote_peer_id);
|
|
}
|
|
|
|
std::unique_ptr<ConnectionFlow> WebRtc::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::IceCandidateInterface* 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](
|
|
WebRtcSocketWrapper 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);
|
|
});
|
|
}},
|
|
},
|
|
*medium_);
|
|
}
|
|
|
|
void WebRtc::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 WebRtc::OffloadFromThread(const std::string& name, Runnable runnable) {
|
|
single_thread_executor_.Execute(name, std::move(runnable));
|
|
}
|
|
|
|
} // namespace mediums
|
|
} // namespace connections
|
|
} // namespace nearby
|
|
|
|
#endif
|