Extend NetworkRoute with more info about local/remote endpoints

This patch extends the NetworkRoute struct with more information
about local/remote endpoints. It adds
- adapter type
- adapter id
- relay

(previously it was "only" network_id)

The patch leaves the {local/remote}_network_id fields
around and populated since downstream projects depend
on them. They will be removed once they have migrated.

OWNER: srte@ call/ test/
OWNER: asapersson@ video/
OWNER: hta@ p2p/ pc/ rtc_base/

BUG: webrtc:11434
Change-Id: I9bcec385b40d707db385fef40b2c7a315dd35dd0
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/170628
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30848}
diff --git a/call/rtp_transport_controller_send.cc b/call/rtp_transport_controller_send.cc
index fb684ca..f219c21 100644
--- a/call/rtp_transport_controller_send.cc
+++ b/call/rtp_transport_controller_send.cc
@@ -249,8 +249,11 @@
     const std::string& transport_name,
     const rtc::NetworkRoute& network_route) {
   // Check if the network route is connected.
+
+  RTC_LOG(LS_INFO) << "Network route changed on transport " << transport_name
+                   << ": new_route = " << network_route.DebugString();
+
   if (!network_route.connected) {
-    RTC_LOG(LS_INFO) << "Transport " << transport_name << " is disconnected";
     // TODO(honghaiz): Perhaps handle this in SignalChannelNetworkState and
     // consider merging these two methods.
     return;
@@ -269,17 +272,23 @@
     // No need to reset BWE if this is the first time the network connects.
     return;
   }
-  if (kv->second.connected != network_route.connected ||
-      kv->second.local_network_id != network_route.local_network_id ||
-      kv->second.remote_network_id != network_route.remote_network_id) {
-    kv->second = network_route;
+  //
+  auto old_route = kv->second;
+  kv->second = network_route;
+  RTC_LOG(LS_INFO) << "old_route = " << old_route.DebugString();
+
+  // Check if enough conditions of the new/old route has changed
+  // to trigger resetting of bitrates (and a probe).
+  // Currently we only check local/remote network id (i.e IP address) and
+  // connected state and do not consider if we change route due to TURN.
+  //
+  // TODO(bugs.webrtc.org/11438) : Experiment with using more information/
+  // other conditions.
+  if (old_route.connected != network_route.connected ||
+      old_route.local.network_id() != network_route.local.network_id() ||
+      old_route.remote.network_id() != network_route.remote.network_id()) {
     BitrateConstraints bitrate_config = bitrate_configurator_.GetConfig();
-    RTC_LOG(LS_INFO) << "Network route changed on transport " << transport_name
-                     << ": new local network id "
-                     << network_route.local_network_id
-                     << " new remote network id "
-                     << network_route.remote_network_id
-                     << " Reset bitrates to min: "
+    RTC_LOG(LS_INFO) << "Reset bitrates to min: "
                      << bitrate_config.min_bitrate_bps
                      << " bps, start: " << bitrate_config.start_bitrate_bps
                      << " bps,  max: " << bitrate_config.max_bitrate_bps
@@ -297,8 +306,11 @@
       RTC_DCHECK_RUN_ON(&task_queue_);
       transport_overhead_bytes_per_packet_ = network_route.packet_overhead;
       if (reset_feedback_on_route_change_) {
+        // TODO(bugs.webrtc.org/11438) : Consider if transport_feedback_adapter
+        // should have a real "route" rather than just local/remote network_id.
         transport_feedback_adapter_.SetNetworkIds(
-            network_route.local_network_id, network_route.remote_network_id);
+            network_route.local.network_id(),
+            network_route.remote.network_id());
       }
       if (controller_) {
         PostUpdates(controller_->OnNetworkRouteChange(msg));
diff --git a/p2p/base/p2p_transport_channel.cc b/p2p/base/p2p_transport_channel.cc
index 906aa59..d935a45 100644
--- a/p2p/base/p2p_transport_channel.cc
+++ b/p2p/base/p2p_transport_channel.cc
@@ -57,6 +57,43 @@
   return cricket::WEAK_PING_INTERVAL;
 }
 
+rtc::AdapterType GuessAdapterTypeFromNetworkCost(int network_cost) {
+  // The current network costs have been unchanged since they were added
+  // to webrtc. If they ever were to change we would need to reconsider
+  // this method.
+  switch (network_cost) {
+    case rtc::kNetworkCostMin:
+      return rtc::ADAPTER_TYPE_ETHERNET;
+    case rtc::kNetworkCostLow:
+      return rtc::ADAPTER_TYPE_WIFI;
+    case rtc::kNetworkCostHigh:
+      return rtc::ADAPTER_TYPE_CELLULAR;
+    case rtc::kNetworkCostUnknown:
+      return rtc::ADAPTER_TYPE_UNKNOWN;
+    case rtc::kNetworkCostMax:
+      return rtc::ADAPTER_TYPE_ANY;
+  }
+  return rtc::ADAPTER_TYPE_UNKNOWN;
+}
+
+rtc::RouteEndpoint CreateRouteEndpointFromCandidate(
+    bool local,
+    const cricket::Candidate& candidate,
+    bool uses_turn) {
+  auto adapter_type = candidate.network_type();
+  if (!local && adapter_type == rtc::ADAPTER_TYPE_UNKNOWN) {
+    adapter_type = GuessAdapterTypeFromNetworkCost(candidate.network_cost());
+  }
+
+  // TODO(bugs.webrtc.org/9446) : Rewrite if information about remote network
+  // adapter becomes available. The implication of this implementation is that
+  // we will only ever report 1 adapter per type. In practice this is probably
+  // fine, since the endpoint also contains network-id.
+  uint16_t adapter_id = static_cast<int>(adapter_type);
+  return rtc::RouteEndpoint(adapter_type, adapter_id, candidate.network_id(),
+                            uses_turn);
+}
+
 }  // unnamed namespace
 
 namespace cricket {
@@ -1687,10 +1724,21 @@
 
     network_route_.emplace(rtc::NetworkRoute());
     network_route_->connected = ReadyToSend(selected_connection_);
-    network_route_->local_network_id =
-        selected_connection_->local_candidate().network_id();
-    network_route_->remote_network_id =
-        selected_connection_->remote_candidate().network_id();
+    network_route_->local = CreateRouteEndpointFromCandidate(
+        /* local= */ true, selected_connection_->local_candidate(),
+        /* uses_turn= */ selected_connection_->port()->Type() ==
+            RELAY_PORT_TYPE);
+    network_route_->remote = CreateRouteEndpointFromCandidate(
+        /* local= */ false, selected_connection_->remote_candidate(),
+        /* uses_turn= */ selected_connection_->remote_candidate().type() ==
+            RELAY_PORT_TYPE);
+
+    // Downstream projects depend on the old representation,
+    // populate that until they have been migrated.
+    // TODO(jonaso): remove.
+    network_route_->local_network_id = network_route_->local.network_id();
+    network_route_->remote_network_id = network_route_->remote.network_id();
+
     network_route_->last_sent_packet_id = last_sent_packet_id_;
     network_route_->packet_overhead =
         selected_connection_->local_candidate().address().ipaddr().overhead() +
diff --git a/p2p/base/p2p_transport_channel_unittest.cc b/p2p/base/p2p_transport_channel_unittest.cc
index ee7456a..c66a9f7 100644
--- a/p2p/base/p2p_transport_channel_unittest.cc
+++ b/p2p/base/p2p_transport_channel_unittest.cc
@@ -3221,9 +3221,9 @@
       return !last_network_route_.has_value();
     } else {
       return pair->local_candidate().network_id() ==
-                 last_network_route_->local_network_id &&
+                 last_network_route_->local.network_id() &&
              pair->remote_candidate().network_id() ==
-                 last_network_route_->remote_network_id;
+                 last_network_route_->remote.network_id();
     }
   }
 
diff --git a/pc/channel_unittest.cc b/pc/channel_unittest.cc
index d5c51ec..c1037f7 100644
--- a/pc/channel_unittest.cc
+++ b/pc/channel_unittest.cc
@@ -843,7 +843,6 @@
       rtc::NetworkRoute network_route;
       // The transport channel becomes disconnected.
       fake_rtp_dtls_transport1_->ice_transport()->SignalNetworkRouteChanged(
-
           absl::optional<rtc::NetworkRoute>(network_route));
     });
     WaitForThreads();
@@ -854,8 +853,10 @@
     network_thread_->Invoke<void>(RTC_FROM_HERE, [this] {
       rtc::NetworkRoute network_route;
       network_route.connected = true;
-      network_route.local_network_id = kLocalNetId;
-      network_route.remote_network_id = kRemoteNetId;
+      network_route.local =
+          rtc::RouteEndpoint::CreateWithNetworkId(kLocalNetId);
+      network_route.remote =
+          rtc::RouteEndpoint::CreateWithNetworkId(kRemoteNetId);
       network_route.last_sent_packet_id = kLastPacketId;
       network_route.packet_overhead = kTransportOverheadPerPacket;
       // The transport channel becomes connected.
@@ -867,9 +868,9 @@
     EXPECT_EQ(1, media_channel1->num_network_route_changes());
     EXPECT_TRUE(media_channel1->last_network_route().connected);
     EXPECT_EQ(kLocalNetId,
-              media_channel1->last_network_route().local_network_id);
+              media_channel1->last_network_route().local.network_id());
     EXPECT_EQ(kRemoteNetId,
-              media_channel1->last_network_route().remote_network_id);
+              media_channel1->last_network_route().remote.network_id());
     EXPECT_EQ(kLastPacketId,
               media_channel1->last_network_route().last_sent_packet_id);
     EXPECT_EQ(kTransportOverheadPerPacket + kSrtpOverheadPerPacket,
diff --git a/pc/composite_rtp_transport_test.cc b/pc/composite_rtp_transport_test.cc
index 0248084..fee8c21 100644
--- a/pc/composite_rtp_transport_test.cc
+++ b/pc/composite_rtp_transport_test.cc
@@ -229,17 +229,17 @@
   SetupRtpTransports(/*rtcp_mux=*/true);
 
   rtc::NetworkRoute route;
-  route.local_network_id = 7;
+  route.local = rtc::RouteEndpoint::CreateWithNetworkId(7);
   packet_transport_1_->SetNetworkRoute(route);
 
   EXPECT_EQ(1, network_route_count_);
-  EXPECT_EQ(7, last_network_route_->local_network_id);
+  EXPECT_EQ(7, last_network_route_->local.network_id());
 
-  route.local_network_id = 8;
+  route.local = rtc::RouteEndpoint::CreateWithNetworkId(8);
   packet_transport_2_->SetNetworkRoute(route);
 
   EXPECT_EQ(2, network_route_count_);
-  EXPECT_EQ(8, last_network_route_->local_network_id);
+  EXPECT_EQ(8, last_network_route_->local.network_id());
 }
 
 TEST_F(CompositeRtpTransportTest, RemoveTransport) {
@@ -249,7 +249,7 @@
 
   // Check that signals are disconnected.
   rtc::NetworkRoute route;
-  route.local_network_id = 7;
+  route.local = rtc::RouteEndpoint::CreateWithNetworkId(7);
   packet_transport_1_->SetNetworkRoute(route);
 
   EXPECT_EQ(0, network_route_count_);
diff --git a/pc/rtp_transport_unittest.cc b/pc/rtp_transport_unittest.cc
index 03e8820..b3bd1db 100644
--- a/pc/rtp_transport_unittest.cc
+++ b/pc/rtp_transport_unittest.cc
@@ -155,16 +155,16 @@
   rtc::NetworkRoute network_route;
   // Set a non-null RTP transport with a new network route.
   network_route.connected = true;
-  network_route.local_network_id = kLocalNetId;
-  network_route.remote_network_id = kRemoteNetId;
+  network_route.local = rtc::RouteEndpoint::CreateWithNetworkId(kLocalNetId);
+  network_route.remote = rtc::RouteEndpoint::CreateWithNetworkId(kRemoteNetId);
   network_route.last_sent_packet_id = kLastPacketId;
   network_route.packet_overhead = kTransportOverheadPerPacket;
   fake_rtp.SetNetworkRoute(absl::optional<rtc::NetworkRoute>(network_route));
   transport.SetRtpPacketTransport(&fake_rtp);
   ASSERT_TRUE(observer.network_route());
   EXPECT_TRUE(observer.network_route()->connected);
-  EXPECT_EQ(kLocalNetId, observer.network_route()->local_network_id);
-  EXPECT_EQ(kRemoteNetId, observer.network_route()->remote_network_id);
+  EXPECT_EQ(kLocalNetId, observer.network_route()->local.network_id());
+  EXPECT_EQ(kRemoteNetId, observer.network_route()->remote.network_id());
   EXPECT_EQ(kTransportOverheadPerPacket,
             observer.network_route()->packet_overhead);
   EXPECT_EQ(kLastPacketId, observer.network_route()->last_sent_packet_id);
@@ -184,16 +184,16 @@
   rtc::NetworkRoute network_route;
   // Set a non-null RTCP transport with a new network route.
   network_route.connected = true;
-  network_route.local_network_id = kLocalNetId;
-  network_route.remote_network_id = kRemoteNetId;
+  network_route.local = rtc::RouteEndpoint::CreateWithNetworkId(kLocalNetId);
+  network_route.remote = rtc::RouteEndpoint::CreateWithNetworkId(kRemoteNetId);
   network_route.last_sent_packet_id = kLastPacketId;
   network_route.packet_overhead = kTransportOverheadPerPacket;
   fake_rtcp.SetNetworkRoute(absl::optional<rtc::NetworkRoute>(network_route));
   transport.SetRtcpPacketTransport(&fake_rtcp);
   ASSERT_TRUE(observer.network_route());
   EXPECT_TRUE(observer.network_route()->connected);
-  EXPECT_EQ(kLocalNetId, observer.network_route()->local_network_id);
-  EXPECT_EQ(kRemoteNetId, observer.network_route()->remote_network_id);
+  EXPECT_EQ(kLocalNetId, observer.network_route()->local.network_id());
+  EXPECT_EQ(kRemoteNetId, observer.network_route()->remote.network_id());
   EXPECT_EQ(kTransportOverheadPerPacket,
             observer.network_route()->packet_overhead);
   EXPECT_EQ(kLastPacketId, observer.network_route()->last_sent_packet_id);
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
index 2e4138e..2c6dd3c 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
@@ -765,6 +765,7 @@
     "../system_wrappers:field_trial",
     "network:sent_packet",
     "system:file_wrapper",
+    "system:inline",
     "system:rtc_export",
     "task_utils:to_queued_task",
     "third_party/base64",
@@ -818,6 +819,7 @@
     "net_helpers.h",
     "network.cc",
     "network.h",
+    "network_constants.cc",
     "network_constants.h",
     "network_monitor.cc",
     "network_monitor.h",
diff --git a/rtc_base/network.cc b/rtc_base/network.cc
index 4906184..07b121b 100644
--- a/rtc_base/network.cc
+++ b/rtc_base/network.cc
@@ -85,28 +85,6 @@
   return a->key() < b->key();
 }
 
-std::string AdapterTypeToString(AdapterType type) {
-  switch (type) {
-    case ADAPTER_TYPE_ANY:
-      return "Wildcard";
-    case ADAPTER_TYPE_UNKNOWN:
-      return "Unknown";
-    case ADAPTER_TYPE_ETHERNET:
-      return "Ethernet";
-    case ADAPTER_TYPE_WIFI:
-      return "Wifi";
-    case ADAPTER_TYPE_CELLULAR:
-      return "Cellular";
-    case ADAPTER_TYPE_VPN:
-      return "VPN";
-    case ADAPTER_TYPE_LOOPBACK:
-      return "Loopback";
-    default:
-      RTC_NOTREACHED() << "Invalid type " << type;
-      return std::string();
-  }
-}
-
 uint16_t ComputeNetworkCostByType(int type) {
   switch (type) {
     case rtc::ADAPTER_TYPE_ETHERNET:
diff --git a/rtc_base/network_constants.cc b/rtc_base/network_constants.cc
new file mode 100644
index 0000000..2cb5233
--- /dev/null
+++ b/rtc_base/network_constants.cc
@@ -0,0 +1,39 @@
+/*
+ *  Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "rtc_base/network_constants.h"
+
+#include "rtc_base/checks.h"
+
+namespace rtc {
+
+std::string AdapterTypeToString(AdapterType type) {
+  switch (type) {
+    case ADAPTER_TYPE_ANY:
+      return "Wildcard";
+    case ADAPTER_TYPE_UNKNOWN:
+      return "Unknown";
+    case ADAPTER_TYPE_ETHERNET:
+      return "Ethernet";
+    case ADAPTER_TYPE_WIFI:
+      return "Wifi";
+    case ADAPTER_TYPE_CELLULAR:
+      return "Cellular";
+    case ADAPTER_TYPE_VPN:
+      return "VPN";
+    case ADAPTER_TYPE_LOOPBACK:
+      return "Loopback";
+    default:
+      RTC_NOTREACHED() << "Invalid type " << type;
+      return std::string();
+  }
+}
+
+}  // namespace rtc
diff --git a/rtc_base/network_constants.h b/rtc_base/network_constants.h
index efb2c83..1b43243 100644
--- a/rtc_base/network_constants.h
+++ b/rtc_base/network_constants.h
@@ -13,6 +13,8 @@
 
 #include <stdint.h>
 
+#include <string>
+
 namespace rtc {
 
 static const uint16_t kNetworkCostMax = 999;
@@ -37,6 +39,8 @@
   ADAPTER_TYPE_ANY = 1 << 5,
 };
 
+std::string AdapterTypeToString(AdapterType type);
+
 }  // namespace rtc
 
 #endif  // RTC_BASE_NETWORK_CONSTANTS_H_
diff --git a/rtc_base/network_route.h b/rtc_base/network_route.h
index 6a8f183..c2b492c 100644
--- a/rtc_base/network_route.h
+++ b/rtc_base/network_route.h
@@ -13,21 +13,82 @@
 
 #include <stdint.h>
 
+#include <string>
+
+#include "rtc_base/network_constants.h"
+#include "rtc_base/strings/string_builder.h"
+#include "rtc_base/system/inline.h"
+
 // TODO(honghaiz): Make a directory that describes the interfaces and structs
 // the media code can rely on and the network code can implement, and both can
 // depend on that, but not depend on each other. Then, move this file to that
 // directory.
 namespace rtc {
 
+class RouteEndpoint {
+ public:
+  RouteEndpoint() {}  // Used by tests.
+  RouteEndpoint(AdapterType adapter_type,
+                uint16_t adapter_id,
+                uint16_t network_id,
+                bool uses_turn)
+      : adapter_type_(adapter_type),
+        adapter_id_(adapter_id),
+        network_id_(network_id),
+        uses_turn_(uses_turn) {}
+
+  RouteEndpoint(const RouteEndpoint&) = default;
+  RouteEndpoint& operator=(const RouteEndpoint&) = default;
+
+  // Used by tests.
+  static RouteEndpoint CreateWithNetworkId(uint16_t network_id) {
+    return RouteEndpoint(ADAPTER_TYPE_UNKNOWN,
+                         /* adapter_id = */ 0, network_id,
+                         /* uses_turn = */ false);
+  }
+
+  AdapterType adapter_type() const { return adapter_type_; }
+  uint16_t adapter_id() const { return adapter_id_; }
+  uint16_t network_id() const { return network_id_; }
+  bool uses_turn() const { return uses_turn_; }
+
+ private:
+  AdapterType adapter_type_ = ADAPTER_TYPE_UNKNOWN;
+  uint16_t adapter_id_ = 0;
+  uint16_t network_id_ = 0;
+  bool uses_turn_ = false;
+};
+
 struct NetworkRoute {
   bool connected = false;
-  uint16_t local_network_id = 0;
-  uint16_t remote_network_id = 0;
+  RouteEndpoint local;
+  RouteEndpoint remote;
   // Last packet id sent on the PREVIOUS route.
   int last_sent_packet_id = -1;
   // The overhead in bytes from IP layer and above.
+  // This is the maximum of any part of the route.
   int packet_overhead = 0;
+
+  // Downstream projects depend on the old representation,
+  // populate that until they have been migrated.
+  // TODO(jonaso): remove.
+  uint16_t local_network_id = 0;
+  uint16_t remote_network_id = 0;
+
+  RTC_NO_INLINE inline std::string DebugString() const {
+    rtc::StringBuilder oss;
+    oss << "[ connected: " << connected << " local: [ " << local.adapter_id()
+        << "/" << local.network_id() << " "
+        << AdapterTypeToString(local.adapter_type())
+        << " turn: " << local.uses_turn() << " ] remote: [ "
+        << remote.adapter_id() << "/" << remote.network_id() << " "
+        << AdapterTypeToString(remote.adapter_type())
+        << " turn: " << remote.uses_turn()
+        << " ] packet_overhead_bytes: " << packet_overhead << " ]";
+    return oss.Release();
+  }
 };
+
 }  // namespace rtc
 
 #endif  // RTC_BASE_NETWORK_ROUTE_H_
diff --git a/test/scenario/network_node.cc b/test/scenario/network_node.cc
index c874add..aa576dc 100644
--- a/test/scenario/network_node.cc
+++ b/test/scenario/network_node.cc
@@ -111,10 +111,10 @@
   rtc::NetworkRoute route;
   route.connected = true;
   // We assume that the address will be unique in the lower bytes.
-  route.local_network_id = static_cast<uint16_t>(
-      receiver_address.ipaddr().v4AddressAsHostOrderInteger());
-  route.remote_network_id = static_cast<uint16_t>(
-      receiver_address.ipaddr().v4AddressAsHostOrderInteger());
+  route.local = rtc::RouteEndpoint::CreateWithNetworkId(static_cast<uint16_t>(
+      receiver_address.ipaddr().v4AddressAsHostOrderInteger()));
+  route.remote = rtc::RouteEndpoint::CreateWithNetworkId(static_cast<uint16_t>(
+      receiver_address.ipaddr().v4AddressAsHostOrderInteger()));
   route.packet_overhead = packet_overhead.bytes() +
                           receiver_address.ipaddr().overhead() +
                           cricket::kUdpHeaderSize;
diff --git a/video/video_send_stream_tests.cc b/video/video_send_stream_tests.cc
index 27bf0f0..cbc12a9 100644
--- a/video/video_send_stream_tests.cc
+++ b/video/video_send_stream_tests.cc
@@ -1772,8 +1772,8 @@
     void PerformTest() override {
       rtc::NetworkRoute new_route;
       new_route.connected = true;
-      new_route.local_network_id = 10;
-      new_route.remote_network_id = 20;
+      new_route.local = rtc::RouteEndpoint::CreateWithNetworkId(10);
+      new_route.remote = rtc::RouteEndpoint::CreateWithNetworkId(20);
       BitrateConstraints bitrate_config;
 
       SendTask(RTC_FROM_HERE, task_queue_,
@@ -1799,7 +1799,8 @@
             // TODO(holmer): We should set the last sent packet id here and
             // verify that we correctly ignore any packet loss reported prior to
             // that id.
-            ++new_route.local_network_id;
+            new_route.local = rtc::RouteEndpoint::CreateWithNetworkId(
+                new_route.local.network_id() + 1);
             call_->GetTransportControllerSend()->OnNetworkRouteChanged(
                 "transport", new_route);
             EXPECT_GE(call_->GetStats().send_bandwidth_bps, kStartBitrateBps);