From 61dbcd115a9e200a3b85eb4c5af2dd743637a03e Mon Sep 17 00:00:00 2001 From: Jonas Oreland Date: Tue, 31 May 2022 11:34:20 +0200 Subject: [PATCH] Rewrite "Reset all maps in AndroidNetworkMonitor Start()/Stop()" This reverts commit 8cd7b0a7babf3f58f6beab70fcabeb75e66c1bed. The assumption in AndroidNetworkMonitor that an interface name is unique has turned out to be incorrect :( for some (weird) devices, i.e ccmni0. It is unclear if it is a permanent setup or a transient state. This cl/ changes the impl. to cope with that, the last OnNetworkConnected_n "owns" the interface name, and when OnNetworkDisconnected_n runs, we check if we're "owner" and maybe set a new "owner" (if we're not "owner" we do nothing). New testcases added. I also 1) change NetworkMonitorInterface to return a struct with all the information that is requested with interface name as key. 2) Change Network.cc adding (debug) assertions that network properties can't change inside a loop (in one thread). Original change's description: > Revert "Reset all maps in AndroidNetworkMonitor Start()/Stop()" > > This reverts commit 02293096f9689fee3d32defa77dca227cc1eee90. > > Reason for revert: mysterious crashes in android_network_monitor.cc > > Original change's description: > > Reset all maps in AndroidNetworkMonitor Start()/Stop() > > > > This cl/ fixes another race condition with the recent additions > > to NetworkMonitorAutoDetect (getAllNetworksFromCache). > > > > The getAllNetworksFromCache-feature uses the by the Android team > > preferred way of enumerating networks, i.e to register network listeners. > > > > Th recent fix to add IsAdapterAvailable, https://webrtc-review.googlesource.com/c/src/+/257400 > > contained a bug in that the adapter_type_by_name_ map was not > > reset either on disconnect or Start/Stop. > > > > This cl/ addresses that including unit test. > > It also de-obfuscates NetworkMonitor so that it always > > calls NotifyOfActiveNetworkList on startMonitoring even > > if list.size() == 0. This should not matter but makes > > code easier to understand. > > > > Bug: webrtc:13741 > > Change-Id: I438b877eebf769a8b2e7292b697ef1c0a349b24f > > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/258721 > > Reviewed-by: Harald Alvestrand > > Commit-Queue: Jonas Oreland > > Cr-Commit-Position: refs/heads/main@{#36530} > > Bug: webrtc:13741 > Change-Id: I36fbf63f658d3e8048e13959cbebfbd14df12b14 > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/264146 > Reviewed-by: Tomas Gunnarsson > Commit-Queue: Jonas Oreland > Cr-Commit-Position: refs/heads/main@{#37016} Bug: webrtc:13741 Change-Id: Ib4eb072b775e493b564528f0be94c685b70ec20f Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/264421 Reviewed-by: Harald Alvestrand Commit-Queue: Jonas Oreland Reviewed-by: Tomas Gunnarsson Cr-Commit-Position: refs/heads/main@{#37056} --- rtc_base/network.cc | 135 ++++++----- rtc_base/network.h | 2 + rtc_base/network_monitor.h | 45 ++-- rtc_base/network_unittest.cc | 27 +-- .../api/org/webrtc/NetworkMonitor.java | 5 +- .../android_network_monitor_unittest.cc | 158 ++++++++++++- .../src/jni/android_network_monitor.cc | 223 ++++++++++-------- sdk/android/src/jni/android_network_monitor.h | 22 +- sdk/objc/native/src/objc_network_monitor.h | 7 +- sdk/objc/native/src/objc_network_monitor.mm | 38 ++- 10 files changed, 420 insertions(+), 242 deletions(-) diff --git a/rtc_base/network.cc b/rtc_base/network.cc index c6442b1b63..74eacb6a35 100644 --- a/rtc_base/network.cc +++ b/rtc_base/network.cc @@ -542,6 +542,25 @@ bool BasicNetworkManager::CreateNetworks( } #elif defined(WEBRTC_POSIX) +NetworkMonitorInterface::InterfaceInfo BasicNetworkManager::GetInterfaceInfo( + struct ifaddrs* cursor) const { + if (cursor->ifa_flags & IFF_LOOPBACK) { + return { + .adapter_type = ADAPTER_TYPE_LOOPBACK, + .underlying_type_for_vpn = ADAPTER_TYPE_UNKNOWN, + .network_preference = NetworkPreference::NEUTRAL, + .available = true, + }; + } else if (network_monitor_) { + return network_monitor_->GetInterfaceInfo(cursor->ifa_name); + } else { + return {.adapter_type = GetAdapterTypeFromName(cursor->ifa_name), + .underlying_type_for_vpn = ADAPTER_TYPE_UNKNOWN, + .network_preference = NetworkPreference::NEUTRAL, + .available = true}; + } +} + void BasicNetworkManager::ConvertIfAddrs( struct ifaddrs* interfaces, IfAddrsConverter* ifaddrs_converter, @@ -584,65 +603,64 @@ void BasicNetworkManager::ConvertIfAddrs( reinterpret_cast(cursor->ifa_addr)->sin6_scope_id; } - AdapterType adapter_type = ADAPTER_TYPE_UNKNOWN; - AdapterType vpn_underlying_adapter_type = ADAPTER_TYPE_UNKNOWN; - NetworkPreference network_preference = NetworkPreference::NEUTRAL; - if (cursor->ifa_flags & IFF_LOOPBACK) { - adapter_type = ADAPTER_TYPE_LOOPBACK; - } else { - // If there is a network_monitor, use it to get the adapter type. - // Otherwise, get the adapter type based on a few name matching rules. - if (network_monitor_) { - adapter_type = network_monitor_->GetAdapterType(cursor->ifa_name); - network_preference = - network_monitor_->GetNetworkPreference(cursor->ifa_name); - } - if (adapter_type == ADAPTER_TYPE_UNKNOWN) { - adapter_type = GetAdapterTypeFromName(cursor->ifa_name); - } - } - - if (adapter_type == ADAPTER_TYPE_VPN && network_monitor_) { - vpn_underlying_adapter_type = - network_monitor_->GetVpnUnderlyingAdapterType(cursor->ifa_name); - } - int prefix_length = CountIPMaskBits(mask); prefix = TruncateIP(ip, prefix_length); - - if (adapter_type != ADAPTER_TYPE_VPN && - IsConfiguredVpn(prefix, prefix_length)) { - vpn_underlying_adapter_type = adapter_type; - adapter_type = ADAPTER_TYPE_VPN; - } - std::string key = MakeNetworkKey(std::string(cursor->ifa_name), prefix, prefix_length); + auto iter = current_networks.find(key); - if (iter == current_networks.end()) { - // TODO(phoglund): Need to recognize other types as well. - auto network = - std::make_unique(cursor->ifa_name, cursor->ifa_name, prefix, - prefix_length, adapter_type); - network->set_default_local_address_provider(this); - network->set_scope_id(scope_id); - network->AddIP(ip); - network->set_ignored(IsIgnoredNetwork(*network)); - network->set_underlying_type_for_vpn(vpn_underlying_adapter_type); - network->set_network_preference(network_preference); - if (include_ignored || !network->ignored()) { - current_networks[key] = network.get(); - networks->push_back(std::move(network)); + if (iter != current_networks.end()) { + // We have already added this network, simply add extra IP. + iter->second->AddIP(ip); +#if RTC_DCHECK_IS_ON + // Validate that different IP of same network has same properties + auto existing_network = iter->second; + + NetworkMonitorInterface::InterfaceInfo if_info = GetInterfaceInfo(cursor); + if (if_info.adapter_type != ADAPTER_TYPE_VPN && + IsConfiguredVpn(prefix, prefix_length)) { + if_info.underlying_type_for_vpn = if_info.adapter_type; + if_info.adapter_type = ADAPTER_TYPE_VPN; } + + RTC_DCHECK(existing_network->type() == if_info.adapter_type); + RTC_DCHECK(existing_network->underlying_type_for_vpn() == + if_info.underlying_type_for_vpn); + RTC_DCHECK(existing_network->network_preference() == + if_info.network_preference); + if (!if_info.available) { + RTC_DCHECK(existing_network->ignored()); + } +#endif // RTC_DCHECK_IS_ON + continue; + } + + // Create a new network. + NetworkMonitorInterface::InterfaceInfo if_info = GetInterfaceInfo(cursor); + + // Check manually configured VPN override. + if (if_info.adapter_type != ADAPTER_TYPE_VPN && + IsConfiguredVpn(prefix, prefix_length)) { + if_info.underlying_type_for_vpn = if_info.adapter_type; + if_info.adapter_type = ADAPTER_TYPE_VPN; + } + + auto network = + std::make_unique(cursor->ifa_name, cursor->ifa_name, prefix, + prefix_length, if_info.adapter_type); + network->set_default_local_address_provider(this); + network->set_scope_id(scope_id); + network->AddIP(ip); + if (!if_info.available) { + network->set_ignored(true); } else { - Network* existing_network = iter->second; - existing_network->AddIP(ip); - if (adapter_type != ADAPTER_TYPE_UNKNOWN) { - existing_network->set_type(adapter_type); - existing_network->set_underlying_type_for_vpn( - vpn_underlying_adapter_type); - } - existing_network->set_network_preference(network_preference); + network->set_ignored(IsIgnoredNetwork(*network)); + } + network->set_underlying_type_for_vpn(if_info.underlying_type_for_vpn); + network->set_network_preference(if_info.network_preference); + if (include_ignored || !network->ignored()) { + current_networks[key] = network.get(); + networks->push_back(std::move(network)); } } } @@ -803,10 +821,10 @@ bool BasicNetworkManager::CreateNetworks( adapter_type = ADAPTER_TYPE_UNKNOWN; break; } - auto vpn_underlying_adapter_type = ADAPTER_TYPE_UNKNOWN; + auto underlying_type_for_vpn = ADAPTER_TYPE_UNKNOWN; if (adapter_type != ADAPTER_TYPE_VPN && IsConfiguredVpn(prefix, prefix_length)) { - vpn_underlying_adapter_type = adapter_type; + underlying_type_for_vpn = adapter_type; adapter_type = ADAPTER_TYPE_VPN; } if (adapter_type != ADAPTER_TYPE_VPN && @@ -814,13 +832,13 @@ bool BasicNetworkManager::CreateNetworks( reinterpret_cast( adapter_addrs->PhysicalAddress), adapter_addrs->PhysicalAddressLength))) { - vpn_underlying_adapter_type = adapter_type; + underlying_type_for_vpn = adapter_type; adapter_type = ADAPTER_TYPE_VPN; } auto network = std::make_unique(name, description, prefix, prefix_length, adapter_type); - network->set_underlying_type_for_vpn(vpn_underlying_adapter_type); + network->set_underlying_type_for_vpn(underlying_type_for_vpn); network->set_default_local_address_provider(this); network->set_mdns_responder_provider(this); network->set_scope_id(scope_id); @@ -871,11 +889,6 @@ bool BasicNetworkManager::IsIgnoredNetwork(const Network& network) const { } #endif - if (network_monitor_ && - !network_monitor_->IsAdapterAvailable(network.name())) { - return true; - } - // Ignore any networks with a 0.x.y.z IP if (network.prefix().family() == AF_INET) { return (network.prefix().v4AddressAsHostOrderInteger() < 0x01000000); diff --git a/rtc_base/network.h b/rtc_base/network.h index ef6c4f06be..f814ef4f1e 100644 --- a/rtc_base/network.h +++ b/rtc_base/network.h @@ -322,6 +322,8 @@ class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase, bool include_ignored, std::vector>* networks) const RTC_RUN_ON(thread_); + NetworkMonitorInterface::InterfaceInfo GetInterfaceInfo( + struct ifaddrs* cursor) const RTC_RUN_ON(thread_); #endif // defined(WEBRTC_POSIX) // Creates a network object for each network available on the machine. diff --git a/rtc_base/network_monitor.h b/rtc_base/network_monitor.h index 72571d1497..605854f6ea 100644 --- a/rtc_base/network_monitor.h +++ b/rtc_base/network_monitor.h @@ -73,18 +73,38 @@ class NetworkBinderInterface { // changes, and fires the SignalNetworksChanged event when networks change. class NetworkMonitorInterface { public: + struct InterfaceInfo { + // The type of adapter if known. + AdapterType adapter_type; + + // Is ADAPTER_TYPE_UNKNOWN unless adapter_type == ADAPTER_TYPE_VPN. + AdapterType underlying_type_for_vpn = ADAPTER_TYPE_UNKNOWN; + + // The OS/firmware specific preference of this interface. + NetworkPreference network_preference = NetworkPreference::NEUTRAL; + + // Is this interface available to use? WebRTC shouldn't attempt to use it if + // this returns false. + // + // It's possible for this status to change, in which case + // SignalNetworksChanged will be fired. + // + // The specific use case this was added for was a phone with two SIM + // cards, where attempting to use all interfaces returned from getifaddrs + // caused the connection to be dropped. + bool available = true; + }; + NetworkMonitorInterface(); virtual ~NetworkMonitorInterface(); virtual void Start() = 0; virtual void Stop() = 0; - virtual AdapterType GetAdapterType(absl::string_view interface_name) = 0; - virtual AdapterType GetVpnUnderlyingAdapterType( - absl::string_view interface_name) = 0; - - virtual NetworkPreference GetNetworkPreference( - absl::string_view interface_name) = 0; + // Get information about an interface. + // If the interface is not known, the return struct will have set + // `adapter_type` to ADAPTER_TYPE_UNKNOWN and `available` to false. + virtual InterfaceInfo GetInterfaceInfo(absl::string_view interface_name) = 0; // Does `this` NetworkMonitorInterface implement BindSocketToNetwork? // Only Android returns true. @@ -99,19 +119,6 @@ class NetworkMonitorInterface { return NetworkBindingResult::NOT_IMPLEMENTED; } - // Is this interface available to use? WebRTC shouldn't attempt to use it if - // this returns false. - // - // It's possible for this status to change, in which case - // SignalNetworksChanged will be fired. - // - // These specific use case this was added for was a phone with two SIM cards, - // where attempting to use all interfaces returned from getifaddrs caused the - // connection to be dropped. - virtual bool IsAdapterAvailable(absl::string_view interface_name) { - return true; - } - void SetNetworksChangedCallback(std::function callback) { networks_changed_callback_ = std::move(callback); } diff --git a/rtc_base/network_unittest.cc b/rtc_base/network_unittest.cc index de731fa5b4..a8152fd525 100644 --- a/rtc_base/network_unittest.cc +++ b/rtc_base/network_unittest.cc @@ -58,26 +58,17 @@ class FakeNetworkMonitor : public NetworkMonitorInterface { void Start() override { started_ = true; } void Stop() override { started_ = false; } bool started() { return started_; } - AdapterType GetAdapterType(absl::string_view if_name) override { - // Note that the name matching rules are different from the - // GetAdapterTypeFromName in NetworkManager. + InterfaceInfo GetInterfaceInfo(absl::string_view if_name) override { + InterfaceInfo if_info = { + .adapter_type = ADAPTER_TYPE_UNKNOWN, + .available = absl::c_count(unavailable_adapters_, if_name) == 0, + }; if (absl::StartsWith(if_name, "wifi")) { - return ADAPTER_TYPE_WIFI; + if_info.adapter_type = ADAPTER_TYPE_WIFI; + } else if (absl::StartsWith(if_name, "cellular")) { + if_info.adapter_type = ADAPTER_TYPE_CELLULAR; } - if (absl::StartsWith(if_name, "cellular")) { - return ADAPTER_TYPE_CELLULAR; - } - return ADAPTER_TYPE_UNKNOWN; - } - AdapterType GetVpnUnderlyingAdapterType(absl::string_view if_name) override { - return ADAPTER_TYPE_UNKNOWN; - } - NetworkPreference GetNetworkPreference(absl::string_view if_name) override { - return NetworkPreference::NEUTRAL; - } - - bool IsAdapterAvailable(absl::string_view if_name) override { - return absl::c_count(unavailable_adapters_, if_name) == 0; + return if_info; } // Used to test IsAdapterAvailable. diff --git a/sdk/android/api/org/webrtc/NetworkMonitor.java b/sdk/android/api/org/webrtc/NetworkMonitor.java index 173f76e87e..0bc461df18 100644 --- a/sdk/android/api/org/webrtc/NetworkMonitor.java +++ b/sdk/android/api/org/webrtc/NetworkMonitor.java @@ -138,10 +138,11 @@ public class NetworkMonitor { startMonitoring( applicationContext != null ? applicationContext : ContextUtils.getApplicationContext(), fieldTrialsString); - // The native observers expect a network list update after they call startMonitoring. + synchronized (nativeNetworkObservers) { nativeNetworkObservers.add(nativeObserver); } + // The native observer expects a network list update after startMonitoring. updateObserverActiveNetworkList(nativeObserver); // currentConnectionType was updated in startMonitoring(). // Need to notify the native observers here. @@ -271,7 +272,7 @@ public class NetworkMonitor { networkInfoList = (networkChangeDetector == null) ? null : networkChangeDetector.getActiveNetworkList(); } - if (networkInfoList == null || networkInfoList.size() == 0) { + if (networkInfoList == null) { return; } diff --git a/sdk/android/native_unittests/android_network_monitor_unittest.cc b/sdk/android/native_unittests/android_network_monitor_unittest.cc index f67991e18e..9aec62d630 100644 --- a/sdk/android/native_unittests/android_network_monitor_unittest.cc +++ b/sdk/android/native_unittests/android_network_monitor_unittest.cc @@ -62,6 +62,10 @@ class AndroidNetworkMonitorTest : public ::testing::Test { network_monitor_->Stop(); } + void Disconnect(jni::NetworkHandle handle) { + network_monitor_->OnNetworkDisconnected_n(handle); + } + protected: test::ScopedKeyValueConfig field_trials_; rtc::AutoThread main_thread_; @@ -168,8 +172,158 @@ TEST_F(AndroidNetworkMonitorTest, TestUnderlyingVpnType) { net_info.underlying_type_for_vpn = jni::NETWORK_WIFI; network_monitor_->SetNetworkInfos({net_info}); - EXPECT_EQ(rtc::ADAPTER_TYPE_WIFI, - network_monitor_->GetVpnUnderlyingAdapterType("v4-wlan0")); + EXPECT_EQ( + rtc::ADAPTER_TYPE_WIFI, + network_monitor_->GetInterfaceInfo("v4-wlan0").underlying_type_for_vpn); +} + +// Verify that Disconnect makes interface unavailable. +TEST_F(AndroidNetworkMonitorTest, Disconnect) { + network_monitor_->Start(); + + jni::NetworkHandle ipv4_handle = 100; + rtc::IPAddress ipv4_address(kTestIpv4Address); + jni::NetworkInformation net_info = + CreateNetworkInformation("wlan0", ipv4_handle, ipv4_address); + net_info.type = jni::NETWORK_WIFI; + network_monitor_->SetNetworkInfos({net_info}); + + EXPECT_TRUE(network_monitor_->GetInterfaceInfo("wlan0").available); + EXPECT_TRUE(network_monitor_ + ->FindNetworkHandleFromAddressOrName(ipv4_address, "v4-wlan0") + .has_value()); + EXPECT_EQ(network_monitor_->GetInterfaceInfo("v4-wlan0").adapter_type, + rtc::ADAPTER_TYPE_WIFI); + + // Check that values are reset on disconnect(). + Disconnect(ipv4_handle); + EXPECT_FALSE(network_monitor_->GetInterfaceInfo("wlan0").available); + EXPECT_FALSE( + network_monitor_ + ->FindNetworkHandleFromAddressOrName(ipv4_address, "v4-wlan0") + .has_value()); + EXPECT_EQ(network_monitor_->GetInterfaceInfo("v4-wlan0").adapter_type, + rtc::ADAPTER_TYPE_UNKNOWN); +} + +// Verify that Stop() resets all caches. +TEST_F(AndroidNetworkMonitorTest, Reset) { + network_monitor_->Start(); + + jni::NetworkHandle ipv4_handle = 100; + rtc::IPAddress ipv4_address(kTestIpv4Address); + jni::NetworkInformation net_info = + CreateNetworkInformation("wlan0", ipv4_handle, ipv4_address); + net_info.type = jni::NETWORK_WIFI; + network_monitor_->SetNetworkInfos({net_info}); + + EXPECT_TRUE(network_monitor_->GetInterfaceInfo("wlan0").available); + EXPECT_TRUE(network_monitor_ + ->FindNetworkHandleFromAddressOrName(ipv4_address, "v4-wlan0") + .has_value()); + EXPECT_EQ(network_monitor_->GetInterfaceInfo("v4-wlan0").adapter_type, + rtc::ADAPTER_TYPE_WIFI); + + // Check that values are reset on Stop(). + network_monitor_->Stop(); + EXPECT_FALSE(network_monitor_->GetInterfaceInfo("wlan0").available); + EXPECT_FALSE( + network_monitor_ + ->FindNetworkHandleFromAddressOrName(ipv4_address, "v4-wlan0") + .has_value()); + EXPECT_EQ(network_monitor_->GetInterfaceInfo("v4-wlan0").adapter_type, + rtc::ADAPTER_TYPE_UNKNOWN); +} + +TEST_F(AndroidNetworkMonitorTest, DuplicateIfname) { + network_monitor_->Start(); + + jni::NetworkHandle ipv4_handle = 100; + rtc::IPAddress ipv4_address(kTestIpv4Address); + jni::NetworkInformation net_info1 = + CreateNetworkInformation("wlan0", ipv4_handle, ipv4_address); + net_info1.type = jni::NETWORK_WIFI; + + jni::NetworkHandle ipv6_handle = 101; + rtc::IPAddress ipv6_address = GetIpAddressFromIpv6String(kTestIpv6Address1); + jni::NetworkInformation net_info2 = + CreateNetworkInformation("wlan0", ipv6_handle, ipv6_address); + net_info2.type = jni::NETWORK_UNKNOWN_CELLULAR; + + network_monitor_->SetNetworkInfos({net_info1, net_info2}); + + // The last added. + EXPECT_TRUE(network_monitor_->GetInterfaceInfo("wlan0").available); + EXPECT_EQ(network_monitor_->GetInterfaceInfo("v-wlan0").adapter_type, + rtc::ADAPTER_TYPE_CELLULAR); + + // But both IP addresses are still searchable. + EXPECT_EQ( + *network_monitor_->FindNetworkHandleFromAddressOrName(ipv4_address, ""), + ipv4_handle); + EXPECT_EQ( + *network_monitor_->FindNetworkHandleFromAddressOrName(ipv6_address, ""), + ipv6_handle); +} + +TEST_F(AndroidNetworkMonitorTest, DuplicateIfnameDisconnectOwner) { + network_monitor_->Start(); + + jni::NetworkHandle ipv4_handle = 100; + rtc::IPAddress ipv4_address(kTestIpv4Address); + jni::NetworkInformation net_info1 = + CreateNetworkInformation("wlan0", ipv4_handle, ipv4_address); + net_info1.type = jni::NETWORK_WIFI; + + jni::NetworkHandle ipv6_handle = 101; + rtc::IPAddress ipv6_address = GetIpAddressFromIpv6String(kTestIpv6Address1); + jni::NetworkInformation net_info2 = + CreateNetworkInformation("wlan0", ipv6_handle, ipv6_address); + net_info2.type = jni::NETWORK_UNKNOWN_CELLULAR; + + network_monitor_->SetNetworkInfos({net_info1, net_info2}); + + // The last added. + EXPECT_TRUE(network_monitor_->GetInterfaceInfo("wlan0").available); + EXPECT_EQ(network_monitor_->GetInterfaceInfo("v-wlan0").adapter_type, + rtc::ADAPTER_TYPE_CELLULAR); + + Disconnect(ipv6_handle); + + // We should now find ipv4_handle. + EXPECT_TRUE(network_monitor_->GetInterfaceInfo("wlan0").available); + EXPECT_EQ(network_monitor_->GetInterfaceInfo("v-wlan0").adapter_type, + rtc::ADAPTER_TYPE_WIFI); +} + +TEST_F(AndroidNetworkMonitorTest, DuplicateIfnameDisconnectNonOwner) { + network_monitor_->Start(); + + jni::NetworkHandle ipv4_handle = 100; + rtc::IPAddress ipv4_address(kTestIpv4Address); + jni::NetworkInformation net_info1 = + CreateNetworkInformation("wlan0", ipv4_handle, ipv4_address); + net_info1.type = jni::NETWORK_WIFI; + + jni::NetworkHandle ipv6_handle = 101; + rtc::IPAddress ipv6_address = GetIpAddressFromIpv6String(kTestIpv6Address1); + jni::NetworkInformation net_info2 = + CreateNetworkInformation("wlan0", ipv6_handle, ipv6_address); + net_info2.type = jni::NETWORK_UNKNOWN_CELLULAR; + + network_monitor_->SetNetworkInfos({net_info1, net_info2}); + + // The last added. + EXPECT_TRUE(network_monitor_->GetInterfaceInfo("wlan0").available); + EXPECT_EQ(network_monitor_->GetInterfaceInfo("wlan0").adapter_type, + rtc::ADAPTER_TYPE_CELLULAR); + + Disconnect(ipv4_handle); + + // We should still find ipv6 network. + EXPECT_TRUE(network_monitor_->GetInterfaceInfo("wlan0").available); + EXPECT_EQ(network_monitor_->GetInterfaceInfo("v-wlan0").adapter_type, + rtc::ADAPTER_TYPE_CELLULAR); } } // namespace test diff --git a/sdk/android/src/jni/android_network_monitor.cc b/sdk/android/src/jni/android_network_monitor.cc index 59a9204fe2..c17e1ee219 100644 --- a/sdk/android/src/jni/android_network_monitor.cc +++ b/sdk/android/src/jni/android_network_monitor.cc @@ -134,14 +134,17 @@ static rtc::AdapterType AdapterTypeFromNetworkType( case NETWORK_UNKNOWN_CELLULAR: return rtc::ADAPTER_TYPE_CELLULAR; case NETWORK_VPN: + return rtc::ADAPTER_TYPE_VPN; case NETWORK_BLUETOOTH: // There is no corresponding mapping for bluetooth networks. - // Map it to VPN for now. - return rtc::ADAPTER_TYPE_VPN; - default: - RTC_DCHECK_NOTREACHED() << "Invalid network type " << network_type; + // Map it to UNKNOWN for now. + return rtc::ADAPTER_TYPE_UNKNOWN; + case NETWORK_NONE: return rtc::ADAPTER_TYPE_UNKNOWN; } + + RTC_DCHECK_NOTREACHED() << "Invalid network type " << network_type; + return rtc::ADAPTER_TYPE_UNKNOWN; } static rtc::IPAddress JavaToNativeIpAddress( @@ -243,6 +246,7 @@ void AndroidNetworkMonitor::Start() { if (started_) { return; } + reset(); started_ = true; surface_cellular_types_ = field_trials_.IsEnabled("WebRTC-SurfaceCellularTypes"); @@ -265,6 +269,14 @@ void AndroidNetworkMonitor::Start() { env, field_trials_.Lookup("WebRTC-NetworkMonitorAutoDetect"))); } +void AndroidNetworkMonitor::reset() { + RTC_DCHECK_RUN_ON(network_thread_); + network_handle_by_address_.clear(); + network_handle_by_if_name_.clear(); + network_info_by_handle_.clear(); + network_preference_by_adapter_type_.clear(); +} + void AndroidNetworkMonitor::Stop() { RTC_DCHECK_RUN_ON(network_thread_); if (!started_) { @@ -281,8 +293,7 @@ void AndroidNetworkMonitor::Stop() { Java_NetworkMonitor_stopMonitoring(env, j_network_monitor_, jlongFromPointer(this)); - network_handle_by_address_.clear(); - network_info_by_handle_.clear(); + reset(); } // The implementation is largely taken from UDPSocketPosix::BindToNetwork in @@ -406,17 +417,13 @@ void AndroidNetworkMonitor::OnNetworkConnected_n( const NetworkInformation& network_info) { RTC_DCHECK_RUN_ON(network_thread_); RTC_LOG(LS_INFO) << "Network connected: " << network_info.ToString(); - adapter_type_by_name_[network_info.interface_name] = - AdapterTypeFromNetworkType(network_info.type, surface_cellular_types_); - if (network_info.type == NETWORK_VPN) { - vpn_underlying_adapter_type_by_name_[network_info.interface_name] = - AdapterTypeFromNetworkType(network_info.underlying_type_for_vpn, - surface_cellular_types_); - } network_info_by_handle_[network_info.handle] = network_info; for (const rtc::IPAddress& address : network_info.ip_addresses) { network_handle_by_address_[address] = network_info.handle; } + network_handle_by_if_name_[network_info.interface_name] = network_info.handle; + RTC_CHECK(network_info_by_handle_.size() >= + network_handle_by_if_name_.size()); InvokeNetworksChangedCallback(); } @@ -451,12 +458,18 @@ absl::optional AndroidNetworkMonitor::FindNetworkHandleFromIfname( absl::string_view if_name) const { RTC_DCHECK_RUN_ON(network_thread_); + + auto iter = network_handle_by_if_name_.find(if_name); + if (iter != network_handle_by_if_name_.end()) { + return iter->second; + } + if (bind_using_ifname_) { - for (auto const& iter : network_info_by_handle_) { + for (auto const& iter : network_handle_by_if_name_) { // Use substring match so that e.g if_name="v4-wlan0" is matched // agains iter="wlan0" - if (if_name.find(iter.second.interface_name) != absl::string_view::npos) { - return absl::make_optional(iter.first); + if (if_name.find(iter.first) != absl::string_view::npos) { + return absl::make_optional(iter.second); } } } @@ -468,12 +481,57 @@ void AndroidNetworkMonitor::OnNetworkDisconnected_n(NetworkHandle handle) { RTC_DCHECK_RUN_ON(network_thread_); RTC_LOG(LS_INFO) << "Network disconnected for handle " << handle; auto iter = network_info_by_handle_.find(handle); - if (iter != network_info_by_handle_.end()) { - for (const rtc::IPAddress& address : iter->second.ip_addresses) { - network_handle_by_address_.erase(address); - } - network_info_by_handle_.erase(iter); + if (iter == network_info_by_handle_.end()) { + return; } + + for (const rtc::IPAddress& address : iter->second.ip_addresses) { + network_handle_by_address_.erase(address); + } + + // We've discovered that the if_name is not always unique, + // i.e it can be several network conencted with same if_name. + // + // This is handled the following way, + // 1) OnNetworkConnected_n overwrites any previous "owner" of an interface + // name ("owner" == entry in network_handle_by_if_name_). + // 2) OnNetworkDisconnected_n, we scan and see if there are any remaining + // connected network with the interface name, and set it as owner. + // + // This means that network_info_by_handle can have more entries than + // network_handle_by_if_name_. + + // Check if we are registered as "owner" of if_name. + const auto& if_name = iter->second.interface_name; + auto iter2 = network_handle_by_if_name_.find(if_name); + RTC_DCHECK(iter2 != network_handle_by_if_name_.end()); + if (iter2 != network_handle_by_if_name_.end() && iter2->second == handle) { + // We are owner... + // Check if there is someone else we can set as owner. + bool found = false; + for (const auto& info : network_info_by_handle_) { + if (info.first == handle) { + continue; + } + if (info.second.interface_name == if_name) { + found = true; + network_handle_by_if_name_[if_name] = info.first; + break; + } + } + if (!found) { + // No new owner... + network_handle_by_if_name_.erase(iter2); + } + } else { + // We are not owner...don't do anything. +#if RTC_DCHECK_IS_ON + auto owner_handle = FindNetworkHandleFromIfname(if_name); + RTC_DCHECK(owner_handle && *owner_handle != handle); +#endif + } + + network_info_by_handle_.erase(iter); } void AndroidNetworkMonitor::OnNetworkPreference_n( @@ -491,8 +549,16 @@ void AndroidNetworkMonitor::OnNetworkPreference_n( void AndroidNetworkMonitor::SetNetworkInfos( const std::vector& network_infos) { RTC_DCHECK_RUN_ON(network_thread_); - network_handle_by_address_.clear(); - network_info_by_handle_.clear(); + + // We expect this method to be called once directly after startMonitoring. + // All the caches should be empty. + RTC_DCHECK(network_handle_by_if_name_.empty()); + RTC_DCHECK(network_handle_by_address_.empty()); + RTC_DCHECK(network_info_by_handle_.empty()); + RTC_DCHECK(network_preference_by_adapter_type_.empty()); + + // ...but reset just in case. + reset(); RTC_LOG(LS_INFO) << "Android network monitor found " << network_infos.size() << " networks"; for (const NetworkInformation& network : network_infos) { @@ -500,68 +566,43 @@ void AndroidNetworkMonitor::SetNetworkInfos( } } -rtc::AdapterType AndroidNetworkMonitor::GetAdapterType( - absl::string_view if_name) { +rtc::NetworkMonitorInterface::InterfaceInfo +AndroidNetworkMonitor::GetInterfaceInfo(absl::string_view if_name) { RTC_DCHECK_RUN_ON(network_thread_); - auto iter = adapter_type_by_name_.find(if_name); - rtc::AdapterType type = (iter == adapter_type_by_name_.end()) - ? rtc::ADAPTER_TYPE_UNKNOWN - : iter->second; - - if (type == rtc::ADAPTER_TYPE_UNKNOWN && bind_using_ifname_) { - for (auto const& iter : adapter_type_by_name_) { - // Use substring match so that e.g if_name="v4-wlan0" is matched - // against iter="wlan0" - if (if_name.find(iter.first) != absl::string_view::npos) { - type = iter.second; - break; - } - } + auto handle = FindNetworkHandleFromIfname(if_name); + if (!handle) { + return { + .adapter_type = rtc::ADAPTER_TYPE_UNKNOWN, + .available = (disable_is_adapter_available_ ? true : false), + }; + } + auto iter = network_info_by_handle_.find(*handle); + RTC_DCHECK(iter != network_info_by_handle_.end()); + if (iter == network_info_by_handle_.end()) { + return { + .adapter_type = rtc::ADAPTER_TYPE_UNKNOWN, + .available = (disable_is_adapter_available_ ? true : false), + }; } - if (type == rtc::ADAPTER_TYPE_UNKNOWN) { - RTC_LOG(LS_WARNING) << "Get an unknown type for the interface " << if_name; - } - return type; -} - -rtc::AdapterType AndroidNetworkMonitor::GetVpnUnderlyingAdapterType( - absl::string_view if_name) { - RTC_DCHECK_RUN_ON(network_thread_); - auto iter = vpn_underlying_adapter_type_by_name_.find(if_name); - rtc::AdapterType type = (iter == vpn_underlying_adapter_type_by_name_.end()) - ? rtc::ADAPTER_TYPE_UNKNOWN - : iter->second; - if (type == rtc::ADAPTER_TYPE_UNKNOWN && bind_using_ifname_) { - // Use partial match so that e.g if_name="v4-wlan0" is matched - // agains iter.first="wlan0" - for (auto const& iter : vpn_underlying_adapter_type_by_name_) { - if (if_name.find(iter.first) != absl::string_view::npos) { - type = iter.second; - break; - } - } - } - - return type; + auto type = + AdapterTypeFromNetworkType(iter->second.type, surface_cellular_types_); + auto vpn_type = + (type == rtc::ADAPTER_TYPE_VPN) + ? AdapterTypeFromNetworkType(iter->second.underlying_type_for_vpn, + surface_cellular_types_) + : rtc::ADAPTER_TYPE_UNKNOWN; + return { + .adapter_type = type, + .underlying_type_for_vpn = vpn_type, + .network_preference = GetNetworkPreference(type), + .available = true, + }; } rtc::NetworkPreference AndroidNetworkMonitor::GetNetworkPreference( - absl::string_view if_name) { + rtc::AdapterType adapter_type) const { RTC_DCHECK_RUN_ON(network_thread_); - auto iter = adapter_type_by_name_.find(if_name); - if (iter == adapter_type_by_name_.end()) { - return rtc::NetworkPreference::NEUTRAL; - } - - rtc::AdapterType adapter_type = iter->second; - if (adapter_type == rtc::ADAPTER_TYPE_VPN) { - auto iter2 = vpn_underlying_adapter_type_by_name_.find(if_name); - if (iter2 != vpn_underlying_adapter_type_by_name_.end()) { - adapter_type = iter2->second; - } - } - auto preference_iter = network_preference_by_adapter_type_.find(adapter_type); if (preference_iter == network_preference_by_adapter_type_.end()) { return rtc::NetworkPreference::NEUTRAL; @@ -570,32 +611,6 @@ rtc::NetworkPreference AndroidNetworkMonitor::GetNetworkPreference( return preference_iter->second; } -// Check if adapter is avaiable, and only return true for the interface -// that has been discovered by NetworkMonitorAutoDetect.java. -bool AndroidNetworkMonitor::IsAdapterAvailable(absl::string_view if_name) { - RTC_DCHECK_RUN_ON(network_thread_); - if (disable_is_adapter_available_) { - return true; - } - if (if_name == "lo") { - // localhost (if_name == lo) is used by unit tests. - return true; - } - bool val = adapter_type_by_name_.find(if_name) != adapter_type_by_name_.end(); - if (!val && bind_using_ifname_) { - for (auto const& iter : network_info_by_handle_) { - // Use substring match so that e.g if_name="v4-wlan0" is matched - // against iter.first="wlan0" - if (if_name.find(iter.second.interface_name) != absl::string_view::npos) { - val = true; - break; - } - } - } - - return val; -} - AndroidNetworkMonitorFactory::AndroidNetworkMonitorFactory() : j_application_context_(nullptr) {} diff --git a/sdk/android/src/jni/android_network_monitor.h b/sdk/android/src/jni/android_network_monitor.h index dd661acdcf..dfeb2beb3b 100644 --- a/sdk/android/src/jni/android_network_monitor.h +++ b/sdk/android/src/jni/android_network_monitor.h @@ -29,6 +29,10 @@ #include "sdk/android/src/jni/jni_helpers.h" namespace webrtc { +namespace test { +class AndroidNetworkMonitorTest; +} // namespace test + namespace jni { typedef int64_t NetworkHandle; @@ -88,12 +92,8 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorInterface { int socket_fd, const rtc::IPAddress& address, absl::string_view if_name) override; - rtc::AdapterType GetAdapterType(absl::string_view if_name) override; - rtc::AdapterType GetVpnUnderlyingAdapterType( - absl::string_view if_name) override; - rtc::NetworkPreference GetNetworkPreference( - absl::string_view if_name) override; - bool IsAdapterAvailable(absl::string_view if_name) override; + + InterfaceInfo GetInterfaceInfo(absl::string_view if_name) override; // Always expected to be called on the network thread. void SetNetworkInfos(const std::vector& network_infos); @@ -120,11 +120,13 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorInterface { absl::string_view ifname) const; private: + void reset(); void OnNetworkConnected_n(const NetworkInformation& network_info); void OnNetworkDisconnected_n(NetworkHandle network_handle); void OnNetworkPreference_n(NetworkType type, rtc::NetworkPreference preference); + rtc::NetworkPreference GetNetworkPreference(rtc::AdapterType) const; absl::optional FindNetworkHandleFromIfname( absl::string_view ifname) const; @@ -133,10 +135,8 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorInterface { ScopedJavaGlobalRef j_network_monitor_; rtc::Thread* const network_thread_; bool started_ RTC_GUARDED_BY(network_thread_) = false; - std::map - adapter_type_by_name_ RTC_GUARDED_BY(network_thread_); - std::map - vpn_underlying_adapter_type_by_name_ RTC_GUARDED_BY(network_thread_); + std::map + network_handle_by_if_name_ RTC_GUARDED_BY(network_thread_); std::map network_handle_by_address_ RTC_GUARDED_BY(network_thread_); std::map network_info_by_handle_ @@ -163,6 +163,8 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorInterface { RTC_PT_GUARDED_BY(network_thread_) = nullptr; const FieldTrialsView& field_trials_; + + friend class webrtc::test::AndroidNetworkMonitorTest; }; class AndroidNetworkMonitorFactory : public rtc::NetworkMonitorFactory { diff --git a/sdk/objc/native/src/objc_network_monitor.h b/sdk/objc/native/src/objc_network_monitor.h index 04566d20e5..a47a0a9a37 100644 --- a/sdk/objc/native/src/objc_network_monitor.h +++ b/sdk/objc/native/src/objc_network_monitor.h @@ -45,12 +45,7 @@ class ObjCNetworkMonitor : public rtc::NetworkMonitorInterface, void Start() override; void Stop() override; - rtc::AdapterType GetAdapterType(absl::string_view interface_name) override; - rtc::AdapterType GetVpnUnderlyingAdapterType( - absl::string_view interface_name) override; - rtc::NetworkPreference GetNetworkPreference( - absl::string_view interface_name) override; - bool IsAdapterAvailable(absl::string_view interface_name) override; + InterfaceInfo GetInterfaceInfo(absl::string_view interface_name) override; // NetworkMonitorObserver override. // Fans out updates to observers on the correct thread. diff --git a/sdk/objc/native/src/objc_network_monitor.mm b/sdk/objc/native/src/objc_network_monitor.mm index 6a43b57421..28fd72b94e 100644 --- a/sdk/objc/native/src/objc_network_monitor.mm +++ b/sdk/objc/native/src/objc_network_monitor.mm @@ -59,31 +59,29 @@ void ObjCNetworkMonitor::Stop() { started_ = false; } -rtc::AdapterType ObjCNetworkMonitor::GetAdapterType(absl::string_view interface_name) { - RTC_DCHECK_RUN_ON(thread_); - auto iter = adapter_type_by_name_.find(interface_name); - if (iter == adapter_type_by_name_.end()) { - return rtc::ADAPTER_TYPE_UNKNOWN; - } - return iter->second; -} - -rtc::AdapterType ObjCNetworkMonitor::GetVpnUnderlyingAdapterType(absl::string_view interface_name) { - return rtc::ADAPTER_TYPE_UNKNOWN; -} - -rtc::NetworkPreference ObjCNetworkMonitor::GetNetworkPreference(absl::string_view interface_name) { - return rtc::NetworkPreference::NEUTRAL; -} - -bool ObjCNetworkMonitor::IsAdapterAvailable(absl::string_view interface_name) { +rtc::NetworkMonitorInterface::InterfaceInfo ObjCNetworkMonitor::GetInterfaceInfo( + absl::string_view interface_name) { RTC_DCHECK_RUN_ON(thread_); if (adapter_type_by_name_.empty()) { // If we have no path update, assume everything's available, because it's // preferable for WebRTC to try all interfaces rather than none at all. - return true; + return { + .adapter_type = rtc::ADAPTER_TYPE_UNKNOWN, + .available = true, + }; } - return adapter_type_by_name_.find(interface_name) != adapter_type_by_name_.end(); + auto iter = adapter_type_by_name_.find(interface_name); + if (iter == adapter_type_by_name_.end()) { + return { + .adapter_type = rtc::ADAPTER_TYPE_UNKNOWN, + .available = false, + }; + } + + return { + .adapter_type = iter->second, + .available = true, + }; } void ObjCNetworkMonitor::OnPathUpdate(