From cec0a08275f767e665287dbf04fd9d04648a7d67 Mon Sep 17 00:00:00 2001 From: honghaiz Date: Fri, 15 Jan 2016 14:49:09 -0800 Subject: [PATCH] Add a new interface for creating a udp socket in which it binds the socket to a network if the network handle is set. Plus, in stunport, turnport and allocation sequence, create a socket using the new interface. BUG= Review URL: https://codereview.webrtc.org/1556743002 Cr-Commit-Position: refs/heads/master@{#11279} --- .../webrtc/androidtests/AndroidManifest.xml | 1 + .../src/org/webrtc/NetworkMonitorTest.java | 6 +- .../android/org/webrtc/NetworkMonitor.java | 22 +- .../org/webrtc/NetworkMonitorAutoDetect.java | 113 ++++++++-- .../java/jni/androidnetworkmonitor_jni.cc | 211 ++++++++++++++++++ .../java/jni/androidnetworkmonitor_jni.h | 43 +++- .../webrtc/java/jni/classreferenceholder.cc | 3 + talk/app/webrtc/java/jni/jni_helpers.cc | 15 ++ talk/app/webrtc/java/jni/jni_helpers.h | 5 + .../app/webrtc/java/jni/peerconnection_jni.cc | 12 - webrtc/base/network.cc | 8 +- webrtc/base/network_unittest.cc | 23 +- webrtc/base/networkmonitor.cc | 4 +- webrtc/base/networkmonitor.h | 26 ++- webrtc/base/physicalsocketserver.cc | 15 +- webrtc/base/socketserver.h | 11 + .../examples/androidapp/AndroidManifest.xml | 1 + 17 files changed, 457 insertions(+), 62 deletions(-) diff --git a/talk/app/webrtc/androidtests/AndroidManifest.xml b/talk/app/webrtc/androidtests/AndroidManifest.xml index 75b6d615d3..0b3b0b1268 100644 --- a/talk/app/webrtc/androidtests/AndroidManifest.xml +++ b/talk/app/webrtc/androidtests/AndroidManifest.xml @@ -15,6 +15,7 @@ + = Build.VERSION_CODES.LOLLIPOP) { + connectivityManager.unregisterNetworkCallback(networkCallback); + } + networkCallback = null; + } + } + } + /** Queries the WifiManager for SSID of the current Wifi connection. */ static class WifiManagerDelegate { private final Context context; - private final WifiManager wifiManager; - private final boolean hasWifiPermission; - WifiManagerDelegate(Context context) { this.context = context; - - hasWifiPermission = context.getPackageManager().checkPermission( - permission.ACCESS_WIFI_STATE, context.getPackageName()) - == PackageManager.PERMISSION_GRANTED; - wifiManager = hasWifiPermission - ? (WifiManager) context.getSystemService(Context.WIFI_SERVICE) : null; } // For testing. WifiManagerDelegate() { // All the methods below should be overridden. context = null; - wifiManager = null; - hasWifiPermission = false; } String getWifiSSID() { @@ -252,9 +319,6 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { return ""; } - boolean getHasWifiPermission() { - return hasWifiPermission; - } } static final int INVALID_NET_ID = -1; @@ -280,6 +344,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { * Called when default network changes. */ public void onConnectionTypeChanged(ConnectionType newConnectionType); + public void onNetworkConnect(NetworkInformation networkInfo); } /** @@ -292,8 +357,8 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { wifiManagerDelegate = new WifiManagerDelegate(context); final NetworkState networkState = connectivityManagerDelegate.getNetworkState(); - connectionType = getCurrentConnectionType(networkState); - wifiSSID = getCurrentWifiSSID(networkState); + connectionType = getConnectionType(networkState); + wifiSSID = getWifiSSID(networkState); intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); registerReceiver(); } @@ -331,6 +396,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { if (!isRegistered) { isRegistered = true; context.registerReceiver(this, intentFilter); + connectivityManagerDelegate.requestMobileNetwork(observer); } } @@ -341,6 +407,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { if (isRegistered) { isRegistered = false; context.unregisterReceiver(this); + connectivityManagerDelegate.releaseCallback(); } } @@ -361,7 +428,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { return connectivityManagerDelegate.getDefaultNetId(); } - public ConnectionType getCurrentConnectionType(NetworkState networkState) { + public static ConnectionType getConnectionType(NetworkState networkState) { if (!networkState.isConnected()) { return ConnectionType.CONNECTION_NONE; } @@ -404,8 +471,8 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { } } - private String getCurrentWifiSSID(NetworkState networkState) { - if (getCurrentConnectionType(networkState) != ConnectionType.CONNECTION_WIFI) return ""; + private String getWifiSSID(NetworkState networkState) { + if (getConnectionType(networkState) != ConnectionType.CONNECTION_WIFI) return ""; return wifiManagerDelegate.getWifiSSID(); } @@ -419,13 +486,13 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { } private void connectionTypeChanged(NetworkState networkState) { - ConnectionType newConnectionType = getCurrentConnectionType(networkState); - String newWifiSSID = getCurrentWifiSSID(networkState); + ConnectionType newConnectionType = getConnectionType(networkState); + String newWifiSSID = getWifiSSID(networkState); if (newConnectionType == connectionType && newWifiSSID.equals(wifiSSID)) return; connectionType = newConnectionType; wifiSSID = newWifiSSID; - Log.d(TAG, "Network connectivity changed, type is: " + connectionType); + Logging.d(TAG, "Network connectivity changed, type is: " + connectionType); observer.onConnectionTypeChanged(newConnectionType); } diff --git a/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc b/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc index f7a8c07c60..1716c19cf8 100644 --- a/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc +++ b/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc @@ -27,13 +27,125 @@ #include "talk/app/webrtc/java/jni/androidnetworkmonitor_jni.h" +#include + +#include "webrtc/base/bind.h" #include "webrtc/base/common.h" +#include "webrtc/base/ipaddress.h" #include "talk/app/webrtc/java/jni/classreferenceholder.h" #include "talk/app/webrtc/java/jni/jni_helpers.h" namespace webrtc_jni { + jobject AndroidNetworkMonitor::application_context_ = nullptr; +static NetworkType GetNetworkTypeFromJava(JNIEnv* jni, jobject j_network_type) { + std::string enum_name = + GetJavaEnumName(jni, "org/webrtc/NetworkMonitorAutoDetect$ConnectionType", + j_network_type); + if (enum_name == "CONNECTION_UNKNOWN") { + return NetworkType::NETWORK_UNKNOWN; + } + if (enum_name == "CONNECTION_ETHERNET") { + return NetworkType::NETWORK_ETHERNET; + } + if (enum_name == "CONNECTION_WIFI") { + return NetworkType::NETWORK_WIFI; + } + if (enum_name == "CONNECTION_4G") { + return NetworkType::NETWORK_4G; + } + if (enum_name == "CONNECTION_3G") { + return NetworkType::NETWORK_3G; + } + if (enum_name == "CONNECTION_2G") { + return NetworkType::NETWORK_2G; + } + if (enum_name == "CONNECTION_BLUETOOTH") { + return NetworkType::NETWORK_BLUETOOTH; + } + if (enum_name == "CONNECTION_NONE") { + return NetworkType::NETWORK_NONE; + } + ASSERT(false); + return NetworkType::NETWORK_UNKNOWN; +} + +static rtc::IPAddress GetIPAddressFromJava(JNIEnv* jni, jobject j_ip_address) { + jclass j_ip_address_class = GetObjectClass(jni, j_ip_address); + jfieldID j_address_id = GetFieldID(jni, j_ip_address_class, "address", "[B"); + jbyteArray j_addresses = + static_cast(GetObjectField(jni, j_ip_address, j_address_id)); + size_t address_length = jni->GetArrayLength(j_addresses); + jbyte* addr_array = jni->GetByteArrayElements(j_addresses, nullptr); + CHECK_EXCEPTION(jni) << "Error during GetIPAddressFromJava"; + if (address_length == 4) { + // IP4 + struct in_addr ip4_addr; + memcpy(&ip4_addr.s_addr, addr_array, 4); + jni->ReleaseByteArrayElements(j_addresses, addr_array, JNI_ABORT); + return rtc::IPAddress(ip4_addr); + } + // IP6 + RTC_CHECK(address_length == 16); + struct in6_addr ip6_addr; + memcpy(ip6_addr.s6_addr, addr_array, address_length); + jni->ReleaseByteArrayElements(j_addresses, addr_array, JNI_ABORT); + return rtc::IPAddress(ip6_addr); +} + +static void GetIPAddressesFromJava(JNIEnv* jni, + jobjectArray j_ip_addresses, + std::vector* ip_addresses) { + ip_addresses->clear(); + size_t num_addresses = jni->GetArrayLength(j_ip_addresses); + CHECK_EXCEPTION(jni) << "Error during GetArrayLength"; + for (size_t i = 0; i < num_addresses; ++i) { + jobject j_ip_address = jni->GetObjectArrayElement(j_ip_addresses, i); + CHECK_EXCEPTION(jni) << "Error during GetObjectArrayElement"; + rtc::IPAddress ip = GetIPAddressFromJava(jni, j_ip_address); + ip_addresses->push_back(ip); + } +} + +static NetworkInformation GetNetworkInformationFromJava( + JNIEnv* jni, + jobject j_network_info) { + jclass j_network_info_class = GetObjectClass(jni, j_network_info); + jfieldID j_interface_name_id = + GetFieldID(jni, j_network_info_class, "name", "Ljava/lang/String;"); + jfieldID j_handle_id = GetFieldID(jni, j_network_info_class, "handle", "I"); + jfieldID j_type_id = + GetFieldID(jni, j_network_info_class, "type", + "Lorg/webrtc/NetworkMonitorAutoDetect$ConnectionType;"); + jfieldID j_ip_addresses_id = + GetFieldID(jni, j_network_info_class, "ipAddresses", + "[Lorg/webrtc/NetworkMonitorAutoDetect$IPAddress;"); + + NetworkInformation network_info; + network_info.interface_name = JavaToStdString( + jni, GetStringField(jni, j_network_info, j_interface_name_id)); + network_info.handle = + static_cast(GetIntField(jni, j_network_info, j_handle_id)); + network_info.type = GetNetworkTypeFromJava( + jni, GetObjectField(jni, j_network_info, j_type_id)); + jobjectArray j_ip_addresses = static_cast( + GetObjectField(jni, j_network_info, j_ip_addresses_id)); + GetIPAddressesFromJava(jni, j_ip_addresses, &network_info.ip_addresses); + return network_info; +} + +std::string NetworkInformation::ToString() const { + std::stringstream ss; + ss << "NetInfo[name " << interface_name << "; handle " << handle << "; type " + << type << "; address"; + for (const rtc::IPAddress address : ip_addresses) { + ss << " " << address.ToString(); + } + ss << "]"; + return ss.str(); +} + // static void AndroidNetworkMonitor::SetAndroidContext(JNIEnv* jni, jobject context) { if (application_context_) { @@ -61,6 +173,16 @@ AndroidNetworkMonitor::AndroidNetworkMonitor() void AndroidNetworkMonitor::Start() { RTC_CHECK(thread_checker_.CalledOnValidThread()); + if (started_) { + return; + } + started_ = true; + + // 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 + // it creates sockets. + worker_thread()->socketserver()->set_network_binder(this); + jmethodID m = GetMethodID(jni(), *j_network_monitor_class_, "startMonitoring", "(J)V"); jni()->CallVoidMethod(*j_network_monitor_, m, jlongFromPointer(this)); @@ -69,10 +191,89 @@ void AndroidNetworkMonitor::Start() { void AndroidNetworkMonitor::Stop() { RTC_CHECK(thread_checker_.CalledOnValidThread()); + if (!started_) { + return; + } + started_ = false; + + // Once the network monitor stops, it will clear all network information and + // it won't find the network handle to bind anyway. + if (worker_thread()->socketserver()->network_binder() == this) { + worker_thread()->socketserver()->set_network_binder(nullptr); + } + jmethodID m = GetMethodID(jni(), *j_network_monitor_class_, "stopMonitoring", "(J)V"); jni()->CallVoidMethod(*j_network_monitor_, m, jlongFromPointer(this)); CHECK_EXCEPTION(jni()) << "Error during NetworkMonitor.stopMonitoring"; + + network_info_by_address_.clear(); +} + +int AndroidNetworkMonitor::BindSocketToNetwork(int socket_fd, + const rtc::IPAddress& address) { + RTC_CHECK(thread_checker_.CalledOnValidThread()); + auto it = network_info_by_address_.find(address); + if (it == network_info_by_address_.end()) { + return rtc::NETWORK_BIND_ADDRESS_NOT_FOUND; + } + // Android prior to Lollipop didn't have support for binding sockets to + // networks. However, in that case it should not have reached here because + // |network_info_by_address_| should only be populated in Android Lollipop + // and above. + NetworkInformation network = it->second; + + // NOTE: This does rely on Android implementation details, but + // these details are unlikely to change. + typedef int (*SetNetworkForSocket)(unsigned netId, int socketFd); + static SetNetworkForSocket setNetworkForSocket; + // This is not threadsafe, but we are running this only on the worker thread. + if (setNetworkForSocket == nullptr) { + // Android's netd client library should always be loaded in our address + // space as it shims libc functions like connect(). + const std::string net_library_path = "libnetd_client.so"; + void* lib = dlopen(net_library_path.c_str(), RTLD_LAZY); + if (lib == nullptr) { + LOG(LS_ERROR) << "Library " << net_library_path << " not found!"; + return rtc::NETWORK_BIND_NOT_IMPLEMENTED; + } + setNetworkForSocket = reinterpret_cast( + dlsym(lib, "setNetworkForSocket")); + } + if (setNetworkForSocket == nullptr) { + LOG(LS_ERROR) << "Symbol setNetworkForSocket not found "; + return rtc::NETWORK_BIND_NOT_IMPLEMENTED; + } + int rv = setNetworkForSocket(network.handle, socket_fd); + // 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) { + return rtc::NETWORK_BIND_SUCCESS; + } + if (rv == ENONET) { + return rtc::NETWORK_BIND_NETWORK_CHANGED; + } + return rtc::NETWORK_BIND_FAILURE; +} + +void AndroidNetworkMonitor::OnNetworkAvailable( + const NetworkInformation& network_info) { + worker_thread()->Invoke(rtc::Bind( + &AndroidNetworkMonitor::OnNetworkAvailable_w, this, network_info)); +} + +void AndroidNetworkMonitor::OnNetworkAvailable_w( + const NetworkInformation& network_info) { + LOG(LS_INFO) << "Network available: " << network_info.ToString(); + for (rtc::IPAddress address : network_info.ip_addresses) { + network_info_by_address_[address] = network_info; + } +} + +rtc::NetworkMonitorInterface* +AndroidNetworkMonitorFactory::CreateNetworkMonitor() { + return new AndroidNetworkMonitor(); } JOW(void, NetworkMonitor_nativeNotifyConnectionTypeChanged)( @@ -82,4 +283,14 @@ JOW(void, NetworkMonitor_nativeNotifyConnectionTypeChanged)( network_monitor->OnNetworksChanged(); } +JOW(void, NetworkMonitor_nativeNotifyOfNetworkConnect)( + JNIEnv* jni, jobject j_monitor, jlong j_native_monitor, + jobject j_network_info) { + AndroidNetworkMonitor* network_monitor = + reinterpret_cast(j_native_monitor); + NetworkInformation network_info = + GetNetworkInformationFromJava(jni, j_network_info); + network_monitor->OnNetworkAvailable(network_info); +} + } // namespace webrtc_jni diff --git a/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.h b/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.h index 3f5110c2c4..17de3603c6 100644 --- a/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.h +++ b/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.h @@ -30,12 +30,41 @@ #include "webrtc/base/networkmonitor.h" +#include + +#include "webrtc/base/basictypes.h" #include "webrtc/base/thread_checker.h" #include "talk/app/webrtc/java/jni/jni_helpers.h" namespace webrtc_jni { -class AndroidNetworkMonitor : public rtc::NetworkMonitorBase { +typedef uint32_t NetworkHandle; + +// c++ equivalent of java NetworkMonitorAutoDetect.ConnectionType. +enum NetworkType { + NETWORK_UNKNOWN, + NETWORK_ETHERNET, + NETWORK_WIFI, + NETWORK_4G, + NETWORK_3G, + NETWORK_2G, + NETWORK_BLUETOOTH, + NETWORK_NONE +}; + +// The information is collected from Android OS so that the native code can get +// the network type and handle (Android network ID) for each interface. +struct NetworkInformation { + std::string interface_name; + NetworkHandle handle; + NetworkType type; + std::vector ip_addresses; + + std::string ToString() const; +}; + +class AndroidNetworkMonitor : public rtc::NetworkMonitorBase, + public rtc::NetworkBinderInterface { public: AndroidNetworkMonitor(); @@ -44,22 +73,28 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorBase { void Start() override; void Stop() override; + int BindSocketToNetwork(int socket_fd, + const rtc::IPAddress& address) override; + void OnNetworkAvailable(const NetworkInformation& network_info); + private: JNIEnv* jni() { return AttachCurrentThreadIfNeeded(); } + void OnNetworkAvailable_w(const NetworkInformation& network_info); + ScopedGlobalRef j_network_monitor_class_; ScopedGlobalRef j_network_monitor_; rtc::ThreadChecker thread_checker_; static jobject application_context_; + bool started_ = false; + std::map network_info_by_address_; }; class AndroidNetworkMonitorFactory : public rtc::NetworkMonitorFactory { public: AndroidNetworkMonitorFactory() {} - rtc::NetworkMonitorInterface* CreateNetworkMonitor() override { - return new AndroidNetworkMonitor(); - } + rtc::NetworkMonitorInterface* CreateNetworkMonitor() override; }; } // namespace webrtc_jni diff --git a/talk/app/webrtc/java/jni/classreferenceholder.cc b/talk/app/webrtc/java/jni/classreferenceholder.cc index 5fe8ec707c..a7d36c226f 100644 --- a/talk/app/webrtc/java/jni/classreferenceholder.cc +++ b/talk/app/webrtc/java/jni/classreferenceholder.cc @@ -81,6 +81,9 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { LoadClass(jni, "org/webrtc/EglBase$Context"); LoadClass(jni, "org/webrtc/EglBase14$Context"); LoadClass(jni, "org/webrtc/NetworkMonitor"); + LoadClass(jni, "org/webrtc/NetworkMonitorAutoDetect$ConnectionType"); + LoadClass(jni, "org/webrtc/NetworkMonitorAutoDetect$IPAddress"); + LoadClass(jni, "org/webrtc/NetworkMonitorAutoDetect$NetworkInformation"); LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder"); LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder$OutputBufferInfo"); LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder$VideoCodecType"); diff --git a/talk/app/webrtc/java/jni/jni_helpers.cc b/talk/app/webrtc/java/jni/jni_helpers.cc index 3a7ff21e77..25b340ff79 100644 --- a/talk/app/webrtc/java/jni/jni_helpers.cc +++ b/talk/app/webrtc/java/jni/jni_helpers.cc @@ -27,6 +27,8 @@ */ #include "talk/app/webrtc/java/jni/jni_helpers.h" +#include "talk/app/webrtc/java/jni/classreferenceholder.h" + #include #include #include @@ -256,6 +258,19 @@ jobject JavaEnumFromIndex(JNIEnv* jni, jclass state_class, return ret; } +std::string GetJavaEnumName(JNIEnv* jni, + const std::string& className, + jobject j_enum) { + jclass enumClass = FindClass(jni, className.c_str()); + jmethodID nameMethod = + GetMethodID(jni, enumClass, "name", "()Ljava/lang/String;"); + jstring name = + reinterpret_cast(jni->CallObjectMethod(j_enum, nameMethod)); + CHECK_EXCEPTION(jni) << "error during CallObjectMethod for " << className + << ".name"; + return JavaToStdString(jni, name); +} + jobject NewGlobalRef(JNIEnv* jni, jobject o) { jobject ret = jni->NewGlobalRef(o); CHECK_EXCEPTION(jni) << "error during NewGlobalRef"; diff --git a/talk/app/webrtc/java/jni/jni_helpers.h b/talk/app/webrtc/java/jni/jni_helpers.h index 7072ee855e..374962be49 100644 --- a/talk/app/webrtc/java/jni/jni_helpers.h +++ b/talk/app/webrtc/java/jni/jni_helpers.h @@ -104,6 +104,11 @@ std::string JavaToStdString(JNIEnv* jni, const jstring& j_string); jobject JavaEnumFromIndex(JNIEnv* jni, jclass state_class, const std::string& state_class_name, int index); +// Returns the name of a Java enum. +std::string GetJavaEnumName(JNIEnv* jni, + const std::string& className, + jobject j_enum); + jobject NewGlobalRef(JNIEnv* jni, jobject o); void DeleteGlobalRef(JNIEnv* jni, jobject o); diff --git a/talk/app/webrtc/java/jni/peerconnection_jni.cc b/talk/app/webrtc/java/jni/peerconnection_jni.cc index f80f5766e8..6059623daa 100644 --- a/talk/app/webrtc/java/jni/peerconnection_jni.cc +++ b/talk/app/webrtc/java/jni/peerconnection_jni.cc @@ -1395,18 +1395,6 @@ JOW(void, PeerConnectionFactory_nativeSetVideoHwAccelerationOptions)( #endif } -static std::string -GetJavaEnumName(JNIEnv* jni, const std::string& className, jobject j_enum) { - jclass enumClass = FindClass(jni, className.c_str()); - jmethodID nameMethod = - GetMethodID(jni, enumClass, "name", "()Ljava/lang/String;"); - jstring name = - reinterpret_cast(jni->CallObjectMethod(j_enum, nameMethod)); - CHECK_EXCEPTION(jni) << "error during CallObjectMethod for " - << className << ".name"; - return JavaToStdString(jni, name); -} - static PeerConnectionInterface::IceTransportsType JavaIceTransportsTypeToNativeType(JNIEnv* jni, jobject j_ice_transports_type) { std::string enum_name = GetJavaEnumName( diff --git a/webrtc/base/network.cc b/webrtc/base/network.cc index 0f13d07548..4525c3ccb6 100644 --- a/webrtc/base/network.cc +++ b/webrtc/base/network.cc @@ -731,10 +731,13 @@ void BasicNetworkManager::StartNetworkMonitor() { if (factory == nullptr) { return; } - network_monitor_.reset(factory->CreateNetworkMonitor()); if (!network_monitor_) { - return; + network_monitor_.reset(factory->CreateNetworkMonitor()); + if (!network_monitor_) { + return; + } } + network_monitor_->SignalNetworksChanged.connect( this, &BasicNetworkManager::OnNetworksChanged); network_monitor_->Start(); @@ -745,7 +748,6 @@ void BasicNetworkManager::StopNetworkMonitor() { return; } network_monitor_->Stop(); - network_monitor_.reset(); } void BasicNetworkManager::OnMessage(Message* msg) { diff --git a/webrtc/base/network_unittest.cc b/webrtc/base/network_unittest.cc index 645cb58a20..43ccb34c28 100644 --- a/webrtc/base/network_unittest.cc +++ b/webrtc/base/network_unittest.cc @@ -29,14 +29,18 @@ namespace { class FakeNetworkMonitor : public NetworkMonitorBase { public: - void Start() override {} - void Stop() override {} + void Start() override { started_ = true; } + void Stop() override { started_ = false; } + bool started() { return started_; } + + private: + bool started_ = false; }; class FakeNetworkMonitorFactory : public NetworkMonitorFactory { public: FakeNetworkMonitorFactory() {} - NetworkMonitorInterface* CreateNetworkMonitor() { + NetworkMonitorInterface* CreateNetworkMonitor() override { return new FakeNetworkMonitor(); } }; @@ -72,9 +76,9 @@ class NetworkTest : public testing::Test, public sigslot::has_slots<> { return list; } - NetworkMonitorInterface* GetNetworkMonitor( - BasicNetworkManager& network_manager) { - return network_manager.network_monitor_.get(); + FakeNetworkMonitor* GetNetworkMonitor(BasicNetworkManager& network_manager) { + return static_cast( + network_manager.network_monitor_.get()); } void ClearNetworks(BasicNetworkManager& network_manager) { for (const auto& kv : network_manager.networks_map_) { @@ -921,7 +925,8 @@ TEST_F(NetworkTest, TestNetworkMonitoring) { FakeNetworkMonitorFactory* factory = new FakeNetworkMonitorFactory(); NetworkMonitorFactory::SetFactory(factory); manager.StartUpdating(); - NetworkMonitorInterface* network_monitor = GetNetworkMonitor(manager); + FakeNetworkMonitor* network_monitor = GetNetworkMonitor(manager); + EXPECT_TRUE(network_monitor && network_monitor->started()); EXPECT_TRUE_WAIT(callback_called_, 1000); callback_called_ = false; @@ -932,9 +937,9 @@ TEST_F(NetworkTest, TestNetworkMonitoring) { network_monitor->OnNetworksChanged(); EXPECT_TRUE_WAIT(callback_called_, 1000); - // Network manager is stopped; the network monitor is removed. + // Network manager is stopped. manager.StopUpdating(); - EXPECT_TRUE(GetNetworkMonitor(manager) == nullptr); + EXPECT_FALSE(GetNetworkMonitor(manager)->started()); NetworkMonitorFactory::ReleaseFactory(factory); } diff --git a/webrtc/base/networkmonitor.cc b/webrtc/base/networkmonitor.cc index 92bf0592b5..858769173e 100644 --- a/webrtc/base/networkmonitor.cc +++ b/webrtc/base/networkmonitor.cc @@ -26,12 +26,12 @@ NetworkMonitorInterface::NetworkMonitorInterface() {} NetworkMonitorInterface::~NetworkMonitorInterface() {} -NetworkMonitorBase::NetworkMonitorBase() : thread_(Thread::Current()) {} +NetworkMonitorBase::NetworkMonitorBase() : worker_thread_(Thread::Current()) {} NetworkMonitorBase::~NetworkMonitorBase() {} void NetworkMonitorBase::OnNetworksChanged() { LOG(LS_VERBOSE) << "Network change is received at the network monitor"; - thread_->Post(this, UPDATE_NETWORKS_MESSAGE); + worker_thread_->Post(this, UPDATE_NETWORKS_MESSAGE); } void NetworkMonitorBase::OnMessage(Message* msg) { diff --git a/webrtc/base/networkmonitor.h b/webrtc/base/networkmonitor.h index c45c817040..dd15f6e8f7 100644 --- a/webrtc/base/networkmonitor.h +++ b/webrtc/base/networkmonitor.h @@ -17,6 +17,27 @@ #include "webrtc/base/thread.h" namespace rtc { + +class IPAddress; + +// Error values are negative. +enum NetworkBindingResults { + NETWORK_BIND_SUCCESS = 0, // No error + NETWORK_BIND_FAILURE = -1, // Generic error + NETWORK_BIND_NOT_IMPLEMENTED = -2, + NETWORK_BIND_ADDRESS_NOT_FOUND = -3, + NETWORK_BIND_NETWORK_CHANGED = -4 +}; + +class NetworkBinderInterface { + public: + // Binds a socket to the network that is attached to |address| so that all + // packets on the socket |socket_fd| will be sent via that network. + // This is needed because some operating systems (like Android) require a + // special bind call to put packets on a non-default network interface. + virtual int BindSocketToNetwork(int socket_fd, const IPAddress& address) = 0; +}; + /* * Receives network-change events via |OnNetworksChanged| and signals the * networks changed event. @@ -62,8 +83,11 @@ class NetworkMonitorBase : public NetworkMonitorInterface, void OnMessage(Message* msg) override; + protected: + Thread* worker_thread() { return worker_thread_; } + private: - Thread* thread_; + Thread* worker_thread_; }; /* diff --git a/webrtc/base/physicalsocketserver.cc b/webrtc/base/physicalsocketserver.cc index 3e454527ca..67fbea0a2f 100644 --- a/webrtc/base/physicalsocketserver.cc +++ b/webrtc/base/physicalsocketserver.cc @@ -7,6 +7,7 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ +#include "webrtc/base/physicalsocketserver.h" #if defined(_MSC_VER) && _MSC_VER < 1300 #pragma warning(disable:4786) @@ -44,7 +45,7 @@ #include "webrtc/base/byteorder.h" #include "webrtc/base/common.h" #include "webrtc/base/logging.h" -#include "webrtc/base/physicalsocketserver.h" +#include "webrtc/base/networkmonitor.h" #include "webrtc/base/timeutils.h" #include "webrtc/base/winping.h" #include "webrtc/base/win32socketinit.h" @@ -174,6 +175,14 @@ int PhysicalSocket::Bind(const SocketAddress& bind_addr) { dbg_addr_.append(GetLocalAddress().ToString()); } #endif + if (ss_->network_binder()) { + int result = + ss_->network_binder()->BindSocketToNetwork(s_, bind_addr.ipaddr()); + if (result < 0) { + LOG(LS_INFO) << "Binding socket to network address " + << bind_addr.ipaddr().ToString() << " result " << result; + } + } return err; } @@ -1109,7 +1118,7 @@ private: PhysicalSocketServer* ss_; WSAEVENT hev_; }; -#endif // WEBRTC_WIN +#endif // WEBRTC_WIN // Sets the value of a boolean value to false when signaled. class Signaler : public EventDispatcher { @@ -1603,6 +1612,6 @@ bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { // Done return true; } -#endif // WEBRTC_WIN +#endif // WEBRTC_WIN } // namespace rtc diff --git a/webrtc/base/socketserver.h b/webrtc/base/socketserver.h index 15c56f4612..1b4136d74e 100644 --- a/webrtc/base/socketserver.h +++ b/webrtc/base/socketserver.h @@ -16,6 +16,7 @@ namespace rtc { class MessageQueue; +class NetworkBinderInterface; // Provides the ability to wait for activity on a set of sockets. The Thread // class provides a nice wrapper on a socket server. @@ -39,6 +40,16 @@ class SocketServer : public SocketFactory { // Causes the current wait (if one is in progress) to wake up. virtual void WakeUp() = 0; + + // A network binder will bind the created sockets to a network. + // It is only used in PhysicalSocketServer. + void set_network_binder(NetworkBinderInterface* binder) { + network_binder_ = binder; + } + NetworkBinderInterface* network_binder() const { return network_binder_; } + + private: + NetworkBinderInterface* network_binder_ = nullptr; }; } // namespace rtc diff --git a/webrtc/examples/androidapp/AndroidManifest.xml b/webrtc/examples/androidapp/AndroidManifest.xml index bd0dee821a..1e9cbf17d9 100644 --- a/webrtc/examples/androidapp/AndroidManifest.xml +++ b/webrtc/examples/androidapp/AndroidManifest.xml @@ -10,6 +10,7 @@ +