Use a new method (android_setsocknetwork) to bind socket to network in Android M and later.

The old method (setNetworkForSocket) is not supported in Android-N.

BUG=
R=glaznev@webrtc.org, pthatcher@webrtc.org

Review URL: https://codereview.webrtc.org/2189753002 .

Cr-Commit-Position: refs/heads/master@{#13591}
This commit is contained in:
Honghai Zhang
2016-08-01 09:27:31 -07:00
parent f734c135a4
commit c980062242
5 changed files with 115 additions and 47 deletions

View File

@ -17,6 +17,7 @@ import static org.webrtc.NetworkMonitorAutoDetect.NetworkInformation;
import org.webrtc.Logging; import org.webrtc.Logging;
import android.content.Context; import android.content.Context;
import android.os.Build;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -115,11 +116,16 @@ public class NetworkMonitor {
nativeNetworkObservers.remove(nativeObserver); 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() { private ConnectionType getCurrentConnectionType() {
return currentConnectionType; return currentConnectionType;
} }
private int getCurrentDefaultNetId() { private long getCurrentDefaultNetId() {
return autoDetector == null ? INVALID_NET_ID : autoDetector.getDefaultNetId(); return autoDetector == null ? INVALID_NET_ID : autoDetector.getDefaultNetId();
} }
@ -150,7 +156,7 @@ public class NetworkMonitor {
} }
@Override @Override
public void onNetworkDisconnect(int networkHandle) { public void onNetworkDisconnect(long networkHandle) {
notifyObserversOfNetworkDisconnect(networkHandle); notifyObserversOfNetworkDisconnect(networkHandle);
} }
}, },
@ -185,7 +191,7 @@ public class NetworkMonitor {
} }
} }
private void notifyObserversOfNetworkDisconnect(int networkHandle) { private void notifyObserversOfNetworkDisconnect(long networkHandle) {
for (long nativeObserver : nativeNetworkObservers) { for (long nativeObserver : nativeNetworkObservers) {
nativeNotifyOfNetworkDisconnect(nativeObserver, networkHandle); nativeNotifyOfNetworkDisconnect(nativeObserver, networkHandle);
} }
@ -236,7 +242,7 @@ public class NetworkMonitor {
private native void nativeNotifyConnectionTypeChanged(long nativePtr); private native void nativeNotifyConnectionTypeChanged(long nativePtr);
private native void nativeNotifyOfNetworkConnect(long nativePtr, NetworkInformation networkInfo); 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, private native void nativeNotifyOfActiveNetworkList(long nativePtr,
NetworkInformation[] networkInfos); NetworkInformation[] networkInfos);

View File

@ -71,9 +71,9 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver {
public static class NetworkInformation{ public static class NetworkInformation{
public final String name; public final String name;
public final ConnectionType type; public final ConnectionType type;
public final int handle; public final long handle;
public final IPAddress[] ipAddresses; public final IPAddress[] ipAddresses;
public NetworkInformation(String name, ConnectionType type, int handle, public NetworkInformation(String name, ConnectionType type, long handle,
IPAddress[] addresses) { IPAddress[] addresses) {
this.name = name; this.name = name;
this.type = type; this.type = type;
@ -246,7 +246,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver {
* Only callable on Lollipop and newer releases. * Only callable on Lollipop and newer releases.
*/ */
@SuppressLint("NewApi") @SuppressLint("NewApi")
int getDefaultNetId() { long getDefaultNetId() {
if (!supportNetworkCallback()) { if (!supportNetworkCallback()) {
return INVALID_NET_ID; return INVALID_NET_ID;
} }
@ -259,7 +259,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver {
return INVALID_NET_ID; return INVALID_NET_ID;
} }
final Network[] networks = getAllNetworks(); final Network[] networks = getAllNetworks();
int defaultNetId = INVALID_NET_ID; long defaultNetId = INVALID_NET_ID;
for (Network network : networks) { for (Network network : networks) {
if (!hasInternetCapability(network)) { if (!hasInternetCapability(network)) {
continue; 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"; private static final String TAG = "NetworkMonitorAutoDetect";
// Observer for the connection type change. // Observer for the connection type change.
@ -433,7 +433,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver {
*/ */
public void onConnectionTypeChanged(ConnectionType newConnectionType); public void onConnectionTypeChanged(ConnectionType newConnectionType);
public void onNetworkConnect(NetworkInformation networkInfo); 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 * Only implemented on Lollipop and newer releases, returns INVALID_NET_ID
* when not implemented. * when not implemented.
*/ */
public int getDefaultNetId() { public long getDefaultNetId() {
return connectivityManagerDelegate.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") @SuppressLint("NewApi")
private static int networkToNetId(Network network) { private static long networkToNetId(Network network) {
// NOTE(pauljensen): This depends on Android framework implementation details. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Fortunately this functionality is unlikely to ever change. return network.getNetworkHandle();
// TODO(honghaiz): When we update to Android M SDK, use 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()); return Integer.parseInt(network.toString());
} }
} }

View File

@ -11,6 +11,8 @@
#include "webrtc/api/android/jni/androidnetworkmonitor_jni.h" #include "webrtc/api/android/jni/androidnetworkmonitor_jni.h"
#include <dlfcn.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/classreferenceholder.h"
#include "webrtc/api/android/jni/jni_helpers.h" #include "webrtc/api/android/jni/jni_helpers.h"
@ -20,7 +22,13 @@
namespace webrtc_jni { namespace webrtc_jni {
enum AndroidSdkVersion {
SDK_VERSION_LOLLIPOP = 21,
SDK_VERSION_MARSHMALLOW = 23
};
jobject AndroidNetworkMonitor::application_context_ = nullptr; jobject AndroidNetworkMonitor::application_context_ = nullptr;
int AndroidNetworkMonitor::android_sdk_int_ = 0;
static NetworkType GetNetworkTypeFromJava(JNIEnv* jni, jobject j_network_type) { static NetworkType GetNetworkTypeFromJava(JNIEnv* jni, jobject j_network_type) {
std::string enum_name = std::string enum_name =
@ -123,7 +131,7 @@ static NetworkInformation GetNetworkInformationFromJava(
jclass j_network_info_class = GetObjectClass(jni, j_network_info); jclass j_network_info_class = GetObjectClass(jni, j_network_info);
jfieldID j_interface_name_id = jfieldID j_interface_name_id =
GetFieldID(jni, j_network_info_class, "name", "Ljava/lang/String;"); 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 = jfieldID j_type_id =
GetFieldID(jni, j_network_info_class, "type", GetFieldID(jni, j_network_info_class, "type",
"Lorg/webrtc/NetworkMonitorAutoDetect$ConnectionType;"); "Lorg/webrtc/NetworkMonitorAutoDetect$ConnectionType;");
@ -134,8 +142,8 @@ static NetworkInformation GetNetworkInformationFromJava(
NetworkInformation network_info; NetworkInformation network_info;
network_info.interface_name = JavaToStdString( network_info.interface_name = JavaToStdString(
jni, GetStringField(jni, j_network_info, j_interface_name_id)); jni, GetStringField(jni, j_network_info, j_interface_name_id));
network_info.handle = network_info.handle = static_cast<NetworkHandle>(
static_cast<NetworkHandle>(GetIntField(jni, j_network_info, j_handle_id)); GetLongField(jni, j_network_info, j_handle_id));
network_info.type = GetNetworkTypeFromJava( network_info.type = GetNetworkTypeFromJava(
jni, GetObjectField(jni, j_network_info, j_type_id)); jni, GetObjectField(jni, j_network_info, j_type_id));
jobjectArray j_ip_addresses = static_cast<jobjectArray>( jobjectArray j_ip_addresses = static_cast<jobjectArray>(
@ -178,6 +186,12 @@ AndroidNetworkMonitor::AndroidNetworkMonitor()
application_context_)) { application_context_)) {
ASSERT(application_context_ != nullptr); ASSERT(application_context_ != nullptr);
CHECK_EXCEPTION(jni()) << "Error during NetworkMonitor.init"; 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() { void AndroidNetworkMonitor::Start() {
@ -220,43 +234,85 @@ void AndroidNetworkMonitor::Stop() {
network_info_by_handle_.clear(); 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, int AndroidNetworkMonitor::BindSocketToNetwork(int socket_fd,
const rtc::IPAddress& address) { const rtc::IPAddress& address) {
RTC_CHECK(thread_checker_.CalledOnValidThread()); RTC_CHECK(thread_checker_.CalledOnValidThread());
// Android prior to Lollipop didn't have support for binding sockets to // Android prior to Lollipop didn't have support for binding sockets to
// networks. However, in that case it should not have reached here because // networks. In that case it should not have reached here because
// |network_handle_by_address_| should only be populated in Android Lollipop // |network_handle_by_address_| is only populated in Android Lollipop
// and above. // and above.
// TODO(honghaiz): Add a check for Android version here so that it won't try if (android_sdk_int_ < SDK_VERSION_LOLLIPOP) {
// to look for handle if the Android version is before 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); auto iter = network_handle_by_address_.find(address);
if (iter == network_handle_by_address_.end()) { if (iter == network_handle_by_address_.end()) {
return rtc::NETWORK_BIND_ADDRESS_NOT_FOUND; return rtc::NETWORK_BIND_ADDRESS_NOT_FOUND;
} }
NetworkHandle network_handle = iter->second; NetworkHandle network_handle = iter->second;
// NOTE: This does rely on Android implementation details, but int rv = 0;
// these details are unlikely to change. if (android_sdk_int_ >= SDK_VERSION_MARSHMALLOW) {
typedef int (*SetNetworkForSocket)(unsigned netId, int socketFd); // See declaration of android_setsocknetwork() here:
static SetNetworkForSocket setNetworkForSocket; // http://androidxref.com/6.0.0_r1/xref/development/ndk/platforms/android-M/include/android/multinetwork.h#65
// This is not threadsafe, but we are running this only on the worker thread. // Function cannot be called directly as it will cause app to fail to load
if (setNetworkForSocket == nullptr) { // 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;
}
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 // Android's netd client library should always be loaded in our address
// space as it shims libc functions like connect(). // space as it shims libc functions like connect().
const std::string net_library_path = "libnetd_client.so"; const std::string net_library_path = "libnetd_client.so";
void* lib = dlopen(net_library_path.c_str(), RTLD_LAZY); // 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) { if (lib == nullptr) {
LOG(LS_ERROR) << "Library " << net_library_path << " not found!"; LOG(LS_ERROR) << "Library " << net_library_path << " not found!";
return rtc::NETWORK_BIND_NOT_IMPLEMENTED; return rtc::NETWORK_BIND_NOT_IMPLEMENTED;
} }
setNetworkForSocket = reinterpret_cast<SetNetworkForSocket>( lollipopSetNetworkForSocket =
reinterpret_cast<LollipopSetNetworkForSocket>(
dlsym(lib, "setNetworkForSocket")); dlsym(lib, "setNetworkForSocket"));
} }
if (setNetworkForSocket == nullptr) { if (!lollipopSetNetworkForSocket) {
LOG(LS_ERROR) << "Symbol setNetworkForSocket not found "; LOG(LS_ERROR) << "Symbol lollipopSetNetworkForSocket is not found ";
return rtc::NETWORK_BIND_NOT_IMPLEMENTED; return rtc::NETWORK_BIND_NOT_IMPLEMENTED;
} }
int rv = setNetworkForSocket(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 // ERR_NETWORK_CHANGED, rather than MapSystemError(ENONET) which gives back
// the less descriptive ERR_FAILED. // the less descriptive ERR_FAILED.
@ -369,7 +425,7 @@ JOW(void, NetworkMonitor_nativeNotifyOfNetworkConnect)(
JOW(void, NetworkMonitor_nativeNotifyOfNetworkDisconnect)( JOW(void, NetworkMonitor_nativeNotifyOfNetworkDisconnect)(
JNIEnv* jni, jobject j_monitor, jlong j_native_monitor, JNIEnv* jni, jobject j_monitor, jlong j_native_monitor,
jint network_handle) { jlong network_handle) {
AndroidNetworkMonitor* network_monitor = AndroidNetworkMonitor* network_monitor =
reinterpret_cast<AndroidNetworkMonitor*>(j_native_monitor); reinterpret_cast<AndroidNetworkMonitor*>(j_native_monitor);
network_monitor->OnNetworkDisconnected( network_monitor->OnNetworkDisconnected(

View File

@ -21,7 +21,7 @@
namespace webrtc_jni { namespace webrtc_jni {
typedef uint32_t NetworkHandle; typedef long NetworkHandle;
// c++ equivalent of java NetworkMonitorAutoDetect.ConnectionType. // c++ equivalent of java NetworkMonitorAutoDetect.ConnectionType.
enum NetworkType { enum NetworkType {
@ -65,6 +65,8 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorBase,
void SetNetworkInfos(const std::vector<NetworkInformation>& network_infos); void SetNetworkInfos(const std::vector<NetworkInformation>& network_infos);
private: private:
static jobject application_context_;
static int android_sdk_int_;
JNIEnv* jni() { return AttachCurrentThreadIfNeeded(); } JNIEnv* jni() { return AttachCurrentThreadIfNeeded(); }
void OnNetworkConnected_w(const NetworkInformation& network_info); void OnNetworkConnected_w(const NetworkInformation& network_info);
@ -73,7 +75,6 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorBase,
ScopedGlobalRef<jclass> j_network_monitor_class_; ScopedGlobalRef<jclass> j_network_monitor_class_;
ScopedGlobalRef<jobject> j_network_monitor_; ScopedGlobalRef<jobject> j_network_monitor_;
rtc::ThreadChecker thread_checker_; rtc::ThreadChecker thread_checker_;
static jobject application_context_;
bool started_ = false; bool started_ = false;
std::map<std::string, rtc::AdapterType> adapter_type_by_name_; std::map<std::string, rtc::AdapterType> adapter_type_by_name_;
std::map<rtc::IPAddress, NetworkHandle> network_handle_by_address_; std::map<rtc::IPAddress, NetworkHandle> network_handle_by_address_;

View File

@ -72,7 +72,7 @@ public class NetworkMonitorTest extends ActivityTestCase {
// Dummy implementations to avoid NullPointerExceptions in default implementations: // Dummy implementations to avoid NullPointerExceptions in default implementations:
@Override @Override
public int getDefaultNetId() { public long getDefaultNetId() {
return INVALID_NET_ID; return INVALID_NET_ID;
} }
@ -127,7 +127,7 @@ public class NetworkMonitorTest extends ActivityTestCase {
public void onNetworkConnect(NetworkInformation networkInfo) {} public void onNetworkConnect(NetworkInformation networkInfo) {}
@Override @Override
public void onNetworkDisconnect(int networkHandle) {} public void onNetworkDisconnect(long networkHandle) {}
} }
private static final Object lock = new Object(); private static final Object lock = new Object();