stats: expose local candidate stats even before pairing

BUG=webrtc:14163

Change-Id: If176a5f1d0ea9a2d998e18b5d4dc5c70ab4dc816
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/265410
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Philipp Hancke <philipp.hancke@googlemail.com>
Cr-Commit-Position: refs/heads/main@{#37188}
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc
index c277ac0..2f5806e 100644
--- a/pc/peer_connection.cc
+++ b/pc/peer_connection.cc
@@ -2275,9 +2275,9 @@
   RTC_DCHECK_RUN_ON(network_thread());
   if (!network_thread_safety_->alive())
     return {};
-  cricket::CandidateStatsList candidate_states_list;
-  port_allocator_->GetCandidateStatsFromPooledSessions(&candidate_states_list);
-  return candidate_states_list;
+  cricket::CandidateStatsList candidate_stats_list;
+  port_allocator_->GetCandidateStatsFromPooledSessions(&candidate_stats_list);
+  return candidate_stats_list;
 }
 
 std::map<std::string, cricket::TransportStats>
diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc
index 1a611bd..2874fb4 100644
--- a/pc/rtc_stats_collector.cc
+++ b/pc/rtc_stats_collector.cc
@@ -1634,7 +1634,7 @@
     for (const auto& channel_stats : transport_stats.channel_stats) {
       std::string transport_id = RTCTransportStatsIDFromTransportChannel(
           transport_name, channel_stats.component);
-      for (const cricket::ConnectionInfo& info :
+      for (const auto& info :
            channel_stats.ice_transport_stats.connection_infos) {
         std::unique_ptr<RTCIceCandidatePairStats> candidate_pair_stats(
             new RTCIceCandidatePairStats(
@@ -1710,6 +1710,15 @@
 
         report->AddStats(std::move(candidate_pair_stats));
       }
+
+      // Produce local candidate stats. If a transport exists these will already
+      // have been produced.
+      for (const auto& candidate_stats :
+           channel_stats.ice_transport_stats.candidate_stats_list) {
+        const auto& candidate = candidate_stats.candidate();
+        ProduceIceCandidateStats(timestamp_us, candidate, true, transport_id,
+                                 report);
+      }
     }
   }
 }
diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc
index 586d07f..1022380 100644
--- a/pc/rtc_stats_collector_unittest.cc
+++ b/pc/rtc_stats_collector_unittest.cc
@@ -1363,6 +1363,26 @@
   expected_a_local_relay_prflx.network_adapter_type =
       RTCNetworkAdapterType::kUnknown;
 
+  // A non-paired local candidate.
+  std::unique_ptr<cricket::Candidate> a_local_host_not_paired =
+      CreateFakeCandidate("1.2.3.4", 4404, "a_local_host_not_paired's protocol",
+                          rtc::ADAPTER_TYPE_VPN, cricket::LOCAL_PORT_TYPE, 0,
+                          rtc::ADAPTER_TYPE_ETHERNET);
+  RTCLocalIceCandidateStats expected_a_local_host_not_paired(
+      "RTCIceCandidate_" + a_local_host_not_paired->id(), 0);
+  expected_a_local_host_not_paired.transport_id = "RTCTransport_a_0";
+  expected_a_local_host_not_paired.network_type = "vpn";
+  expected_a_local_host_not_paired.ip = "1.2.3.4";
+  expected_a_local_host_not_paired.address = "1.2.3.4";
+  expected_a_local_host_not_paired.port = 4404;
+  expected_a_local_host_not_paired.protocol =
+      "a_local_host_not_paired's protocol";
+  expected_a_local_host_not_paired.candidate_type = "host";
+  expected_a_local_host_not_paired.priority = 0;
+  expected_a_local_host_not_paired.vpn = true;
+  expected_a_local_host_not_paired.network_adapter_type =
+      RTCNetworkAdapterType::kEthernet;
+
   // Candidates in the second transport stats.
   std::unique_ptr<cricket::Candidate> b_local =
       CreateFakeCandidate("42.42.42.42", 42, "b_local's protocol",
@@ -1419,6 +1439,8 @@
       .local_candidate = *a_local_relay_prflx.get();
   a_transport_channel_stats.ice_transport_stats.connection_infos[3]
       .remote_candidate = *a_remote_relay.get();
+  a_transport_channel_stats.ice_transport_stats.candidate_stats_list.push_back(
+      cricket::CandidateStats(*a_local_host_not_paired.get()));
 
   pc_->AddVoiceChannel("audio", "a");
   pc_->SetTransportStats("a", a_transport_channel_stats);
@@ -1439,6 +1461,12 @@
   ASSERT_TRUE(report->Get(expected_a_local_host.id()));
   EXPECT_EQ(expected_a_local_host, report->Get(expected_a_local_host.id())
                                        ->cast_to<RTCLocalIceCandidateStats>());
+
+  ASSERT_TRUE(report->Get(expected_a_local_host_not_paired.id()));
+  EXPECT_EQ(expected_a_local_host_not_paired,
+            report->Get(expected_a_local_host_not_paired.id())
+                ->cast_to<RTCLocalIceCandidateStats>());
+
   ASSERT_TRUE(report->Get(expected_a_remote_srflx.id()));
   EXPECT_EQ(expected_a_remote_srflx,
             report->Get(expected_a_remote_srflx.id())