Use active ICE controller in P2PTransportChannel with an adapter (#6/n)
Controlled by a field trial, P2PTransportChannel can now use an active ICE controller instead of a legacy ICE controller.
P2PTransportChannel unit tests need non-trivial changes to exercise the refactored code path, so the testing changes are added in a follow-up CL.
Bug: webrtc:14367, webrtc:14131
Change-Id: I00d4930a5692c7d6d331ea9d6c2a2199304e363c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/274701
Commit-Queue: Sameer Vijaykar <samvi@google.com>
Reviewed-by: Jonas Oreland <jonaso@webrtc.org>
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38114}
diff --git a/api/ice_transport_interface.h b/api/ice_transport_interface.h
index ca67771..cd6ada8 100644
--- a/api/ice_transport_interface.h
+++ b/api/ice_transport_interface.h
@@ -89,7 +89,18 @@
// transport, in contrast with a legacy ICE controller that only picks the
// best connection to use or ping, and lets the transport decide when and
// whether to switch.
- // TODO(bugs.webrtc.org/14367): currently unused, update doc when used.
+ //
+ // Which ICE controller is used is determined based on the field trial
+ // "WebRTC-UseActiveIceController" as follows:
+ //
+ // 1. If the field trial is not enabled
+ // a. The legacy ICE controller factory is used if one is supplied.
+ // b. If not, a default ICE controller (BasicIceController) is
+ // constructed and used.
+ //
+ // 2. If the field trial is enabled
+ // - then an active ICE controller factory must be supplied and is used.
+ // - the legacy ICE controller factory is not used in this case.
void set_active_ice_controller_factory(
cricket::ActiveIceControllerFactoryInterface*
active_ice_controller_factory) {
diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn
index 7e5f173..1009304 100644
--- a/p2p/BUILD.gn
+++ b/p2p/BUILD.gn
@@ -199,7 +199,9 @@
sources = [
"base/fake_dtls_transport.h",
"base/fake_packet_transport.h",
+ "base/mock_active_ice_controller.h",
"base/mock_async_resolver.h",
+ "base/mock_ice_controller.h",
"base/mock_ice_transport.h",
"base/test_stun_server.cc",
"base/test_stun_server.h",
diff --git a/p2p/base/mock_active_ice_controller.h b/p2p/base/mock_active_ice_controller.h
new file mode 100644
index 0000000..908967b
--- /dev/null
+++ b/p2p/base/mock_active_ice_controller.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#ifndef P2P_BASE_MOCK_ACTIVE_ICE_CONTROLLER_H_
+#define P2P_BASE_MOCK_ACTIVE_ICE_CONTROLLER_H_
+
+#include <memory>
+
+#include "p2p/base/active_ice_controller_factory_interface.h"
+#include "p2p/base/active_ice_controller_interface.h"
+#include "test/gmock.h"
+
+namespace cricket {
+
+class MockActiveIceController : public cricket::ActiveIceControllerInterface {
+ public:
+ explicit MockActiveIceController(
+ const cricket::ActiveIceControllerFactoryArgs& args) {}
+ ~MockActiveIceController() override = default;
+
+ MOCK_METHOD(void, SetIceConfig, (const cricket::IceConfig&), (override));
+ MOCK_METHOD(void,
+ OnConnectionAdded,
+ (const cricket::Connection*),
+ (override));
+ MOCK_METHOD(void,
+ OnConnectionSwitched,
+ (const cricket::Connection*),
+ (override));
+ MOCK_METHOD(void,
+ OnConnectionDestroyed,
+ (const cricket::Connection*),
+ (override));
+ MOCK_METHOD(void,
+ OnConnectionPinged,
+ (const cricket::Connection*),
+ (override));
+ MOCK_METHOD(void,
+ OnConnectionUpdated,
+ (const cricket::Connection*),
+ (override));
+ MOCK_METHOD(bool,
+ GetUseCandidateAttribute,
+ (const cricket::Connection*,
+ cricket::NominationMode,
+ cricket::IceMode),
+ (const, override));
+ MOCK_METHOD(void,
+ OnSortAndSwitchRequest,
+ (cricket::IceSwitchReason),
+ (override));
+ MOCK_METHOD(void,
+ OnImmediateSortAndSwitchRequest,
+ (cricket::IceSwitchReason),
+ (override));
+ MOCK_METHOD(bool,
+ OnImmediateSwitchRequest,
+ (cricket::IceSwitchReason, const cricket::Connection*),
+ (override));
+ MOCK_METHOD(const cricket::Connection*,
+ FindNextPingableConnection,
+ (),
+ (override));
+};
+
+class MockActiveIceControllerFactory
+ : public cricket::ActiveIceControllerFactoryInterface {
+ public:
+ ~MockActiveIceControllerFactory() override = default;
+
+ std::unique_ptr<cricket::ActiveIceControllerInterface> Create(
+ const cricket::ActiveIceControllerFactoryArgs& args) {
+ RecordActiveIceControllerCreated();
+ return std::make_unique<MockActiveIceController>(args);
+ }
+
+ MOCK_METHOD(void, RecordActiveIceControllerCreated, ());
+};
+
+} // namespace cricket
+
+#endif // P2P_BASE_MOCK_ACTIVE_ICE_CONTROLLER_H_
diff --git a/p2p/base/mock_ice_controller.h b/p2p/base/mock_ice_controller.h
new file mode 100644
index 0000000..bde9254
--- /dev/null
+++ b/p2p/base/mock_ice_controller.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#ifndef P2P_BASE_MOCK_ICE_CONTROLLER_H_
+#define P2P_BASE_MOCK_ICE_CONTROLLER_H_
+
+#include <memory>
+#include <vector>
+
+#include "p2p/base/ice_controller_factory_interface.h"
+#include "p2p/base/ice_controller_interface.h"
+#include "test/gmock.h"
+
+namespace cricket {
+
+class MockIceController : public cricket::IceControllerInterface {
+ public:
+ explicit MockIceController(const cricket::IceControllerFactoryArgs& args) {}
+ ~MockIceController() override = default;
+
+ MOCK_METHOD(void, SetIceConfig, (const cricket::IceConfig&), (override));
+ MOCK_METHOD(void,
+ SetSelectedConnection,
+ (const cricket::Connection*),
+ (override));
+ MOCK_METHOD(void, AddConnection, (const cricket::Connection*), (override));
+ MOCK_METHOD(void,
+ OnConnectionDestroyed,
+ (const cricket::Connection*),
+ (override));
+ MOCK_METHOD(rtc::ArrayView<const cricket::Connection*>,
+ connections,
+ (),
+ (const, override));
+ MOCK_METHOD(bool, HasPingableConnection, (), (const, override));
+ MOCK_METHOD(cricket::IceControllerInterface::PingResult,
+ SelectConnectionToPing,
+ (int64_t),
+ (override));
+ MOCK_METHOD(bool,
+ GetUseCandidateAttr,
+ (const cricket::Connection*,
+ cricket::NominationMode,
+ cricket::IceMode),
+ (const, override));
+ MOCK_METHOD(const cricket::Connection*,
+ FindNextPingableConnection,
+ (),
+ (override));
+ MOCK_METHOD(void,
+ MarkConnectionPinged,
+ (const cricket::Connection*),
+ (override));
+ MOCK_METHOD(cricket::IceControllerInterface::SwitchResult,
+ ShouldSwitchConnection,
+ (cricket::IceSwitchReason, const cricket::Connection*),
+ (override));
+ MOCK_METHOD(cricket::IceControllerInterface::SwitchResult,
+ SortAndSwitchConnection,
+ (cricket::IceSwitchReason),
+ (override));
+ MOCK_METHOD(std::vector<const cricket::Connection*>,
+ PruneConnections,
+ (),
+ (override));
+};
+
+class MockIceControllerFactory : public cricket::IceControllerFactoryInterface {
+ public:
+ ~MockIceControllerFactory() override = default;
+
+ std::unique_ptr<cricket::IceControllerInterface> Create(
+ const cricket::IceControllerFactoryArgs& args) override {
+ RecordIceControllerCreated();
+ return std::make_unique<MockIceController>(args);
+ }
+
+ MOCK_METHOD(void, RecordIceControllerCreated, ());
+};
+
+} // namespace cricket
+
+#endif // P2P_BASE_MOCK_ICE_CONTROLLER_H_
diff --git a/p2p/base/p2p_transport_channel.cc b/p2p/base/p2p_transport_channel.cc
index 6cc82b8..2b0e906 100644
--- a/p2p/base/p2p_transport_channel.cc
+++ b/p2p/base/p2p_transport_channel.cc
@@ -93,15 +93,23 @@
uses_turn);
}
-} // unnamed namespace
-
-namespace cricket {
+bool UseActiveIceControllerFieldTrialEnabled(
+ const webrtc::FieldTrialsView* field_trials) {
+ // Feature to refactor ICE controller and enable active ICE controllers.
+ // Field trial key reserved in bugs.webrtc.org/14367
+ return field_trials &&
+ field_trials->IsEnabled("WebRTC-UseActiveIceController");
+}
using ::webrtc::RTCError;
using ::webrtc::RTCErrorType;
using ::webrtc::SafeTask;
using ::webrtc::TimeDelta;
+} // unnamed namespace
+
+namespace cricket {
+
bool IceCredentialsChanged(absl::string_view old_ufrag,
absl::string_view old_pwd,
absl::string_view new_ufrag,
@@ -122,12 +130,14 @@
transport_name, component, init.port_allocator(), nullptr,
std::make_unique<webrtc::WrappingAsyncDnsResolverFactory>(
init.async_resolver_factory()),
- init.event_log(), init.ice_controller_factory(), init.field_trials()));
+ init.event_log(), init.ice_controller_factory(),
+ init.active_ice_controller_factory(), init.field_trials()));
} else {
return absl::WrapUnique(new P2PTransportChannel(
transport_name, component, init.port_allocator(),
init.async_dns_resolver_factory(), nullptr, init.event_log(),
- init.ice_controller_factory(), init.field_trials()));
+ init.ice_controller_factory(), init.active_ice_controller_factory(),
+ init.field_trials()));
}
}
@@ -143,6 +153,7 @@
/* owned_dns_resolver_factory= */ nullptr,
/* event_log= */ nullptr,
/* ice_controller_factory= */ nullptr,
+ /* active_ice_controller_factory= */ nullptr,
field_trials) {}
// Private constructor, called from Create()
@@ -155,6 +166,7 @@
owned_dns_resolver_factory,
webrtc::RtcEventLog* event_log,
IceControllerFactoryInterface* ice_controller_factory,
+ ActiveIceControllerFactoryInterface* active_ice_controller_factory,
const webrtc::FieldTrialsView* field_trials)
: transport_name_(transport_name),
component_(component),
@@ -210,11 +222,9 @@
&ice_field_trials_,
field_trials ? field_trials->Lookup("WebRTC-IceControllerFieldTrials")
: ""};
- if (ice_controller_factory != nullptr) {
- ice_controller_ = ice_controller_factory->Create(args);
- } else {
- ice_controller_ = std::make_unique<BasicIceController>(args);
- }
+ ice_adapter_ = std::make_unique<IceControllerAdapter>(
+ args, ice_controller_factory, active_ice_controller_factory, field_trials,
+ /* transport= */ this);
}
P2PTransportChannel::~P2PTransportChannel() {
@@ -282,18 +292,21 @@
webrtc::IceCandidatePairConfigType::kAdded);
connections_.push_back(connection);
- ice_controller_->AddConnection(connection);
+ ice_adapter_->OnConnectionAdded(connection);
}
+// TODO(bugs.webrtc.org/14367) remove once refactor lands.
bool P2PTransportChannel::MaybeSwitchSelectedConnection(
- Connection* new_connection,
+ const Connection* new_connection,
IceSwitchReason reason) {
RTC_DCHECK_RUN_ON(network_thread_);
return MaybeSwitchSelectedConnection(
- reason, ice_controller_->ShouldSwitchConnection(reason, new_connection));
+ reason,
+ ice_adapter_->LegacyShouldSwitchConnection(reason, new_connection));
}
+// TODO(bugs.webrtc.org/14367) remove once refactor lands.
bool P2PTransportChannel::MaybeSwitchSelectedConnection(
IceSwitchReason reason,
IceControllerInterface::SwitchResult result) {
@@ -530,7 +543,7 @@
ice_params, static_cast<int>(remote_ice_parameters_.size() - 1));
}
// Updating the remote ICE candidate generation could change the sort order.
- RequestSortAndStateUpdate(
+ ice_adapter_->OnSortAndSwitchRequest(
IceSwitchReason::REMOTE_CANDIDATE_GENERATION_CHANGE);
}
@@ -685,7 +698,8 @@
if (config_.network_preference != config.network_preference) {
config_.network_preference = config.network_preference;
- RequestSortAndStateUpdate(IceSwitchReason::NETWORK_PREFERENCE_CHANGE);
+ ice_adapter_->OnSortAndSwitchRequest(
+ IceSwitchReason::NETWORK_PREFERENCE_CHANGE);
RTC_LOG(LS_INFO) << "Set network preference to "
<< (config_.network_preference.has_value()
? config_.network_preference.value()
@@ -711,7 +725,7 @@
config_.vpn_preference = config.vpn_preference;
allocator_->SetVpnPreference(config_.vpn_preference);
- ice_controller_->SetIceConfig(config_);
+ ice_adapter_->SetIceConfig(config_);
RTC_DCHECK(ValidateIceConfig(config_).ok());
}
@@ -988,7 +1002,7 @@
CreateConnection(port, *iter, iter->origin_port());
}
- SortConnectionsAndUpdateState(
+ ice_adapter_->OnImmediateSortAndSwitchRequest(
IceSwitchReason::NEW_CONNECTION_FROM_LOCAL_CANDIDATE);
}
@@ -1168,7 +1182,7 @@
// Update the list of connections since we just added another. We do this
// after sending the response since it could (in principle) delete the
// connection in question.
- SortConnectionsAndUpdateState(
+ ice_adapter_->OnImmediateSortAndSwitchRequest(
IceSwitchReason::NEW_CONNECTION_FROM_UNKNOWN_REMOTE_ADDRESS);
}
@@ -1219,11 +1233,12 @@
// TODO(qingsi): RequestSortAndStateUpdate will eventually call
// MaybeSwitchSelectedConnection again. Rewrite this logic.
- if (MaybeSwitchSelectedConnection(
- conn, IceSwitchReason::NOMINATION_ON_CONTROLLED_SIDE)) {
+ if (ice_adapter_->OnImmediateSwitchRequest(
+ IceSwitchReason::NOMINATION_ON_CONTROLLED_SIDE, conn)) {
// Now that we have selected a connection, it is time to prune other
// connections and update the read/write state of the channel.
- RequestSortAndStateUpdate(IceSwitchReason::NOMINATION_ON_CONTROLLED_SIDE);
+ ice_adapter_->OnSortAndSwitchRequest(
+ IceSwitchReason::NOMINATION_ON_CONTROLLED_SIDE);
} else {
RTC_LOG(LS_INFO)
<< "Not switching the selected connection on controlled side yet: "
@@ -1371,7 +1386,7 @@
CreateConnections(new_remote_candidate, NULL);
// Resort the connections list, which may have new elements.
- SortConnectionsAndUpdateState(
+ ice_adapter_->OnImmediateSortAndSwitchRequest(
IceSwitchReason::NEW_CONNECTION_FROM_REMOTE_CANDIDATE);
}
@@ -1691,9 +1706,7 @@
rtc::ArrayView<Connection*> P2PTransportChannel::connections() const {
RTC_DCHECK_RUN_ON(network_thread_);
- rtc::ArrayView<const Connection*> res = ice_controller_->connections();
- return rtc::ArrayView<Connection*>(const_cast<Connection**>(res.data()),
- res.size());
+ return ice_adapter_->LegacyConnections();
}
void P2PTransportChannel::RemoveConnectionForTest(Connection* connection) {
@@ -1724,6 +1737,7 @@
}
// Prepare for best candidate sorting.
+// TODO(bugs.webrtc.org/14367) remove once refactor lands.
void P2PTransportChannel::RequestSortAndStateUpdate(
IceSwitchReason reason_to_sort) {
RTC_DCHECK_RUN_ON(network_thread_);
@@ -1736,13 +1750,14 @@
}
}
+// TODO(bugs.webrtc.org/14367) remove once refactor lands.
void P2PTransportChannel::MaybeStartPinging() {
RTC_DCHECK_RUN_ON(network_thread_);
if (started_pinging_) {
return;
}
- if (ice_controller_->HasPingableConnection()) {
+ if (ice_adapter_->LegacyHasPingableConnection()) {
RTC_LOG(LS_INFO) << ToString()
<< ": Have a pingable connection for the first time; "
"starting to ping.";
@@ -1782,6 +1797,7 @@
// Sort the available connections to find the best one. We also monitor
// the number of available connections and the current state.
+// TODO(bugs.webrtc.org/14367) remove once refactor lands.
void P2PTransportChannel::SortConnectionsAndUpdateState(
IceSwitchReason reason_to_sort) {
RTC_DCHECK_RUN_ON(network_thread_);
@@ -1797,7 +1813,8 @@
// have to be writable to become the selected connection although it will
// have higher priority if it is writable.
MaybeSwitchSelectedConnection(
- reason_to_sort, ice_controller_->SortAndSwitchConnection(reason_to_sort));
+ reason_to_sort,
+ ice_adapter_->LegacySortAndSwitchConnection(reason_to_sort));
// The controlled side can prune only if the selected connection has been
// nominated because otherwise it may prune the connection that will be
@@ -1865,7 +1882,7 @@
void P2PTransportChannel::PruneConnections() {
RTC_DCHECK_RUN_ON(network_thread_);
std::vector<const Connection*> connections_to_prune =
- ice_controller_->PruneConnections();
+ ice_adapter_->LegacyPruneConnections();
PruneConnections(connections_to_prune);
}
@@ -1976,7 +1993,7 @@
++selected_candidate_pair_changes_;
- ice_controller_->SetSelectedConnection(selected_connection_);
+ ice_adapter_->OnConnectionSwitched(selected_connection_);
}
int64_t P2PTransportChannel::ComputeEstimatedDisconnectedTimeMs(
@@ -1990,11 +2007,11 @@
}
// Warning: UpdateTransportState should eventually be called whenever a
-// connection is added, deleted, or the write state of any connection changes
-// so that the transport controller will get the up-to-date channel state.
-// However it should not be called too often; in the case that multiple
-// connection states change, it should be called after all the connection
-// states have changed. For example, we call this at the end of
+// connection is added, deleted, or the write state of any connection changes so
+// that the transport controller will get the up-to-date channel state. However
+// it should not be called too often; in the case that multiple connection
+// states change, it should be called after all the connection states have
+// changed. For example, we call this at the end of
// SortConnectionsAndUpdateState.
void P2PTransportChannel::UpdateTransportState() {
RTC_DCHECK_RUN_ON(network_thread_);
@@ -2091,7 +2108,7 @@
RTC_LOG(LS_INFO) << "Selected connection destroyed. Will choose a new one.";
IceSwitchReason reason = IceSwitchReason::SELECTED_CONNECTION_DESTROYED;
SwitchSelectedConnectionInternal(nullptr, reason);
- RequestSortAndStateUpdate(reason);
+ ice_adapter_->OnSortAndSwitchRequest(reason);
}
// If all connections timed out, delete them all.
@@ -2125,13 +2142,14 @@
}
// Handle queued up check-and-ping request
+// TODO(bugs.webrtc.org/14367) remove once refactor lands.
void P2PTransportChannel::CheckAndPing() {
RTC_DCHECK_RUN_ON(network_thread_);
// Make sure the states of the connections are up-to-date (since this
// affects which ones are pingable).
UpdateConnectionStates();
- auto result = ice_controller_->SelectConnectionToPing(last_ping_sent_ms_);
+ auto result = ice_adapter_->LegacySelectConnectionToPing(last_ping_sent_ms_);
TimeDelta delay = TimeDelta::Millis(result.recheck_delay_ms);
if (result.connection.value_or(nullptr)) {
@@ -2145,7 +2163,7 @@
// This method is only for unit testing.
Connection* P2PTransportChannel::FindNextPingableConnection() {
RTC_DCHECK_RUN_ON(network_thread_);
- auto* conn = ice_controller_->FindNextPingableConnection();
+ const Connection* conn = ice_adapter_->FindNextPingableConnection();
if (conn) {
return FromIceController(conn);
} else {
@@ -2174,7 +2192,7 @@
// active.
void P2PTransportChannel::MarkConnectionPinged(Connection* conn) {
RTC_DCHECK_RUN_ON(network_thread_);
- ice_controller_->MarkConnectionPinged(conn);
+ ice_adapter_->OnConnectionPinged(conn);
}
// Apart from sending ping from `conn` this method also updates
@@ -2208,7 +2226,7 @@
// Nominate a connection based on the NominationMode.
bool P2PTransportChannel::GetUseCandidateAttr(Connection* conn) const {
RTC_DCHECK_RUN_ON(network_thread_);
- return ice_controller_->GetUseCandidateAttr(
+ return ice_adapter_->GetUseCandidateAttribute(
conn, config_.default_nomination_mode, remote_ice_mode_);
}
@@ -2233,7 +2251,7 @@
}
// We have to unroll the stack before doing this because we may be changing
// the state of connections while sorting.
- RequestSortAndStateUpdate(
+ ice_adapter_->OnSortAndSwitchRequest(
IceSwitchReason::CONNECT_STATE_CHANGE); // "candidate pair state
// changed");
}
@@ -2261,9 +2279,9 @@
if (selected_connection_ == connection) {
OnSelectedConnectionDestroyed();
} else {
- // If a non-selected connection was destroyed, we don't need to re-sort
- // but we do need to update state, because we could be switching to
- // "failed" or "completed".
+ // If a non-selected connection was destroyed, we don't need to re-sort but
+ // we do need to update state, because we could be switching to "failed" or
+ // "completed".
UpdateTransportState();
}
}
@@ -2273,7 +2291,7 @@
auto it = absl::c_find(connections_, connection);
RTC_DCHECK(it != connections_.end());
connections_.erase(it);
- ice_controller_->OnConnectionDestroyed(connection);
+ ice_adapter_->OnConnectionDestroyed(connection);
}
// When a port is destroyed, remove it from our list of ports to use for
@@ -2374,7 +2392,8 @@
// May need to switch the sending connection based on the receiving media
// path if this is the controlled side.
if (ice_role_ == ICEROLE_CONTROLLED) {
- MaybeSwitchSelectedConnection(connection, IceSwitchReason::DATA_RECEIVED);
+ ice_adapter_->OnImmediateSwitchRequest(IceSwitchReason::DATA_RECEIVED,
+ connection);
}
}
@@ -2445,4 +2464,167 @@
conn->ToLogDescription());
}
+P2PTransportChannel::IceControllerAdapter::IceControllerAdapter(
+ const IceControllerFactoryArgs& args,
+ IceControllerFactoryInterface* ice_controller_factory,
+ ActiveIceControllerFactoryInterface* active_ice_controller_factory,
+ const webrtc::FieldTrialsView* field_trials,
+ P2PTransportChannel* transport)
+ : transport_(transport) {
+ if (UseActiveIceControllerFieldTrialEnabled(field_trials)) {
+ RTC_DCHECK(active_ice_controller_factory);
+ ActiveIceControllerFactoryArgs active_args{args,
+ /* ice_agent= */ transport};
+ active_ice_controller_ = active_ice_controller_factory->Create(active_args);
+ } else {
+ if (ice_controller_factory != nullptr) {
+ legacy_ice_controller_ = ice_controller_factory->Create(args);
+ } else {
+ legacy_ice_controller_ = std::make_unique<BasicIceController>(args);
+ }
+ }
+}
+
+P2PTransportChannel::IceControllerAdapter::~IceControllerAdapter() = default;
+
+void P2PTransportChannel::IceControllerAdapter::SetIceConfig(
+ const IceConfig& config) {
+ active_ice_controller_ ? active_ice_controller_->SetIceConfig(config)
+ : legacy_ice_controller_->SetIceConfig(config);
+}
+
+void P2PTransportChannel::IceControllerAdapter::OnConnectionAdded(
+ const Connection* connection) {
+ active_ice_controller_ ? active_ice_controller_->OnConnectionAdded(connection)
+ : legacy_ice_controller_->AddConnection(connection);
+}
+
+void P2PTransportChannel::IceControllerAdapter::OnConnectionSwitched(
+ const Connection* connection) {
+ active_ice_controller_
+ ? active_ice_controller_->OnConnectionSwitched(connection)
+ : legacy_ice_controller_->SetSelectedConnection(connection);
+}
+
+void P2PTransportChannel::IceControllerAdapter::OnConnectionPinged(
+ const Connection* connection) {
+ active_ice_controller_
+ ? active_ice_controller_->OnConnectionPinged(connection)
+ : legacy_ice_controller_->MarkConnectionPinged(connection);
+}
+
+void P2PTransportChannel::IceControllerAdapter::OnConnectionDestroyed(
+ const Connection* connection) {
+ active_ice_controller_
+ ? active_ice_controller_->OnConnectionDestroyed(connection)
+ : legacy_ice_controller_->OnConnectionDestroyed(connection);
+}
+
+void P2PTransportChannel::IceControllerAdapter::OnConnectionUpdated(
+ const Connection* connection) {
+ if (active_ice_controller_) {
+ active_ice_controller_->OnConnectionUpdated(connection);
+ return;
+ }
+ RTC_DCHECK_NOTREACHED();
+}
+
+void P2PTransportChannel::IceControllerAdapter::OnSortAndSwitchRequest(
+ IceSwitchReason reason) {
+ active_ice_controller_
+ ? active_ice_controller_->OnSortAndSwitchRequest(reason)
+ : transport_->RequestSortAndStateUpdate(reason);
+}
+
+void P2PTransportChannel::IceControllerAdapter::OnImmediateSortAndSwitchRequest(
+ IceSwitchReason reason) {
+ active_ice_controller_
+ ? active_ice_controller_->OnImmediateSortAndSwitchRequest(reason)
+ : transport_->SortConnectionsAndUpdateState(reason);
+}
+
+bool P2PTransportChannel::IceControllerAdapter::OnImmediateSwitchRequest(
+ IceSwitchReason reason,
+ const Connection* connection) {
+ return active_ice_controller_
+ ? active_ice_controller_->OnImmediateSwitchRequest(reason,
+ connection)
+ : transport_->MaybeSwitchSelectedConnection(connection, reason);
+}
+
+bool P2PTransportChannel::IceControllerAdapter::GetUseCandidateAttribute(
+ const cricket::Connection* connection,
+ cricket::NominationMode mode,
+ cricket::IceMode remote_ice_mode) const {
+ return active_ice_controller_
+ ? active_ice_controller_->GetUseCandidateAttribute(
+ connection, mode, remote_ice_mode)
+ : legacy_ice_controller_->GetUseCandidateAttr(connection, mode,
+ remote_ice_mode);
+}
+
+const Connection*
+P2PTransportChannel::IceControllerAdapter::FindNextPingableConnection() {
+ return active_ice_controller_
+ ? active_ice_controller_->FindNextPingableConnection()
+ : legacy_ice_controller_->FindNextPingableConnection();
+}
+
+rtc::ArrayView<Connection*>
+P2PTransportChannel::IceControllerAdapter::LegacyConnections() const {
+ RTC_DCHECK_RUN_ON(transport_->network_thread_);
+ if (active_ice_controller_) {
+ return rtc::ArrayView<Connection*>(transport_->connections_.data(),
+ transport_->connections_.size());
+ }
+
+ rtc::ArrayView<const Connection*> res = legacy_ice_controller_->connections();
+ return rtc::ArrayView<Connection*>(const_cast<Connection**>(res.data()),
+ res.size());
+}
+
+bool P2PTransportChannel::IceControllerAdapter::LegacyHasPingableConnection()
+ const {
+ if (active_ice_controller_) {
+ RTC_DCHECK_NOTREACHED();
+ }
+ return legacy_ice_controller_->HasPingableConnection();
+}
+
+IceControllerInterface::PingResult
+P2PTransportChannel::IceControllerAdapter::LegacySelectConnectionToPing(
+ int64_t last_ping_sent_ms) {
+ if (active_ice_controller_) {
+ RTC_DCHECK_NOTREACHED();
+ }
+ return legacy_ice_controller_->SelectConnectionToPing(last_ping_sent_ms);
+}
+
+IceControllerInterface::SwitchResult
+P2PTransportChannel::IceControllerAdapter::LegacyShouldSwitchConnection(
+ IceSwitchReason reason,
+ const Connection* connection) {
+ if (active_ice_controller_) {
+ RTC_DCHECK_NOTREACHED();
+ }
+ return legacy_ice_controller_->ShouldSwitchConnection(reason, connection);
+}
+
+IceControllerInterface::SwitchResult
+P2PTransportChannel::IceControllerAdapter::LegacySortAndSwitchConnection(
+ IceSwitchReason reason) {
+ if (active_ice_controller_) {
+ RTC_DCHECK_NOTREACHED();
+ }
+ return legacy_ice_controller_->SortAndSwitchConnection(reason);
+}
+
+std::vector<const Connection*>
+P2PTransportChannel::IceControllerAdapter::LegacyPruneConnections() {
+ if (active_ice_controller_) {
+ RTC_DCHECK_NOTREACHED();
+ }
+ return legacy_ice_controller_->PruneConnections();
+}
+
} // namespace cricket
diff --git a/p2p/base/p2p_transport_channel.h b/p2p/base/p2p_transport_channel.h
index 5592a4b..d7bcd23 100644
--- a/p2p/base/p2p_transport_channel.h
+++ b/p2p/base/p2p_transport_channel.h
@@ -46,6 +46,7 @@
#include "api/transport/stun.h"
#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h"
#include "logging/rtc_event_log/ice_logger.h"
+#include "p2p/base/active_ice_controller_factory_interface.h"
#include "p2p/base/basic_async_resolver_factory.h"
#include "p2p/base/candidate_pair_interface.h"
#include "p2p/base/connection.h"
@@ -265,7 +266,9 @@
owned_dns_resolver_factory,
webrtc::RtcEventLog* event_log,
IceControllerFactoryInterface* ice_controller_factory,
+ ActiveIceControllerFactoryInterface* active_ice_controller_factory,
const webrtc::FieldTrialsView* field_trials);
+
bool IsGettingPorts() {
RTC_DCHECK_RUN_ON(network_thread_);
return allocator_session()->IsGettingPorts();
@@ -274,12 +277,15 @@
// Returns true if it's possible to send packets on `connection`.
bool ReadyToSend(const Connection* connection) const;
bool PresumedWritable(const Connection* conn) const;
+ // TODO(bugs.webrtc.org/14367) remove once refactor lands.
void RequestSortAndStateUpdate(IceSwitchReason reason_to_sort);
// Start pinging if we haven't already started, and we now have a connection
// that's pingable.
+ // TODO(bugs.webrtc.org/14367) remove once refactor lands.
void MaybeStartPinging();
void SendPingRequestInternal(Connection* connection);
+ // TODO(bugs.webrtc.org/14367) remove once refactor lands.
void SortConnectionsAndUpdateState(IceSwitchReason reason_to_sort);
rtc::NetworkRoute ConfigureNetworkRoute(const Connection* conn);
void SwitchSelectedConnectionInternal(Connection* conn,
@@ -348,6 +354,7 @@
void OnNominated(Connection* conn);
+ // TODO(bugs.webrtc.org/14367) remove once refactor lands.
void CheckAndPing();
void LogCandidatePairConfig(Connection* conn,
@@ -357,12 +364,15 @@
bool GetUseCandidateAttr(Connection* conn) const;
// Returns true if the new_connection is selected for transmission.
- bool MaybeSwitchSelectedConnection(Connection* new_connection,
+ // TODO(bugs.webrtc.org/14367) remove once refactor lands.
+ bool MaybeSwitchSelectedConnection(const Connection* new_connection,
IceSwitchReason reason);
+ // TODO(bugs.webrtc.org/14367) remove once refactor lands.
bool MaybeSwitchSelectedConnection(
IceSwitchReason reason,
IceControllerInterface::SwitchResult result);
bool AllowedToPruneConnections() const;
+ // TODO(bugs.webrtc.org/14367) remove once refactor lands.
void PruneConnections();
// Returns the latest remote ICE parameters or nullptr if there are no remote
@@ -420,6 +430,7 @@
void ParseFieldTrials(const webrtc::FieldTrialsView* field_trials);
+ // TODO(bugs.webrtc.org/14367) remove once refactor lands.
webrtc::ScopedTaskSafety task_safety_;
std::string transport_name_ RTC_GUARDED_BY(network_thread_);
int component_ RTC_GUARDED_BY(network_thread_);
@@ -447,6 +458,7 @@
std::vector<RemoteCandidate> remote_candidates_
RTC_GUARDED_BY(network_thread_);
+ // TODO(bugs.webrtc.org/14367) remove once refactor lands.
bool sort_dirty_ RTC_GUARDED_BY(
network_thread_); // indicates whether another sort is needed right now
bool had_connection_ RTC_GUARDED_BY(network_thread_) =
@@ -473,6 +485,7 @@
IceConfig config_ RTC_GUARDED_BY(network_thread_);
int last_sent_packet_id_ RTC_GUARDED_BY(network_thread_) =
-1; // -1 indicates no packet was sent before.
+ // TODO(bugs.webrtc.org/14367) remove once refactor lands.
bool started_pinging_ RTC_GUARDED_BY(network_thread_) = false;
// The value put in the "nomination" attribute for the next nominated
// connection. A zero-value indicates the connection will not be nominated.
@@ -486,7 +499,53 @@
RTC_GUARDED_BY(network_thread_);
webrtc::IceEventLog ice_event_log_ RTC_GUARDED_BY(network_thread_);
- std::unique_ptr<IceControllerInterface> ice_controller_
+ // The adapter transparently delegates ICE controller interactions to either
+ // the legacy or the active ICE controller depending on field trials.
+ // TODO(bugs.webrtc.org/14367) replace with active ICE controller eventually.
+ class IceControllerAdapter : public ActiveIceControllerInterface {
+ public:
+ IceControllerAdapter(
+ const IceControllerFactoryArgs& args,
+ IceControllerFactoryInterface* ice_controller_factory,
+ ActiveIceControllerFactoryInterface* active_ice_controller_factory,
+ const webrtc::FieldTrialsView* field_trials,
+ P2PTransportChannel* transport);
+ ~IceControllerAdapter() override;
+
+ // ActiveIceControllerInterface overrides
+ void SetIceConfig(const IceConfig& config) override;
+ void OnConnectionAdded(const Connection* connection) override;
+ void OnConnectionSwitched(const Connection* connection) override;
+ void OnConnectionPinged(const Connection* connection) override;
+ void OnConnectionDestroyed(const Connection* connection) override;
+ void OnConnectionUpdated(const Connection* connection) override;
+ void OnSortAndSwitchRequest(IceSwitchReason reason) override;
+ void OnImmediateSortAndSwitchRequest(IceSwitchReason reason) override;
+ bool OnImmediateSwitchRequest(IceSwitchReason reason,
+ const Connection* connection) override;
+ bool GetUseCandidateAttribute(const Connection* connection,
+ NominationMode mode,
+ IceMode remote_ice_mode) const override;
+ const Connection* FindNextPingableConnection() override;
+
+ // Methods only available with legacy ICE controller.
+ rtc::ArrayView<Connection*> LegacyConnections() const;
+ bool LegacyHasPingableConnection() const;
+ IceControllerInterface::PingResult LegacySelectConnectionToPing(
+ int64_t last_ping_sent_ms);
+ IceControllerInterface::SwitchResult LegacyShouldSwitchConnection(
+ IceSwitchReason reason,
+ const Connection* connection);
+ IceControllerInterface::SwitchResult LegacySortAndSwitchConnection(
+ IceSwitchReason reason);
+ std::vector<const Connection*> LegacyPruneConnections();
+
+ private:
+ P2PTransportChannel* transport_;
+ std::unique_ptr<IceControllerInterface> legacy_ice_controller_;
+ std::unique_ptr<ActiveIceControllerInterface> active_ice_controller_;
+ };
+ std::unique_ptr<IceControllerAdapter> ice_adapter_
RTC_GUARDED_BY(network_thread_);
struct CandidateAndResolver final {
diff --git a/p2p/base/p2p_transport_channel_unittest.cc b/p2p/base/p2p_transport_channel_unittest.cc
index d3178f7..928e9b2 100644
--- a/p2p/base/p2p_transport_channel_unittest.cc
+++ b/p2p/base/p2p_transport_channel_unittest.cc
@@ -16,11 +16,15 @@
#include "absl/strings/string_view.h"
#include "api/test/mock_async_dns_resolver.h"
+#include "p2p/base/active_ice_controller_factory_interface.h"
+#include "p2p/base/active_ice_controller_interface.h"
#include "p2p/base/basic_ice_controller.h"
#include "p2p/base/connection.h"
#include "p2p/base/fake_port_allocator.h"
#include "p2p/base/ice_transport_internal.h"
+#include "p2p/base/mock_active_ice_controller.h"
#include "p2p/base/mock_async_resolver.h"
+#include "p2p/base/mock_ice_controller.h"
#include "p2p/base/packet_transport_internal.h"
#include "p2p/base/test_stun_server.h"
#include "p2p/base/test_turn_server.h"
@@ -56,7 +60,6 @@
using ::testing::InSequence;
using ::testing::InvokeArgument;
using ::testing::InvokeWithoutArgs;
-using ::testing::NiceMock;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::SaveArg;
@@ -184,18 +187,6 @@
return allocator.release();
}
-class MockIceControllerFactory : public cricket::IceControllerFactoryInterface {
- public:
- ~MockIceControllerFactory() override = default;
- std::unique_ptr<cricket::IceControllerInterface> Create(
- const cricket::IceControllerFactoryArgs& args) override {
- RecordIceControllerCreated();
- return std::make_unique<cricket::BasicIceController>(args);
- }
-
- MOCK_METHOD(void, RecordIceControllerCreated, ());
-};
-
// An one-shot resolver factory with default return arguments.
// Resolution is immediate, always succeeds, and returns nonsense.
class ResolverFactoryFixture : public webrtc::MockAsyncDnsResolverFactory {
@@ -6123,6 +6114,25 @@
/* component= */ 77, std::move(init));
}
+TEST(P2PTransportChannel, InjectActiveIceController) {
+ std::unique_ptr<rtc::SocketServer> socket_server =
+ rtc::CreateDefaultSocketServer();
+ rtc::AutoSocketServerThread main_thread(socket_server.get());
+ rtc::BasicPacketSocketFactory packet_socket_factory(socket_server.get());
+ MockActiveIceControllerFactory factory;
+ FakePortAllocator pa(rtc::Thread::Current(), &packet_socket_factory);
+ webrtc::test::ScopedKeyValueConfig field_trials(
+ "WebRTC-UseActiveIceController/Enabled/");
+ EXPECT_CALL(factory, RecordActiveIceControllerCreated()).Times(1);
+ webrtc::IceTransportInit init;
+ init.set_port_allocator(&pa);
+ init.set_active_ice_controller_factory(&factory);
+ init.set_field_trials(&field_trials);
+ auto dummy =
+ P2PTransportChannel::Create("transport_name",
+ /* component= */ 77, std::move(init));
+}
+
class ForgetLearnedStateController : public cricket::BasicIceController {
public:
explicit ForgetLearnedStateController(