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:
@ -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);
|
||||
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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();
|
||||
|
||||
Reference in New Issue
Block a user