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