Index: talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc |
diff --git a/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc b/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc |
index f7a8c07c60a3e63926ec1914dabf3aae08cfbe69..948b2bc394a50d1c435877c747447d9f3b51fdb0 100644 |
--- a/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc |
+++ b/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc |
@@ -27,13 +27,126 @@ |
#include "talk/app/webrtc/java/jni/androidnetworkmonitor_jni.h" |
+#include <dlfcn.h> |
+ |
+#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<jbyteArray>(GetObjectField(jni, j_ip_address, j_address_id)); |
+ size_t address_length = jni->GetArrayLength(j_addresses); |
+ jbyte* addr_array = jni->GetByteArrayElements(j_addresses, NULL); |
Taylor Brandstetter
2016/01/15 19:36:12
nit: "nullptr" instead of "NULL", since that's wha
honghaiz3
2016/01/15 21:37:36
Done.
|
+ CHECK_EXCEPTION(jni) << "Error during NetworkMonitor.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<rtc::IPAddress>* ip_addresses) { |
+ ip_addresses->clear(); |
+ size_t num_addresses = jni->GetArrayLength(j_ip_addresses); |
+ for (size_t i = 0; i < num_addresses; ++i) { |
+ jobject j_ip_address = jni->GetObjectArrayElement(j_ip_addresses, i); |
+ rtc::IPAddress ip = GetIPAddressFromJava(jni, j_ip_address); |
+ |
Taylor Brandstetter
2016/01/15 19:36:12
nit: Seems like extra whitespace here.
honghaiz3
2016/01/15 21:37:36
Done.
|
+ ip_addresses->push_back(ip); |
+ } |
+ CHECK_EXCEPTION(jni) << "Error during NetworkMonitor.GetIPAddressesFromJava"; |
Taylor Brandstetter
2016/01/15 19:36:12
Should this CHECK_EXCEPTION be right after GetArra
honghaiz3
2016/01/15 21:37:36
Done. Added another check after GetObjectArrayElem
|
+} |
+ |
+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<NetworkHandle>(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<jobjectArray>( |
+ GetObjectField(jni, j_network_info, j_ip_addresses_id)); |
+ GetIPAddressesFromJava(jni, j_ip_addresses, &network_info.ip_addresses); |
+ CHECK_EXCEPTION(jni) << "Error during NetworkMonitor.GetNetworkInfoFromJava"; |
Taylor Brandstetter
2016/01/15 19:36:12
I don't think you need this CHECK_EXCEPTION since
honghaiz3
2016/01/15 21:37:36
Done.
|
+ 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() << " "; |
Taylor Brandstetter
2016/01/15 19:36:12
nit: This will leave an extra space between the la
honghaiz3
2016/01/15 21:37:36
Done. Thanks!
|
+ } |
+ ss << "]"; |
+ return ss.str(); |
+} |
+ |
// static |
void AndroidNetworkMonitor::SetAndroidContext(JNIEnv* jni, jobject context) { |
if (application_context_) { |
@@ -61,6 +174,11 @@ AndroidNetworkMonitor::AndroidNetworkMonitor() |
void AndroidNetworkMonitor::Start() { |
RTC_CHECK(thread_checker_.CalledOnValidThread()); |
+ if (started_) { |
+ return; |
+ } |
+ started_ = true; |
+ |
jmethodID m = |
GetMethodID(jni(), *j_network_monitor_class_, "startMonitoring", "(J)V"); |
jni()->CallVoidMethod(*j_network_monitor_, m, jlongFromPointer(this)); |
@@ -69,10 +187,83 @@ void AndroidNetworkMonitor::Start() { |
void AndroidNetworkMonitor::Stop() { |
RTC_CHECK(thread_checker_.CalledOnValidThread()); |
+ if (!started_) { |
+ return; |
+ } |
+ started_ = false; |
+ |
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::ERR_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) { |
Taylor Brandstetter
2016/01/15 19:36:12
nit: "if (!setNetworkForSocket)", here and below.
honghaiz3
2016/01/15 21:37:36
I feel using explicit checking is more readable an
|
+ // 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) { |
Taylor Brandstetter
2016/01/15 19:36:12
nit: "if (!lib)"
honghaiz3
2016/01/15 21:37:36
same as above.
|
+ LOG(LS_ERROR) << "Library " << net_library_path << " not found!"; |
+ return rtc::ERR_NOT_IMPLEMENTED; |
+ } |
+ setNetworkForSocket = reinterpret_cast<SetNetworkForSocket>( |
+ dlsym(lib, "setNetworkForSocket")); |
+ } |
+ if (setNetworkForSocket == nullptr) { |
+ LOG(LS_ERROR) << "Symbol setNetworkForSocket not found "; |
+ return rtc::ERR_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::OK; |
+ } |
+ if (rv == ENONET) { |
+ return rtc::ERR_NETWORK_CHANGED; |
+ } |
+ return rtc::ERR_FAILED; |
+} |
+ |
+void AndroidNetworkMonitor::OnNetworkAvailable( |
+ const NetworkInformation& network_info) { |
+ worker_thread()->Invoke<void>(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 +273,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<AndroidNetworkMonitor*>(j_native_monitor); |
+ NetworkInformation network_info = |
+ GetNetworkInformationFromJava(jni, j_network_info); |
+ network_monitor->OnNetworkAvailable(network_info); |
+} |
+ |
} // namespace webrtc_jni |