diff --git a/api/ice_transport_interface.h b/api/ice_transport_interface.h index 2ec41aaa69..cd6ada8e01 100644 --- a/api/ice_transport_interface.h +++ b/api/ice_transport_interface.h @@ -99,10 +99,8 @@ struct IceTransportInit final { // constructed and used. // // 2. If the field trial is enabled - // a. If an active ICE controller factory is supplied, it is used and - // the legacy ICE controller factory is not used. - // b. If not, a default active ICE controller is used, wrapping over the - // supplied or the default legacy ICE controller. + // - 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 85ac605b1f..10093048b4 100644 --- a/p2p/BUILD.gn +++ b/p2p/BUILD.gn @@ -81,8 +81,6 @@ rtc_library("rtc_p2p") { "base/turn_port.cc", "base/turn_port.h", "base/udp_port.h", - "base/wrapping_active_ice_controller.cc", - "base/wrapping_active_ice_controller.h", "client/basic_port_allocator.cc", "client/basic_port_allocator.h", "client/relay_port_factory_interface.h", @@ -203,7 +201,6 @@ if (rtc_include_tests) { "base/fake_packet_transport.h", "base/mock_active_ice_controller.h", "base/mock_async_resolver.h", - "base/mock_ice_agent.h", "base/mock_ice_controller.h", "base/mock_ice_transport.h", "base/test_stun_server.cc", @@ -263,7 +260,6 @@ if (rtc_include_tests) { "base/transport_description_unittest.cc", "base/turn_port_unittest.cc", "base/turn_server_unittest.cc", - "base/wrapping_active_ice_controller_unittest.cc", "client/basic_port_allocator_unittest.cc", ] deps = [ diff --git a/p2p/base/mock_ice_agent.h b/p2p/base/mock_ice_agent.h deleted file mode 100644 index e4100ecd7a..0000000000 --- a/p2p/base/mock_ice_agent.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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_AGENT_H_ -#define P2P_BASE_MOCK_ICE_AGENT_H_ - -#include - -#include "p2p/base/connection.h" -#include "p2p/base/ice_agent_interface.h" -#include "p2p/base/ice_switch_reason.h" -#include "p2p/base/transport_description.h" -#include "test/gmock.h" - -namespace cricket { - -class MockIceAgent : public IceAgentInterface { - public: - ~MockIceAgent() override = default; - - MOCK_METHOD(int64_t, GetLastPingSentMs, (), (override, const)); - MOCK_METHOD(IceRole, GetIceRole, (), (override, const)); - MOCK_METHOD(void, OnStartedPinging, (), (override)); - MOCK_METHOD(void, UpdateConnectionStates, (), (override)); - MOCK_METHOD(void, UpdateState, (), (override)); - MOCK_METHOD(void, - ForgetLearnedStateForConnections, - (std::vector), - (override)); - MOCK_METHOD(void, SendPingRequest, (const Connection*), (override)); - MOCK_METHOD(void, - SwitchSelectedConnection, - (const Connection*, IceSwitchReason), - (override)); - MOCK_METHOD(bool, - PruneConnections, - (std::vector), - (override)); -}; - -} // namespace cricket - -#endif // P2P_BASE_MOCK_ICE_AGENT_H_ diff --git a/p2p/base/p2p_transport_channel.cc b/p2p/base/p2p_transport_channel.cc index 0a0d8c8b1d..2b0e906f09 100644 --- a/p2p/base/p2p_transport_channel.cc +++ b/p2p/base/p2p_transport_channel.cc @@ -33,7 +33,6 @@ #include "p2p/base/connection.h" #include "p2p/base/connection_info.h" #include "p2p/base/port.h" -#include "p2p/base/wrapping_active_ice_controller.h" #include "rtc_base/checks.h" #include "rtc_base/crc32.h" #include "rtc_base/experiments/struct_parameters_parser.h" @@ -2473,15 +2472,10 @@ P2PTransportChannel::IceControllerAdapter::IceControllerAdapter( P2PTransportChannel* transport) : transport_(transport) { if (UseActiveIceControllerFieldTrialEnabled(field_trials)) { - if (active_ice_controller_factory) { - ActiveIceControllerFactoryArgs active_args{args, - /* ice_agent= */ transport}; - active_ice_controller_ = - active_ice_controller_factory->Create(active_args); - } else { - active_ice_controller_ = std::make_unique( - /* ice_agent= */ transport, ice_controller_factory, args); - } + 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); diff --git a/p2p/base/wrapping_active_ice_controller.cc b/p2p/base/wrapping_active_ice_controller.cc deleted file mode 100644 index c6659217fc..0000000000 --- a/p2p/base/wrapping_active_ice_controller.cc +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright 2022 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "p2p/base/wrapping_active_ice_controller.h" - -#include -#include -#include - -#include "api/sequence_checker.h" -#include "api/task_queue/pending_task_safety_flag.h" -#include "api/units/time_delta.h" -#include "p2p/base/basic_ice_controller.h" -#include "p2p/base/connection.h" -#include "p2p/base/ice_agent_interface.h" -#include "p2p/base/ice_controller_interface.h" -#include "p2p/base/ice_switch_reason.h" -#include "p2p/base/ice_transport_internal.h" -#include "p2p/base/transport_description.h" -#include "rtc_base/logging.h" -#include "rtc_base/thread.h" -#include "rtc_base/time_utils.h" - -namespace { -using ::webrtc::SafeTask; -using ::webrtc::TimeDelta; -} // unnamed namespace - -namespace cricket { - -WrappingActiveIceController::WrappingActiveIceController( - IceAgentInterface* ice_agent, - std::unique_ptr wrapped) - : network_thread_(rtc::Thread::Current()), - wrapped_(std::move(wrapped)), - agent_(*ice_agent) { - RTC_DCHECK(ice_agent != nullptr); -} - -WrappingActiveIceController::WrappingActiveIceController( - IceAgentInterface* ice_agent, - IceControllerFactoryInterface* wrapped_factory, - const IceControllerFactoryArgs& wrapped_factory_args) - : network_thread_(rtc::Thread::Current()), agent_(*ice_agent) { - RTC_DCHECK(ice_agent != nullptr); - if (wrapped_factory) { - wrapped_ = wrapped_factory->Create(wrapped_factory_args); - } else { - wrapped_ = std::make_unique(wrapped_factory_args); - } -} - -WrappingActiveIceController::~WrappingActiveIceController() {} - -void WrappingActiveIceController::SetIceConfig(const IceConfig& config) { - RTC_DCHECK_RUN_ON(network_thread_); - wrapped_->SetIceConfig(config); -} - -bool WrappingActiveIceController::GetUseCandidateAttribute( - const Connection* connection, - NominationMode mode, - IceMode remote_ice_mode) const { - RTC_DCHECK_RUN_ON(network_thread_); - return wrapped_->GetUseCandidateAttr(connection, mode, remote_ice_mode); -} - -void WrappingActiveIceController::OnConnectionAdded( - const Connection* connection) { - RTC_DCHECK_RUN_ON(network_thread_); - wrapped_->AddConnection(connection); -} - -void WrappingActiveIceController::OnConnectionPinged( - const Connection* connection) { - RTC_DCHECK_RUN_ON(network_thread_); - wrapped_->MarkConnectionPinged(connection); -} - -void WrappingActiveIceController::OnConnectionUpdated( - const Connection* connection) { - RTC_LOG(LS_VERBOSE) << "Connection report for " << connection->ToString(); - // Do nothing. Native ICE controllers have direct access to Connection, so no - // need to update connection state separately. -} - -void WrappingActiveIceController::OnConnectionSwitched( - const Connection* connection) { - RTC_DCHECK_RUN_ON(network_thread_); - selected_connection_ = connection; - wrapped_->SetSelectedConnection(connection); -} - -void WrappingActiveIceController::OnConnectionDestroyed( - const Connection* connection) { - RTC_DCHECK_RUN_ON(network_thread_); - wrapped_->OnConnectionDestroyed(connection); -} - -void WrappingActiveIceController::MaybeStartPinging() { - RTC_DCHECK_RUN_ON(network_thread_); - if (started_pinging_) { - return; - } - - if (wrapped_->HasPingableConnection()) { - network_thread_->PostTask( - SafeTask(task_safety_.flag(), [this]() { SelectAndPingConnection(); })); - agent_.OnStartedPinging(); - started_pinging_ = true; - } -} - -void WrappingActiveIceController::SelectAndPingConnection() { - RTC_DCHECK_RUN_ON(network_thread_); - agent_.UpdateConnectionStates(); - - IceControllerInterface::PingResult result = - wrapped_->SelectConnectionToPing(agent_.GetLastPingSentMs()); - HandlePingResult(result); -} - -void WrappingActiveIceController::HandlePingResult( - IceControllerInterface::PingResult result) { - RTC_DCHECK_RUN_ON(network_thread_); - - if (result.connection.has_value()) { - agent_.SendPingRequest(result.connection.value()); - } - - network_thread_->PostDelayedTask( - SafeTask(task_safety_.flag(), [this]() { SelectAndPingConnection(); }), - TimeDelta::Millis(result.recheck_delay_ms)); -} - -void WrappingActiveIceController::OnSortAndSwitchRequest( - IceSwitchReason reason) { - RTC_DCHECK_RUN_ON(network_thread_); - if (!sort_pending_) { - network_thread_->PostTask(SafeTask(task_safety_.flag(), [this, reason]() { - SortAndSwitchToBestConnection(reason); - })); - sort_pending_ = true; - } -} - -void WrappingActiveIceController::OnImmediateSortAndSwitchRequest( - IceSwitchReason reason) { - RTC_DCHECK_RUN_ON(network_thread_); - SortAndSwitchToBestConnection(reason); -} - -void WrappingActiveIceController::SortAndSwitchToBestConnection( - IceSwitchReason reason) { - RTC_DCHECK_RUN_ON(network_thread_); - - // Make sure the connection states are up-to-date since this affects how they - // will be sorted. - agent_.UpdateConnectionStates(); - - // Any changes after this point will require a re-sort. - sort_pending_ = false; - - IceControllerInterface::SwitchResult result = - wrapped_->SortAndSwitchConnection(reason); - HandleSwitchResult(reason, result); - UpdateStateOnConnectionsResorted(); -} - -bool WrappingActiveIceController::OnImmediateSwitchRequest( - IceSwitchReason reason, - const Connection* selected) { - RTC_DCHECK_RUN_ON(network_thread_); - IceControllerInterface::SwitchResult result = - wrapped_->ShouldSwitchConnection(reason, selected); - HandleSwitchResult(reason, result); - return result.connection.has_value(); -} - -void WrappingActiveIceController::HandleSwitchResult( - IceSwitchReason reason_for_switch, - IceControllerInterface::SwitchResult result) { - RTC_DCHECK_RUN_ON(network_thread_); - if (result.connection.has_value()) { - RTC_LOG(LS_INFO) << "Switching selected connection due to: " - << IceSwitchReasonToString(reason_for_switch); - agent_.SwitchSelectedConnection(result.connection.value(), - reason_for_switch); - } - - if (result.recheck_event.has_value()) { - // If we do not switch to the connection because it missed the receiving - // threshold, the new connection is in a better receiving state than the - // currently selected connection. So we need to re-check whether it needs - // to be switched at a later time. - network_thread_->PostDelayedTask( - SafeTask(task_safety_.flag(), - [this, recheck_reason = result.recheck_event->reason]() { - SortAndSwitchToBestConnection(recheck_reason); - }), - TimeDelta::Millis(result.recheck_event->recheck_delay_ms)); - } - - agent_.ForgetLearnedStateForConnections( - result.connections_to_forget_state_on); -} - -void WrappingActiveIceController::UpdateStateOnConnectionsResorted() { - RTC_DCHECK_RUN_ON(network_thread_); - PruneConnections(); - - // Update the internal state of the ICE agentl. - agent_.UpdateState(); - - // Also possibly start pinging. - // We could start pinging if: - // * The first connection was created. - // * ICE credentials were provided. - // * A TCP connection became connected. - MaybeStartPinging(); -} - -void WrappingActiveIceController::PruneConnections() { - RTC_DCHECK_RUN_ON(network_thread_); - - // The controlled side can prune only if the selected connection has been - // nominated because otherwise it may prune the connection that will be - // selected by the controlling side. - // TODO(honghaiz): This is not enough to prevent a connection from being - // pruned too early because with aggressive nomination, the controlling side - // will nominate every connection until it becomes writable. - if (agent_.GetIceRole() == ICEROLE_CONTROLLING || - (selected_connection_ && selected_connection_->nominated())) { - std::vector connections_to_prune = - wrapped_->PruneConnections(); - agent_.PruneConnections(connections_to_prune); - } -} - -// Only for unit tests -const Connection* WrappingActiveIceController::FindNextPingableConnection() { - RTC_DCHECK_RUN_ON(network_thread_); - return wrapped_->FindNextPingableConnection(); -} - -} // namespace cricket diff --git a/p2p/base/wrapping_active_ice_controller.h b/p2p/base/wrapping_active_ice_controller.h deleted file mode 100644 index 449c0f0ee1..0000000000 --- a/p2p/base/wrapping_active_ice_controller.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2022 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef P2P_BASE_WRAPPING_ACTIVE_ICE_CONTROLLER_H_ -#define P2P_BASE_WRAPPING_ACTIVE_ICE_CONTROLLER_H_ - -#include - -#include "absl/types/optional.h" -#include "api/task_queue/pending_task_safety_flag.h" -#include "p2p/base/active_ice_controller_interface.h" -#include "p2p/base/connection.h" -#include "p2p/base/ice_agent_interface.h" -#include "p2p/base/ice_controller_factory_interface.h" -#include "p2p/base/ice_controller_interface.h" -#include "p2p/base/ice_switch_reason.h" -#include "p2p/base/ice_transport_internal.h" -#include "p2p/base/transport_description.h" -#include "rtc_base/thread.h" -#include "rtc_base/thread_annotations.h" - -namespace cricket { - -// WrappingActiveIceController provides the functionality of a legacy passive -// ICE controller but packaged as an active ICE Controller. -class WrappingActiveIceController : public ActiveIceControllerInterface { - public: - // Constructs an active ICE controller wrapping an already constructed legacy - // ICE controller. Does not take ownership of the ICE agent, which must - // already exist and outlive the ICE controller. - WrappingActiveIceController(IceAgentInterface* ice_agent, - std::unique_ptr wrapped); - // Constructs an active ICE controller that wraps over a legacy ICE - // controller. The legacy ICE controller is constructed through a factory, if - // one is supplied. If not, a default BasicIceController is wrapped instead. - // Does not take ownership of the ICE agent, which must already exist and - // outlive the ICE controller. - WrappingActiveIceController( - IceAgentInterface* ice_agent, - IceControllerFactoryInterface* wrapped_factory, - const IceControllerFactoryArgs& wrapped_factory_args); - virtual ~WrappingActiveIceController(); - - void SetIceConfig(const IceConfig& config) override; - bool GetUseCandidateAttribute(const Connection* connection, - NominationMode mode, - IceMode remote_ice_mode) const override; - - void OnConnectionAdded(const Connection* connection) override; - void OnConnectionPinged(const Connection* connection) override; - void OnConnectionUpdated(const Connection* connection) override; - void OnConnectionSwitched(const Connection* connection) override; - void OnConnectionDestroyed(const Connection* connection) override; - - void OnSortAndSwitchRequest(IceSwitchReason reason) override; - void OnImmediateSortAndSwitchRequest(IceSwitchReason reason) override; - bool OnImmediateSwitchRequest(IceSwitchReason reason, - const Connection* selected) override; - - // Only for unit tests - const Connection* FindNextPingableConnection() override; - - private: - void MaybeStartPinging(); - void SelectAndPingConnection(); - void HandlePingResult(IceControllerInterface::PingResult result); - - void SortAndSwitchToBestConnection(IceSwitchReason reason); - void HandleSwitchResult(IceSwitchReason reason_for_switch, - IceControllerInterface::SwitchResult result); - void UpdateStateOnConnectionsResorted(); - - void PruneConnections(); - - rtc::Thread* const network_thread_; - webrtc::ScopedTaskSafety task_safety_; - - bool started_pinging_ RTC_GUARDED_BY(network_thread_) = false; - bool sort_pending_ RTC_GUARDED_BY(network_thread_) = false; - const Connection* selected_connection_ RTC_GUARDED_BY(network_thread_) = - nullptr; - - std::unique_ptr wrapped_ - RTC_GUARDED_BY(network_thread_); - IceAgentInterface& agent_ RTC_GUARDED_BY(network_thread_); -}; - -} // namespace cricket - -#endif // P2P_BASE_WRAPPING_ACTIVE_ICE_CONTROLLER_H_ diff --git a/p2p/base/wrapping_active_ice_controller_unittest.cc b/p2p/base/wrapping_active_ice_controller_unittest.cc deleted file mode 100644 index 19f9b95a57..0000000000 --- a/p2p/base/wrapping_active_ice_controller_unittest.cc +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright 2009 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "p2p/base/wrapping_active_ice_controller.h" - -#include -#include -#include - -#include "p2p/base/connection.h" -#include "p2p/base/mock_ice_agent.h" -#include "p2p/base/mock_ice_controller.h" -#include "rtc_base/fake_clock.h" -#include "rtc_base/gunit.h" -#include "rtc_base/thread.h" - -namespace { - -using ::cricket::Connection; -using ::cricket::IceConfig; -using ::cricket::IceControllerFactoryArgs; -using ::cricket::IceControllerInterface; -using ::cricket::IceMode; -using ::cricket::IceRecheckEvent; -using ::cricket::IceSwitchReason; -using ::cricket::MockIceAgent; -using ::cricket::MockIceController; -using ::cricket::MockIceControllerFactory; -using ::cricket::NominationMode; -using ::cricket::WrappingActiveIceController; - -using ::testing::_; -using ::testing::NiceMock; -using ::testing::Ref; -using ::testing::Return; -using ::testing::Sequence; - -using ::rtc::AutoThread; -using ::rtc::Event; -using ::rtc::ScopedFakeClock; -using ::webrtc::TimeDelta; - -using NiceMockIceController = NiceMock; - -static const Connection* kConnection = - reinterpret_cast(0xabcd); -static const Connection* kConnectionTwo = - reinterpret_cast(0xbcde); -static const Connection* kConnectionThree = - reinterpret_cast(0xcdef); - -static const std::vector kEmptyConnsList = - std::vector(); - -static const TimeDelta kTick = TimeDelta::Millis(1); - -TEST(WrappingActiveIceControllerTest, CreateLegacyIceControllerFromFactory) { - MockIceAgent agent; - IceControllerFactoryArgs args; - MockIceControllerFactory legacy_controller_factory; - EXPECT_CALL(legacy_controller_factory, RecordIceControllerCreated()).Times(1); - WrappingActiveIceController controller(&agent, &legacy_controller_factory, - args); -} - -TEST(WrappingActiveIceControllerTest, PassthroughIceControllerInterface) { - MockIceAgent agent; - std::unique_ptr will_move = - std::make_unique(IceControllerFactoryArgs{}); - MockIceController* wrapped = will_move.get(); - WrappingActiveIceController controller(&agent, std::move(will_move)); - - IceConfig config{}; - EXPECT_CALL(*wrapped, SetIceConfig(Ref(config))); - controller.SetIceConfig(config); - - EXPECT_CALL(*wrapped, - GetUseCandidateAttr(kConnection, NominationMode::AGGRESSIVE, - IceMode::ICEMODE_LITE)) - .WillOnce(Return(true)); - EXPECT_TRUE(controller.GetUseCandidateAttribute( - kConnection, NominationMode::AGGRESSIVE, IceMode::ICEMODE_LITE)); - - EXPECT_CALL(*wrapped, AddConnection(kConnection)); - controller.OnConnectionAdded(kConnection); - - EXPECT_CALL(*wrapped, OnConnectionDestroyed(kConnection)); - controller.OnConnectionDestroyed(kConnection); - - EXPECT_CALL(*wrapped, SetSelectedConnection(kConnection)); - controller.OnConnectionSwitched(kConnection); - - EXPECT_CALL(*wrapped, MarkConnectionPinged(kConnection)); - controller.OnConnectionPinged(kConnection); - - EXPECT_CALL(*wrapped, FindNextPingableConnection()) - .WillOnce(Return(kConnection)); - EXPECT_EQ(controller.FindNextPingableConnection(), kConnection); -} - -TEST(WrappingActiveIceControllerTest, HandlesImmediateSwitchRequest) { - AutoThread main; - ScopedFakeClock clock; - NiceMock agent; - std::unique_ptr will_move = - std::make_unique(IceControllerFactoryArgs{}); - NiceMockIceController* wrapped = will_move.get(); - WrappingActiveIceController controller(&agent, std::move(will_move)); - - IceSwitchReason reason = IceSwitchReason::NOMINATION_ON_CONTROLLED_SIDE; - std::vector conns_to_forget{kConnectionTwo}; - int recheck_delay_ms = 10; - IceControllerInterface::SwitchResult switch_result{ - kConnection, - IceRecheckEvent(IceSwitchReason::ICE_CONTROLLER_RECHECK, - recheck_delay_ms), - conns_to_forget}; - - // ICE controller should switch to given connection immediately. - Sequence check_then_switch; - EXPECT_CALL(*wrapped, ShouldSwitchConnection(reason, kConnection)) - .InSequence(check_then_switch) - .WillOnce(Return(switch_result)); - EXPECT_CALL(agent, SwitchSelectedConnection(kConnection, reason)) - .InSequence(check_then_switch); - EXPECT_CALL(agent, ForgetLearnedStateForConnections(conns_to_forget)); - - EXPECT_TRUE(controller.OnImmediateSwitchRequest(reason, kConnection)); - - // No rechecks before recheck delay. - clock.AdvanceTime(TimeDelta::Millis(recheck_delay_ms - 1)); - - // ICE controller should recheck for best connection after the recheck delay. - Sequence recheck_sort; - EXPECT_CALL(agent, UpdateConnectionStates()).InSequence(recheck_sort); - EXPECT_CALL(*wrapped, - SortAndSwitchConnection(IceSwitchReason::ICE_CONTROLLER_RECHECK)) - .InSequence(recheck_sort) - .WillOnce(Return(IceControllerInterface::SwitchResult{})); - EXPECT_CALL(agent, ForgetLearnedStateForConnections(kEmptyConnsList)); - - clock.AdvanceTime(kTick); -} - -TEST(WrappingActiveIceControllerTest, HandlesImmediateSortAndSwitchRequest) { - AutoThread main; - ScopedFakeClock clock; - NiceMock agent; - std::unique_ptr will_move = - std::make_unique(IceControllerFactoryArgs{}); - NiceMockIceController* wrapped = will_move.get(); - WrappingActiveIceController controller(&agent, std::move(will_move)); - - IceSwitchReason reason = IceSwitchReason::NEW_CONNECTION_FROM_LOCAL_CANDIDATE; - std::vector conns_to_forget{kConnectionTwo}; - std::vector conns_to_prune{kConnectionThree}; - int recheck_delay_ms = 10; - IceControllerInterface::SwitchResult switch_result{ - kConnection, - IceRecheckEvent(IceSwitchReason::ICE_CONTROLLER_RECHECK, - recheck_delay_ms), - conns_to_forget}; - - Sequence sort_and_switch; - EXPECT_CALL(agent, UpdateConnectionStates()).InSequence(sort_and_switch); - EXPECT_CALL(*wrapped, SortAndSwitchConnection(reason)) - .InSequence(sort_and_switch) - .WillOnce(Return(switch_result)); - EXPECT_CALL(agent, SwitchSelectedConnection(kConnection, reason)) - .InSequence(sort_and_switch); - EXPECT_CALL(*wrapped, PruneConnections()) - .InSequence(sort_and_switch) - .WillOnce(Return(conns_to_prune)); - EXPECT_CALL(agent, PruneConnections(conns_to_prune)) - .InSequence(sort_and_switch); - - controller.OnImmediateSortAndSwitchRequest(reason); - - // No rechecks before recheck delay. - clock.AdvanceTime(TimeDelta::Millis(recheck_delay_ms - 1)); - - // ICE controller should recheck for best connection after the recheck delay. - Sequence recheck_sort; - EXPECT_CALL(agent, UpdateConnectionStates()).InSequence(recheck_sort); - EXPECT_CALL(*wrapped, - SortAndSwitchConnection(IceSwitchReason::ICE_CONTROLLER_RECHECK)) - .InSequence(recheck_sort) - .WillOnce(Return(IceControllerInterface::SwitchResult{})); - EXPECT_CALL(*wrapped, PruneConnections()) - .InSequence(recheck_sort) - .WillOnce(Return(kEmptyConnsList)); - EXPECT_CALL(agent, PruneConnections(kEmptyConnsList)) - .InSequence(recheck_sort); - - clock.AdvanceTime(kTick); -} - -TEST(WrappingActiveIceControllerTest, HandlesSortAndSwitchRequest) { - AutoThread main; - ScopedFakeClock clock; - - // Block the main task queue until ready. - Event init; - TimeDelta init_delay = TimeDelta::Millis(10); - main.PostTask([&init, &init_delay] { init.Wait(init_delay); }); - - NiceMock agent; - std::unique_ptr will_move = - std::make_unique(IceControllerFactoryArgs{}); - NiceMockIceController* wrapped = will_move.get(); - WrappingActiveIceController controller(&agent, std::move(will_move)); - - IceSwitchReason reason = IceSwitchReason::NETWORK_PREFERENCE_CHANGE; - - // No action should occur immediately - EXPECT_CALL(agent, UpdateConnectionStates()).Times(0); - EXPECT_CALL(*wrapped, SortAndSwitchConnection(_)).Times(0); - EXPECT_CALL(agent, SwitchSelectedConnection(_, _)).Times(0); - - controller.OnSortAndSwitchRequest(reason); - - std::vector conns_to_forget{kConnectionTwo}; - int recheck_delay_ms = 10; - IceControllerInterface::SwitchResult switch_result{ - kConnection, - IceRecheckEvent(IceSwitchReason::ICE_CONTROLLER_RECHECK, - recheck_delay_ms), - conns_to_forget}; - - // Sort and switch should take place as the subsequent task. - Sequence sort_and_switch; - EXPECT_CALL(agent, UpdateConnectionStates()).InSequence(sort_and_switch); - EXPECT_CALL(*wrapped, SortAndSwitchConnection(reason)) - .InSequence(sort_and_switch) - .WillOnce(Return(switch_result)); - EXPECT_CALL(agent, SwitchSelectedConnection(kConnection, reason)) - .InSequence(sort_and_switch); - - // Unblock the init task. - clock.AdvanceTime(init_delay); -} - -TEST(WrappingActiveIceControllerTest, StartPingingAfterSortAndSwitch) { - AutoThread main; - ScopedFakeClock clock; - - // Block the main task queue until ready. - Event init; - TimeDelta init_delay = TimeDelta::Millis(10); - main.PostTask([&init, &init_delay] { init.Wait(init_delay); }); - - NiceMock agent; - std::unique_ptr will_move = - std::make_unique(IceControllerFactoryArgs{}); - NiceMockIceController* wrapped = will_move.get(); - WrappingActiveIceController controller(&agent, std::move(will_move)); - - // Pinging does not start automatically, unless triggered through a sort. - EXPECT_CALL(*wrapped, HasPingableConnection()).Times(0); - EXPECT_CALL(*wrapped, SelectConnectionToPing(_)).Times(0); - EXPECT_CALL(agent, OnStartedPinging()).Times(0); - - controller.OnSortAndSwitchRequest(IceSwitchReason::DATA_RECEIVED); - - // Pinging does not start if no pingable connection. - EXPECT_CALL(*wrapped, HasPingableConnection()).WillOnce(Return(false)); - EXPECT_CALL(*wrapped, SelectConnectionToPing(_)).Times(0); - EXPECT_CALL(agent, OnStartedPinging()).Times(0); - - // Unblock the init task. - clock.AdvanceTime(init_delay); - - int recheck_delay_ms = 10; - IceControllerInterface::PingResult ping_result(kConnection, recheck_delay_ms); - - // Pinging starts when there is a pingable connection. - Sequence start_pinging; - EXPECT_CALL(*wrapped, HasPingableConnection()) - .InSequence(start_pinging) - .WillOnce(Return(true)); - EXPECT_CALL(agent, OnStartedPinging()).InSequence(start_pinging); - EXPECT_CALL(agent, GetLastPingSentMs()) - .InSequence(start_pinging) - .WillOnce(Return(123)); - EXPECT_CALL(*wrapped, SelectConnectionToPing(123)) - .InSequence(start_pinging) - .WillOnce(Return(ping_result)); - EXPECT_CALL(agent, SendPingRequest(kConnection)).InSequence(start_pinging); - - controller.OnSortAndSwitchRequest(IceSwitchReason::DATA_RECEIVED); - clock.AdvanceTime(kTick); - - // ICE controller should recheck and ping after the recheck delay. - // No ping should be sent if no connection selected to ping. - EXPECT_CALL(agent, GetLastPingSentMs()).WillOnce(Return(456)); - EXPECT_CALL(*wrapped, SelectConnectionToPing(456)) - .WillOnce(Return(IceControllerInterface::PingResult( - /* connection= */ nullptr, recheck_delay_ms))); - EXPECT_CALL(agent, SendPingRequest(kConnection)).Times(0); - - clock.AdvanceTime(TimeDelta::Millis(recheck_delay_ms)); -} - -} // namespace