diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn index 4a805f887a..796d3bea4f 100644 --- a/sdk/android/BUILD.gn +++ b/sdk/android/BUILD.gn @@ -557,6 +557,7 @@ if (current_os == "linux" || is_android) { "../../rtc_base", "../../rtc_base:checks", "../../rtc_base:rtc_base_approved", + "../../system_wrappers:field_trial", "../../system_wrappers:metrics", "//third_party/abseil-cpp/absl/types:optional", ] @@ -1453,6 +1454,7 @@ if (is_android) { testonly = true sources = [ + "native_unittests/android_network_monitor_unittest.cc", "native_unittests/application_context_provider.cc", "native_unittests/application_context_provider.h", "native_unittests/audio_device/audio_device_unittest.cc", @@ -1498,9 +1500,11 @@ if (is_android) { "../../modules/utility", "../../pc:libjingle_peerconnection", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:rtc_base", "../../rtc_base/system:inline", "../../system_wrappers", + "../../system_wrappers:field_trial", + "../../test:field_trial", "../../test:fileutils", "../../test:test_support", "../../testing/gtest", diff --git a/sdk/android/native_unittests/android_network_monitor_unittest.cc b/sdk/android/native_unittests/android_network_monitor_unittest.cc new file mode 100644 index 0000000000..5c17d44fb2 --- /dev/null +++ b/sdk/android/native_unittests/android_network_monitor_unittest.cc @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2019 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 "sdk/android/src/jni/android_network_monitor.h" + +#include "rtc_base/ip_address.h" +#include "sdk/android/native_unittests/application_context_provider.h" +#include "sdk/android/src/jni/jni_helpers.h" +#include "system_wrappers/include/field_trial.h" +#include "test/field_trial.h" +#include "test/gtest.h" + +namespace webrtc { +namespace test { +static const uint32_t kTestIpv4Address = 0xC0A80011; // 192.168.0.17 +// The following two ipv6 addresses only diff by the last 64 bits. +static const char kTestIpv6Address1[] = "2a00:8a00:a000:1190:0000:0001:000:252"; +static const char kTestIpv6Address2[] = "2a00:8a00:a000:1190:0000:0002:000:253"; + +jni::NetworkInformation CreateNetworkInformation( + const std::string& interface_name, + jni::NetworkHandle network_handle, + const rtc::IPAddress& ip_address) { + jni::NetworkInformation net_info; + net_info.interface_name = interface_name; + net_info.handle = network_handle; + net_info.type = jni::NETWORK_WIFI; + net_info.ip_addresses.push_back(ip_address); + return net_info; +} + +rtc::IPAddress GetIpAddressFromIpv6String(const std::string& str) { + rtc::IPAddress ipv6; + RTC_CHECK(rtc::IPFromString(str, &ipv6)); + return ipv6; +} + +class AndroidNetworkMonitorTest : public ::testing::Test { + public: + AndroidNetworkMonitorTest() { + JNIEnv* env = AttachCurrentThreadIfNeeded(); + ScopedJavaLocalRef context = test::GetAppContextForTest(env); + network_monitor_ = + std::make_unique(env, context); + } + + void SetUp() { + // Reset network monitor states. + network_monitor_->Stop(); + } + + protected: + std::unique_ptr network_monitor_; +}; + +TEST_F(AndroidNetworkMonitorTest, TestFindNetworkHandleUsingIpv4Address) { + jni::NetworkHandle ipv4_handle = 100; + rtc::IPAddress ipv4_address(kTestIpv4Address); + jni::NetworkInformation net_info = + CreateNetworkInformation("wlan0", ipv4_handle, ipv4_address); + std::vector net_infos(1, net_info); + network_monitor_->SetNetworkInfos(net_infos); + + auto network_handle = + network_monitor_->FindNetworkHandleFromAddress(ipv4_address); + + ASSERT_TRUE(network_handle.has_value()); + EXPECT_EQ(ipv4_handle, *network_handle); +} + +TEST_F(AndroidNetworkMonitorTest, TestFindNetworkHandleUsingFullIpv6Address) { + jni::NetworkHandle ipv6_handle = 200; + rtc::IPAddress ipv6_address1 = GetIpAddressFromIpv6String(kTestIpv6Address1); + rtc::IPAddress ipv6_address2 = GetIpAddressFromIpv6String(kTestIpv6Address2); + // Set up an IPv6 network. + jni::NetworkInformation net_info = + CreateNetworkInformation("wlan0", ipv6_handle, ipv6_address1); + std::vector net_infos(1, net_info); + network_monitor_->SetNetworkInfos(net_infos); + + auto network_handle1 = + network_monitor_->FindNetworkHandleFromAddress(ipv6_address1); + auto network_handle2 = + network_monitor_->FindNetworkHandleFromAddress(ipv6_address2); + + ASSERT_TRUE(network_handle1.has_value()); + EXPECT_EQ(ipv6_handle, *network_handle1); + EXPECT_TRUE(!network_handle2); +} + +TEST_F(AndroidNetworkMonitorTest, + TestFindNetworkHandleIgnoringIpv6TemporaryPart) { + ScopedFieldTrials field_trials( + "WebRTC-FindNetworkHandleWithoutIpv6TemporaryPart/Enabled/"); + // Start() updates the states introduced by the field trial. + network_monitor_->Start(); + jni::NetworkHandle ipv6_handle = 200; + rtc::IPAddress ipv6_address1 = GetIpAddressFromIpv6String(kTestIpv6Address1); + rtc::IPAddress ipv6_address2 = GetIpAddressFromIpv6String(kTestIpv6Address2); + // Set up an IPv6 network. + jni::NetworkInformation net_info = + CreateNetworkInformation("wlan0", ipv6_handle, ipv6_address1); + std::vector net_infos(1, net_info); + network_monitor_->SetNetworkInfos(net_infos); + + auto network_handle1 = + network_monitor_->FindNetworkHandleFromAddress(ipv6_address1); + auto network_handle2 = + network_monitor_->FindNetworkHandleFromAddress(ipv6_address2); + + ASSERT_TRUE(network_handle1.has_value()); + EXPECT_EQ(ipv6_handle, *network_handle1); + ASSERT_TRUE(network_handle2.has_value()); + EXPECT_EQ(ipv6_handle, *network_handle2); +} + +} // namespace test +} // namespace webrtc diff --git a/sdk/android/src/jni/android_network_monitor.cc b/sdk/android/src/jni/android_network_monitor.cc index 3e124be771..993bbb5e84 100644 --- a/sdk/android/src/jni/android_network_monitor.cc +++ b/sdk/android/src/jni/android_network_monitor.cc @@ -25,6 +25,7 @@ #include "sdk/android/generated_base_jni/NetworkMonitor_jni.h" #include "sdk/android/native_api/jni/java_types.h" #include "sdk/android/src/jni/jni_helpers.h" +#include "system_wrappers/include/field_trial.h" namespace webrtc { namespace jni { @@ -135,6 +136,22 @@ static NetworkInformation GetNetworkInformationFromJava( return network_info; } +static bool AddressMatch(const rtc::IPAddress& ip1, const rtc::IPAddress& ip2) { + if (ip1.family() != ip2.family()) { + return false; + } + if (ip1.family() == AF_INET) { + return ip1.ipv4_address().s_addr == ip2.ipv4_address().s_addr; + } + if (ip1.family() == AF_INET6) { + // The last 64-bits of an ipv6 address are temporary address and it could + // change over time. So we only compare the first 64-bits. + return memcmp(ip1.ipv6_address().s6_addr, ip2.ipv6_address().s6_addr, + sizeof(in6_addr) / 2) == 0; + } + return false; +} + NetworkInformation::NetworkInformation() = default; NetworkInformation::NetworkInformation(const NetworkInformation&) = default; @@ -179,6 +196,9 @@ void AndroidNetworkMonitor::Start() { return; } started_ = true; + find_network_handle_without_ipv6_temporary_part_ = + webrtc::field_trial::IsEnabled( + "WebRTC-FindNetworkHandleWithoutIpv6TemporaryPart"); // This is kind of magic behavior, but doing this allows the SocketServer to // use this as a NetworkBinder to bind sockets on a particular network when @@ -196,6 +216,7 @@ void AndroidNetworkMonitor::Stop() { return; } started_ = false; + find_network_handle_without_ipv6_temporary_part_ = false; // Once the network monitor stops, it will clear all network information and // it won't find the network handle to bind anyway. @@ -219,7 +240,8 @@ rtc::NetworkBindingResult AndroidNetworkMonitor::BindSocketToNetwork( RTC_CHECK(thread_checker_.IsCurrent()); // Android prior to Lollipop didn't have support for binding sockets to - // networks. This may also occur if there is no connectivity manager service. + // networks. This may also occur if there is no connectivity manager + // service. JNIEnv* env = AttachCurrentThreadIfNeeded(); const bool network_binding_supported = Java_NetworkMonitor_networkBindingSupported(env, j_network_monitor_); @@ -230,13 +252,13 @@ rtc::NetworkBindingResult AndroidNetworkMonitor::BindSocketToNetwork( return rtc::NetworkBindingResult::NOT_IMPLEMENTED; } - auto iter = network_handle_by_address_.find(address); - if (iter == network_handle_by_address_.end()) { + absl::optional network_handle = + FindNetworkHandleFromAddress(address); + if (!network_handle) { return rtc::NetworkBindingResult::ADDRESS_NOT_FOUND; } - NetworkHandle network_handle = iter->second; - if (network_handle == 0 /* NETWORK_UNSPECIFIED */) { + if (*network_handle == 0 /* NETWORK_UNSPECIFIED */) { return rtc::NetworkBindingResult::NOT_IMPLEMENTED; } @@ -267,10 +289,10 @@ rtc::NetworkBindingResult AndroidNetworkMonitor::BindSocketToNetwork( RTC_LOG(LS_ERROR) << "Symbol marshmallowSetNetworkForSocket is not found"; return rtc::NetworkBindingResult::NOT_IMPLEMENTED; } - rv = marshmallowSetNetworkForSocket(network_handle, socket_fd); + rv = marshmallowSetNetworkForSocket(*network_handle, socket_fd); } else { - // NOTE: This relies on Android implementation details, but it won't change - // because Lollipop is already released. + // NOTE: This relies on Android implementation details, but it won't + // change because Lollipop is already released. typedef int (*LollipopSetNetworkForSocket)(unsigned net, int socket); static LollipopSetNetworkForSocket lollipopSetNetworkForSocket; // This is not threadsafe, but we are running this only on the worker @@ -296,10 +318,10 @@ rtc::NetworkBindingResult AndroidNetworkMonitor::BindSocketToNetwork( RTC_LOG(LS_ERROR) << "Symbol lollipopSetNetworkForSocket is not found "; return rtc::NetworkBindingResult::NOT_IMPLEMENTED; } - rv = lollipopSetNetworkForSocket(network_handle, socket_fd); + rv = lollipopSetNetworkForSocket(*network_handle, socket_fd); } - // If |network| has since disconnected, |rv| will be ENONET. Surface this as + // If |network| has since disconnected, |rv| will be ENONET. Surface this as // ERR_NETWORK_CHANGED, rather than MapSystemError(ENONET) which gives back // the less descriptive ERR_FAILED. if (rv == 0) { @@ -335,6 +357,32 @@ void AndroidNetworkMonitor::OnNetworkConnected_w( } } +absl::optional +AndroidNetworkMonitor::FindNetworkHandleFromAddress( + const rtc::IPAddress& ip_address) const { + RTC_LOG(LS_INFO) << "Find network handle for address: " + << ip_address.ToString(); + if (find_network_handle_without_ipv6_temporary_part_) { + for (auto const& iter : network_info_by_handle_) { + const std::vector& addresses = iter.second.ip_addresses; + auto address_it = std::find_if(addresses.begin(), addresses.end(), + [ip_address](rtc::IPAddress address) { + return AddressMatch(ip_address, address); + }); + if (address_it != addresses.end()) { + return absl::make_optional(iter.first); + } + } + return absl::nullopt; + } else { + auto iter = network_handle_by_address_.find(ip_address); + if (iter == network_handle_by_address_.end()) { + return absl::nullopt; + } + return absl::make_optional(iter->second); + } +} + void AndroidNetworkMonitor::OnNetworkDisconnected(NetworkHandle handle) { RTC_LOG(LS_INFO) << "Network disconnected for handle " << handle; worker_thread()->Invoke( diff --git a/sdk/android/src/jni/android_network_monitor.h b/sdk/android/src/jni/android_network_monitor.h index 898ddc98b0..81647bd307 100644 --- a/sdk/android/src/jni/android_network_monitor.h +++ b/sdk/android/src/jni/android_network_monitor.h @@ -16,6 +16,7 @@ #include #include +#include "absl/types/optional.h" #include "rtc_base/network_monitor.h" #include "rtc_base/thread_checker.h" #include "sdk/android/src/jni/jni_helpers.h" @@ -61,8 +62,8 @@ struct NetworkInformation { class AndroidNetworkMonitor : public rtc::NetworkMonitorBase, public rtc::NetworkBinderInterface { public: - explicit AndroidNetworkMonitor(JNIEnv* env, - const JavaRef& j_application_context); + AndroidNetworkMonitor(JNIEnv* env, + const JavaRef& j_application_context); ~AndroidNetworkMonitor() override; // TODO(sakal): Remove once down stream dependencies have been updated. @@ -94,6 +95,10 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorBase, const JavaRef& j_caller, const JavaRef& j_network_infos); + // Visible for testing. + absl::optional FindNetworkHandleFromAddress( + const rtc::IPAddress& address) const; + private: void OnNetworkConnected_w(const NetworkInformation& network_info); void OnNetworkDisconnected_w(NetworkHandle network_handle); @@ -107,6 +112,7 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorBase, std::map vpn_underlying_adapter_type_by_name_; std::map network_handle_by_address_; std::map network_info_by_handle_; + bool find_network_handle_without_ipv6_temporary_part_; }; class AndroidNetworkMonitorFactory : public rtc::NetworkMonitorFactory {