From 9a5c6f8f3fc8adefc69809c712e0466586f2a2d8 Mon Sep 17 00:00:00 2001 From: Qingsi Wang Date: Thu, 1 Feb 2018 10:38:40 -0800 Subject: [PATCH] Add the network preference to RTCConfiguration. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The network preference is added to RTCConfiguration and passed to ICE. ICE considers now the preference set by applications over network interface types when making decisions in candidate pair switching. Bug: webrtc:8816 Change-Id: I40d2612705b54c83dd45772ac855808e0a76b1e1 Reviewed-on: https://webrtc-review.googlesource.com/44020 Commit-Queue: Qingsi Wang Reviewed-by: Sami Kalliomäki Reviewed-by: Taylor Brandstetter Cr-Commit-Position: refs/heads/master@{#21855} --- api/peerconnectioninterface.h | 6 ++ p2p/base/icetransportinternal.cc | 6 +- p2p/base/icetransportinternal.h | 5 +- p2p/base/p2ptransportchannel.cc | 91 +++++++++++++++---- p2p/base/p2ptransportchannel.h | 5 + pc/peerconnection.cc | 7 +- .../api/org/webrtc/PeerConnection.java | 19 ++++ .../api/org/webrtc/PeerConnectionFactory.java | 2 + sdk/android/src/jni/pc/icecandidate.cc | 27 ++++++ sdk/android/src/jni/pc/icecandidate.h | 4 + sdk/android/src/jni/pc/peerconnection.cc | 4 + 11 files changed, 154 insertions(+), 22 deletions(-) diff --git a/api/peerconnectioninterface.h b/api/peerconnectioninterface.h index 7087de6a9f..0125455ee4 100644 --- a/api/peerconnectioninterface.h +++ b/api/peerconnectioninterface.h @@ -491,6 +491,12 @@ class PeerConnectionInterface : public rtc::RefCountInterface { // called. webrtc::TurnCustomizer* turn_customizer = nullptr; + // Preferred network interface. + // A candidate pair on a preferred network has a higher precedence in ICE + // than one on an un-preferred network, regardless of priority or network + // cost. + rtc::Optional network_preference; + // Configure the SDP semantics used by this PeerConnection. Note that the // WebRTC 1.0 specification requires kUnifiedPlan semantics. The // RtpTransceiver API is only available with kUnifiedPlan semantics. diff --git a/p2p/base/icetransportinternal.cc b/p2p/base/icetransportinternal.cc index e4e56fe39f..1edbf63a72 100644 --- a/p2p/base/icetransportinternal.cc +++ b/p2p/base/icetransportinternal.cc @@ -21,7 +21,8 @@ IceConfig::IceConfig(int receiving_timeout_ms, int stable_writable_connection_ping_interval_ms, bool presume_writable_when_fully_relayed, int regather_on_failed_networks_interval_ms, - int receiving_switching_delay_ms) + int receiving_switching_delay_ms, + rtc::Optional network_preference) : receiving_timeout(receiving_timeout_ms), backup_connection_ping_interval(backup_connection_ping_interval), continual_gathering_policy(gathering_policy), @@ -32,7 +33,8 @@ IceConfig::IceConfig(int receiving_timeout_ms, presume_writable_when_fully_relayed(presume_writable_when_fully_relayed), regather_on_failed_networks_interval( regather_on_failed_networks_interval_ms), - receiving_switching_delay(receiving_switching_delay_ms) {} + receiving_switching_delay(receiving_switching_delay_ms), + network_preference(network_preference) {} IceConfig::~IceConfig() = default; diff --git a/p2p/base/icetransportinternal.h b/p2p/base/icetransportinternal.h index 787f66e2a4..6888992618 100644 --- a/p2p/base/icetransportinternal.h +++ b/p2p/base/icetransportinternal.h @@ -115,6 +115,8 @@ struct IceConfig { // Measure in milliseconds. rtc::Optional ice_check_min_interval; + rtc::Optional network_preference; + IceConfig(); IceConfig(int receiving_timeout_ms, int backup_connection_ping_interval, @@ -123,7 +125,8 @@ struct IceConfig { int stable_writable_connection_ping_interval_ms, bool presume_writable_when_fully_relayed, int regather_on_failed_networks_interval_ms, - int receiving_switching_delay_ms); + int receiving_switching_delay_ms, + rtc::Optional network_preference); ~IceConfig(); }; diff --git a/p2p/base/p2ptransportchannel.cc b/p2p/base/p2ptransportchannel.cc index ddd240322e..02b943a401 100644 --- a/p2p/base/p2ptransportchannel.cc +++ b/p2p/base/p2ptransportchannel.cc @@ -61,6 +61,35 @@ cricket::PortInterface::CandidateOrigin GetOrigin(cricket::PortInterface* port, return cricket::PortInterface::ORIGIN_OTHER_PORT; } +// TODO(qingsi) Use an enum to replace the following constants for all +// comparision results. +static constexpr int a_is_better = 1; +static constexpr int b_is_better = -1; +static constexpr int a_and_b_equal = 0; + +bool LocalCandidateUsesPreferredNetwork( + const cricket::Connection* conn, + rtc::Optional network_preference) { + rtc::AdapterType network_type = conn->port()->Network()->type(); + return network_preference.has_value() && (network_type == network_preference); +} + +int CompareCandidatePairsByNetworkPreference( + const cricket::Connection* a, + const cricket::Connection* b, + rtc::Optional network_preference) { + bool a_uses_preferred_network = + LocalCandidateUsesPreferredNetwork(a, network_preference); + bool b_uses_preferred_network = + LocalCandidateUsesPreferredNetwork(b, network_preference); + if (a_uses_preferred_network && !b_uses_preferred_network) { + return a_is_better; + } else if (!a_uses_preferred_network && b_uses_preferred_network) { + return b_is_better; + } + return a_and_b_equal; +} + } // unnamed namespace namespace cricket { @@ -98,9 +127,6 @@ static const int DEFAULT_REGATHER_ON_FAILED_NETWORKS_INTERVAL = 5 * 60 * 1000; static constexpr int DEFAULT_BACKUP_CONNECTION_PING_INTERVAL = 25 * 1000; -static constexpr int a_is_better = 1; -static constexpr int b_is_better = -1; - bool IceCredentialsChanged(const std::string& old_ufrag, const std::string& old_pwd, const std::string& new_ufrag, @@ -135,7 +161,8 @@ P2PTransportChannel::P2PTransportChannel(const std::string& transport_name, STRONG_AND_STABLE_WRITABLE_CONNECTION_PING_INTERVAL, true /* presume_writable_when_fully_relayed */, DEFAULT_REGATHER_ON_FAILED_NETWORKS_INTERVAL, - RECEIVING_SWITCHING_DELAY) { + RECEIVING_SWITCHING_DELAY, + rtc::nullopt) { uint32_t weak_ping_interval = ::strtoul( webrtc::field_trial::FindFullName("WebRTC-StunInterPacketDelay").c_str(), nullptr, 10); @@ -218,11 +245,12 @@ bool P2PTransportChannel::ShouldSwitchSelectedConnection( return true; } - // Do not switch to a connection that is not receiving if it has higher cost - // because it may be just spuriously better. - if (new_connection->ComputeNetworkCost() > - selected_connection_->ComputeNetworkCost() && - !new_connection->receiving()) { + // Do not switch to a connection that is not receiving if it is not on a + // preferred network or it has higher cost because it may be just spuriously + // better. + int compare_a_b_by_networks = CompareCandidatePairNetworks( + new_connection, selected_connection_, config_.network_preference); + if (compare_a_b_by_networks == b_is_better && !new_connection->receiving()) { return false; } @@ -497,6 +525,14 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) { RTC_LOG(LS_INFO) << "Set min ping interval to " << *config_.ice_check_min_interval; } + + if (config_.network_preference != config.network_preference) { + config_.network_preference = config.network_preference; + RTC_LOG(LS_INFO) << "Set network preference to " + << (config_.network_preference.has_value() + ? config_.network_preference.value() + : 0); + } } const IceConfig& P2PTransportChannel::config() const { @@ -1150,6 +1186,30 @@ void P2PTransportChannel::MaybeStartPinging() { } } +int P2PTransportChannel::CompareCandidatePairNetworks( + const Connection* a, + const Connection* b, + rtc::Optional network_preference) const { + int compare_a_b_by_network_preference = + CompareCandidatePairsByNetworkPreference(a, b, + config_.network_preference); + // The network preference has a higher precedence than the network cost. + if (compare_a_b_by_network_preference != a_and_b_equal) { + return compare_a_b_by_network_preference; + } + + uint32_t a_cost = a->ComputeNetworkCost(); + uint32_t b_cost = b->ComputeNetworkCost(); + // Prefer lower network cost. + if (a_cost < b_cost) { + return a_is_better; + } + if (a_cost > b_cost) { + return b_is_better; + } + return a_and_b_equal; +} + // Compare two connections based on their writing, receiving, and connected // states. int P2PTransportChannel::CompareConnectionStates( @@ -1230,15 +1290,10 @@ int P2PTransportChannel::CompareConnectionStates( int P2PTransportChannel::CompareConnectionCandidates( const Connection* a, const Connection* b) const { - // Prefer lower network cost. - uint32_t a_cost = a->ComputeNetworkCost(); - uint32_t b_cost = b->ComputeNetworkCost(); - // Smaller cost is better. - if (a_cost < b_cost) { - return a_is_better; - } - if (a_cost > b_cost) { - return b_is_better; + int compare_a_b_by_networks = + CompareCandidatePairNetworks(a, b, config_.network_preference); + if (compare_a_b_by_networks != a_and_b_equal) { + return compare_a_b_by_networks; } // Compare connection priority. Lower values get sorted last. diff --git a/p2p/base/p2ptransportchannel.h b/p2p/base/p2ptransportchannel.h index c2febbf1d3..ed0be2e111 100644 --- a/p2p/base/p2ptransportchannel.h +++ b/p2p/base/p2ptransportchannel.h @@ -195,6 +195,11 @@ class P2PTransportChannel : public IceTransportInternal, // that's pingable. void MaybeStartPinging(); + int CompareCandidatePairNetworks( + const Connection* a, + const Connection* b, + rtc::Optional network_preference) const; + // The methods below return a positive value if |a| is preferable to |b|, // a negative value if |b| is preferable, and 0 if they're equally preferable. // If |receiving_unchanged_threshold| is set, then when |b| is receiving and diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc index a6297616b9..0596869eba 100644 --- a/pc/peerconnection.cc +++ b/pc/peerconnection.cc @@ -603,6 +603,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( rtc::Optional ice_regather_interval_range; webrtc::TurnCustomizer* turn_customizer; SdpSemantics sdp_semantics; + rtc::Optional network_preference; }; static_assert(sizeof(stuff_being_tested_for_equality) == sizeof(*this), "Did you add something to RTCConfiguration and forget to " @@ -639,7 +640,8 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( ice_check_min_interval == o.ice_check_min_interval && ice_regather_interval_range == o.ice_regather_interval_range && turn_customizer == o.turn_customizer && - sdp_semantics == o.sdp_semantics; + sdp_semantics == o.sdp_semantics && + network_preference == o.network_preference; } bool PeerConnectionInterface::RTCConfiguration::operator!=( @@ -2519,6 +2521,7 @@ bool PeerConnection::SetConfiguration(const RTCConfiguration& configuration, modified_config.prune_turn_ports = configuration.prune_turn_ports; modified_config.ice_check_min_interval = configuration.ice_check_min_interval; modified_config.turn_customizer = configuration.turn_customizer; + modified_config.network_preference = configuration.network_preference; if (configuration != modified_config) { RTC_LOG(LS_ERROR) << "Modifying the configuration in an unsupported way."; return SafeSetError(RTCErrorType::INVALID_MODIFICATION, error); @@ -4613,6 +4616,7 @@ cricket::IceConfig PeerConnection::ParseIceConfig( RTC_NOTREACHED(); gathering_policy = cricket::GATHER_ONCE; } + cricket::IceConfig ice_config; ice_config.receiving_timeout = config.ice_connection_receiving_timeout; ice_config.prioritize_most_likely_candidate_pairs = @@ -4625,6 +4629,7 @@ cricket::IceConfig PeerConnection::ParseIceConfig( ice_config.ice_check_min_interval = config.ice_check_min_interval; ice_config.regather_all_networks_interval_range = config.ice_regather_interval_range; + ice_config.network_preference = config.network_preference; return ice_config; } diff --git a/sdk/android/api/org/webrtc/PeerConnection.java b/sdk/android/api/org/webrtc/PeerConnection.java index 20b06d5c08..c6598b76d1 100644 --- a/sdk/android/api/org/webrtc/PeerConnection.java +++ b/sdk/android/api/org/webrtc/PeerConnection.java @@ -304,6 +304,16 @@ public class PeerConnection { /** Java version of PeerConnectionInterface.CandidateNetworkPolicy */ public enum CandidateNetworkPolicy { ALL, LOW_COST } + // Keep in sync with webrtc/rtc_base/network_constants.h. + public enum AdapterType { + UNKNOWN, + ETHERNET, + WIFI, + CELLULAR, + VPN, + LOOPBACK, + } + /** Java version of rtc::KeyType */ public enum KeyType { RSA, ECDSA } @@ -368,6 +378,9 @@ public class PeerConnection { public Integer screencastMinBitrate; public Boolean combinedAudioVideoBwe; public Boolean enableDtlsSrtp; + // Use "Unknown" to represent no preference of adapter types, not the + // preference of adapters of unknown types. + public AdapterType networkPreference; // This is an optional wrapper for the C++ webrtc::TurnCustomizer. public TurnCustomizer turnCustomizer; @@ -403,6 +416,7 @@ public class PeerConnection { screencastMinBitrate = null; combinedAudioVideoBwe = null; enableDtlsSrtp = null; + networkPreference = AdapterType.UNKNOWN; } @CalledByNative("RTCConfiguration") @@ -544,6 +558,11 @@ public class PeerConnection { Boolean getEnableDtlsSrtp() { return enableDtlsSrtp; } + + @CalledByNative("RTCConfiguration") + AdapterType getNetworkPreference() { + return networkPreference; + } }; private final List localStreams = new ArrayList<>(); diff --git a/sdk/android/api/org/webrtc/PeerConnectionFactory.java b/sdk/android/api/org/webrtc/PeerConnectionFactory.java index 90b4cb6f28..71e4b6c388 100644 --- a/sdk/android/api/org/webrtc/PeerConnectionFactory.java +++ b/sdk/android/api/org/webrtc/PeerConnectionFactory.java @@ -95,6 +95,8 @@ public class PeerConnectionFactory { public static class Options { // Keep in sync with webrtc/rtc_base/network.h! + // + // These bit fields are defined for |networkIgnoreMask| below. static final int ADAPTER_TYPE_UNKNOWN = 0; static final int ADAPTER_TYPE_ETHERNET = 1 << 0; static final int ADAPTER_TYPE_WIFI = 1 << 1; diff --git a/sdk/android/src/jni/pc/icecandidate.cc b/sdk/android/src/jni/pc/icecandidate.cc index e6fad74b8b..84e0f6e7c6 100644 --- a/sdk/android/src/jni/pc/icecandidate.cc +++ b/sdk/android/src/jni/pc/icecandidate.cc @@ -207,5 +207,32 @@ PeerConnectionInterface::TlsCertPolicy JavaToNativeTlsCertPolicy( return PeerConnectionInterface::kTlsCertPolicySecure; } +rtc::Optional JavaToNativeNetworkPreference( + JNIEnv* jni, + const JavaRef& j_network_preference) { + std::string enum_name = GetJavaEnumName(jni, j_network_preference); + + if (enum_name == "UNKNOWN") + return rtc::nullopt; + + if (enum_name == "ETHERNET") + return rtc::ADAPTER_TYPE_ETHERNET; + + if (enum_name == "WIFI") + return rtc::ADAPTER_TYPE_WIFI; + + if (enum_name == "CELLULAR") + return rtc::ADAPTER_TYPE_CELLULAR; + + if (enum_name == "VPN") + return rtc::ADAPTER_TYPE_VPN; + + if (enum_name == "LOOPBACK") + return rtc::ADAPTER_TYPE_LOOPBACK; + + RTC_CHECK(false) << "Unexpected NetworkPreference enum_name " << enum_name; + return rtc::nullopt; +} + } // namespace jni } // namespace webrtc diff --git a/sdk/android/src/jni/pc/icecandidate.h b/sdk/android/src/jni/pc/icecandidate.h index 324aaa6130..be4d27ca17 100644 --- a/sdk/android/src/jni/pc/icecandidate.h +++ b/sdk/android/src/jni/pc/icecandidate.h @@ -75,6 +75,10 @@ PeerConnectionInterface::TlsCertPolicy JavaToNativeTlsCertPolicy( JNIEnv* jni, const JavaRef& j_ice_server_tls_cert_policy); +rtc::Optional JavaToNativeNetworkPreference( + JNIEnv* jni, + const JavaRef& j_network_preference); + } // namespace jni } // namespace webrtc diff --git a/sdk/android/src/jni/pc/peerconnection.cc b/sdk/android/src/jni/pc/peerconnection.cc index 4055467b1f..f6f3932fa5 100644 --- a/sdk/android/src/jni/pc/peerconnection.cc +++ b/sdk/android/src/jni/pc/peerconnection.cc @@ -121,6 +121,8 @@ void JavaToNativeRTCConfiguration( Java_RTCConfiguration_getContinualGatheringPolicy(jni, j_rtc_config); ScopedJavaLocalRef j_turn_customizer = Java_RTCConfiguration_getTurnCustomizer(jni, j_rtc_config); + ScopedJavaLocalRef j_network_preference = + Java_RTCConfiguration_getNetworkPreference(jni, j_rtc_config); rtc_config->type = JavaToNativeIceTransportsType(jni, j_ice_transports_type); rtc_config->bundle_policy = JavaToNativeBundlePolicy(jni, j_bundle_policy); @@ -184,6 +186,8 @@ void JavaToNativeRTCConfiguration( jni, Java_RTCConfiguration_getCombinedAudioVideoBwe(jni, j_rtc_config)); rtc_config->enable_dtls_srtp = JavaToNativeOptionalBool( jni, Java_RTCConfiguration_getEnableDtlsSrtp(jni, j_rtc_config)); + rtc_config->network_preference = + JavaToNativeNetworkPreference(jni, j_network_preference); } rtc::KeyType GetRtcConfigKeyType(JNIEnv* env,