diff --git a/webrtc/api/android/java/src/org/webrtc/NetworkMonitor.java b/webrtc/api/android/java/src/org/webrtc/NetworkMonitor.java index cbe68d052c..76a1bb5769 100644 --- a/webrtc/api/android/java/src/org/webrtc/NetworkMonitor.java +++ b/webrtc/api/android/java/src/org/webrtc/NetworkMonitor.java @@ -17,6 +17,7 @@ import static org.webrtc.NetworkMonitorAutoDetect.NetworkInformation; import org.webrtc.Logging; import android.content.Context; +import android.os.Build; import java.util.ArrayList; import java.util.List; @@ -115,11 +116,16 @@ public class NetworkMonitor { nativeNetworkObservers.remove(nativeObserver); } + // Called by the native code to get the Android SDK version. + private static int androidSdkInt() { + return Build.VERSION.SDK_INT; + } + private ConnectionType getCurrentConnectionType() { return currentConnectionType; } - private int getCurrentDefaultNetId() { + private long getCurrentDefaultNetId() { return autoDetector == null ? INVALID_NET_ID : autoDetector.getDefaultNetId(); } @@ -150,7 +156,7 @@ public class NetworkMonitor { } @Override - public void onNetworkDisconnect(int networkHandle) { + public void onNetworkDisconnect(long networkHandle) { notifyObserversOfNetworkDisconnect(networkHandle); } }, @@ -185,7 +191,7 @@ public class NetworkMonitor { } } - private void notifyObserversOfNetworkDisconnect(int networkHandle) { + private void notifyObserversOfNetworkDisconnect(long networkHandle) { for (long nativeObserver : nativeNetworkObservers) { nativeNotifyOfNetworkDisconnect(nativeObserver, networkHandle); } @@ -236,7 +242,7 @@ public class NetworkMonitor { private native void nativeNotifyConnectionTypeChanged(long nativePtr); private native void nativeNotifyOfNetworkConnect(long nativePtr, NetworkInformation networkInfo); - private native void nativeNotifyOfNetworkDisconnect(long nativePtr, int networkHandle); + private native void nativeNotifyOfNetworkDisconnect(long nativePtr, long networkHandle); private native void nativeNotifyOfActiveNetworkList(long nativePtr, NetworkInformation[] networkInfos); diff --git a/webrtc/api/android/java/src/org/webrtc/NetworkMonitorAutoDetect.java b/webrtc/api/android/java/src/org/webrtc/NetworkMonitorAutoDetect.java index 4335e5ce73..15a2a5c266 100644 --- a/webrtc/api/android/java/src/org/webrtc/NetworkMonitorAutoDetect.java +++ b/webrtc/api/android/java/src/org/webrtc/NetworkMonitorAutoDetect.java @@ -71,9 +71,9 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { public static class NetworkInformation{ public final String name; public final ConnectionType type; - public final int handle; + public final long handle; public final IPAddress[] ipAddresses; - public NetworkInformation(String name, ConnectionType type, int handle, + public NetworkInformation(String name, ConnectionType type, long handle, IPAddress[] addresses) { this.name = name; this.type = type; @@ -246,7 +246,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { * Only callable on Lollipop and newer releases. */ @SuppressLint("NewApi") - int getDefaultNetId() { + long getDefaultNetId() { if (!supportNetworkCallback()) { return INVALID_NET_ID; } @@ -259,7 +259,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { return INVALID_NET_ID; } final Network[] networks = getAllNetworks(); - int defaultNetId = INVALID_NET_ID; + long defaultNetId = INVALID_NET_ID; for (Network network : networks) { if (!hasInternetCapability(network)) { continue; @@ -404,7 +404,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { } - static final int INVALID_NET_ID = -1; + static final long INVALID_NET_ID = -1; private static final String TAG = "NetworkMonitorAutoDetect"; // Observer for the connection type change. @@ -433,7 +433,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { */ public void onConnectionTypeChanged(ConnectionType newConnectionType); public void onNetworkConnect(NetworkInformation networkInfo); - public void onNetworkDisconnect(int networkHandle); + public void onNetworkDisconnect(long networkHandle); } /** @@ -537,7 +537,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { * Only implemented on Lollipop and newer releases, returns INVALID_NET_ID * when not implemented. */ - public int getDefaultNetId() { + public long getDefaultNetId() { return connectivityManagerDelegate.getDefaultNetId(); } @@ -610,13 +610,18 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { } /** - * Extracts NetID of network. Only available on Lollipop and newer releases. + * Extracts NetID of network on Lollipop and NetworkHandle (which is mungled + * NetID) on Marshmallow and newer releases. Only available on Lollipop and + * newer releases. Returns long since getNetworkHandle returns long. */ @SuppressLint("NewApi") - private static int networkToNetId(Network network) { - // NOTE(pauljensen): This depends on Android framework implementation details. - // Fortunately this functionality is unlikely to ever change. - // TODO(honghaiz): When we update to Android M SDK, use Network.getNetworkHandle(). + private static long networkToNetId(Network network) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return network.getNetworkHandle(); + } + + // NOTE(honghaiz): This depends on Android framework implementation details. + // These details cannot change because Lollipop has been released. return Integer.parseInt(network.toString()); } } diff --git a/webrtc/api/android/jni/androidnetworkmonitor_jni.cc b/webrtc/api/android/jni/androidnetworkmonitor_jni.cc index 403badc515..16da5faa92 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 +// 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(GetIntField(jni, j_network_info, j_handle_id)); + network_info.handle = static_cast( + 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( @@ -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( + dlsym(lib, "android_setsocknetwork")); + } + if (!marshmallowSetNetworkForSocket) { + LOG(LS_ERROR) << "Symbol marshmallowSetNetworkForSocket is not found"; return rtc::NETWORK_BIND_NOT_IMPLEMENTED; } - setNetworkForSocket = reinterpret_cast( - dlsym(lib, "setNetworkForSocket")); + 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( + 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); } - 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. @@ -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(j_native_monitor); network_monitor->OnNetworkDisconnected( diff --git a/webrtc/api/android/jni/androidnetworkmonitor_jni.h b/webrtc/api/android/jni/androidnetworkmonitor_jni.h index c8d7fcde5b..f164986eff 100644 --- a/webrtc/api/android/jni/androidnetworkmonitor_jni.h +++ b/webrtc/api/android/jni/androidnetworkmonitor_jni.h @@ -21,7 +21,7 @@ namespace webrtc_jni { -typedef uint32_t NetworkHandle; +typedef long NetworkHandle; // c++ equivalent of java NetworkMonitorAutoDetect.ConnectionType. enum NetworkType { @@ -65,6 +65,8 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorBase, void SetNetworkInfos(const std::vector& network_infos); private: + static jobject application_context_; + static int android_sdk_int_; JNIEnv* jni() { return AttachCurrentThreadIfNeeded(); } void OnNetworkConnected_w(const NetworkInformation& network_info); @@ -73,7 +75,6 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorBase, ScopedGlobalRef j_network_monitor_class_; ScopedGlobalRef j_network_monitor_; rtc::ThreadChecker thread_checker_; - static jobject application_context_; bool started_ = false; std::map adapter_type_by_name_; std::map network_handle_by_address_; diff --git a/webrtc/api/androidtests/src/org/webrtc/NetworkMonitorTest.java b/webrtc/api/androidtests/src/org/webrtc/NetworkMonitorTest.java index cee3bd1d91..785f37fe17 100644 --- a/webrtc/api/androidtests/src/org/webrtc/NetworkMonitorTest.java +++ b/webrtc/api/androidtests/src/org/webrtc/NetworkMonitorTest.java @@ -72,7 +72,7 @@ public class NetworkMonitorTest extends ActivityTestCase { // Dummy implementations to avoid NullPointerExceptions in default implementations: @Override - public int getDefaultNetId() { + public long getDefaultNetId() { return INVALID_NET_ID; } @@ -127,7 +127,7 @@ public class NetworkMonitorTest extends ActivityTestCase { public void onNetworkConnect(NetworkInformation networkInfo) {} @Override - public void onNetworkDisconnect(int networkHandle) {} + public void onNetworkDisconnect(long networkHandle) {} } private static final Object lock = new Object();