• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright 2019 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/basic_ice_controller.h"
12 
13 namespace {
14 
15 // The minimum improvement in RTT that justifies a switch.
16 const int kMinImprovement = 10;
17 
IsRelayRelay(const cricket::Connection * conn)18 bool IsRelayRelay(const cricket::Connection* conn) {
19   return conn->local_candidate().type() == cricket::RELAY_PORT_TYPE &&
20          conn->remote_candidate().type() == cricket::RELAY_PORT_TYPE;
21 }
22 
IsUdp(const cricket::Connection * conn)23 bool IsUdp(const cricket::Connection* conn) {
24   return conn->local_candidate().relay_protocol() == cricket::UDP_PROTOCOL_NAME;
25 }
26 
27 // TODO(qingsi) Use an enum to replace the following constants for all
28 // comparision results.
29 static constexpr int a_is_better = 1;
30 static constexpr int b_is_better = -1;
31 static constexpr int a_and_b_equal = 0;
32 
LocalCandidateUsesPreferredNetwork(const cricket::Connection * conn,absl::optional<rtc::AdapterType> network_preference)33 bool LocalCandidateUsesPreferredNetwork(
34     const cricket::Connection* conn,
35     absl::optional<rtc::AdapterType> network_preference) {
36   rtc::AdapterType network_type = conn->network()->type();
37   return network_preference.has_value() && (network_type == network_preference);
38 }
39 
CompareCandidatePairsByNetworkPreference(const cricket::Connection * a,const cricket::Connection * b,absl::optional<rtc::AdapterType> network_preference)40 int CompareCandidatePairsByNetworkPreference(
41     const cricket::Connection* a,
42     const cricket::Connection* b,
43     absl::optional<rtc::AdapterType> network_preference) {
44   bool a_uses_preferred_network =
45       LocalCandidateUsesPreferredNetwork(a, network_preference);
46   bool b_uses_preferred_network =
47       LocalCandidateUsesPreferredNetwork(b, network_preference);
48   if (a_uses_preferred_network && !b_uses_preferred_network) {
49     return a_is_better;
50   } else if (!a_uses_preferred_network && b_uses_preferred_network) {
51     return b_is_better;
52   }
53   return a_and_b_equal;
54 }
55 
56 }  // namespace
57 
58 namespace cricket {
59 
BasicIceController(const IceControllerFactoryArgs & args)60 BasicIceController::BasicIceController(const IceControllerFactoryArgs& args)
61     : ice_transport_state_func_(args.ice_transport_state_func),
62       ice_role_func_(args.ice_role_func),
63       is_connection_pruned_func_(args.is_connection_pruned_func),
64       field_trials_(args.ice_field_trials) {}
65 
~BasicIceController()66 BasicIceController::~BasicIceController() {}
67 
SetIceConfig(const IceConfig & config)68 void BasicIceController::SetIceConfig(const IceConfig& config) {
69   config_ = config;
70 }
71 
SetSelectedConnection(const Connection * selected_connection)72 void BasicIceController::SetSelectedConnection(
73     const Connection* selected_connection) {
74   selected_connection_ = selected_connection;
75 }
76 
AddConnection(const Connection * connection)77 void BasicIceController::AddConnection(const Connection* connection) {
78   connections_.push_back(connection);
79   unpinged_connections_.insert(connection);
80 }
81 
OnConnectionDestroyed(const Connection * connection)82 void BasicIceController::OnConnectionDestroyed(const Connection* connection) {
83   pinged_connections_.erase(connection);
84   unpinged_connections_.erase(connection);
85   connections_.erase(absl::c_find(connections_, connection));
86 }
87 
HasPingableConnection() const88 bool BasicIceController::HasPingableConnection() const {
89   int64_t now = rtc::TimeMillis();
90   return absl::c_any_of(connections_, [this, now](const Connection* c) {
91     return IsPingable(c, now);
92   });
93 }
94 
SelectConnectionToPing(int64_t last_ping_sent_ms)95 IceControllerInterface::PingResult BasicIceController::SelectConnectionToPing(
96     int64_t last_ping_sent_ms) {
97   // When the selected connection is not receiving or not writable, or any
98   // active connection has not been pinged enough times, use the weak ping
99   // interval.
100   bool need_more_pings_at_weak_interval =
101       absl::c_any_of(connections_, [](const Connection* conn) {
102         return conn->active() &&
103                conn->num_pings_sent() < MIN_PINGS_AT_WEAK_PING_INTERVAL;
104       });
105   int ping_interval = (weak() || need_more_pings_at_weak_interval)
106                           ? weak_ping_interval()
107                           : strong_ping_interval();
108 
109   const Connection* conn = nullptr;
110   if (rtc::TimeMillis() >= last_ping_sent_ms + ping_interval) {
111     conn = FindNextPingableConnection();
112   }
113   PingResult res(conn, std::min(ping_interval, check_receiving_interval()));
114   return res;
115 }
116 
MarkConnectionPinged(const Connection * conn)117 void BasicIceController::MarkConnectionPinged(const Connection* conn) {
118   if (conn && pinged_connections_.insert(conn).second) {
119     unpinged_connections_.erase(conn);
120   }
121 }
122 
123 // Returns the next pingable connection to ping.
FindNextPingableConnection()124 const Connection* BasicIceController::FindNextPingableConnection() {
125   int64_t now = rtc::TimeMillis();
126 
127   // Rule 1: Selected connection takes priority over non-selected ones.
128   if (selected_connection_ && selected_connection_->connected() &&
129       selected_connection_->writable() &&
130       WritableConnectionPastPingInterval(selected_connection_, now)) {
131     return selected_connection_;
132   }
133 
134   // Rule 2: If the channel is weak, we need to find a new writable and
135   // receiving connection, probably on a different network. If there are lots of
136   // connections, it may take several seconds between two pings for every
137   // non-selected connection. This will cause the receiving state of those
138   // connections to be false, and thus they won't be selected. This is
139   // problematic for network fail-over. We want to make sure at least one
140   // connection per network is pinged frequently enough in order for it to be
141   // selectable. So we prioritize one connection per network.
142   // Rule 2.1: Among such connections, pick the one with the earliest
143   // last-ping-sent time.
144   if (weak()) {
145     std::vector<const Connection*> pingable_selectable_connections;
146     absl::c_copy_if(GetBestWritableConnectionPerNetwork(),
147                     std::back_inserter(pingable_selectable_connections),
148                     [this, now](const Connection* conn) {
149                       return WritableConnectionPastPingInterval(conn, now);
150                     });
151     auto iter = absl::c_min_element(
152         pingable_selectable_connections,
153         [](const Connection* conn1, const Connection* conn2) {
154           return conn1->last_ping_sent() < conn2->last_ping_sent();
155         });
156     if (iter != pingable_selectable_connections.end()) {
157       return *iter;
158     }
159   }
160 
161   // Rule 3: Triggered checks have priority over non-triggered connections.
162   // Rule 3.1: Among triggered checks, oldest takes precedence.
163   const Connection* oldest_triggered_check =
164       FindOldestConnectionNeedingTriggeredCheck(now);
165   if (oldest_triggered_check) {
166     return oldest_triggered_check;
167   }
168 
169   // Rule 4: Unpinged connections have priority over pinged ones.
170   RTC_CHECK(connections_.size() ==
171             pinged_connections_.size() + unpinged_connections_.size());
172   // If there are unpinged and pingable connections, only ping those.
173   // Otherwise, treat everything as unpinged.
174   // TODO(honghaiz): Instead of adding two separate vectors, we can add a state
175   // "pinged" to filter out unpinged connections.
176   if (absl::c_none_of(unpinged_connections_,
177                       [this, now](const Connection* conn) {
178                         return this->IsPingable(conn, now);
179                       })) {
180     unpinged_connections_.insert(pinged_connections_.begin(),
181                                  pinged_connections_.end());
182     pinged_connections_.clear();
183   }
184 
185   // Among un-pinged pingable connections, "more pingable" takes precedence.
186   std::vector<const Connection*> pingable_connections;
187   absl::c_copy_if(
188       unpinged_connections_, std::back_inserter(pingable_connections),
189       [this, now](const Connection* conn) { return IsPingable(conn, now); });
190   auto iter = absl::c_max_element(
191       pingable_connections,
192       [this](const Connection* conn1, const Connection* conn2) {
193         // Some implementations of max_element
194         // compare an element with itself.
195         if (conn1 == conn2) {
196           return false;
197         }
198         return MorePingable(conn1, conn2) == conn2;
199       });
200   if (iter != pingable_connections.end()) {
201     return *iter;
202   }
203   return nullptr;
204 }
205 
206 // Find "triggered checks".  We ping first those connections that have
207 // received a ping but have not sent a ping since receiving it
208 // (last_ping_received > last_ping_sent).  But we shouldn't do
209 // triggered checks if the connection is already writable.
FindOldestConnectionNeedingTriggeredCheck(int64_t now)210 const Connection* BasicIceController::FindOldestConnectionNeedingTriggeredCheck(
211     int64_t now) {
212   const Connection* oldest_needing_triggered_check = nullptr;
213   for (auto* conn : connections_) {
214     if (!IsPingable(conn, now)) {
215       continue;
216     }
217     bool needs_triggered_check =
218         (!conn->writable() &&
219          conn->last_ping_received() > conn->last_ping_sent());
220     if (needs_triggered_check &&
221         (!oldest_needing_triggered_check ||
222          (conn->last_ping_received() <
223           oldest_needing_triggered_check->last_ping_received()))) {
224       oldest_needing_triggered_check = conn;
225     }
226   }
227 
228   if (oldest_needing_triggered_check) {
229     RTC_LOG(LS_INFO) << "Selecting connection for triggered check: "
230                      << oldest_needing_triggered_check->ToString();
231   }
232   return oldest_needing_triggered_check;
233 }
234 
WritableConnectionPastPingInterval(const Connection * conn,int64_t now) const235 bool BasicIceController::WritableConnectionPastPingInterval(
236     const Connection* conn,
237     int64_t now) const {
238   int interval = CalculateActiveWritablePingInterval(conn, now);
239   return conn->last_ping_sent() + interval <= now;
240 }
241 
CalculateActiveWritablePingInterval(const Connection * conn,int64_t now) const242 int BasicIceController::CalculateActiveWritablePingInterval(
243     const Connection* conn,
244     int64_t now) const {
245   // Ping each connection at a higher rate at least
246   // MIN_PINGS_AT_WEAK_PING_INTERVAL times.
247   if (conn->num_pings_sent() < MIN_PINGS_AT_WEAK_PING_INTERVAL) {
248     return weak_ping_interval();
249   }
250 
251   int stable_interval =
252       config_.stable_writable_connection_ping_interval_or_default();
253   int weak_or_stablizing_interval = std::min(
254       stable_interval, WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL);
255   // If the channel is weak or the connection is not stable yet, use the
256   // weak_or_stablizing_interval.
257   return (!weak() && conn->stable(now)) ? stable_interval
258                                         : weak_or_stablizing_interval;
259 }
260 
261 // Is the connection in a state for us to even consider pinging the other side?
262 // We consider a connection pingable even if it's not connected because that's
263 // how a TCP connection is kicked into reconnecting on the active side.
IsPingable(const Connection * conn,int64_t now) const264 bool BasicIceController::IsPingable(const Connection* conn, int64_t now) const {
265   const Candidate& remote = conn->remote_candidate();
266   // We should never get this far with an empty remote ufrag.
267   RTC_DCHECK(!remote.username().empty());
268   if (remote.username().empty() || remote.password().empty()) {
269     // If we don't have an ICE ufrag and pwd, there's no way we can ping.
270     return false;
271   }
272 
273   // A failed connection will not be pinged.
274   if (conn->state() == IceCandidatePairState::FAILED) {
275     return false;
276   }
277 
278   // An never connected connection cannot be written to at all, so pinging is
279   // out of the question. However, if it has become WRITABLE, it is in the
280   // reconnecting state so ping is needed.
281   if (!conn->connected() && !conn->writable()) {
282     return false;
283   }
284 
285   // If we sent a number of pings wo/ reply, skip sending more
286   // until we get one.
287   if (conn->TooManyOutstandingPings(field_trials_->max_outstanding_pings)) {
288     return false;
289   }
290 
291   // If the channel is weakly connected, ping all connections.
292   if (weak()) {
293     return true;
294   }
295 
296   // Always ping active connections regardless whether the channel is completed
297   // or not, but backup connections are pinged at a slower rate.
298   if (IsBackupConnection(conn)) {
299     return conn->rtt_samples() == 0 ||
300            (now >= conn->last_ping_response_received() +
301                        config_.backup_connection_ping_interval_or_default());
302   }
303   // Don't ping inactive non-backup connections.
304   if (!conn->active()) {
305     return false;
306   }
307 
308   // Do ping unwritable, active connections.
309   if (!conn->writable()) {
310     return true;
311   }
312 
313   // Ping writable, active connections if it's been long enough since the last
314   // ping.
315   return WritableConnectionPastPingInterval(conn, now);
316 }
317 
318 // A connection is considered a backup connection if the channel state
319 // is completed, the connection is not the selected connection and it is active.
IsBackupConnection(const Connection * conn) const320 bool BasicIceController::IsBackupConnection(const Connection* conn) const {
321   return ice_transport_state_func_() == IceTransportState::STATE_COMPLETED &&
322          conn != selected_connection_ && conn->active();
323 }
324 
MorePingable(const Connection * conn1,const Connection * conn2)325 const Connection* BasicIceController::MorePingable(const Connection* conn1,
326                                                    const Connection* conn2) {
327   RTC_DCHECK(conn1 != conn2);
328   if (config_.prioritize_most_likely_candidate_pairs) {
329     const Connection* most_likely_to_work_conn = MostLikelyToWork(conn1, conn2);
330     if (most_likely_to_work_conn) {
331       return most_likely_to_work_conn;
332     }
333   }
334 
335   const Connection* least_recently_pinged_conn =
336       LeastRecentlyPinged(conn1, conn2);
337   if (least_recently_pinged_conn) {
338     return least_recently_pinged_conn;
339   }
340 
341   // During the initial state when nothing has been pinged yet, return the first
342   // one in the ordered |connections_|.
343   auto connections = connections_;
344   return *(std::find_if(connections.begin(), connections.end(),
345                         [conn1, conn2](const Connection* conn) {
346                           return conn == conn1 || conn == conn2;
347                         }));
348 }
349 
MostLikelyToWork(const Connection * conn1,const Connection * conn2)350 const Connection* BasicIceController::MostLikelyToWork(
351     const Connection* conn1,
352     const Connection* conn2) {
353   bool rr1 = IsRelayRelay(conn1);
354   bool rr2 = IsRelayRelay(conn2);
355   if (rr1 && !rr2) {
356     return conn1;
357   } else if (rr2 && !rr1) {
358     return conn2;
359   } else if (rr1 && rr2) {
360     bool udp1 = IsUdp(conn1);
361     bool udp2 = IsUdp(conn2);
362     if (udp1 && !udp2) {
363       return conn1;
364     } else if (udp2 && udp1) {
365       return conn2;
366     }
367   }
368   return nullptr;
369 }
370 
LeastRecentlyPinged(const Connection * conn1,const Connection * conn2)371 const Connection* BasicIceController::LeastRecentlyPinged(
372     const Connection* conn1,
373     const Connection* conn2) {
374   if (conn1->last_ping_sent() < conn2->last_ping_sent()) {
375     return conn1;
376   }
377   if (conn1->last_ping_sent() > conn2->last_ping_sent()) {
378     return conn2;
379   }
380   return nullptr;
381 }
382 
383 std::map<const rtc::Network*, const Connection*>
GetBestConnectionByNetwork() const384 BasicIceController::GetBestConnectionByNetwork() const {
385   // |connections_| has been sorted, so the first one in the list on a given
386   // network is the best connection on the network, except that the selected
387   // connection is always the best connection on the network.
388   std::map<const rtc::Network*, const Connection*> best_connection_by_network;
389   if (selected_connection_) {
390     best_connection_by_network[selected_connection_->network()] =
391         selected_connection_;
392   }
393   // TODO(honghaiz): Need to update this if |connections_| are not sorted.
394   for (const Connection* conn : connections_) {
395     const rtc::Network* network = conn->network();
396     // This only inserts when the network does not exist in the map.
397     best_connection_by_network.insert(std::make_pair(network, conn));
398   }
399   return best_connection_by_network;
400 }
401 
402 std::vector<const Connection*>
GetBestWritableConnectionPerNetwork() const403 BasicIceController::GetBestWritableConnectionPerNetwork() const {
404   std::vector<const Connection*> connections;
405   for (auto kv : GetBestConnectionByNetwork()) {
406     const Connection* conn = kv.second;
407     if (conn->writable() && conn->connected()) {
408       connections.push_back(conn);
409     }
410   }
411   return connections;
412 }
413 
414 IceControllerInterface::SwitchResult
HandleInitialSelectDampening(IceControllerEvent reason,const Connection * new_connection)415 BasicIceController::HandleInitialSelectDampening(
416     IceControllerEvent reason,
417     const Connection* new_connection) {
418   if (!field_trials_->initial_select_dampening.has_value() &&
419       !field_trials_->initial_select_dampening_ping_received.has_value()) {
420     // experiment not enabled => select connection.
421     return {new_connection, absl::nullopt};
422   }
423 
424   int64_t now = rtc::TimeMillis();
425   int64_t max_delay = 0;
426   if (new_connection->last_ping_received() > 0 &&
427       field_trials_->initial_select_dampening_ping_received.has_value()) {
428     max_delay = *field_trials_->initial_select_dampening_ping_received;
429   } else if (field_trials_->initial_select_dampening.has_value()) {
430     max_delay = *field_trials_->initial_select_dampening;
431   }
432 
433   int64_t start_wait =
434       initial_select_timestamp_ms_ == 0 ? now : initial_select_timestamp_ms_;
435   int64_t max_wait_until = start_wait + max_delay;
436 
437   if (now >= max_wait_until) {
438     RTC_LOG(LS_INFO) << "reset initial_select_timestamp_ = "
439                      << initial_select_timestamp_ms_
440                      << " selection delayed by: " << (now - start_wait) << "ms";
441     initial_select_timestamp_ms_ = 0;
442     return {new_connection, absl::nullopt};
443   }
444 
445   // We are not yet ready to select first connection...
446   if (initial_select_timestamp_ms_ == 0) {
447     // Set timestamp on first time...
448     // but run the delayed invokation everytime to
449     // avoid possibility that we miss it.
450     initial_select_timestamp_ms_ = now;
451     RTC_LOG(LS_INFO) << "set initial_select_timestamp_ms_ = "
452                      << initial_select_timestamp_ms_;
453   }
454 
455   int min_delay = max_delay;
456   if (field_trials_->initial_select_dampening.has_value()) {
457     min_delay = std::min(min_delay, *field_trials_->initial_select_dampening);
458   }
459   if (field_trials_->initial_select_dampening_ping_received.has_value()) {
460     min_delay = std::min(
461         min_delay, *field_trials_->initial_select_dampening_ping_received);
462   }
463 
464   RTC_LOG(LS_INFO) << "delay initial selection up to " << min_delay << "ms";
465   reason.type = IceControllerEvent::ICE_CONTROLLER_RECHECK;
466   reason.recheck_delay_ms = min_delay;
467   return {absl::nullopt, reason};
468 }
469 
ShouldSwitchConnection(IceControllerEvent reason,const Connection * new_connection)470 IceControllerInterface::SwitchResult BasicIceController::ShouldSwitchConnection(
471     IceControllerEvent reason,
472     const Connection* new_connection) {
473   if (!ReadyToSend(new_connection) || selected_connection_ == new_connection) {
474     return {absl::nullopt, absl::nullopt};
475   }
476 
477   if (selected_connection_ == nullptr) {
478     return HandleInitialSelectDampening(reason, new_connection);
479   }
480 
481   // Do not switch to a connection that is not receiving if it is not on a
482   // preferred network or it has higher cost because it may be just spuriously
483   // better.
484   int compare_a_b_by_networks = CompareCandidatePairNetworks(
485       new_connection, selected_connection_, config_.network_preference);
486   if (compare_a_b_by_networks == b_is_better && !new_connection->receiving()) {
487     return {absl::nullopt, absl::nullopt};
488   }
489 
490   bool missed_receiving_unchanged_threshold = false;
491   absl::optional<int64_t> receiving_unchanged_threshold(
492       rtc::TimeMillis() - config_.receiving_switching_delay_or_default());
493   int cmp = CompareConnections(selected_connection_, new_connection,
494                                receiving_unchanged_threshold,
495                                &missed_receiving_unchanged_threshold);
496 
497   absl::optional<IceControllerEvent> recheck_event;
498   if (missed_receiving_unchanged_threshold &&
499       config_.receiving_switching_delay_or_default()) {
500     // If we do not switch to the connection because it missed the receiving
501     // threshold, the new connection is in a better receiving state than the
502     // currently selected connection. So we need to re-check whether it needs
503     // to be switched at a later time.
504     recheck_event = reason;
505     recheck_event->recheck_delay_ms =
506         config_.receiving_switching_delay_or_default();
507   }
508 
509   if (cmp < 0) {
510     return {new_connection, absl::nullopt};
511   } else if (cmp > 0) {
512     return {absl::nullopt, recheck_event};
513   }
514 
515   // If everything else is the same, switch only if rtt has improved by
516   // a margin.
517   if (new_connection->rtt() <= selected_connection_->rtt() - kMinImprovement) {
518     return {new_connection, absl::nullopt};
519   }
520 
521   return {absl::nullopt, recheck_event};
522 }
523 
524 IceControllerInterface::SwitchResult
SortAndSwitchConnection(IceControllerEvent reason)525 BasicIceController::SortAndSwitchConnection(IceControllerEvent reason) {
526   // Find the best alternative connection by sorting.  It is important to note
527   // that amongst equal preference, writable connections, this will choose the
528   // one whose estimated latency is lowest.  So it is the only one that we
529   // need to consider switching to.
530   // TODO(honghaiz): Don't sort;  Just use std::max_element in the right places.
531   absl::c_stable_sort(
532       connections_, [this](const Connection* a, const Connection* b) {
533         int cmp = CompareConnections(a, b, absl::nullopt, nullptr);
534         if (cmp != 0) {
535           return cmp > 0;
536         }
537         // Otherwise, sort based on latency estimate.
538         return a->rtt() < b->rtt();
539       });
540 
541   RTC_LOG(LS_VERBOSE) << "Sorting " << connections_.size()
542                       << " available connections";
543   for (size_t i = 0; i < connections_.size(); ++i) {
544     RTC_LOG(LS_VERBOSE) << connections_[i]->ToString();
545   }
546 
547   const Connection* top_connection =
548       (!connections_.empty()) ? connections_[0] : nullptr;
549 
550   return ShouldSwitchConnection(reason, top_connection);
551 }
552 
ReadyToSend(const Connection * connection) const553 bool BasicIceController::ReadyToSend(const Connection* connection) const {
554   // Note that we allow sending on an unreliable connection, because it's
555   // possible that it became unreliable simply due to bad chance.
556   // So this shouldn't prevent attempting to send media.
557   return connection != nullptr &&
558          (connection->writable() ||
559           connection->write_state() == Connection::STATE_WRITE_UNRELIABLE ||
560           PresumedWritable(connection));
561 }
562 
PresumedWritable(const Connection * conn) const563 bool BasicIceController::PresumedWritable(const Connection* conn) const {
564   return (conn->write_state() == Connection::STATE_WRITE_INIT &&
565           config_.presume_writable_when_fully_relayed &&
566           conn->local_candidate().type() == RELAY_PORT_TYPE &&
567           (conn->remote_candidate().type() == RELAY_PORT_TYPE ||
568            conn->remote_candidate().type() == PRFLX_PORT_TYPE));
569 }
570 
571 // Compare two connections based on their writing, receiving, and connected
572 // states.
CompareConnectionStates(const Connection * a,const Connection * b,absl::optional<int64_t> receiving_unchanged_threshold,bool * missed_receiving_unchanged_threshold) const573 int BasicIceController::CompareConnectionStates(
574     const Connection* a,
575     const Connection* b,
576     absl::optional<int64_t> receiving_unchanged_threshold,
577     bool* missed_receiving_unchanged_threshold) const {
578   // First, prefer a connection that's writable or presumed writable over
579   // one that's not writable.
580   bool a_writable = a->writable() || PresumedWritable(a);
581   bool b_writable = b->writable() || PresumedWritable(b);
582   if (a_writable && !b_writable) {
583     return a_is_better;
584   }
585   if (!a_writable && b_writable) {
586     return b_is_better;
587   }
588 
589   // Sort based on write-state. Better states have lower values.
590   if (a->write_state() < b->write_state()) {
591     return a_is_better;
592   }
593   if (b->write_state() < a->write_state()) {
594     return b_is_better;
595   }
596 
597   // We prefer a receiving connection to a non-receiving, higher-priority
598   // connection when sorting connections and choosing which connection to
599   // switch to.
600   if (a->receiving() && !b->receiving()) {
601     return a_is_better;
602   }
603   if (!a->receiving() && b->receiving()) {
604     if (!receiving_unchanged_threshold ||
605         (a->receiving_unchanged_since() <= *receiving_unchanged_threshold &&
606          b->receiving_unchanged_since() <= *receiving_unchanged_threshold)) {
607       return b_is_better;
608     }
609     *missed_receiving_unchanged_threshold = true;
610   }
611 
612   // WARNING: Some complexity here about TCP reconnecting.
613   // When a TCP connection fails because of a TCP socket disconnecting, the
614   // active side of the connection will attempt to reconnect for 5 seconds while
615   // pretending to be writable (the connection is not set to the unwritable
616   // state).  On the passive side, the connection also remains writable even
617   // though it is disconnected, and a new connection is created when the active
618   // side connects.  At that point, there are two TCP connections on the passive
619   // side: 1. the old, disconnected one that is pretending to be writable, and
620   // 2.  the new, connected one that is maybe not yet writable.  For purposes of
621   // pruning, pinging, and selecting the selected connection, we want to treat
622   // the new connection as "better" than the old one. We could add a method
623   // called something like Connection::ImReallyBadEvenThoughImWritable, but that
624   // is equivalent to the existing Connection::connected(), which we already
625   // have. So, in code throughout this file, we'll check whether the connection
626   // is connected() or not, and if it is not, treat it as "worse" than a
627   // connected one, even though it's writable.  In the code below, we're doing
628   // so to make sure we treat a new writable connection as better than an old
629   // disconnected connection.
630 
631   // In the case where we reconnect TCP connections, the original best
632   // connection is disconnected without changing to WRITE_TIMEOUT. In this case,
633   // the new connection, when it becomes writable, should have higher priority.
634   if (a->write_state() == Connection::STATE_WRITABLE &&
635       b->write_state() == Connection::STATE_WRITABLE) {
636     if (a->connected() && !b->connected()) {
637       return a_is_better;
638     }
639     if (!a->connected() && b->connected()) {
640       return b_is_better;
641     }
642   }
643 
644   return 0;
645 }
646 
647 // Compares two connections based only on the candidate and network information.
648 // Returns positive if |a| is better than |b|.
CompareConnectionCandidates(const Connection * a,const Connection * b) const649 int BasicIceController::CompareConnectionCandidates(const Connection* a,
650                                                     const Connection* b) const {
651   int compare_a_b_by_networks =
652       CompareCandidatePairNetworks(a, b, config_.network_preference);
653   if (compare_a_b_by_networks != a_and_b_equal) {
654     return compare_a_b_by_networks;
655   }
656 
657   // Compare connection priority. Lower values get sorted last.
658   if (a->priority() > b->priority()) {
659     return a_is_better;
660   }
661   if (a->priority() < b->priority()) {
662     return b_is_better;
663   }
664 
665   // If we're still tied at this point, prefer a younger generation.
666   // (Younger generation means a larger generation number).
667   int cmp = (a->remote_candidate().generation() + a->generation()) -
668             (b->remote_candidate().generation() + b->generation());
669   if (cmp != 0) {
670     return cmp;
671   }
672 
673   // A periodic regather (triggered by the regather_all_networks_interval_range)
674   // will produce candidates that appear the same but would use a new port. We
675   // want to use the new candidates and purge the old candidates as they come
676   // in, so use the fact that the old ports get pruned immediately to rank the
677   // candidates with an active port/remote candidate higher.
678   bool a_pruned = is_connection_pruned_func_(a);
679   bool b_pruned = is_connection_pruned_func_(b);
680   if (!a_pruned && b_pruned) {
681     return a_is_better;
682   }
683   if (a_pruned && !b_pruned) {
684     return b_is_better;
685   }
686 
687   // Otherwise, must be equal
688   return 0;
689 }
690 
CompareConnections(const Connection * a,const Connection * b,absl::optional<int64_t> receiving_unchanged_threshold,bool * missed_receiving_unchanged_threshold) const691 int BasicIceController::CompareConnections(
692     const Connection* a,
693     const Connection* b,
694     absl::optional<int64_t> receiving_unchanged_threshold,
695     bool* missed_receiving_unchanged_threshold) const {
696   RTC_CHECK(a != nullptr);
697   RTC_CHECK(b != nullptr);
698 
699   // We prefer to switch to a writable and receiving connection over a
700   // non-writable or non-receiving connection, even if the latter has
701   // been nominated by the controlling side.
702   int state_cmp = CompareConnectionStates(a, b, receiving_unchanged_threshold,
703                                           missed_receiving_unchanged_threshold);
704   if (state_cmp != 0) {
705     return state_cmp;
706   }
707 
708   if (ice_role_func_() == ICEROLE_CONTROLLED) {
709     // Compare the connections based on the nomination states and the last data
710     // received time if this is on the controlled side.
711     if (a->remote_nomination() > b->remote_nomination()) {
712       return a_is_better;
713     }
714     if (a->remote_nomination() < b->remote_nomination()) {
715       return b_is_better;
716     }
717 
718     if (a->last_data_received() > b->last_data_received()) {
719       return a_is_better;
720     }
721     if (a->last_data_received() < b->last_data_received()) {
722       return b_is_better;
723     }
724   }
725 
726   // Compare the network cost and priority.
727   return CompareConnectionCandidates(a, b);
728 }
729 
CompareCandidatePairNetworks(const Connection * a,const Connection * b,absl::optional<rtc::AdapterType> network_preference) const730 int BasicIceController::CompareCandidatePairNetworks(
731     const Connection* a,
732     const Connection* b,
733     absl::optional<rtc::AdapterType> network_preference) const {
734   int compare_a_b_by_network_preference =
735       CompareCandidatePairsByNetworkPreference(a, b,
736                                                config_.network_preference);
737   // The network preference has a higher precedence than the network cost.
738   if (compare_a_b_by_network_preference != a_and_b_equal) {
739     return compare_a_b_by_network_preference;
740   }
741 
742   uint32_t a_cost = a->ComputeNetworkCost();
743   uint32_t b_cost = b->ComputeNetworkCost();
744   // Prefer lower network cost.
745   if (a_cost < b_cost) {
746     return a_is_better;
747   }
748   if (a_cost > b_cost) {
749     return b_is_better;
750   }
751   return a_and_b_equal;
752 }
753 
PruneConnections()754 std::vector<const Connection*> BasicIceController::PruneConnections() {
755   // We can prune any connection for which there is a connected, writable
756   // connection on the same network with better or equal priority.  We leave
757   // those with better priority just in case they become writable later (at
758   // which point, we would prune out the current selected connection).  We leave
759   // connections on other networks because they may not be using the same
760   // resources and they may represent very distinct paths over which we can
761   // switch. If |best_conn_on_network| is not connected, we may be reconnecting
762   // a TCP connection and should not prune connections in this network.
763   // See the big comment in CompareConnectionStates.
764   //
765   // An exception is made for connections on an "any address" network, meaning
766   // not bound to any specific network interface. We don't want to keep one of
767   // these alive as a backup, since it could be using the same network
768   // interface as the higher-priority, selected candidate pair.
769   std::vector<const Connection*> connections_to_prune;
770   auto best_connection_by_network = GetBestConnectionByNetwork();
771   for (const Connection* conn : connections_) {
772     const Connection* best_conn = selected_connection_;
773     if (!rtc::IPIsAny(conn->network()->ip())) {
774       // If the connection is bound to a specific network interface (not an
775       // "any address" network), compare it against the best connection for
776       // that network interface rather than the best connection overall. This
777       // ensures that at least one connection per network will be left
778       // unpruned.
779       best_conn = best_connection_by_network[conn->network()];
780     }
781     // Do not prune connections if the connection being compared against is
782     // weak. Otherwise, it may delete connections prematurely.
783     if (best_conn && conn != best_conn && !best_conn->weak() &&
784         CompareConnectionCandidates(best_conn, conn) >= 0) {
785       connections_to_prune.push_back(conn);
786     }
787   }
788   return connections_to_prune;
789 }
790 
GetUseCandidateAttr(const Connection * conn,NominationMode mode,IceMode remote_ice_mode) const791 bool BasicIceController::GetUseCandidateAttr(const Connection* conn,
792                                              NominationMode mode,
793                                              IceMode remote_ice_mode) const {
794   switch (mode) {
795     case NominationMode::REGULAR:
796       // TODO(honghaiz): Implement regular nomination.
797       return false;
798     case NominationMode::AGGRESSIVE:
799       if (remote_ice_mode == ICEMODE_LITE) {
800         return GetUseCandidateAttr(conn, NominationMode::REGULAR,
801                                    remote_ice_mode);
802       }
803       return true;
804     case NominationMode::SEMI_AGGRESSIVE: {
805       // Nominate if
806       // a) Remote is in FULL ICE AND
807       //    a.1) |conn| is the selected connection OR
808       //    a.2) there is no selected connection OR
809       //    a.3) the selected connection is unwritable OR
810       //    a.4) |conn| has higher priority than selected_connection.
811       // b) Remote is in LITE ICE AND
812       //    b.1) |conn| is the selected_connection AND
813       //    b.2) |conn| is writable.
814       bool selected = conn == selected_connection_;
815       if (remote_ice_mode == ICEMODE_LITE) {
816         return selected && conn->writable();
817       }
818       bool better_than_selected =
819           !selected_connection_ || !selected_connection_->writable() ||
820           CompareConnectionCandidates(selected_connection_, conn) < 0;
821       return selected || better_than_selected;
822     }
823     default:
824       RTC_NOTREACHED();
825       return false;
826   }
827 }
828 
829 }  // namespace cricket
830