From 603470576ec4043e868c72f38bf7644831a38dc3 Mon Sep 17 00:00:00 2001 From: honghaiz Date: Tue, 31 May 2016 18:29:12 -0700 Subject: [PATCH] Add a flag to filter out high-cost networks. This allows webrtc to not gather on cellular networks if wifi or other low cost networks are present. BUG= Review-Url: https://codereview.webrtc.org/1987833002 Cr-Commit-Position: refs/heads/master@{#12979} --- webrtc/api/java/jni/classreferenceholder.cc | 1 + webrtc/api/java/jni/peerconnection_jni.cc | 26 ++++++++++ .../java/src/org/webrtc/PeerConnection.java | 7 +++ webrtc/api/peerconnection.cc | 6 +++ webrtc/api/peerconnectioninterface.h | 7 +++ .../api/peerconnectioninterface_unittest.cc | 4 ++ webrtc/p2p/base/portallocator.h | 10 ++++ webrtc/p2p/client/basicportallocator.cc | 13 +++++ .../p2p/client/basicportallocator_unittest.cc | 47 +++++++++++++++++++ 9 files changed, 121 insertions(+) diff --git a/webrtc/api/java/jni/classreferenceholder.cc b/webrtc/api/java/jni/classreferenceholder.cc index d332e11711..8ed01c51ee 100644 --- a/webrtc/api/java/jni/classreferenceholder.cc +++ b/webrtc/api/java/jni/classreferenceholder.cc @@ -83,6 +83,7 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { LoadClass(jni, "org/webrtc/PeerConnection$IceGatheringState"); LoadClass(jni, "org/webrtc/PeerConnection$IceTransportsType"); LoadClass(jni, "org/webrtc/PeerConnection$TcpCandidatePolicy"); + LoadClass(jni, "org/webrtc/PeerConnection$CandidateNetworkPolicy"); LoadClass(jni, "org/webrtc/PeerConnection$KeyType"); LoadClass(jni, "org/webrtc/PeerConnection$SignalingState"); LoadClass(jni, "org/webrtc/RtpReceiver"); diff --git a/webrtc/api/java/jni/peerconnection_jni.cc b/webrtc/api/java/jni/peerconnection_jni.cc index 88631719ed..091b9d3307 100644 --- a/webrtc/api/java/jni/peerconnection_jni.cc +++ b/webrtc/api/java/jni/peerconnection_jni.cc @@ -1444,6 +1444,24 @@ JavaTcpCandidatePolicyToNativeType( return PeerConnectionInterface::kTcpCandidatePolicyEnabled; } +static PeerConnectionInterface::CandidateNetworkPolicy +JavaCandidateNetworkPolicyToNativeType(JNIEnv* jni, + jobject j_candidate_network_policy) { + std::string enum_name = + GetJavaEnumName(jni, "org/webrtc/PeerConnection$CandidateNetworkPolicy", + j_candidate_network_policy); + + if (enum_name == "ALL") + return PeerConnectionInterface::kCandidateNetworkPolicyAll; + + if (enum_name == "LOW_COST") + return PeerConnectionInterface::kCandidateNetworkPolicyLowCost; + + RTC_CHECK(false) << "Unexpected CandidateNetworkPolicy enum_name " + << enum_name; + return PeerConnectionInterface::kCandidateNetworkPolicyAll; +} + static rtc::KeyType JavaKeyTypeToNativeType(JNIEnv* jni, jobject j_key_type) { std::string enum_name = GetJavaEnumName( jni, "org/webrtc/PeerConnection$KeyType", j_key_type); @@ -1529,6 +1547,12 @@ static void JavaRTCConfigurationToJsepRTCConfiguration( jobject j_tcp_candidate_policy = GetObjectField( jni, j_rtc_config, j_tcp_candidate_policy_id); + jfieldID j_candidate_network_policy_id = GetFieldID( + jni, j_rtc_config_class, "candidateNetworkPolicy", + "Lorg/webrtc/PeerConnection$CandidateNetworkPolicy;"); + jobject j_candidate_network_policy = GetObjectField( + jni, j_rtc_config, j_candidate_network_policy_id); + jfieldID j_ice_servers_id = GetFieldID( jni, j_rtc_config_class, "iceServers", "Ljava/util/List;"); jobject j_ice_servers = GetObjectField(jni, j_rtc_config, j_ice_servers_id); @@ -1561,6 +1585,8 @@ static void JavaRTCConfigurationToJsepRTCConfiguration( JavaRtcpMuxPolicyToNativeType(jni, j_rtcp_mux_policy); rtc_config->tcp_candidate_policy = JavaTcpCandidatePolicyToNativeType(jni, j_tcp_candidate_policy); + rtc_config->candidate_network_policy = + JavaCandidateNetworkPolicyToNativeType(jni, j_candidate_network_policy); JavaIceServersToJsepIceServers(jni, j_ice_servers, &rtc_config->servers); rtc_config->audio_jitter_buffer_max_packets = GetIntField(jni, j_rtc_config, j_audio_jitter_buffer_max_packets_id); diff --git a/webrtc/api/java/src/org/webrtc/PeerConnection.java b/webrtc/api/java/src/org/webrtc/PeerConnection.java index c112d27052..ad8362d485 100644 --- a/webrtc/api/java/src/org/webrtc/PeerConnection.java +++ b/webrtc/api/java/src/org/webrtc/PeerConnection.java @@ -116,6 +116,11 @@ public class PeerConnection { ENABLED, DISABLED }; + /** Java version of PeerConnectionInterface.CandidateNetworkPolicy */ + public enum CandidateNetworkPolicy { + ALL, LOW_COST + }; + /** Java version of rtc::KeyType */ public enum KeyType { RSA, ECDSA @@ -133,6 +138,7 @@ public class PeerConnection { public BundlePolicy bundlePolicy; public RtcpMuxPolicy rtcpMuxPolicy; public TcpCandidatePolicy tcpCandidatePolicy; + public CandidateNetworkPolicy candidateNetworkPolicy; public int audioJitterBufferMaxPackets; public boolean audioJitterBufferFastAccelerate; public int iceConnectionReceivingTimeout; @@ -146,6 +152,7 @@ public class PeerConnection { bundlePolicy = BundlePolicy.BALANCED; rtcpMuxPolicy = RtcpMuxPolicy.NEGOTIATE; tcpCandidatePolicy = TcpCandidatePolicy.ENABLED; + candidateNetworkPolicy = candidateNetworkPolicy.ALL; this.iceServers = iceServers; audioJitterBufferMaxPackets = 50; audioJitterBufferFastAccelerate = false; diff --git a/webrtc/api/peerconnection.cc b/webrtc/api/peerconnection.cc index 5a0d32a5ff..d5cbd5ea87 100644 --- a/webrtc/api/peerconnection.cc +++ b/webrtc/api/peerconnection.cc @@ -2128,6 +2128,12 @@ bool PeerConnection::InitializePortAllocator_n( LOG(LS_INFO) << "TCP candidates are disabled."; } + if (configuration.candidate_network_policy == + kCandidateNetworkPolicyLowCost) { + portallocator_flags |= cricket::PORTALLOCATOR_DISABLE_COSTLY_NETWORKS; + LOG(LS_INFO) << "Do not gather candidates on high-cost networks"; + } + port_allocator_->set_flags(portallocator_flags); // No step delay is used while allocating ports. port_allocator_->set_step_delay(cricket::kMinimumStepDelay); diff --git a/webrtc/api/peerconnectioninterface.h b/webrtc/api/peerconnectioninterface.h index 4fdbd935f9..166a1e5ef9 100644 --- a/webrtc/api/peerconnectioninterface.h +++ b/webrtc/api/peerconnectioninterface.h @@ -217,6 +217,11 @@ class PeerConnectionInterface : public rtc::RefCountInterface { kTcpCandidatePolicyDisabled }; + enum CandidateNetworkPolicy { + kCandidateNetworkPolicyAll, + kCandidateNetworkPolicyLowCost + }; + enum ContinualGatheringPolicy { GATHER_ONCE, GATHER_CONTINUALLY @@ -276,6 +281,8 @@ class PeerConnectionInterface : public rtc::RefCountInterface { BundlePolicy bundle_policy = kBundlePolicyBalanced; RtcpMuxPolicy rtcp_mux_policy = kRtcpMuxPolicyNegotiate; TcpCandidatePolicy tcp_candidate_policy = kTcpCandidatePolicyEnabled; + CandidateNetworkPolicy candidate_network_policy = + kCandidateNetworkPolicyAll; int audio_jitter_buffer_max_packets = kAudioJitterBufferMaxPackets; bool audio_jitter_buffer_fast_accelerate = false; int ice_connection_receiving_timeout = kUndefined; // ms diff --git a/webrtc/api/peerconnectioninterface_unittest.cc b/webrtc/api/peerconnectioninterface_unittest.cc index 0ec7478452..6760a5464e 100644 --- a/webrtc/api/peerconnectioninterface_unittest.cc +++ b/webrtc/api/peerconnectioninterface_unittest.cc @@ -1050,6 +1050,8 @@ TEST_F(PeerConnectionInterfaceTest, CreatePeerConnectionWithPooledCandidates) { config.disable_ipv6 = true; config.tcp_candidate_policy = PeerConnectionInterface::kTcpCandidatePolicyDisabled; + config.candidate_network_policy = + PeerConnectionInterface::kCandidateNetworkPolicyLowCost; config.ice_candidate_pool_size = 1; CreatePeerConnection(config, nullptr); @@ -1060,6 +1062,8 @@ TEST_F(PeerConnectionInterfaceTest, CreatePeerConnectionWithPooledCandidates) { EXPECT_EQ(1UL, session->stun_servers().size()); EXPECT_EQ(0U, session->flags() & cricket::PORTALLOCATOR_ENABLE_IPV6); EXPECT_LT(0U, session->flags() & cricket::PORTALLOCATOR_DISABLE_TCP); + EXPECT_LT(0U, + session->flags() & cricket::PORTALLOCATOR_DISABLE_COSTLY_NETWORKS); } TEST_F(PeerConnectionInterfaceTest, AddStreams) { diff --git a/webrtc/p2p/base/portallocator.h b/webrtc/p2p/base/portallocator.h index 75354bcad3..7d2a59f278 100644 --- a/webrtc/p2p/base/portallocator.h +++ b/webrtc/p2p/base/portallocator.h @@ -60,6 +60,16 @@ enum { // Disallow use of UDP when connecting to a relay server. Since proxy servers // usually don't handle UDP, using UDP will leak the IP address. PORTALLOCATOR_DISABLE_UDP_RELAY = 0x1000, + + // When multiple networks exist, do not gather candidates on the ones with + // high cost. So if both Wi-Fi and cellular networks exist, gather only on the + // Wi-Fi network. If a network type is "unknown", it has a cost lower than + // cellular but higher than Wi-Fi/Ethernet. So if an unknown network exists, + // cellular networks will not be used to gather candidates and if a Wi-Fi + // network is present, "unknown" networks will not be usd to gather + // candidates. Doing so ensures that even if a cellular network type was not + // detected initially, it would not be used if a Wi-Fi network is present. + PORTALLOCATOR_DISABLE_COSTLY_NETWORKS = 0x2000, }; const uint32_t kDefaultPortAllocatorFlags = 0; diff --git a/webrtc/p2p/client/basicportallocator.cc b/webrtc/p2p/client/basicportallocator.cc index 14f7b13212..20fc84a38a 100644 --- a/webrtc/p2p/client/basicportallocator.cc +++ b/webrtc/p2p/client/basicportallocator.cc @@ -417,6 +417,19 @@ void BasicPortAllocatorSession::GetNetworks( network->type(); }), networks->end()); + + if (flags() & PORTALLOCATOR_DISABLE_COSTLY_NETWORKS) { + uint16_t lowest_cost = rtc::kNetworkCostMax; + for (rtc::Network* network : *networks) { + lowest_cost = std::min(lowest_cost, network->GetCost()); + } + networks->erase(std::remove_if(networks->begin(), networks->end(), + [lowest_cost](rtc::Network* network) { + return network->GetCost() > + lowest_cost + rtc::kNetworkCostLow; + }), + networks->end()); + } } // For each network, see if we have a sequence that covers it already. If not, diff --git a/webrtc/p2p/client/basicportallocator_unittest.cc b/webrtc/p2p/client/basicportallocator_unittest.cc index 0eaa49e4d8..6d5d1086a9 100644 --- a/webrtc/p2p/client/basicportallocator_unittest.cc +++ b/webrtc/p2p/client/basicportallocator_unittest.cc @@ -519,6 +519,53 @@ TEST_F(BasicPortAllocatorTest, TestIgnoreNetworksAccordingToIgnoreMask) { EXPECT_EQ(0x12345602U, candidates_[0].address().ip()); } +// Test that high cost networks are filtered if the flag +// PORTALLOCATOR_DISABLE_COSTLY_NETWORKS is set. +TEST_F(BasicPortAllocatorTest, TestGatherLowCostNetworkOnly) { + SocketAddress addr_wifi(IPAddress(0x12345600U), 0); + SocketAddress addr_cellular(IPAddress(0x12345601U), 0); + SocketAddress addr_unknown1(IPAddress(0x12345602U), 0); + SocketAddress addr_unknown2(IPAddress(0x12345603U), 0); + // If both Wi-Fi and cellular interfaces are present, only gather on the Wi-Fi + // interface. + AddInterface(addr_wifi, "test_wlan0", rtc::ADAPTER_TYPE_WIFI); + AddInterface(addr_cellular, "test_cell0", rtc::ADAPTER_TYPE_CELLULAR); + allocator().set_flags(cricket::PORTALLOCATOR_DISABLE_STUN | + cricket::PORTALLOCATOR_DISABLE_RELAY | + cricket::PORTALLOCATOR_DISABLE_TCP | + cricket::PORTALLOCATOR_DISABLE_COSTLY_NETWORKS); + EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout); + EXPECT_EQ(1U, candidates_.size()); + EXPECT_TRUE(addr_wifi.EqualIPs(candidates_[0].address())); + + // If both cellular and unknown interfaces are present, only gather on the + // unknown interfaces. + candidates_.clear(); + candidate_allocation_done_ = false; + RemoveInterface(addr_wifi); + AddInterface(addr_unknown1, "test_unknown0", rtc::ADAPTER_TYPE_UNKNOWN); + AddInterface(addr_unknown2, "test_unknown1", rtc::ADAPTER_TYPE_UNKNOWN); + session_->StartGettingPorts(); + EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout); + EXPECT_EQ(2U, candidates_.size()); + EXPECT_TRUE((addr_unknown1.EqualIPs(candidates_[0].address()) && + addr_unknown2.EqualIPs(candidates_[1].address())) || + (addr_unknown1.EqualIPs(candidates_[1].address()) && + addr_unknown2.EqualIPs(candidates_[0].address()))); + + // If Wi-Fi, cellular, unknown interfaces are all present, only gather on the + // Wi-Fi interface. + candidates_.clear(); + candidate_allocation_done_ = false; + AddInterface(addr_wifi, "test_wlan0", rtc::ADAPTER_TYPE_WIFI); + session_->StartGettingPorts(); + EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout); + EXPECT_EQ(1U, candidates_.size()); + EXPECT_TRUE(addr_wifi.EqualIPs(candidates_[0].address())); +} + // Tests that we allocator session not trying to allocate ports for every 250ms. TEST_F(BasicPortAllocatorTest, TestNoNetworkInterface) { EXPECT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP));