• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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