Add triggered checks.

BUG=4590
R=guoweis@webrtc.org, juberti@webrtc.org

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

Cr-Commit-Position: refs/heads/master@{#9409}
diff --git a/webrtc/base/firewallsocketserver.cc b/webrtc/base/firewallsocketserver.cc
index c35a687..6339017 100644
--- a/webrtc/base/firewallsocketserver.cc
+++ b/webrtc/base/firewallsocketserver.cc
@@ -126,13 +126,13 @@
 void FirewallSocketServer::AddRule(bool allow, FirewallProtocol p,
                                    FirewallDirection d,
                                    const SocketAddress& addr) {
-  SocketAddress src, dst;
-  if (d == FD_IN) {
-    dst = addr;
-  } else {
-    src = addr;
+  SocketAddress any;
+  if (d == FD_IN || d == FD_ANY) {
+    AddRule(allow, p, any, addr);
   }
-  AddRule(allow, p, src, dst);
+  if (d == FD_OUT || d == FD_ANY) {
+    AddRule(allow, p, addr, any);
+  }
 }
 
 
diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc
index a1f8967..95696e1 100644
--- a/webrtc/p2p/base/p2ptransportchannel.cc
+++ b/webrtc/p2p/base/p2ptransportchannel.cc
@@ -1222,17 +1222,37 @@
     return best_connection_;
   }
 
-  Connection* oldest_conn = NULL;
-  uint32 oldest_time = 0xFFFFFFFF;
-  for (uint32 i = 0; i < connections_.size(); ++i) {
-    if (IsPingable(connections_[i])) {
-      if (connections_[i]->last_ping_sent() < oldest_time) {
-        oldest_time = connections_[i]->last_ping_sent();
-        oldest_conn = connections_[i];
-      }
+  // First, find "triggered checks".  We ping first those connections
+  // that have received a ping but have not sent a ping since receiving
+  // it (last_received_ping > last_sent_ping).  But we shouldn't do
+  // triggered checks if the connection is already writable.
+  Connection* oldest_needing_triggered_check = nullptr;
+  Connection* oldest = nullptr;
+  for (Connection* conn : connections_) {
+    if (!IsPingable(conn)) {
+      continue;
+    }
+    bool needs_triggered_check =
+        (protocol_type_ == ICEPROTO_RFC5245 &&
+         !conn->writable() &&
+         conn->last_ping_received() > conn->last_ping_sent());
+    if (needs_triggered_check &&
+        (!oldest_needing_triggered_check ||
+         (conn->last_ping_received() <
+          oldest_needing_triggered_check->last_ping_received()))) {
+      oldest_needing_triggered_check = conn;
+    }
+    if (!oldest || (conn->last_ping_sent() < oldest->last_ping_sent())) {
+      oldest = conn;
     }
   }
-  return oldest_conn;
+
+  if (oldest_needing_triggered_check) {
+    LOG(LS_INFO) << "Selecting connection for triggered check: " <<
+        oldest_needing_triggered_check->ToString();
+    return oldest_needing_triggered_check;
+  }
+  return oldest;
 }
 
 // Apart from sending ping from |conn| this method also updates
diff --git a/webrtc/p2p/base/p2ptransportchannel.h b/webrtc/p2p/base/p2ptransportchannel.h
index a014701..11399d0 100644
--- a/webrtc/p2p/base/p2ptransportchannel.h
+++ b/webrtc/p2p/base/p2ptransportchannel.h
@@ -87,7 +87,7 @@
 
   // Note: This is only for testing purpose.
   // |ports_| should not be changed from outside.
-  const std::vector<PortInterface *>& ports() { return ports_; }
+  const std::vector<PortInterface*>& ports() { return ports_; }
 
   IceMode remote_ice_mode() const { return remote_ice_mode_; }
 
diff --git a/webrtc/p2p/base/p2ptransportchannel_unittest.cc b/webrtc/p2p/base/p2ptransportchannel_unittest.cc
index 42ef919..f6beee0 100644
--- a/webrtc/p2p/base/p2ptransportchannel_unittest.cc
+++ b/webrtc/p2p/base/p2ptransportchannel_unittest.cc
@@ -13,6 +13,7 @@
 #include "webrtc/p2p/base/teststunserver.h"
 #include "webrtc/p2p/base/testturnserver.h"
 #include "webrtc/p2p/client/basicportallocator.h"
+#include "webrtc/p2p/client/fakeportallocator.h"
 #include "webrtc/base/dscp.h"
 #include "webrtc/base/fakenetwork.h"
 #include "webrtc/base/firewallsocketserver.h"
@@ -1017,7 +1018,7 @@
     P2PTransportChannelTest::kMatrixSharedSocketAsIce
         [NUM_CONFIGS][NUM_CONFIGS] = {
 //      OPEN  CONE  ADDR  PORT  SYMM  2CON  SCON  !UDP  !TCP  HTTP  PRXH  PRXS
-/*OP*/ {LULU, LUSU, LUSU, LUSU, LUPU, LUSU, LUPU, PTLT, LTPT, LSRS, NULL, PTLT},
+/*OP*/ {LULU, LUSU, LUSU, LUSU, LUPU, LUSU, LUPU, PTLT, LTPT, LSRS, NULL, LTPT},
 /*CO*/ {LULU, LUSU, LUSU, LUSU, LUPU, LUSU, LUPU, NULL, NULL, LSRS, NULL, LTRT},
 /*AD*/ {LULU, LUSU, LUSU, LUSU, LUPU, LUSU, LUPU, NULL, NULL, LSRS, NULL, LTRT},
 /*PO*/ {LULU, LUSU, LUSU, LUSU, LURU, LUSU, LURU, NULL, NULL, LSRS, NULL, LTRT},
@@ -1698,3 +1699,111 @@
 
   DestroyChannels();
 }
+
+class P2PTransportChannelPingOrderTest : public testing::Test,
+                                         public sigslot::has_slots<> {
+ public:
+  P2PTransportChannelPingOrderTest() :
+      pss_(new rtc::PhysicalSocketServer),
+      vss_(new rtc::VirtualSocketServer(pss_.get())),
+      ss_scope_(vss_.get()) {
+  }
+
+ protected:
+  void PrepareChannel(cricket::P2PTransportChannel* ch) {
+    ch->SignalRequestSignaling.connect(
+        this, &P2PTransportChannelPingOrderTest::OnChannelRequestSignaling);
+    ch->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
+    ch->SetIceRole(cricket::ICEROLE_CONTROLLING);
+    ch->SetIceCredentials(kIceUfrag[0], kIcePwd[0]);
+    ch->SetRemoteIceCredentials(kIceUfrag[1], kIcePwd[1]);
+  }
+
+  void OnChannelRequestSignaling(cricket::TransportChannelImpl* channel) {
+    channel->OnSignalingReady();
+  }
+
+  cricket::Candidate CreateCandidate(const std::string& ip,
+                                     int port,
+                                     int priority) {
+    cricket::Candidate c;
+    c.set_address(rtc::SocketAddress(ip, port));
+    c.set_component(1);
+    c.set_protocol(cricket::UDP_PROTOCOL_NAME);
+    c.set_priority(priority);
+    return c;
+  }
+
+  cricket::Connection* WaitForConnectionTo(cricket::P2PTransportChannel* ch,
+                                           const std::string& ip,
+                                           int port_num) {
+    EXPECT_TRUE_WAIT(GetConnectionTo(ch, ip, port_num) != nullptr, 3000);
+    return GetConnectionTo(ch, ip, port_num);
+  }
+
+  cricket::Connection* GetConnectionTo(cricket::P2PTransportChannel* ch,
+                                       const std::string& ip,
+                                       int port_num) {
+    if (ch->ports().empty()) {
+      return nullptr;
+    }
+    cricket::Port* port = static_cast<cricket::Port*>(ch->ports()[0]);
+    if (!port) {
+      return nullptr;
+    }
+    return port->GetConnection(rtc::SocketAddress(ip, port_num));
+  }
+
+ private:
+  rtc::scoped_ptr<rtc::PhysicalSocketServer> pss_;
+  rtc::scoped_ptr<rtc::VirtualSocketServer> vss_;
+  rtc::SocketServerScope ss_scope_;
+};
+
+TEST_F(P2PTransportChannelPingOrderTest, TestTriggeredChecks) {
+  cricket::FakePortAllocator pa(rtc::Thread::Current(), nullptr);
+  cricket::P2PTransportChannel ch("trigger checks", 1, nullptr, &pa);
+  PrepareChannel(&ch);
+  ch.Connect();
+  ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
+  ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 2));
+
+  cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
+  cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
+  ASSERT_TRUE(conn1 != nullptr);
+  ASSERT_TRUE(conn2 != nullptr);
+
+  // Before a triggered check, the first connection to ping is the
+  // highest priority one.
+  EXPECT_EQ(conn2, ch.FindNextPingableConnection());
+
+  // Receiving a ping causes a triggered check which should make conn1
+  // be pinged first instead of conn2, even though conn2 has a higher
+  // priority.
+  conn1->ReceivedPing();
+  EXPECT_EQ(conn1, ch.FindNextPingableConnection());
+}
+
+TEST_F(P2PTransportChannelPingOrderTest, TestNoTriggeredChecksWhenWritable) {
+  cricket::FakePortAllocator pa(rtc::Thread::Current(), nullptr);
+  cricket::P2PTransportChannel ch("trigger checks", 1, nullptr, &pa);
+  PrepareChannel(&ch);
+  ch.Connect();
+  ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
+  ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 2));
+
+  cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
+  cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
+  ASSERT_TRUE(conn1 != nullptr);
+  ASSERT_TRUE(conn2 != nullptr);
+
+  EXPECT_EQ(conn2, ch.FindNextPingableConnection());
+  conn1->ReceivedPingResponse();
+  ASSERT_TRUE(conn1->writable());
+  conn1->ReceivedPing();
+
+  // Ping received, but the connection is already writable, so no
+  // "triggered check" and conn2 is pinged before conn1 because it has
+  // a higher priority.
+  EXPECT_EQ(conn2, ch.FindNextPingableConnection());
+}
diff --git a/webrtc/p2p/base/port.cc b/webrtc/p2p/base/port.cc
index ee3991d..25b5efd 100644
--- a/webrtc/p2p/base/port.cc
+++ b/webrtc/p2p/base/port.cc
@@ -1240,6 +1240,18 @@
   set_read_state(STATE_READABLE);
 }
 
+void Connection::ReceivedPingResponse() {
+  // We've already validated that this is a STUN binding response with
+  // the correct local and remote username for this connection.
+  // So if we're not already, become writable. We may be bringing a pruned
+  // connection back to life, but if we don't really want it, we can always
+  // prune it again.
+  set_write_state(STATE_WRITABLE);
+  set_state(STATE_SUCCEEDED);
+  pings_since_last_response_.clear();
+  last_ping_response_received_ = rtc::Time();
+}
+
 std::string Connection::ToDebugId() const {
   std::stringstream ss;
   ss << std::hex << this;
@@ -1304,19 +1316,13 @@
   // connection.
   rtc::LoggingSeverity sev = !writable() ? rtc::LS_INFO : rtc::LS_VERBOSE;
 
-  // We've already validated that this is a STUN binding response with
-  // the correct local and remote username for this connection.
-  // So if we're not already, become writable. We may be bringing a pruned
-  // connection back to life, but if we don't really want it, we can always
-  // prune it again.
   uint32 rtt = request->Elapsed();
-  set_write_state(STATE_WRITABLE);
-  set_state(STATE_SUCCEEDED);
 
+  ReceivedPingResponse();
   if (remote_ice_mode_ == ICEMODE_LITE) {
     // A ice-lite end point never initiates ping requests. This will allow
-    // us to move to STATE_READABLE.
-    ReceivedPing();
+    // us to move to STATE_READABLE without an incoming ping request.
+    set_read_state(STATE_READABLE);
   }
 
   if (LOG_CHECK_LEVEL_V(sev)) {
@@ -1332,8 +1338,6 @@
                       << ", pings_since_last_response=" << pings;
   }
 
-  pings_since_last_response_.clear();
-  last_ping_response_received_ = rtc::Time();
   rtt_ = (RTT_RATIO * rtt_ + rtt) / (RTT_RATIO + 1);
 
   // Peer reflexive candidate is only for RFC 5245 ICE.
diff --git a/webrtc/p2p/base/port.h b/webrtc/p2p/base/port.h
index a6a54e8..a36c9f1 100644
--- a/webrtc/p2p/base/port.h
+++ b/webrtc/p2p/base/port.h
@@ -519,6 +519,7 @@
   // Called when this connection should try checking writability again.
   uint32 last_ping_sent() const { return last_ping_sent_; }
   void Ping(uint32 now);
+  void ReceivedPingResponse();
 
   // Called whenever a valid ping is received on this connection.  This is
   // public because the connection intercepts the first ping for us.