Implement missing candidate pair packets/bytes sent/received stats.

Specifically:
* packetsSent
* packetsReceived
* packetsDiscardedOnSend
* bytesDiscardedOnSend

Bug: webrtc:10569
Change-Id: Id92c20b93dea57637239a6321bd8aa644867f272
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/232961
Commit-Queue: Taylor Brandstetter <deadbeef@webrtc.org>
Reviewed-by: Jonas Oreland <jonaso@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35113}
diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h
index 849ef80..7742538 100644
--- a/api/stats/rtcstats_objects.h
+++ b/api/stats/rtcstats_objects.h
@@ -170,6 +170,8 @@
   RTCStatsMember<bool> writable;
   // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7062
   RTCStatsMember<bool> readable;
+  RTCStatsMember<uint64_t> packets_sent;
+  RTCStatsMember<uint64_t> packets_received;
   RTCStatsMember<uint64_t> bytes_sent;
   RTCStatsMember<uint64_t> bytes_received;
   RTCStatsMember<double> total_round_trip_time;
@@ -194,6 +196,8 @@
   RTCStatsMember<uint64_t> consent_responses_received;
   // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7062
   RTCStatsMember<uint64_t> consent_responses_sent;
+  RTCStatsMember<uint64_t> packets_discarded_on_send;
+  RTCStatsMember<uint64_t> bytes_discarded_on_send;
 };
 
 // https://w3c.github.io/webrtc-stats/#icecandidate-dict*
diff --git a/p2p/base/connection.cc b/p2p/base/connection.cc
index ebfe6fd..f7bd47d 100644
--- a/p2p/base/connection.cc
+++ b/p2p/base/connection.cc
@@ -1374,6 +1374,7 @@
     RTC_DCHECK(sent < 0);
     error_ = port_->GetError();
     stats_.sent_discarded_packets++;
+    stats_.sent_discarded_bytes += size;
   } else {
     send_rate_tracker_.AddSamplesAtTime(now, sent);
   }
diff --git a/p2p/base/connection_info.cc b/p2p/base/connection_info.cc
index ebea2ab..d0cd323 100644
--- a/p2p/base/connection_info.cc
+++ b/p2p/base/connection_info.cc
@@ -19,6 +19,7 @@
       timeout(false),
       new_connection(false),
       rtt(0),
+      sent_discarded_bytes(0),
       sent_total_bytes(0),
       sent_bytes_second(0),
       sent_discarded_packets(0),
diff --git a/p2p/base/connection_info.h b/p2p/base/connection_info.h
index b5e1c14..1117595 100644
--- a/p2p/base/connection_info.h
+++ b/p2p/base/connection_info.h
@@ -41,12 +41,15 @@
   bool timeout;              // Has this connection timed out?
   bool new_connection;       // Is this a newly created connection?
   size_t rtt;                // The STUN RTT for this connection.
-  size_t sent_total_bytes;   // Total bytes sent on this connection.
+  size_t sent_discarded_bytes;  // Number of outgoing bytes discarded due to
+                                // socket errors.
+  size_t sent_total_bytes;      // Total bytes sent on this connection. Does not
+                                // include discarded bytes.
   size_t sent_bytes_second;  // Bps over the last measurement interval.
   size_t sent_discarded_packets;  // Number of outgoing packets discarded due to
                                   // socket errors.
   size_t sent_total_packets;  // Number of total outgoing packets attempted for
-                              // sending.
+                              // sending, including discarded packets.
   size_t sent_ping_requests_total;  // Number of STUN ping request sent.
   size_t sent_ping_requests_before_first_response;  // Number of STUN ping
   // sent before receiving the first response.
diff --git a/p2p/base/p2p_transport_channel_unittest.cc b/p2p/base/p2p_transport_channel_unittest.cc
index 0ddeb2d..dfc3cec 100644
--- a/p2p/base/p2p_transport_channel_unittest.cc
+++ b/p2p/base/p2p_transport_channel_unittest.cc
@@ -1338,6 +1338,13 @@
                              kMediumTimeout, clock);
   // Sends and receives 10 packets.
   TestSendRecv(&clock);
+
+  // Try sending a packet which is discarded due to the socket being blocked.
+  virtual_socket_server()->SetSendingBlocked(true);
+  const char* data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
+  int len = static_cast<int>(strlen(data));
+  EXPECT_EQ(-1, SendData(ep1_ch1(), data, len));
+
   IceTransportStats ice_transport_stats;
   ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats));
   ASSERT_GE(ice_transport_stats.connection_infos.size(), 1u);
@@ -1355,9 +1362,12 @@
   EXPECT_TRUE(best_conn_info->receiving);
   EXPECT_TRUE(best_conn_info->writable);
   EXPECT_FALSE(best_conn_info->timeout);
-  EXPECT_EQ(10U, best_conn_info->sent_total_packets);
-  EXPECT_EQ(0U, best_conn_info->sent_discarded_packets);
+  // Note that discarded packets are counted in sent_total_packets but not
+  // sent_total_bytes.
+  EXPECT_EQ(11U, best_conn_info->sent_total_packets);
+  EXPECT_EQ(1U, best_conn_info->sent_discarded_packets);
   EXPECT_EQ(10 * 36U, best_conn_info->sent_total_bytes);
+  EXPECT_EQ(36U, best_conn_info->sent_discarded_bytes);
   EXPECT_EQ(10 * 36U, best_conn_info->recv_total_bytes);
   EXPECT_EQ(10U, best_conn_info->packets_received);
   DestroyChannels();
diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc
index 1f8b28d..7637c07 100644
--- a/pc/rtc_stats_collector.cc
+++ b/pc/rtc_stats_collector.cc
@@ -1552,8 +1552,18 @@
         // false after a certain amount of time without a response passes.
         // https://crbug.com/633550
         candidate_pair_stats->writable = info.writable;
+        // Note that sent_total_packets includes discarded packets but
+        // sent_total_bytes does not.
+        candidate_pair_stats->packets_sent = static_cast<uint64_t>(
+            info.sent_total_packets - info.sent_discarded_packets);
+        candidate_pair_stats->packets_discarded_on_send =
+            static_cast<uint64_t>(info.sent_discarded_packets);
+        candidate_pair_stats->packets_received =
+            static_cast<uint64_t>(info.packets_received);
         candidate_pair_stats->bytes_sent =
             static_cast<uint64_t>(info.sent_total_bytes);
+        candidate_pair_stats->bytes_discarded_on_send =
+            static_cast<uint64_t>(info.sent_discarded_bytes);
         candidate_pair_stats->bytes_received =
             static_cast<uint64_t>(info.recv_total_bytes);
         candidate_pair_stats->total_round_trip_time =
diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc
index 8c4f0a6..5f94d81 100644
--- a/pc/rtc_stats_collector_unittest.cc
+++ b/pc/rtc_stats_collector_unittest.cc
@@ -1421,6 +1421,10 @@
   connection_info.local_candidate = *local_candidate.get();
   connection_info.remote_candidate = *remote_candidate.get();
   connection_info.writable = true;
+  connection_info.sent_discarded_packets = 3;
+  connection_info.sent_total_packets = 10;
+  connection_info.packets_received = 51;
+  connection_info.sent_discarded_bytes = 7;
   connection_info.sent_total_bytes = 42;
   connection_info.recv_total_bytes = 1234;
   connection_info.total_round_trip_time_ms = 0;
@@ -1458,8 +1462,12 @@
   expected_pair.priority = 5555;
   expected_pair.nominated = false;
   expected_pair.writable = true;
+  expected_pair.packets_sent = 7;
+  expected_pair.packets_received = 51;
+  expected_pair.packets_discarded_on_send = 3;
   expected_pair.bytes_sent = 42;
   expected_pair.bytes_received = 1234;
+  expected_pair.bytes_discarded_on_send = 7;
   expected_pair.total_round_trip_time = 0.0;
   expected_pair.requests_received = 2020;
   expected_pair.requests_sent = 2000;
diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc
index afa50d8..81ead00 100644
--- a/pc/rtc_stats_integrationtest.cc
+++ b/pc/rtc_stats_integrationtest.cc
@@ -489,7 +489,13 @@
     verifier.TestMemberIsDefined(candidate_pair.nominated);
     verifier.TestMemberIsDefined(candidate_pair.writable);
     verifier.TestMemberIsUndefined(candidate_pair.readable);
+    verifier.TestMemberIsNonNegative<uint64_t>(candidate_pair.packets_sent);
+    verifier.TestMemberIsNonNegative<uint64_t>(
+        candidate_pair.packets_discarded_on_send);
+    verifier.TestMemberIsNonNegative<uint64_t>(candidate_pair.packets_received);
     verifier.TestMemberIsNonNegative<uint64_t>(candidate_pair.bytes_sent);
+    verifier.TestMemberIsNonNegative<uint64_t>(
+        candidate_pair.bytes_discarded_on_send);
     verifier.TestMemberIsNonNegative<uint64_t>(candidate_pair.bytes_received);
     verifier.TestMemberIsNonNegative<double>(
         candidate_pair.total_round_trip_time);
diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc
index 762011d..961d17c 100644
--- a/stats/rtcstats_objects.cc
+++ b/stats/rtcstats_objects.cc
@@ -173,6 +173,8 @@
     &nominated,
     &writable,
     &readable,
+    &packets_sent,
+    &packets_received,
     &bytes_sent,
     &bytes_received,
     &total_round_trip_time,
@@ -188,7 +190,9 @@
     &consent_requests_received,
     &consent_requests_sent,
     &consent_responses_received,
-    &consent_responses_sent)
+    &consent_responses_sent,
+    &packets_discarded_on_send,
+    &bytes_discarded_on_send)
 // clang-format on
 
 RTCIceCandidatePairStats::RTCIceCandidatePairStats(const std::string& id,
@@ -206,6 +210,8 @@
       nominated("nominated"),
       writable("writable"),
       readable("readable"),
+      packets_sent("packetsSent"),
+      packets_received("packetsReceived"),
       bytes_sent("bytesSent"),
       bytes_received("bytesReceived"),
       total_round_trip_time("totalRoundTripTime"),
@@ -221,7 +227,9 @@
       consent_requests_received("consentRequestsReceived"),
       consent_requests_sent("consentRequestsSent"),
       consent_responses_received("consentResponsesReceived"),
-      consent_responses_sent("consentResponsesSent") {}
+      consent_responses_sent("consentResponsesSent"),
+      packets_discarded_on_send("packetsDiscardedOnSend"),
+      bytes_discarded_on_send("bytesDiscardedOnSend") {}
 
 RTCIceCandidatePairStats::RTCIceCandidatePairStats(
     const RTCIceCandidatePairStats& other)
@@ -234,6 +242,8 @@
       nominated(other.nominated),
       writable(other.writable),
       readable(other.readable),
+      packets_sent(other.packets_sent),
+      packets_received(other.packets_received),
       bytes_sent(other.bytes_sent),
       bytes_received(other.bytes_received),
       total_round_trip_time(other.total_round_trip_time),
@@ -249,7 +259,9 @@
       consent_requests_received(other.consent_requests_received),
       consent_requests_sent(other.consent_requests_sent),
       consent_responses_received(other.consent_responses_received),
-      consent_responses_sent(other.consent_responses_sent) {}
+      consent_responses_sent(other.consent_responses_sent),
+      packets_discarded_on_send(other.packets_discarded_on_send),
+      bytes_discarded_on_send(other.bytes_discarded_on_send) {}
 
 RTCIceCandidatePairStats::~RTCIceCandidatePairStats() {}