Relanding https://code.google.com/p/webrtc/source/detail?r=7093, after it got
reverted due to some internal compile failures.

In this CL changes are done in portallocator_unittest.cc, in particular to EXPECT_EQ checking in new tests.

Original patch committed in https://code.google.com/p/webrtc/source/detail?r=7093

TBR=juberti@webrtc.org
BUG=1179

Review URL: https://webrtc-codereview.appspot.com/22329004

git-svn-id: http://webrtc.googlecode.com/svn/trunk/talk@7118 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/app/webrtc/peerconnection.cc b/app/webrtc/peerconnection.cc
index 201269a..3fba862 100644
--- a/app/webrtc/peerconnection.cc
+++ b/app/webrtc/peerconnection.cc
@@ -672,7 +672,7 @@
       }
     }
   }
-  return session_->UpdateIce(config.type);
+  return session_->SetIceTransports(config.type);
 }
 
 bool PeerConnection::AddIceCandidate(
diff --git a/app/webrtc/webrtcsession.cc b/app/webrtc/webrtcsession.cc
index ae81f24..13e2128 100644
--- a/app/webrtc/webrtcsession.cc
+++ b/app/webrtc/webrtcsession.cc
@@ -387,6 +387,22 @@
   }
 }
 
+uint32 ConvertIceTransportTypeToCandidateFilter(
+    PeerConnectionInterface::IceTransportsType type) {
+  switch (type) {
+    case PeerConnectionInterface::kNone:
+        return cricket::CF_NONE;
+    case PeerConnectionInterface::kRelay:
+        return cricket::CF_RELAY;
+    case PeerConnectionInterface::kNoHost:
+        return (cricket::CF_ALL & ~cricket::CF_HOST);
+    case PeerConnectionInterface::kAll:
+        return cricket::CF_ALL;
+    default: ASSERT(false);
+  }
+  return cricket::CF_NONE;
+}
+
 // Help class used to remember if a a remote peer has requested ice restart by
 // by sending a description with new ice ufrag and password.
 class IceRestartAnswerLatch {
@@ -640,7 +656,8 @@
   if (options.disable_encryption) {
     webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED);
   }
-
+  port_allocator()->set_candidate_filter(
+      ConvertIceTransportTypeToCandidateFilter(ice_transport));
   return true;
 }
 
@@ -746,6 +763,7 @@
   if (!UpdateSessionState(action, cricket::CS_LOCAL, err_desc)) {
     return false;
   }
+
   // Kick starting the ice candidates allocation.
   StartCandidatesAllocation();
 
@@ -907,8 +925,10 @@
   return UseCandidate(candidate);
 }
 
-bool WebRtcSession::UpdateIce(PeerConnectionInterface::IceTransportsType type) {
-  return false;
+bool WebRtcSession::SetIceTransports(
+    PeerConnectionInterface::IceTransportsType type) {
+  return port_allocator()->set_candidate_filter(
+        ConvertIceTransportTypeToCandidateFilter(type));
 }
 
 bool WebRtcSession::GetLocalTrackIdBySsrc(uint32 ssrc, std::string* track_id) {
diff --git a/app/webrtc/webrtcsession.h b/app/webrtc/webrtcsession.h
index d1e5645..86ae435 100644
--- a/app/webrtc/webrtcsession.h
+++ b/app/webrtc/webrtcsession.h
@@ -160,7 +160,7 @@
                             std::string* err_desc);
   bool ProcessIceMessage(const IceCandidateInterface* ice_candidate);
 
-  bool UpdateIce(PeerConnectionInterface::IceTransportsType type);
+  bool SetIceTransports(PeerConnectionInterface::IceTransportsType type);
 
   const SessionDescriptionInterface* local_description() const {
     return local_desc_.get();
diff --git a/app/webrtc/webrtcsession_unittest.cc b/app/webrtc/webrtcsession_unittest.cc
index 2f731a9..2cf422b 100644
--- a/app/webrtc/webrtcsession_unittest.cc
+++ b/app/webrtc/webrtcsession_unittest.cc
@@ -108,6 +108,8 @@
 static const char kStunAddrHost[] = "99.99.99.1";
 static const SocketAddress kTurnUdpIntAddr("99.99.99.4", 3478);
 static const SocketAddress kTurnUdpExtAddr("99.99.99.6", 0);
+static const char kTurnUsername[] = "test";
+static const char kTurnPassword[] = "test";
 
 static const char kSessionVersion[] = "1";
 
@@ -1083,6 +1085,18 @@
     }
   }
 
+  void ConfigureAllocatorWithTurn() {
+    cricket::RelayServerConfig relay_server(cricket::RELAY_TURN);
+    cricket::RelayCredentials credentials(kTurnUsername, kTurnPassword);
+    relay_server.credentials = credentials;
+    relay_server.ports.push_back(cricket::ProtocolAddress(
+        kTurnUdpIntAddr, cricket::PROTO_UDP, false));
+    allocator_->AddRelay(relay_server);
+    allocator_->set_step_delay(cricket::kMinimumStepDelay);
+    allocator_->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
+                          cricket::PORTALLOCATOR_ENABLE_BUNDLE);
+  }
+
   cricket::FakeMediaEngine* media_engine_;
   cricket::FakeDataEngine* data_engine_;
   cricket::FakeDeviceManager* device_manager_;
@@ -1162,6 +1176,53 @@
   EXPECT_EQ(6u, observer_.mline_1_candidates_.size());
 }
 
+// Test session delivers no candidates gathered when constraint set to "none".
+TEST_F(WebRtcSessionTest, TestIceTransportsNone) {
+  AddInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
+  SetIceTransportType(PeerConnectionInterface::kNone);
+  Init(NULL);
+  mediastream_signaling_.SendAudioVideoStream1();
+  InitiateCall();
+  EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout);
+  EXPECT_EQ(0u, observer_.mline_0_candidates_.size());
+  EXPECT_EQ(0u, observer_.mline_1_candidates_.size());
+}
+
+// Test session delivers only relay candidates gathered when constaint set to
+// "relay".
+TEST_F(WebRtcSessionTest, TestIceTransportsRelay) {
+  AddInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
+  ConfigureAllocatorWithTurn();
+  SetIceTransportType(PeerConnectionInterface::kRelay);
+  Init(NULL);
+  mediastream_signaling_.SendAudioVideoStream1();
+  InitiateCall();
+  EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout);
+  EXPECT_EQ(2u, observer_.mline_0_candidates_.size());
+  EXPECT_EQ(2u, observer_.mline_1_candidates_.size());
+  for (size_t i = 0; i < observer_.mline_0_candidates_.size(); ++i) {
+    EXPECT_EQ(cricket::RELAY_PORT_TYPE,
+              observer_.mline_0_candidates_[i].type());
+  }
+  for (size_t i = 0; i < observer_.mline_1_candidates_.size(); ++i) {
+    EXPECT_EQ(cricket::RELAY_PORT_TYPE,
+              observer_.mline_1_candidates_[i].type());
+  }
+}
+
+// Test session delivers all candidates gathered when constaint set to "all".
+TEST_F(WebRtcSessionTest, TestIceTransportsAll) {
+  AddInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
+  SetIceTransportType(PeerConnectionInterface::kAll);
+  Init(NULL);
+  mediastream_signaling_.SendAudioVideoStream1();
+  InitiateCall();
+  EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout);
+  // Host + STUN. By default allocator is disabled to gather relay candidates.
+  EXPECT_EQ(4u, observer_.mline_0_candidates_.size());
+  EXPECT_EQ(4u, observer_.mline_1_candidates_.size());
+}
+
 TEST_F(WebRtcSessionTest, SetSdpFailedOnInvalidSdp) {
   Init(NULL);
   SessionDescriptionInterface* offer = NULL;
diff --git a/p2p/base/portallocator.h b/p2p/base/portallocator.h
index 84e5fea..a3ff031 100644
--- a/p2p/base/portallocator.h
+++ b/p2p/base/portallocator.h
@@ -44,17 +44,19 @@
 // Clients can override this class to control port allocation, including
 // what kinds of ports are allocated.
 
-const uint32 PORTALLOCATOR_DISABLE_UDP = 0x01;
-const uint32 PORTALLOCATOR_DISABLE_STUN = 0x02;
-const uint32 PORTALLOCATOR_DISABLE_RELAY = 0x04;
-const uint32 PORTALLOCATOR_DISABLE_TCP = 0x08;
-const uint32 PORTALLOCATOR_ENABLE_SHAKER = 0x10;
-const uint32 PORTALLOCATOR_ENABLE_BUNDLE = 0x20;
-const uint32 PORTALLOCATOR_ENABLE_IPV6 = 0x40;
-const uint32 PORTALLOCATOR_ENABLE_SHARED_UFRAG = 0x80;
-const uint32 PORTALLOCATOR_ENABLE_SHARED_SOCKET = 0x100;
-const uint32 PORTALLOCATOR_ENABLE_STUN_RETRANSMIT_ATTRIBUTE = 0x200;
-const uint32 PORTALLOCATOR_ENABLE_TURN_SHARED_SOCKET = 0x400;
+enum {
+  PORTALLOCATOR_DISABLE_UDP = 0x01,
+  PORTALLOCATOR_DISABLE_STUN = 0x02,
+  PORTALLOCATOR_DISABLE_RELAY = 0x04,
+  PORTALLOCATOR_DISABLE_TCP = 0x08,
+  PORTALLOCATOR_ENABLE_SHAKER = 0x10,
+  PORTALLOCATOR_ENABLE_BUNDLE = 0x20,
+  PORTALLOCATOR_ENABLE_IPV6 = 0x40,
+  PORTALLOCATOR_ENABLE_SHARED_UFRAG = 0x80,
+  PORTALLOCATOR_ENABLE_SHARED_SOCKET = 0x100,
+  PORTALLOCATOR_ENABLE_STUN_RETRANSMIT_ATTRIBUTE = 0x200,
+  PORTALLOCATOR_ENABLE_TURN_SHARED_SOCKET = 0x400,
+};
 
 const uint32 kDefaultPortAllocatorFlags = 0;
 
@@ -63,6 +65,15 @@
 // internal. Less than 20ms is not acceptable. We choose 50ms as our default.
 const uint32 kMinimumStepDelay = 50;
 
+// CF = CANDIDATE FILTER
+enum {
+  CF_NONE = 0x0,
+  CF_HOST = 0x1,
+  CF_REFLEXIVE = 0x2,
+  CF_RELAY = 0x4,
+  CF_ALL = 0x7,
+};
+
 class PortAllocatorSessionMuxer;
 
 class PortAllocatorSession : public sigslot::has_slots<> {
@@ -118,7 +129,8 @@
       min_port_(0),
       max_port_(0),
       step_delay_(kDefaultStepDelay),
-      allow_tcp_listen_(true) {
+      allow_tcp_listen_(true),
+      candidate_filter_(CF_ALL) {
     // This will allow us to have old behavior on non webrtc clients.
   }
   virtual ~PortAllocator();
@@ -167,6 +179,13 @@
     allow_tcp_listen_ = allow_tcp_listen;
   }
 
+  uint32 candidate_filter() { return candidate_filter_; }
+  bool set_candidate_filter(uint32 filter) {
+    // TODO(mallinath) - Do transition check?
+    candidate_filter_ = filter;
+    return true;
+  }
+
  protected:
   virtual PortAllocatorSession* CreateSessionInternal(
       const std::string& content_name,
@@ -184,6 +203,7 @@
   uint32 step_delay_;
   SessionMuxerMap muxers_;
   bool allow_tcp_listen_;
+  uint32 candidate_filter_;
 };
 
 }  // namespace cricket
diff --git a/p2p/client/basicportallocator.cc b/p2p/client/basicportallocator.cc
index d39db8f..6c184b4 100644
--- a/p2p/client/basicportallocator.cc
+++ b/p2p/client/basicportallocator.cc
@@ -47,13 +47,15 @@
 
 namespace {
 
-const uint32 MSG_CONFIG_START = 1;
-const uint32 MSG_CONFIG_READY = 2;
-const uint32 MSG_ALLOCATE = 3;
-const uint32 MSG_ALLOCATION_PHASE = 4;
-const uint32 MSG_SHAKE = 5;
-const uint32 MSG_SEQUENCEOBJECTS_CREATED = 6;
-const uint32 MSG_CONFIG_STOP = 7;
+enum {
+  MSG_CONFIG_START,
+  MSG_CONFIG_READY,
+  MSG_ALLOCATE,
+  MSG_ALLOCATION_PHASE,
+  MSG_SHAKE,
+  MSG_SEQUENCEOBJECTS_CREATED,
+  MSG_CONFIG_STOP,
+};
 
 const int PHASE_UDP = 0;
 const int PHASE_RELAY = 1;
@@ -228,10 +230,11 @@
 PortAllocatorSession *BasicPortAllocator::CreateSessionInternal(
     const std::string& content_name, int component,
     const std::string& ice_ufrag, const std::string& ice_pwd) {
-  return new BasicPortAllocatorSession(this, content_name, component,
-                                       ice_ufrag, ice_pwd);
+  return new BasicPortAllocatorSession(
+      this, content_name, component, ice_ufrag, ice_pwd);
 }
 
+
 // BasicPortAllocatorSession
 BasicPortAllocatorSession::BasicPortAllocatorSession(
     BasicPortAllocator *allocator,
@@ -530,8 +533,10 @@
   // Send candidates whose protocol is enabled.
   std::vector<Candidate> candidates;
   ProtocolType pvalue;
+  bool candidate_allowed_to_send = CheckCandidateFilter(c);
   if (StringToProto(c.protocol().c_str(), &pvalue) &&
-      data->sequence()->ProtocolEnabled(pvalue)) {
+      data->sequence()->ProtocolEnabled(pvalue) &&
+      candidate_allowed_to_send) {
     candidates.push_back(c);
   }
 
@@ -542,7 +547,9 @@
   // Moving to READY state as we have atleast one candidate from the port.
   // Since this port has atleast one candidate we should forward this port
   // to listners, to allow connections from this port.
-  if (!data->ready()) {
+  // Also we should make sure that candidate gathered from this port is allowed
+  // to send outside.
+  if (!data->ready() && candidate_allowed_to_send) {
     data->set_ready();
     SignalPortReady(this, port);
   }
@@ -588,6 +595,8 @@
 
     const std::vector<Candidate>& potentials = it->port()->Candidates();
     for (size_t i = 0; i < potentials.size(); ++i) {
+      if (!CheckCandidateFilter(potentials[i]))
+        continue;
       ProtocolType pvalue;
       if (!StringToProto(potentials[i].protocol().c_str(), &pvalue))
         continue;
@@ -602,6 +611,31 @@
   }
 }
 
+bool BasicPortAllocatorSession::CheckCandidateFilter(const Candidate& c) {
+  uint32 filter = allocator_->candidate_filter();
+  bool allowed = false;
+  if (filter & CF_RELAY) {
+    allowed |= (c.type() == RELAY_PORT_TYPE);
+  }
+
+  if (filter & CF_REFLEXIVE) {
+    // We allow host candidates if the filter allows server-reflexive candidates
+    // and the candidate is a public IP. Because we don't generate
+    // server-reflexive candidates if they have the same IP as the host
+    // candidate (i.e. when the host candidate is a public IP), filtering to
+    // only server-reflexive candidates won't work right when the host
+    // candidates have public IPs.
+    allowed |= (c.type() == STUN_PORT_TYPE) ||
+               (c.type() == LOCAL_PORT_TYPE && !c.address().IsPrivateIP());
+  }
+
+  if (filter & CF_HOST) {
+    allowed |= (c.type() == LOCAL_PORT_TYPE);
+  }
+
+  return allowed;
+}
+
 void BasicPortAllocatorSession::OnPortAllocationComplete(
     AllocationSequence* seq) {
   // Send candidate allocation complete signal if all ports are done.
diff --git a/p2p/client/basicportallocator.h b/p2p/client/basicportallocator.h
index 5f43880..d424772 100644
--- a/p2p/client/basicportallocator.h
+++ b/p2p/client/basicportallocator.h
@@ -160,7 +160,6 @@
 
     void set_ready() { ASSERT(state_ == STATE_INIT); state_ = STATE_READY; }
     void set_complete() {
-      ASSERT(state_ == STATE_READY);
       state_ = STATE_COMPLETE;
     }
     void set_error() {
@@ -201,6 +200,8 @@
   void OnPortAllocationComplete(AllocationSequence* seq);
   PortData* FindPort(Port* port);
 
+  bool CheckCandidateFilter(const Candidate& c);
+
   BasicPortAllocator* allocator_;
   rtc::Thread* network_thread_;
   rtc::scoped_ptr<rtc::PacketSocketFactory> owned_socket_factory_;
diff --git a/p2p/client/portallocator_unittest.cc b/p2p/client/portallocator_unittest.cc
index 57cfbe3..c383032 100644
--- a/p2p/client/portallocator_unittest.cc
+++ b/p2p/client/portallocator_unittest.cc
@@ -53,6 +53,7 @@
 using rtc::Thread;
 
 static const SocketAddress kClientAddr("11.11.11.11", 0);
+static const SocketAddress kPrivateAddr("192.168.1.11", 0);
 static const SocketAddress kClientIPv6Addr(
     "2401:fa00:4:1000:be30:5bff:fee5:c3", 0);
 static const SocketAddress kClientAddr2("22.22.22.22", 0);
@@ -538,7 +539,7 @@
 // Test to verify ICE restart process.
 TEST_F(PortAllocatorTest, TestGetAllPortsRestarts) {
   AddInterface(kClientAddr);
-  EXPECT_TRUE(CreateSession(1));
+  EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP));
   session_->StartGettingPorts();
   EXPECT_EQ_WAIT(7U, candidates_.size(), kDefaultAllocationTimeout);
   EXPECT_EQ(4U, ports_.size());
@@ -546,6 +547,78 @@
   // TODO - Extend this to verify ICE restart.
 }
 
+// Test ICE candidate filter mechanism with options Relay/Host/Reflexive.
+TEST_F(PortAllocatorTest, TestCandidateFilterWithRelayOnly) {
+  AddInterface(kClientAddr);
+  allocator().set_candidate_filter(cricket::CF_RELAY);
+  EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP));
+  session_->StartGettingPorts();
+  EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout);
+  // Using GTURN, we will have 4 candidates.
+  EXPECT_EQ(4U, candidates_.size());
+  EXPECT_EQ(1U, ports_.size());  // Only Relay port will be in ready state.
+  for (size_t i = 0; i < candidates_.size(); ++i) {
+    EXPECT_EQ(std::string(cricket::RELAY_PORT_TYPE), candidates_[i].type());
+  }
+}
+
+TEST_F(PortAllocatorTest, TestCandidateFilterWithHostOnly) {
+  AddInterface(kClientAddr);
+  allocator().set_flags(cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG |
+                        cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET);
+  allocator().set_candidate_filter(cricket::CF_HOST);
+  EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP));
+  session_->StartGettingPorts();
+  EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout);
+  EXPECT_EQ(2U, candidates_.size()); // Host UDP/TCP candidates only.
+  EXPECT_EQ(2U, ports_.size()); // UDP/TCP ports only.
+  for (size_t i = 0; i < candidates_.size(); ++i) {
+    EXPECT_EQ(std::string(cricket::LOCAL_PORT_TYPE), candidates_[i].type());
+  }
+}
+
+// Host is behind the NAT.
+TEST_F(PortAllocatorTest, TestCandidateFilterWithReflexiveOnly) {
+  AddInterface(kPrivateAddr);
+  rtc::scoped_ptr<rtc::NATServer> nat_server(
+      CreateNatServer(kNatAddr, rtc::NAT_OPEN_CONE));
+  ServerAddresses stun_servers;
+  stun_servers.insert(kStunAddr);
+  allocator_.reset(new cricket::BasicPortAllocator(
+      &network_manager_, &nat_socket_factory_, stun_servers));
+  allocator().set_step_delay(cricket::kMinimumStepDelay);
+  allocator().set_flags(cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG |
+                        cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET);
+  allocator().set_candidate_filter(cricket::CF_REFLEXIVE);
+  EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP));
+  session_->StartGettingPorts();
+  EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout);
+  // Host is behind NAT, no private address will be exposed. Hence only UDP
+  // port with STUN candidate will be sent outside.
+  EXPECT_EQ(1U, candidates_.size()); // Only STUN candidate.
+  EXPECT_EQ(1U, ports_.size());  // Only UDP port will be in ready state.
+  for (size_t i = 0; i < candidates_.size(); ++i) {
+    EXPECT_EQ(std::string(cricket::STUN_PORT_TYPE), candidates_[i].type());
+  }
+}
+
+// Host is not behind the NAT.
+TEST_F(PortAllocatorTest, TestCandidateFilterWithReflexiveOnlyAndNoNAT) {
+  AddInterface(kClientAddr);
+  allocator().set_flags(cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG |
+                        cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET);
+  allocator().set_candidate_filter(cricket::CF_REFLEXIVE);
+  EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP));
+  session_->StartGettingPorts();
+  EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout);
+  // Host has a public address, both UDP and TCP candidates will be exposed.
+  EXPECT_EQ(2U, candidates_.size()); // Local UDP + TCP candidate.
+  EXPECT_EQ(2U, ports_.size());  //  UDP and TCP ports will be in ready state.
+  for (size_t i = 0; i < candidates_.size(); ++i) {
+    EXPECT_EQ(std::string(cricket::LOCAL_PORT_TYPE), candidates_[i].type());
+  }
+}
+
 TEST_F(PortAllocatorTest, TestBasicMuxFeatures) {
   AddInterface(kClientAddr);
   allocator().set_flags(cricket::PORTALLOCATOR_ENABLE_BUNDLE);