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 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);

View File

@ -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());
}
}

View File

@ -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"));
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);
}
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<AndroidNetworkMonitor*>(j_native_monitor);
network_monitor->OnNetworkDisconnected(

View File

@ -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<NetworkInformation>& 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<jclass> j_network_monitor_class_;
ScopedGlobalRef<jobject> j_network_monitor_;
rtc::ThreadChecker thread_checker_;
static jobject application_context_;
bool started_ = false;
std::map<std::string, rtc::AdapterType> adapter_type_by_name_;
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:
@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();