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}
This commit is contained in:
Sameer Vijaykar
2022-09-16 15:04:34 +02:00
committed by WebRTC LUCI CQ
parent b555e83395
commit 1adcde9dfe
7 changed files with 504 additions and 61 deletions

View File

@ -89,7 +89,18 @@ struct IceTransportInit final {
// transport, in contrast with a legacy ICE controller that only picks the // 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 // best connection to use or ping, and lets the transport decide when and
// whether to switch. // 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( void set_active_ice_controller_factory(
cricket::ActiveIceControllerFactoryInterface* cricket::ActiveIceControllerFactoryInterface*
active_ice_controller_factory) { active_ice_controller_factory) {

View File

@ -199,7 +199,9 @@ if (rtc_include_tests) {
sources = [ sources = [
"base/fake_dtls_transport.h", "base/fake_dtls_transport.h",
"base/fake_packet_transport.h", "base/fake_packet_transport.h",
"base/mock_active_ice_controller.h",
"base/mock_async_resolver.h", "base/mock_async_resolver.h",
"base/mock_ice_controller.h",
"base/mock_ice_transport.h", "base/mock_ice_transport.h",
"base/test_stun_server.cc", "base/test_stun_server.cc",
"base/test_stun_server.h", "base/test_stun_server.h",

View File

@ -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_

View File

@ -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_

View File

@ -93,15 +93,23 @@ rtc::RouteEndpoint CreateRouteEndpointFromCandidate(
uses_turn); uses_turn);
} }
} // unnamed namespace bool UseActiveIceControllerFieldTrialEnabled(
const webrtc::FieldTrialsView* field_trials) {
namespace cricket { // 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::RTCError;
using ::webrtc::RTCErrorType; using ::webrtc::RTCErrorType;
using ::webrtc::SafeTask; using ::webrtc::SafeTask;
using ::webrtc::TimeDelta; using ::webrtc::TimeDelta;
} // unnamed namespace
namespace cricket {
bool IceCredentialsChanged(absl::string_view old_ufrag, bool IceCredentialsChanged(absl::string_view old_ufrag,
absl::string_view old_pwd, absl::string_view old_pwd,
absl::string_view new_ufrag, absl::string_view new_ufrag,
@ -122,12 +130,14 @@ std::unique_ptr<P2PTransportChannel> P2PTransportChannel::Create(
transport_name, component, init.port_allocator(), nullptr, transport_name, component, init.port_allocator(), nullptr,
std::make_unique<webrtc::WrappingAsyncDnsResolverFactory>( std::make_unique<webrtc::WrappingAsyncDnsResolverFactory>(
init.async_resolver_factory()), 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 { } else {
return absl::WrapUnique(new P2PTransportChannel( return absl::WrapUnique(new P2PTransportChannel(
transport_name, component, init.port_allocator(), transport_name, component, init.port_allocator(),
init.async_dns_resolver_factory(), nullptr, init.event_log(), 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 @@ P2PTransportChannel::P2PTransportChannel(
/* owned_dns_resolver_factory= */ nullptr, /* owned_dns_resolver_factory= */ nullptr,
/* event_log= */ nullptr, /* event_log= */ nullptr,
/* ice_controller_factory= */ nullptr, /* ice_controller_factory= */ nullptr,
/* active_ice_controller_factory= */ nullptr,
field_trials) {} field_trials) {}
// Private constructor, called from Create() // Private constructor, called from Create()
@ -155,6 +166,7 @@ P2PTransportChannel::P2PTransportChannel(
owned_dns_resolver_factory, owned_dns_resolver_factory,
webrtc::RtcEventLog* event_log, webrtc::RtcEventLog* event_log,
IceControllerFactoryInterface* ice_controller_factory, IceControllerFactoryInterface* ice_controller_factory,
ActiveIceControllerFactoryInterface* active_ice_controller_factory,
const webrtc::FieldTrialsView* field_trials) const webrtc::FieldTrialsView* field_trials)
: transport_name_(transport_name), : transport_name_(transport_name),
component_(component), component_(component),
@ -210,11 +222,9 @@ P2PTransportChannel::P2PTransportChannel(
&ice_field_trials_, &ice_field_trials_,
field_trials ? field_trials->Lookup("WebRTC-IceControllerFieldTrials") field_trials ? field_trials->Lookup("WebRTC-IceControllerFieldTrials")
: ""}; : ""};
if (ice_controller_factory != nullptr) { ice_adapter_ = std::make_unique<IceControllerAdapter>(
ice_controller_ = ice_controller_factory->Create(args); args, ice_controller_factory, active_ice_controller_factory, field_trials,
} else { /* transport= */ this);
ice_controller_ = std::make_unique<BasicIceController>(args);
}
} }
P2PTransportChannel::~P2PTransportChannel() { P2PTransportChannel::~P2PTransportChannel() {
@ -282,18 +292,21 @@ void P2PTransportChannel::AddConnection(Connection* connection) {
webrtc::IceCandidatePairConfigType::kAdded); webrtc::IceCandidatePairConfigType::kAdded);
connections_.push_back(connection); connections_.push_back(connection);
ice_controller_->AddConnection(connection); ice_adapter_->OnConnectionAdded(connection);
} }
// TODO(bugs.webrtc.org/14367) remove once refactor lands.
bool P2PTransportChannel::MaybeSwitchSelectedConnection( bool P2PTransportChannel::MaybeSwitchSelectedConnection(
Connection* new_connection, const Connection* new_connection,
IceSwitchReason reason) { IceSwitchReason reason) {
RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK_RUN_ON(network_thread_);
return MaybeSwitchSelectedConnection( 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( bool P2PTransportChannel::MaybeSwitchSelectedConnection(
IceSwitchReason reason, IceSwitchReason reason,
IceControllerInterface::SwitchResult result) { IceControllerInterface::SwitchResult result) {
@ -530,7 +543,7 @@ void P2PTransportChannel::SetRemoteIceParameters(
ice_params, static_cast<int>(remote_ice_parameters_.size() - 1)); ice_params, static_cast<int>(remote_ice_parameters_.size() - 1));
} }
// Updating the remote ICE candidate generation could change the sort order. // Updating the remote ICE candidate generation could change the sort order.
RequestSortAndStateUpdate( ice_adapter_->OnSortAndSwitchRequest(
IceSwitchReason::REMOTE_CANDIDATE_GENERATION_CHANGE); IceSwitchReason::REMOTE_CANDIDATE_GENERATION_CHANGE);
} }
@ -685,7 +698,8 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) {
if (config_.network_preference != config.network_preference) { if (config_.network_preference != config.network_preference) {
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 " RTC_LOG(LS_INFO) << "Set network preference to "
<< (config_.network_preference.has_value() << (config_.network_preference.has_value()
? config_.network_preference.value() ? config_.network_preference.value()
@ -711,7 +725,7 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) {
config_.vpn_preference = config.vpn_preference; config_.vpn_preference = config.vpn_preference;
allocator_->SetVpnPreference(config_.vpn_preference); allocator_->SetVpnPreference(config_.vpn_preference);
ice_controller_->SetIceConfig(config_); ice_adapter_->SetIceConfig(config_);
RTC_DCHECK(ValidateIceConfig(config_).ok()); RTC_DCHECK(ValidateIceConfig(config_).ok());
} }
@ -988,7 +1002,7 @@ void P2PTransportChannel::OnPortReady(PortAllocatorSession* session,
CreateConnection(port, *iter, iter->origin_port()); CreateConnection(port, *iter, iter->origin_port());
} }
SortConnectionsAndUpdateState( ice_adapter_->OnImmediateSortAndSwitchRequest(
IceSwitchReason::NEW_CONNECTION_FROM_LOCAL_CANDIDATE); IceSwitchReason::NEW_CONNECTION_FROM_LOCAL_CANDIDATE);
} }
@ -1168,7 +1182,7 @@ void P2PTransportChannel::OnUnknownAddress(PortInterface* port,
// Update the list of connections since we just added another. We do this // Update the list of connections since we just added another. We do this
// after sending the response since it could (in principle) delete the // after sending the response since it could (in principle) delete the
// connection in question. // connection in question.
SortConnectionsAndUpdateState( ice_adapter_->OnImmediateSortAndSwitchRequest(
IceSwitchReason::NEW_CONNECTION_FROM_UNKNOWN_REMOTE_ADDRESS); IceSwitchReason::NEW_CONNECTION_FROM_UNKNOWN_REMOTE_ADDRESS);
} }
@ -1219,11 +1233,12 @@ void P2PTransportChannel::OnNominated(Connection* conn) {
// TODO(qingsi): RequestSortAndStateUpdate will eventually call // TODO(qingsi): RequestSortAndStateUpdate will eventually call
// MaybeSwitchSelectedConnection again. Rewrite this logic. // MaybeSwitchSelectedConnection again. Rewrite this logic.
if (MaybeSwitchSelectedConnection( if (ice_adapter_->OnImmediateSwitchRequest(
conn, IceSwitchReason::NOMINATION_ON_CONTROLLED_SIDE)) { IceSwitchReason::NOMINATION_ON_CONTROLLED_SIDE, conn)) {
// Now that we have selected a connection, it is time to prune other // Now that we have selected a connection, it is time to prune other
// connections and update the read/write state of the channel. // 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 { } else {
RTC_LOG(LS_INFO) RTC_LOG(LS_INFO)
<< "Not switching the selected connection on controlled side yet: " << "Not switching the selected connection on controlled side yet: "
@ -1371,7 +1386,7 @@ void P2PTransportChannel::FinishAddingRemoteCandidate(
CreateConnections(new_remote_candidate, NULL); CreateConnections(new_remote_candidate, NULL);
// Resort the connections list, which may have new elements. // Resort the connections list, which may have new elements.
SortConnectionsAndUpdateState( ice_adapter_->OnImmediateSortAndSwitchRequest(
IceSwitchReason::NEW_CONNECTION_FROM_REMOTE_CANDIDATE); IceSwitchReason::NEW_CONNECTION_FROM_REMOTE_CANDIDATE);
} }
@ -1691,9 +1706,7 @@ rtc::DiffServCodePoint P2PTransportChannel::DefaultDscpValue() const {
rtc::ArrayView<Connection*> P2PTransportChannel::connections() const { rtc::ArrayView<Connection*> P2PTransportChannel::connections() const {
RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK_RUN_ON(network_thread_);
rtc::ArrayView<const Connection*> res = ice_controller_->connections(); return ice_adapter_->LegacyConnections();
return rtc::ArrayView<Connection*>(const_cast<Connection**>(res.data()),
res.size());
} }
void P2PTransportChannel::RemoveConnectionForTest(Connection* connection) { void P2PTransportChannel::RemoveConnectionForTest(Connection* connection) {
@ -1724,6 +1737,7 @@ void P2PTransportChannel::UpdateConnectionStates() {
} }
// Prepare for best candidate sorting. // Prepare for best candidate sorting.
// TODO(bugs.webrtc.org/14367) remove once refactor lands.
void P2PTransportChannel::RequestSortAndStateUpdate( void P2PTransportChannel::RequestSortAndStateUpdate(
IceSwitchReason reason_to_sort) { IceSwitchReason reason_to_sort) {
RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK_RUN_ON(network_thread_);
@ -1736,13 +1750,14 @@ void P2PTransportChannel::RequestSortAndStateUpdate(
} }
} }
// TODO(bugs.webrtc.org/14367) remove once refactor lands.
void P2PTransportChannel::MaybeStartPinging() { void P2PTransportChannel::MaybeStartPinging() {
RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK_RUN_ON(network_thread_);
if (started_pinging_) { if (started_pinging_) {
return; return;
} }
if (ice_controller_->HasPingableConnection()) { if (ice_adapter_->LegacyHasPingableConnection()) {
RTC_LOG(LS_INFO) << ToString() RTC_LOG(LS_INFO) << ToString()
<< ": Have a pingable connection for the first time; " << ": Have a pingable connection for the first time; "
"starting to ping."; "starting to ping.";
@ -1782,6 +1797,7 @@ bool P2PTransportChannel::PresumedWritable(const Connection* conn) const {
// Sort the available connections to find the best one. We also monitor // Sort the available connections to find the best one. We also monitor
// the number of available connections and the current state. // the number of available connections and the current state.
// TODO(bugs.webrtc.org/14367) remove once refactor lands.
void P2PTransportChannel::SortConnectionsAndUpdateState( void P2PTransportChannel::SortConnectionsAndUpdateState(
IceSwitchReason reason_to_sort) { IceSwitchReason reason_to_sort) {
RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK_RUN_ON(network_thread_);
@ -1797,7 +1813,8 @@ void P2PTransportChannel::SortConnectionsAndUpdateState(
// have to be writable to become the selected connection although it will // have to be writable to become the selected connection although it will
// have higher priority if it is writable. // have higher priority if it is writable.
MaybeSwitchSelectedConnection( 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 // The controlled side can prune only if the selected connection has been
// nominated because otherwise it may prune the connection that will be // nominated because otherwise it may prune the connection that will be
@ -1865,7 +1882,7 @@ bool P2PTransportChannel::AllowedToPruneConnections() const {
void P2PTransportChannel::PruneConnections() { void P2PTransportChannel::PruneConnections() {
RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK_RUN_ON(network_thread_);
std::vector<const Connection*> connections_to_prune = std::vector<const Connection*> connections_to_prune =
ice_controller_->PruneConnections(); ice_adapter_->LegacyPruneConnections();
PruneConnections(connections_to_prune); PruneConnections(connections_to_prune);
} }
@ -1976,7 +1993,7 @@ void P2PTransportChannel::SwitchSelectedConnectionInternal(
++selected_candidate_pair_changes_; ++selected_candidate_pair_changes_;
ice_controller_->SetSelectedConnection(selected_connection_); ice_adapter_->OnConnectionSwitched(selected_connection_);
} }
int64_t P2PTransportChannel::ComputeEstimatedDisconnectedTimeMs( int64_t P2PTransportChannel::ComputeEstimatedDisconnectedTimeMs(
@ -1990,11 +2007,11 @@ int64_t P2PTransportChannel::ComputeEstimatedDisconnectedTimeMs(
} }
// Warning: UpdateTransportState should eventually be called whenever a // Warning: UpdateTransportState should eventually be called whenever a
// connection is added, deleted, or the write state of any connection changes // connection is added, deleted, or the write state of any connection changes so
// so that the transport controller will get the up-to-date channel state. // that the transport controller will get the up-to-date channel state. However
// However it should not be called too often; in the case that multiple // it should not be called too often; in the case that multiple connection
// connection states change, it should be called after all the connection // states change, it should be called after all the connection states have
// states have changed. For example, we call this at the end of // changed. For example, we call this at the end of
// SortConnectionsAndUpdateState. // SortConnectionsAndUpdateState.
void P2PTransportChannel::UpdateTransportState() { void P2PTransportChannel::UpdateTransportState() {
RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK_RUN_ON(network_thread_);
@ -2091,7 +2108,7 @@ void P2PTransportChannel::OnSelectedConnectionDestroyed() {
RTC_LOG(LS_INFO) << "Selected connection destroyed. Will choose a new one."; RTC_LOG(LS_INFO) << "Selected connection destroyed. Will choose a new one.";
IceSwitchReason reason = IceSwitchReason::SELECTED_CONNECTION_DESTROYED; IceSwitchReason reason = IceSwitchReason::SELECTED_CONNECTION_DESTROYED;
SwitchSelectedConnectionInternal(nullptr, reason); SwitchSelectedConnectionInternal(nullptr, reason);
RequestSortAndStateUpdate(reason); ice_adapter_->OnSortAndSwitchRequest(reason);
} }
// If all connections timed out, delete them all. // If all connections timed out, delete them all.
@ -2125,13 +2142,14 @@ bool P2PTransportChannel::ReadyToSend(const Connection* connection) const {
} }
// Handle queued up check-and-ping request // Handle queued up check-and-ping request
// TODO(bugs.webrtc.org/14367) remove once refactor lands.
void P2PTransportChannel::CheckAndPing() { void P2PTransportChannel::CheckAndPing() {
RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK_RUN_ON(network_thread_);
// Make sure the states of the connections are up-to-date (since this // Make sure the states of the connections are up-to-date (since this
// affects which ones are pingable). // affects which ones are pingable).
UpdateConnectionStates(); 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); TimeDelta delay = TimeDelta::Millis(result.recheck_delay_ms);
if (result.connection.value_or(nullptr)) { if (result.connection.value_or(nullptr)) {
@ -2145,7 +2163,7 @@ void P2PTransportChannel::CheckAndPing() {
// This method is only for unit testing. // This method is only for unit testing.
Connection* P2PTransportChannel::FindNextPingableConnection() { Connection* P2PTransportChannel::FindNextPingableConnection() {
RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK_RUN_ON(network_thread_);
auto* conn = ice_controller_->FindNextPingableConnection(); const Connection* conn = ice_adapter_->FindNextPingableConnection();
if (conn) { if (conn) {
return FromIceController(conn); return FromIceController(conn);
} else { } else {
@ -2174,7 +2192,7 @@ void P2PTransportChannel::SendPingRequestInternal(Connection* connection) {
// active. // active.
void P2PTransportChannel::MarkConnectionPinged(Connection* conn) { void P2PTransportChannel::MarkConnectionPinged(Connection* conn) {
RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK_RUN_ON(network_thread_);
ice_controller_->MarkConnectionPinged(conn); ice_adapter_->OnConnectionPinged(conn);
} }
// Apart from sending ping from `conn` this method also updates // Apart from sending ping from `conn` this method also updates
@ -2208,7 +2226,7 @@ uint32_t P2PTransportChannel::GetNominationAttr(Connection* conn) const {
// Nominate a connection based on the NominationMode. // Nominate a connection based on the NominationMode.
bool P2PTransportChannel::GetUseCandidateAttr(Connection* conn) const { bool P2PTransportChannel::GetUseCandidateAttr(Connection* conn) const {
RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK_RUN_ON(network_thread_);
return ice_controller_->GetUseCandidateAttr( return ice_adapter_->GetUseCandidateAttribute(
conn, config_.default_nomination_mode, remote_ice_mode_); conn, config_.default_nomination_mode, remote_ice_mode_);
} }
@ -2233,7 +2251,7 @@ void P2PTransportChannel::OnConnectionStateChange(Connection* connection) {
} }
// We have to unroll the stack before doing this because we may be changing // We have to unroll the stack before doing this because we may be changing
// the state of connections while sorting. // the state of connections while sorting.
RequestSortAndStateUpdate( ice_adapter_->OnSortAndSwitchRequest(
IceSwitchReason::CONNECT_STATE_CHANGE); // "candidate pair state IceSwitchReason::CONNECT_STATE_CHANGE); // "candidate pair state
// changed"); // changed");
} }
@ -2261,9 +2279,9 @@ void P2PTransportChannel::OnConnectionDestroyed(Connection* connection) {
if (selected_connection_ == connection) { if (selected_connection_ == connection) {
OnSelectedConnectionDestroyed(); OnSelectedConnectionDestroyed();
} else { } else {
// If a non-selected connection was destroyed, we don't need to re-sort // If a non-selected connection was destroyed, we don't need to re-sort but
// but we do need to update state, because we could be switching to // we do need to update state, because we could be switching to "failed" or
// "failed" or "completed". // "completed".
UpdateTransportState(); UpdateTransportState();
} }
} }
@ -2273,7 +2291,7 @@ void P2PTransportChannel::RemoveConnection(const Connection* connection) {
auto it = absl::c_find(connections_, connection); auto it = absl::c_find(connections_, connection);
RTC_DCHECK(it != connections_.end()); RTC_DCHECK(it != connections_.end());
connections_.erase(it); 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 // When a port is destroyed, remove it from our list of ports to use for
@ -2374,7 +2392,8 @@ void P2PTransportChannel::OnReadPacket(Connection* connection,
// May need to switch the sending connection based on the receiving media // May need to switch the sending connection based on the receiving media
// path if this is the controlled side. // path if this is the controlled side.
if (ice_role_ == ICEROLE_CONTROLLED) { if (ice_role_ == ICEROLE_CONTROLLED) {
MaybeSwitchSelectedConnection(connection, IceSwitchReason::DATA_RECEIVED); ice_adapter_->OnImmediateSwitchRequest(IceSwitchReason::DATA_RECEIVED,
connection);
} }
} }
@ -2445,4 +2464,167 @@ void P2PTransportChannel::LogCandidatePairConfig(
conn->ToLogDescription()); 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 } // namespace cricket

View File

@ -46,6 +46,7 @@
#include "api/transport/stun.h" #include "api/transport/stun.h"
#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h" #include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h"
#include "logging/rtc_event_log/ice_logger.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/basic_async_resolver_factory.h"
#include "p2p/base/candidate_pair_interface.h" #include "p2p/base/candidate_pair_interface.h"
#include "p2p/base/connection.h" #include "p2p/base/connection.h"
@ -265,7 +266,9 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal,
owned_dns_resolver_factory, owned_dns_resolver_factory,
webrtc::RtcEventLog* event_log, webrtc::RtcEventLog* event_log,
IceControllerFactoryInterface* ice_controller_factory, IceControllerFactoryInterface* ice_controller_factory,
ActiveIceControllerFactoryInterface* active_ice_controller_factory,
const webrtc::FieldTrialsView* field_trials); const webrtc::FieldTrialsView* field_trials);
bool IsGettingPorts() { bool IsGettingPorts() {
RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK_RUN_ON(network_thread_);
return allocator_session()->IsGettingPorts(); return allocator_session()->IsGettingPorts();
@ -274,12 +277,15 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal,
// Returns true if it's possible to send packets on `connection`. // Returns true if it's possible to send packets on `connection`.
bool ReadyToSend(const Connection* connection) const; bool ReadyToSend(const Connection* connection) const;
bool PresumedWritable(const Connection* conn) const; bool PresumedWritable(const Connection* conn) const;
// TODO(bugs.webrtc.org/14367) remove once refactor lands.
void RequestSortAndStateUpdate(IceSwitchReason reason_to_sort); void RequestSortAndStateUpdate(IceSwitchReason reason_to_sort);
// Start pinging if we haven't already started, and we now have a connection // Start pinging if we haven't already started, and we now have a connection
// that's pingable. // that's pingable.
// TODO(bugs.webrtc.org/14367) remove once refactor lands.
void MaybeStartPinging(); void MaybeStartPinging();
void SendPingRequestInternal(Connection* connection); void SendPingRequestInternal(Connection* connection);
// TODO(bugs.webrtc.org/14367) remove once refactor lands.
void SortConnectionsAndUpdateState(IceSwitchReason reason_to_sort); void SortConnectionsAndUpdateState(IceSwitchReason reason_to_sort);
rtc::NetworkRoute ConfigureNetworkRoute(const Connection* conn); rtc::NetworkRoute ConfigureNetworkRoute(const Connection* conn);
void SwitchSelectedConnectionInternal(Connection* conn, void SwitchSelectedConnectionInternal(Connection* conn,
@ -348,6 +354,7 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal,
void OnNominated(Connection* conn); void OnNominated(Connection* conn);
// TODO(bugs.webrtc.org/14367) remove once refactor lands.
void CheckAndPing(); void CheckAndPing();
void LogCandidatePairConfig(Connection* conn, void LogCandidatePairConfig(Connection* conn,
@ -357,12 +364,15 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal,
bool GetUseCandidateAttr(Connection* conn) const; bool GetUseCandidateAttr(Connection* conn) const;
// Returns true if the new_connection is selected for transmission. // 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); IceSwitchReason reason);
// TODO(bugs.webrtc.org/14367) remove once refactor lands.
bool MaybeSwitchSelectedConnection( bool MaybeSwitchSelectedConnection(
IceSwitchReason reason, IceSwitchReason reason,
IceControllerInterface::SwitchResult result); IceControllerInterface::SwitchResult result);
bool AllowedToPruneConnections() const; bool AllowedToPruneConnections() const;
// TODO(bugs.webrtc.org/14367) remove once refactor lands.
void PruneConnections(); void PruneConnections();
// Returns the latest remote ICE parameters or nullptr if there are no remote // Returns the latest remote ICE parameters or nullptr if there are no remote
@ -420,6 +430,7 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal,
void ParseFieldTrials(const webrtc::FieldTrialsView* field_trials); void ParseFieldTrials(const webrtc::FieldTrialsView* field_trials);
// TODO(bugs.webrtc.org/14367) remove once refactor lands.
webrtc::ScopedTaskSafety task_safety_; webrtc::ScopedTaskSafety task_safety_;
std::string transport_name_ RTC_GUARDED_BY(network_thread_); std::string transport_name_ RTC_GUARDED_BY(network_thread_);
int component_ RTC_GUARDED_BY(network_thread_); int component_ RTC_GUARDED_BY(network_thread_);
@ -447,6 +458,7 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal,
std::vector<RemoteCandidate> remote_candidates_ std::vector<RemoteCandidate> remote_candidates_
RTC_GUARDED_BY(network_thread_); RTC_GUARDED_BY(network_thread_);
// TODO(bugs.webrtc.org/14367) remove once refactor lands.
bool sort_dirty_ RTC_GUARDED_BY( bool sort_dirty_ RTC_GUARDED_BY(
network_thread_); // indicates whether another sort is needed right now network_thread_); // indicates whether another sort is needed right now
bool had_connection_ RTC_GUARDED_BY(network_thread_) = bool had_connection_ RTC_GUARDED_BY(network_thread_) =
@ -473,6 +485,7 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal,
IceConfig config_ RTC_GUARDED_BY(network_thread_); IceConfig config_ RTC_GUARDED_BY(network_thread_);
int last_sent_packet_id_ RTC_GUARDED_BY(network_thread_) = int last_sent_packet_id_ RTC_GUARDED_BY(network_thread_) =
-1; // -1 indicates no packet was sent before. -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; bool started_pinging_ RTC_GUARDED_BY(network_thread_) = false;
// The value put in the "nomination" attribute for the next nominated // The value put in the "nomination" attribute for the next nominated
// connection. A zero-value indicates the connection will not be nominated. // connection. A zero-value indicates the connection will not be nominated.
@ -486,7 +499,53 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal,
RTC_GUARDED_BY(network_thread_); RTC_GUARDED_BY(network_thread_);
webrtc::IceEventLog ice_event_log_ 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_); RTC_GUARDED_BY(network_thread_);
struct CandidateAndResolver final { struct CandidateAndResolver final {

View File

@ -16,11 +16,15 @@
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "api/test/mock_async_dns_resolver.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/basic_ice_controller.h"
#include "p2p/base/connection.h" #include "p2p/base/connection.h"
#include "p2p/base/fake_port_allocator.h" #include "p2p/base/fake_port_allocator.h"
#include "p2p/base/ice_transport_internal.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_async_resolver.h"
#include "p2p/base/mock_ice_controller.h"
#include "p2p/base/packet_transport_internal.h" #include "p2p/base/packet_transport_internal.h"
#include "p2p/base/test_stun_server.h" #include "p2p/base/test_stun_server.h"
#include "p2p/base/test_turn_server.h" #include "p2p/base/test_turn_server.h"
@ -56,7 +60,6 @@ using ::testing::DoAll;
using ::testing::InSequence; using ::testing::InSequence;
using ::testing::InvokeArgument; using ::testing::InvokeArgument;
using ::testing::InvokeWithoutArgs; using ::testing::InvokeWithoutArgs;
using ::testing::NiceMock;
using ::testing::Return; using ::testing::Return;
using ::testing::ReturnRef; using ::testing::ReturnRef;
using ::testing::SaveArg; using ::testing::SaveArg;
@ -184,18 +187,6 @@ cricket::BasicPortAllocator* CreateBasicPortAllocator(
return allocator.release(); 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. // An one-shot resolver factory with default return arguments.
// Resolution is immediate, always succeeds, and returns nonsense. // Resolution is immediate, always succeeds, and returns nonsense.
class ResolverFactoryFixture : public webrtc::MockAsyncDnsResolverFactory { class ResolverFactoryFixture : public webrtc::MockAsyncDnsResolverFactory {
@ -6123,6 +6114,25 @@ TEST(P2PTransportChannel, InjectIceController) {
/* component= */ 77, std::move(init)); /* 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 { class ForgetLearnedStateController : public cricket::BasicIceController {
public: public:
explicit ForgetLearnedStateController( explicit ForgetLearnedStateController(