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