summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/p2p/base/wrapping_active_ice_controller.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/p2p/base/wrapping_active_ice_controller.cc')
-rw-r--r--third_party/libwebrtc/p2p/base/wrapping_active_ice_controller.cc253
1 files changed, 253 insertions, 0 deletions
diff --git a/third_party/libwebrtc/p2p/base/wrapping_active_ice_controller.cc b/third_party/libwebrtc/p2p/base/wrapping_active_ice_controller.cc
new file mode 100644
index 0000000000..c6659217fc
--- /dev/null
+++ b/third_party/libwebrtc/p2p/base/wrapping_active_ice_controller.cc
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2022 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "p2p/base/wrapping_active_ice_controller.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "api/sequence_checker.h"
+#include "api/task_queue/pending_task_safety_flag.h"
+#include "api/units/time_delta.h"
+#include "p2p/base/basic_ice_controller.h"
+#include "p2p/base/connection.h"
+#include "p2p/base/ice_agent_interface.h"
+#include "p2p/base/ice_controller_interface.h"
+#include "p2p/base/ice_switch_reason.h"
+#include "p2p/base/ice_transport_internal.h"
+#include "p2p/base/transport_description.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/thread.h"
+#include "rtc_base/time_utils.h"
+
+namespace {
+using ::webrtc::SafeTask;
+using ::webrtc::TimeDelta;
+} // unnamed namespace
+
+namespace cricket {
+
+WrappingActiveIceController::WrappingActiveIceController(
+ IceAgentInterface* ice_agent,
+ std::unique_ptr<IceControllerInterface> wrapped)
+ : network_thread_(rtc::Thread::Current()),
+ wrapped_(std::move(wrapped)),
+ agent_(*ice_agent) {
+ RTC_DCHECK(ice_agent != nullptr);
+}
+
+WrappingActiveIceController::WrappingActiveIceController(
+ IceAgentInterface* ice_agent,
+ IceControllerFactoryInterface* wrapped_factory,
+ const IceControllerFactoryArgs& wrapped_factory_args)
+ : network_thread_(rtc::Thread::Current()), agent_(*ice_agent) {
+ RTC_DCHECK(ice_agent != nullptr);
+ if (wrapped_factory) {
+ wrapped_ = wrapped_factory->Create(wrapped_factory_args);
+ } else {
+ wrapped_ = std::make_unique<BasicIceController>(wrapped_factory_args);
+ }
+}
+
+WrappingActiveIceController::~WrappingActiveIceController() {}
+
+void WrappingActiveIceController::SetIceConfig(const IceConfig& config) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ wrapped_->SetIceConfig(config);
+}
+
+bool WrappingActiveIceController::GetUseCandidateAttribute(
+ const Connection* connection,
+ NominationMode mode,
+ IceMode remote_ice_mode) const {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ return wrapped_->GetUseCandidateAttr(connection, mode, remote_ice_mode);
+}
+
+void WrappingActiveIceController::OnConnectionAdded(
+ const Connection* connection) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ wrapped_->AddConnection(connection);
+}
+
+void WrappingActiveIceController::OnConnectionPinged(
+ const Connection* connection) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ wrapped_->MarkConnectionPinged(connection);
+}
+
+void WrappingActiveIceController::OnConnectionUpdated(
+ const Connection* connection) {
+ RTC_LOG(LS_VERBOSE) << "Connection report for " << connection->ToString();
+ // Do nothing. Native ICE controllers have direct access to Connection, so no
+ // need to update connection state separately.
+}
+
+void WrappingActiveIceController::OnConnectionSwitched(
+ const Connection* connection) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ selected_connection_ = connection;
+ wrapped_->SetSelectedConnection(connection);
+}
+
+void WrappingActiveIceController::OnConnectionDestroyed(
+ const Connection* connection) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ wrapped_->OnConnectionDestroyed(connection);
+}
+
+void WrappingActiveIceController::MaybeStartPinging() {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ if (started_pinging_) {
+ return;
+ }
+
+ if (wrapped_->HasPingableConnection()) {
+ network_thread_->PostTask(
+ SafeTask(task_safety_.flag(), [this]() { SelectAndPingConnection(); }));
+ agent_.OnStartedPinging();
+ started_pinging_ = true;
+ }
+}
+
+void WrappingActiveIceController::SelectAndPingConnection() {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ agent_.UpdateConnectionStates();
+
+ IceControllerInterface::PingResult result =
+ wrapped_->SelectConnectionToPing(agent_.GetLastPingSentMs());
+ HandlePingResult(result);
+}
+
+void WrappingActiveIceController::HandlePingResult(
+ IceControllerInterface::PingResult result) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+
+ if (result.connection.has_value()) {
+ agent_.SendPingRequest(result.connection.value());
+ }
+
+ network_thread_->PostDelayedTask(
+ SafeTask(task_safety_.flag(), [this]() { SelectAndPingConnection(); }),
+ TimeDelta::Millis(result.recheck_delay_ms));
+}
+
+void WrappingActiveIceController::OnSortAndSwitchRequest(
+ IceSwitchReason reason) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ if (!sort_pending_) {
+ network_thread_->PostTask(SafeTask(task_safety_.flag(), [this, reason]() {
+ SortAndSwitchToBestConnection(reason);
+ }));
+ sort_pending_ = true;
+ }
+}
+
+void WrappingActiveIceController::OnImmediateSortAndSwitchRequest(
+ IceSwitchReason reason) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ SortAndSwitchToBestConnection(reason);
+}
+
+void WrappingActiveIceController::SortAndSwitchToBestConnection(
+ IceSwitchReason reason) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+
+ // Make sure the connection states are up-to-date since this affects how they
+ // will be sorted.
+ agent_.UpdateConnectionStates();
+
+ // Any changes after this point will require a re-sort.
+ sort_pending_ = false;
+
+ IceControllerInterface::SwitchResult result =
+ wrapped_->SortAndSwitchConnection(reason);
+ HandleSwitchResult(reason, result);
+ UpdateStateOnConnectionsResorted();
+}
+
+bool WrappingActiveIceController::OnImmediateSwitchRequest(
+ IceSwitchReason reason,
+ const Connection* selected) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ IceControllerInterface::SwitchResult result =
+ wrapped_->ShouldSwitchConnection(reason, selected);
+ HandleSwitchResult(reason, result);
+ return result.connection.has_value();
+}
+
+void WrappingActiveIceController::HandleSwitchResult(
+ IceSwitchReason reason_for_switch,
+ IceControllerInterface::SwitchResult result) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ if (result.connection.has_value()) {
+ RTC_LOG(LS_INFO) << "Switching selected connection due to: "
+ << IceSwitchReasonToString(reason_for_switch);
+ agent_.SwitchSelectedConnection(result.connection.value(),
+ reason_for_switch);
+ }
+
+ if (result.recheck_event.has_value()) {
+ // If we do not switch to the connection because it missed the receiving
+ // threshold, the new connection is in a better receiving state than the
+ // currently selected connection. So we need to re-check whether it needs
+ // to be switched at a later time.
+ network_thread_->PostDelayedTask(
+ SafeTask(task_safety_.flag(),
+ [this, recheck_reason = result.recheck_event->reason]() {
+ SortAndSwitchToBestConnection(recheck_reason);
+ }),
+ TimeDelta::Millis(result.recheck_event->recheck_delay_ms));
+ }
+
+ agent_.ForgetLearnedStateForConnections(
+ result.connections_to_forget_state_on);
+}
+
+void WrappingActiveIceController::UpdateStateOnConnectionsResorted() {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ PruneConnections();
+
+ // Update the internal state of the ICE agentl.
+ agent_.UpdateState();
+
+ // Also possibly start pinging.
+ // We could start pinging if:
+ // * The first connection was created.
+ // * ICE credentials were provided.
+ // * A TCP connection became connected.
+ MaybeStartPinging();
+}
+
+void WrappingActiveIceController::PruneConnections() {
+ RTC_DCHECK_RUN_ON(network_thread_);
+
+ // The controlled side can prune only if the selected connection has been
+ // nominated because otherwise it may prune the connection that will be
+ // selected by the controlling side.
+ // TODO(honghaiz): This is not enough to prevent a connection from being
+ // pruned too early because with aggressive nomination, the controlling side
+ // will nominate every connection until it becomes writable.
+ if (agent_.GetIceRole() == ICEROLE_CONTROLLING ||
+ (selected_connection_ && selected_connection_->nominated())) {
+ std::vector<const Connection*> connections_to_prune =
+ wrapped_->PruneConnections();
+ agent_.PruneConnections(connections_to_prune);
+ }
+}
+
+// Only for unit tests
+const Connection* WrappingActiveIceController::FindNextPingableConnection() {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ return wrapped_->FindNextPingableConnection();
+}
+
+} // namespace cricket