diff --git a/api/ice_transport_interface.h b/api/ice_transport_interface.h index ca677712bd..cd6ada8e01 100644 --- a/api/ice_transport_interface.h +++ b/api/ice_transport_interface.h @@ -89,7 +89,18 @@ struct IceTransportInit final { // 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 7e5f1735c2..10093048b4 100644 --- a/p2p/BUILD.gn +++ b/p2p/BUILD.gn @@ -199,7 +199,9 @@ if (rtc_include_tests) { 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 0000000000..908967bd1d --- /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 + +#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 Create( + const cricket::ActiveIceControllerFactoryArgs& args) { + RecordActiveIceControllerCreated(); + return std::make_unique(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 0000000000..bde9254e7d --- /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 +#include + +#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, + 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, + PruneConnections, + (), + (override)); +}; + +class MockIceControllerFactory : public cricket::IceControllerFactoryInterface { + public: + ~MockIceControllerFactory() override = default; + + std::unique_ptr Create( + const cricket::IceControllerFactoryArgs& args) override { + RecordIceControllerCreated(); + return std::make_unique(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 6cc82b826b..2b0e906f09 100644 --- a/p2p/base/p2p_transport_channel.cc +++ b/p2p/base/p2p_transport_channel.cc @@ -93,15 +93,23 @@ rtc::RouteEndpoint CreateRouteEndpointFromCandidate( 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 @@ std::unique_ptr P2PTransportChannel::Create( transport_name, component, init.port_allocator(), nullptr, std::make_unique( 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 @@ P2PTransportChannel::P2PTransportChannel( /* 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 @@ P2PTransportChannel::P2PTransportChannel( 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 @@ P2PTransportChannel::P2PTransportChannel( &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(args); - } + ice_adapter_ = std::make_unique( + args, ice_controller_factory, active_ice_controller_factory, field_trials, + /* transport= */ this); } P2PTransportChannel::~P2PTransportChannel() { @@ -282,18 +292,21 @@ void P2PTransportChannel::AddConnection(Connection* connection) { 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 @@ void P2PTransportChannel::SetRemoteIceParameters( ice_params, static_cast(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 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) { 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 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) { 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 @@ void P2PTransportChannel::OnPortReady(PortAllocatorSession* session, CreateConnection(port, *iter, iter->origin_port()); } - SortConnectionsAndUpdateState( + ice_adapter_->OnImmediateSortAndSwitchRequest( 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 // 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 @@ void P2PTransportChannel::OnNominated(Connection* conn) { // 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 @@ void P2PTransportChannel::FinishAddingRemoteCandidate( 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::DiffServCodePoint P2PTransportChannel::DefaultDscpValue() const { rtc::ArrayView P2PTransportChannel::connections() const { RTC_DCHECK_RUN_ON(network_thread_); - rtc::ArrayView res = ice_controller_->connections(); - return rtc::ArrayView(const_cast(res.data()), - res.size()); + return ice_adapter_->LegacyConnections(); } void P2PTransportChannel::RemoveConnectionForTest(Connection* connection) { @@ -1724,6 +1737,7 @@ void P2PTransportChannel::UpdateConnectionStates() { } // 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 @@ void P2PTransportChannel::RequestSortAndStateUpdate( } } +// 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 @@ bool P2PTransportChannel::PresumedWritable(const Connection* conn) const { // 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 @@ void P2PTransportChannel::SortConnectionsAndUpdateState( // 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 @@ bool P2PTransportChannel::AllowedToPruneConnections() const { void P2PTransportChannel::PruneConnections() { RTC_DCHECK_RUN_ON(network_thread_); std::vector connections_to_prune = - ice_controller_->PruneConnections(); + ice_adapter_->LegacyPruneConnections(); PruneConnections(connections_to_prune); } @@ -1976,7 +1993,7 @@ void P2PTransportChannel::SwitchSelectedConnectionInternal( ++selected_candidate_pair_changes_; - ice_controller_->SetSelectedConnection(selected_connection_); + ice_adapter_->OnConnectionSwitched(selected_connection_); } int64_t P2PTransportChannel::ComputeEstimatedDisconnectedTimeMs( @@ -1990,11 +2007,11 @@ int64_t P2PTransportChannel::ComputeEstimatedDisconnectedTimeMs( } // 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 @@ void P2PTransportChannel::OnSelectedConnectionDestroyed() { 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 @@ bool P2PTransportChannel::ReadyToSend(const Connection* connection) const { } // 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 @@ void P2PTransportChannel::CheckAndPing() { // 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 @@ void P2PTransportChannel::SendPingRequestInternal(Connection* connection) { // 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 @@ uint32_t P2PTransportChannel::GetNominationAttr(Connection* conn) const { // 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 @@ void P2PTransportChannel::OnConnectionStateChange(Connection* connection) { } // 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 @@ void P2PTransportChannel::OnConnectionDestroyed(Connection* connection) { 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 @@ void P2PTransportChannel::RemoveConnection(const Connection* connection) { 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 @@ void P2PTransportChannel::OnReadPacket(Connection* connection, // 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 @@ void P2PTransportChannel::LogCandidatePairConfig( 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(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 +P2PTransportChannel::IceControllerAdapter::LegacyConnections() const { + RTC_DCHECK_RUN_ON(transport_->network_thread_); + if (active_ice_controller_) { + return rtc::ArrayView(transport_->connections_.data(), + transport_->connections_.size()); + } + + rtc::ArrayView res = legacy_ice_controller_->connections(); + return rtc::ArrayView(const_cast(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 +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 5592a4b1ad..d7bcd239df 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 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal, 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 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal, // 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 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal, void OnNominated(Connection* conn); + // TODO(bugs.webrtc.org/14367) remove once refactor lands. void CheckAndPing(); void LogCandidatePairConfig(Connection* conn, @@ -357,12 +364,15 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal, 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 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal, 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 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal, std::vector 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 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal, 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 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal, RTC_GUARDED_BY(network_thread_); webrtc::IceEventLog ice_event_log_ RTC_GUARDED_BY(network_thread_); - std::unique_ptr 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 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 LegacyPruneConnections(); + + private: + P2PTransportChannel* transport_; + std::unique_ptr legacy_ice_controller_; + std::unique_ptr active_ice_controller_; + }; + std::unique_ptr 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 d3178f7a0c..928e9b258a 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::DoAll; 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 @@ cricket::BasicPortAllocator* CreateBasicPortAllocator( return allocator.release(); } -class MockIceControllerFactory : public cricket::IceControllerFactoryInterface { - public: - ~MockIceControllerFactory() override = default; - std::unique_ptr Create( - const cricket::IceControllerFactoryArgs& args) override { - RecordIceControllerCreated(); - return std::make_unique(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 @@ TEST(P2PTransportChannel, InjectIceController) { /* component= */ 77, std::move(init)); } +TEST(P2PTransportChannel, InjectActiveIceController) { + std::unique_ptr 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(