1 /*
2 * Copyright 2022 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "p2p/base/wrapping_active_ice_controller.h"
12
13 #include <memory>
14 #include <utility>
15 #include <vector>
16
17 #include "api/sequence_checker.h"
18 #include "api/task_queue/pending_task_safety_flag.h"
19 #include "api/units/time_delta.h"
20 #include "p2p/base/basic_ice_controller.h"
21 #include "p2p/base/connection.h"
22 #include "p2p/base/ice_agent_interface.h"
23 #include "p2p/base/ice_controller_interface.h"
24 #include "p2p/base/ice_switch_reason.h"
25 #include "p2p/base/ice_transport_internal.h"
26 #include "p2p/base/transport_description.h"
27 #include "rtc_base/logging.h"
28 #include "rtc_base/thread.h"
29 #include "rtc_base/time_utils.h"
30
31 namespace {
32 using ::webrtc::SafeTask;
33 using ::webrtc::TimeDelta;
34 } // unnamed namespace
35
36 namespace cricket {
37
WrappingActiveIceController(IceAgentInterface * ice_agent,std::unique_ptr<IceControllerInterface> wrapped)38 WrappingActiveIceController::WrappingActiveIceController(
39 IceAgentInterface* ice_agent,
40 std::unique_ptr<IceControllerInterface> wrapped)
41 : network_thread_(rtc::Thread::Current()),
42 wrapped_(std::move(wrapped)),
43 agent_(*ice_agent) {
44 RTC_DCHECK(ice_agent != nullptr);
45 }
46
WrappingActiveIceController(IceAgentInterface * ice_agent,IceControllerFactoryInterface * wrapped_factory,const IceControllerFactoryArgs & wrapped_factory_args)47 WrappingActiveIceController::WrappingActiveIceController(
48 IceAgentInterface* ice_agent,
49 IceControllerFactoryInterface* wrapped_factory,
50 const IceControllerFactoryArgs& wrapped_factory_args)
51 : network_thread_(rtc::Thread::Current()), agent_(*ice_agent) {
52 RTC_DCHECK(ice_agent != nullptr);
53 if (wrapped_factory) {
54 wrapped_ = wrapped_factory->Create(wrapped_factory_args);
55 } else {
56 wrapped_ = std::make_unique<BasicIceController>(wrapped_factory_args);
57 }
58 }
59
~WrappingActiveIceController()60 WrappingActiveIceController::~WrappingActiveIceController() {}
61
SetIceConfig(const IceConfig & config)62 void WrappingActiveIceController::SetIceConfig(const IceConfig& config) {
63 RTC_DCHECK_RUN_ON(network_thread_);
64 wrapped_->SetIceConfig(config);
65 }
66
GetUseCandidateAttribute(const Connection * connection,NominationMode mode,IceMode remote_ice_mode) const67 bool WrappingActiveIceController::GetUseCandidateAttribute(
68 const Connection* connection,
69 NominationMode mode,
70 IceMode remote_ice_mode) const {
71 RTC_DCHECK_RUN_ON(network_thread_);
72 return wrapped_->GetUseCandidateAttr(connection, mode, remote_ice_mode);
73 }
74
OnConnectionAdded(const Connection * connection)75 void WrappingActiveIceController::OnConnectionAdded(
76 const Connection* connection) {
77 RTC_DCHECK_RUN_ON(network_thread_);
78 wrapped_->AddConnection(connection);
79 }
80
OnConnectionPinged(const Connection * connection)81 void WrappingActiveIceController::OnConnectionPinged(
82 const Connection* connection) {
83 RTC_DCHECK_RUN_ON(network_thread_);
84 wrapped_->MarkConnectionPinged(connection);
85 }
86
OnConnectionUpdated(const Connection * connection)87 void WrappingActiveIceController::OnConnectionUpdated(
88 const Connection* connection) {
89 RTC_LOG(LS_VERBOSE) << "Connection report for " << connection->ToString();
90 // Do nothing. Native ICE controllers have direct access to Connection, so no
91 // need to update connection state separately.
92 }
93
OnConnectionSwitched(const Connection * connection)94 void WrappingActiveIceController::OnConnectionSwitched(
95 const Connection* connection) {
96 RTC_DCHECK_RUN_ON(network_thread_);
97 selected_connection_ = connection;
98 wrapped_->SetSelectedConnection(connection);
99 }
100
OnConnectionDestroyed(const Connection * connection)101 void WrappingActiveIceController::OnConnectionDestroyed(
102 const Connection* connection) {
103 RTC_DCHECK_RUN_ON(network_thread_);
104 wrapped_->OnConnectionDestroyed(connection);
105 }
106
MaybeStartPinging()107 void WrappingActiveIceController::MaybeStartPinging() {
108 RTC_DCHECK_RUN_ON(network_thread_);
109 if (started_pinging_) {
110 return;
111 }
112
113 if (wrapped_->HasPingableConnection()) {
114 network_thread_->PostTask(
115 SafeTask(task_safety_.flag(), [this]() { SelectAndPingConnection(); }));
116 agent_.OnStartedPinging();
117 started_pinging_ = true;
118 }
119 }
120
SelectAndPingConnection()121 void WrappingActiveIceController::SelectAndPingConnection() {
122 RTC_DCHECK_RUN_ON(network_thread_);
123 agent_.UpdateConnectionStates();
124
125 IceControllerInterface::PingResult result =
126 wrapped_->SelectConnectionToPing(agent_.GetLastPingSentMs());
127 HandlePingResult(result);
128 }
129
HandlePingResult(IceControllerInterface::PingResult result)130 void WrappingActiveIceController::HandlePingResult(
131 IceControllerInterface::PingResult result) {
132 RTC_DCHECK_RUN_ON(network_thread_);
133
134 if (result.connection.has_value()) {
135 agent_.SendPingRequest(result.connection.value());
136 }
137
138 network_thread_->PostDelayedTask(
139 SafeTask(task_safety_.flag(), [this]() { SelectAndPingConnection(); }),
140 TimeDelta::Millis(result.recheck_delay_ms));
141 }
142
OnSortAndSwitchRequest(IceSwitchReason reason)143 void WrappingActiveIceController::OnSortAndSwitchRequest(
144 IceSwitchReason reason) {
145 RTC_DCHECK_RUN_ON(network_thread_);
146 if (!sort_pending_) {
147 network_thread_->PostTask(SafeTask(task_safety_.flag(), [this, reason]() {
148 SortAndSwitchToBestConnection(reason);
149 }));
150 sort_pending_ = true;
151 }
152 }
153
OnImmediateSortAndSwitchRequest(IceSwitchReason reason)154 void WrappingActiveIceController::OnImmediateSortAndSwitchRequest(
155 IceSwitchReason reason) {
156 RTC_DCHECK_RUN_ON(network_thread_);
157 SortAndSwitchToBestConnection(reason);
158 }
159
SortAndSwitchToBestConnection(IceSwitchReason reason)160 void WrappingActiveIceController::SortAndSwitchToBestConnection(
161 IceSwitchReason reason) {
162 RTC_DCHECK_RUN_ON(network_thread_);
163
164 // Make sure the connection states are up-to-date since this affects how they
165 // will be sorted.
166 agent_.UpdateConnectionStates();
167
168 // Any changes after this point will require a re-sort.
169 sort_pending_ = false;
170
171 IceControllerInterface::SwitchResult result =
172 wrapped_->SortAndSwitchConnection(reason);
173 HandleSwitchResult(reason, result);
174 UpdateStateOnConnectionsResorted();
175 }
176
OnImmediateSwitchRequest(IceSwitchReason reason,const Connection * selected)177 bool WrappingActiveIceController::OnImmediateSwitchRequest(
178 IceSwitchReason reason,
179 const Connection* selected) {
180 RTC_DCHECK_RUN_ON(network_thread_);
181 IceControllerInterface::SwitchResult result =
182 wrapped_->ShouldSwitchConnection(reason, selected);
183 HandleSwitchResult(reason, result);
184 return result.connection.has_value();
185 }
186
HandleSwitchResult(IceSwitchReason reason_for_switch,IceControllerInterface::SwitchResult result)187 void WrappingActiveIceController::HandleSwitchResult(
188 IceSwitchReason reason_for_switch,
189 IceControllerInterface::SwitchResult result) {
190 RTC_DCHECK_RUN_ON(network_thread_);
191 if (result.connection.has_value()) {
192 RTC_LOG(LS_INFO) << "Switching selected connection due to: "
193 << IceSwitchReasonToString(reason_for_switch);
194 agent_.SwitchSelectedConnection(result.connection.value(),
195 reason_for_switch);
196 }
197
198 if (result.recheck_event.has_value()) {
199 // If we do not switch to the connection because it missed the receiving
200 // threshold, the new connection is in a better receiving state than the
201 // currently selected connection. So we need to re-check whether it needs
202 // to be switched at a later time.
203 network_thread_->PostDelayedTask(
204 SafeTask(task_safety_.flag(),
205 [this, recheck_reason = result.recheck_event->reason]() {
206 SortAndSwitchToBestConnection(recheck_reason);
207 }),
208 TimeDelta::Millis(result.recheck_event->recheck_delay_ms));
209 }
210
211 agent_.ForgetLearnedStateForConnections(
212 result.connections_to_forget_state_on);
213 }
214
UpdateStateOnConnectionsResorted()215 void WrappingActiveIceController::UpdateStateOnConnectionsResorted() {
216 RTC_DCHECK_RUN_ON(network_thread_);
217 PruneConnections();
218
219 // Update the internal state of the ICE agentl.
220 agent_.UpdateState();
221
222 // Also possibly start pinging.
223 // We could start pinging if:
224 // * The first connection was created.
225 // * ICE credentials were provided.
226 // * A TCP connection became connected.
227 MaybeStartPinging();
228 }
229
PruneConnections()230 void WrappingActiveIceController::PruneConnections() {
231 RTC_DCHECK_RUN_ON(network_thread_);
232
233 // The controlled side can prune only if the selected connection has been
234 // nominated because otherwise it may prune the connection that will be
235 // selected by the controlling side.
236 // TODO(honghaiz): This is not enough to prevent a connection from being
237 // pruned too early because with aggressive nomination, the controlling side
238 // will nominate every connection until it becomes writable.
239 if (agent_.GetIceRole() == ICEROLE_CONTROLLING ||
240 (selected_connection_ && selected_connection_->nominated())) {
241 std::vector<const Connection*> connections_to_prune =
242 wrapped_->PruneConnections();
243 agent_.PruneConnections(connections_to_prune);
244 }
245 }
246
247 // Only for unit tests
FindNextPingableConnection()248 const Connection* WrappingActiveIceController::FindNextPingableConnection() {
249 RTC_DCHECK_RUN_ON(network_thread_);
250 return wrapped_->FindNextPingableConnection();
251 }
252
253 } // namespace cricket
254