| Index: webrtc/api/android/jni/androidnetworkmonitor_jni.cc
|
| diff --git a/webrtc/api/android/jni/androidnetworkmonitor_jni.cc b/webrtc/api/android/jni/androidnetworkmonitor_jni.cc
|
| index 403badc515b1905a3e516025f9191e6f63a4f665..16da5faa9238fa3b6b1ee7ab63b268c257b90276 100644
|
| --- a/webrtc/api/android/jni/androidnetworkmonitor_jni.cc
|
| +++ b/webrtc/api/android/jni/androidnetworkmonitor_jni.cc
|
| @@ -11,6 +11,8 @@
|
| #include "webrtc/api/android/jni/androidnetworkmonitor_jni.h"
|
|
|
| #include <dlfcn.h>
|
| +// This was added in Lollipop to dlfcn.h
|
| +#define RTLD_NOLOAD 4
|
|
|
| #include "webrtc/api/android/jni/classreferenceholder.h"
|
| #include "webrtc/api/android/jni/jni_helpers.h"
|
| @@ -20,7 +22,13 @@
|
|
|
| namespace webrtc_jni {
|
|
|
| +enum AndroidSdkVersion {
|
| + SDK_VERSION_LOLLIPOP = 21,
|
| + SDK_VERSION_MARSHMALLOW = 23
|
| +};
|
| +
|
| jobject AndroidNetworkMonitor::application_context_ = nullptr;
|
| +int AndroidNetworkMonitor::android_sdk_int_ = 0;
|
|
|
| static NetworkType GetNetworkTypeFromJava(JNIEnv* jni, jobject j_network_type) {
|
| std::string enum_name =
|
| @@ -123,7 +131,7 @@ static NetworkInformation GetNetworkInformationFromJava(
|
| 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_handle_id = GetFieldID(jni, j_network_info_class, "handle", "J");
|
| jfieldID j_type_id =
|
| GetFieldID(jni, j_network_info_class, "type",
|
| "Lorg/webrtc/NetworkMonitorAutoDetect$ConnectionType;");
|
| @@ -134,8 +142,8 @@ static NetworkInformation GetNetworkInformationFromJava(
|
| 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.handle = static_cast<NetworkHandle>(
|
| + GetLongField(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>(
|
| @@ -178,6 +186,12 @@ AndroidNetworkMonitor::AndroidNetworkMonitor()
|
| application_context_)) {
|
| ASSERT(application_context_ != nullptr);
|
| CHECK_EXCEPTION(jni()) << "Error during NetworkMonitor.init";
|
| + if (android_sdk_int_ <= 0) {
|
| + jmethodID m = GetStaticMethodID(jni(), *j_network_monitor_class_,
|
| + "androidSdkInt", "()I");
|
| + android_sdk_int_ = jni()->CallStaticIntMethod(*j_network_monitor_class_, m);
|
| + CHECK_EXCEPTION(jni()) << "Error during NetworkMonitor.androidSdkInt";
|
| + }
|
| }
|
|
|
| void AndroidNetworkMonitor::Start() {
|
| @@ -220,43 +234,85 @@ void AndroidNetworkMonitor::Stop() {
|
| network_info_by_handle_.clear();
|
| }
|
|
|
| +// The implementation is largely taken from UDPSocketPosix::BindToNetwork in
|
| +// https://cs.chromium.org/chromium/src/net/udp/udp_socket_posix.cc
|
| int AndroidNetworkMonitor::BindSocketToNetwork(int socket_fd,
|
| const rtc::IPAddress& address) {
|
| RTC_CHECK(thread_checker_.CalledOnValidThread());
|
| // 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_handle_by_address_| should only be populated in Android Lollipop
|
| + // networks. In that case it should not have reached here because
|
| + // |network_handle_by_address_| is only populated in Android Lollipop
|
| // and above.
|
| - // TODO(honghaiz): Add a check for Android version here so that it won't try
|
| - // to look for handle if the Android version is before Lollipop.
|
| + if (android_sdk_int_ < SDK_VERSION_LOLLIPOP) {
|
| + LOG(LS_ERROR) << "BindSocketToNetwork is not supported in Android SDK "
|
| + << android_sdk_int_;
|
| + return rtc::NETWORK_BIND_NOT_IMPLEMENTED;
|
| + }
|
| +
|
| auto iter = network_handle_by_address_.find(address);
|
| if (iter == network_handle_by_address_.end()) {
|
| return rtc::NETWORK_BIND_ADDRESS_NOT_FOUND;
|
| }
|
| NetworkHandle network_handle = iter->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!";
|
| + int rv = 0;
|
| + if (android_sdk_int_ >= SDK_VERSION_MARSHMALLOW) {
|
| + // See declaration of android_setsocknetwork() here:
|
| + // http://androidxref.com/6.0.0_r1/xref/development/ndk/platforms/android-M/include/android/multinetwork.h#65
|
| + // Function cannot be called directly as it will cause app to fail to load
|
| + // on pre-marshmallow devices.
|
| + typedef int (*MarshmallowSetNetworkForSocket)(NetworkHandle net,
|
| + int socket);
|
| + static MarshmallowSetNetworkForSocket marshmallowSetNetworkForSocket;
|
| + // This is not thread-safe, but we are running this only on the worker
|
| + // thread.
|
| + if (!marshmallowSetNetworkForSocket) {
|
| + const std::string android_native_lib_path = "libandroid.so";
|
| + void* lib = dlopen(android_native_lib_path.c_str(), RTLD_NOW);
|
| + if (lib == nullptr) {
|
| + LOG(LS_ERROR) << "Library " << android_native_lib_path << " not found!";
|
| + return rtc::NETWORK_BIND_NOT_IMPLEMENTED;
|
| + }
|
| + marshmallowSetNetworkForSocket =
|
| + reinterpret_cast<MarshmallowSetNetworkForSocket>(
|
| + dlsym(lib, "android_setsocknetwork"));
|
| + }
|
| + if (!marshmallowSetNetworkForSocket) {
|
| + LOG(LS_ERROR) << "Symbol marshmallowSetNetworkForSocket is not found";
|
| return rtc::NETWORK_BIND_NOT_IMPLEMENTED;
|
| }
|
| - setNetworkForSocket = reinterpret_cast<SetNetworkForSocket>(
|
| - dlsym(lib, "setNetworkForSocket"));
|
| - }
|
| - if (setNetworkForSocket == nullptr) {
|
| - LOG(LS_ERROR) << "Symbol setNetworkForSocket not found ";
|
| - return rtc::NETWORK_BIND_NOT_IMPLEMENTED;
|
| + rv = marshmallowSetNetworkForSocket(network_handle, socket_fd);
|
| + } else {
|
| + // 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
|
| + // thread.
|
| + if (!lollipopSetNetworkForSocket) {
|
| + // 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";
|
| + // Use RTLD_NOW to match Android's prior loading of the library:
|
| + // http://androidxref.com/6.0.0_r5/xref/bionic/libc/bionic/NetdClient.cpp#37
|
| + // Use RTLD_NOLOAD to assert that the library is already loaded and
|
| + // avoid doing any disk IO.
|
| + void* lib = dlopen(net_library_path.c_str(), RTLD_NOW | RTLD_NOLOAD);
|
| + if (lib == nullptr) {
|
| + LOG(LS_ERROR) << "Library " << net_library_path << " not found!";
|
| + return rtc::NETWORK_BIND_NOT_IMPLEMENTED;
|
| + }
|
| + lollipopSetNetworkForSocket =
|
| + reinterpret_cast<LollipopSetNetworkForSocket>(
|
| + dlsym(lib, "setNetworkForSocket"));
|
| + }
|
| + if (!lollipopSetNetworkForSocket) {
|
| + LOG(LS_ERROR) << "Symbol lollipopSetNetworkForSocket is not found ";
|
| + return rtc::NETWORK_BIND_NOT_IMPLEMENTED;
|
| + }
|
| + rv = lollipopSetNetworkForSocket(network_handle, socket_fd);
|
| }
|
| - 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.
|
| @@ -369,7 +425,7 @@ JOW(void, NetworkMonitor_nativeNotifyOfNetworkConnect)(
|
|
|
| JOW(void, NetworkMonitor_nativeNotifyOfNetworkDisconnect)(
|
| JNIEnv* jni, jobject j_monitor, jlong j_native_monitor,
|
| - jint network_handle) {
|
| + jlong network_handle) {
|
| AndroidNetworkMonitor* network_monitor =
|
| reinterpret_cast<AndroidNetworkMonitor*>(j_native_monitor);
|
| network_monitor->OnNetworkDisconnected(
|
|
|