Loading jni/network_stack_utils_jni.cpp +65 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #define LOG_TAG "NetworkStackUtils-JNI" #include <dlfcn.h> #include <errno.h> #include <jni.h> #include <linux/filter.h> Loading @@ -27,11 +28,11 @@ #include <netinet/ip6.h> #include <netinet/udp.h> #include <stdlib.h> #include <sys/system_properties.h> #include <string> #include <nativehelper/JNIHelp.h> #include <nativehelper/JNIHelpCompat.h> #include <android/log.h> Loading @@ -57,6 +58,65 @@ static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* return true; } static int getNativeFileDescriptorWithoutNdk(JNIEnv* env, jobject javaFd) { // Prior to Android S, we need to find the descriptor field in the FileDescriptor class. The // symbol name has been stable in libcore, but is a private implementation detail. static jfieldID descriptorFieldID = nullptr; if (descriptorFieldID == nullptr) { jclass fileDescriptorClass = env->FindClass("java/io/FileDescriptor"); descriptorFieldID = env->GetFieldID(fileDescriptorClass, "descriptor", "I"); env->DeleteLocalRef(fileDescriptorClass); if (descriptorFieldID == nullptr) { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Failed to get descriptor field."); return -1; } } return javaFd != nullptr ? env->GetIntField(javaFd, descriptorFieldID) : -1; } static int getNativeFileDescriptorWithNdk(JNIEnv* env, jobject javaFd) { // Since Android S, there is an NDK API to get a file descriptor present in libnativehelper.so. // libnativehelper is loaded into all processes by the zygote since the zygote uses it // to load the Android Runtime and is also a public library (because of the NDK API). static int (*ndkGetFD)(JNIEnv*, jobject) = nullptr; if (ndkGetFD == nullptr) { void* handle = dlopen("libnativehelper.so", RTLD_NOLOAD | RTLD_NODELETE); ndkGetFD = reinterpret_cast<decltype(ndkGetFD)>(dlsym(handle, "AFileDescriptor_getFD")); if (ndkGetFD == nullptr) { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Failed to dlsym(AFileDescriptor_getFD): %s", dlerror()); dlclose(handle); return -1; } dlclose(handle); } return javaFd != nullptr ? ndkGetFD(env, javaFd) : -1; } static int getNativeFileDescriptor(JNIEnv* env, jobject javaFd) { static bool preferNdkFileDescriptorApi = false; static bool probeNdkFileDescriptorApi = true; if (probeNdkFileDescriptorApi) { // Check if we should use the NDK File Descriptor API introduced in S. preferNdkFileDescriptorApi = (android_get_device_api_level() >= __ANDROID_API_S__); if (!preferNdkFileDescriptorApi) { // Check if this is a device with Android S dogfood build. static constexpr const char* kCodenameProperty = "ro.build.version.codename"; char codename[PROP_VALUE_MAX] = { 0 }; preferNdkFileDescriptorApi = (__system_property_get(kCodenameProperty, codename) > 0 && strncmp(codename, "S", 2) == 0); } probeNdkFileDescriptorApi = false; } if (preferNdkFileDescriptorApi) { return getNativeFileDescriptorWithNdk(env, javaFd); } else { return getNativeFileDescriptorWithoutNdk(env, javaFd); } } static void network_stack_utils_addArpEntry(JNIEnv *env, jobject thiz, jbyteArray ethAddr, jbyteArray ipv4Addr, jstring ifname, jobject javaFd) { arpreq req = {}; Loading Loading @@ -84,7 +144,7 @@ static void network_stack_utils_addArpEntry(JNIEnv *env, jobject thiz, jbyteArra env->GetStringUTFRegion(ifname, 0, ifLen, req.arp_dev); req.arp_flags = ATF_COM; // Completed entry (ha valid) int fd = jniGetFDFromFileDescriptor(env, javaFd); int fd = getNativeFileDescriptor(env, javaFd); if (fd < 0) { jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor"); return; Loading Loading @@ -122,7 +182,7 @@ static void network_stack_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, job filter_code, }; int fd = jniGetFDFromFileDescriptor(env, javaFd); int fd = getNativeFileDescriptor(env, javaFd); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { jniThrowExceptionFmt(env, "java/net/SocketException", "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); Loading Loading @@ -155,7 +215,7 @@ static void network_stack_utils_attachRaFilter(JNIEnv *env, jobject clazz, jobje filter_code, }; int fd = jniGetFDFromFileDescriptor(env, javaFd); int fd = getNativeFileDescriptor(env, javaFd); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { jniThrowExceptionFmt(env, "java/net/SocketException", "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); Loading Loading @@ -230,7 +290,7 @@ static void network_stack_utils_attachControlPacketFilter( filter_code, }; int fd = jniGetFDFromFileDescriptor(env, javaFd); int fd = getNativeFileDescriptor(env, javaFd); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { jniThrowExceptionFmt(env, "java/net/SocketException", "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); Loading Loading
jni/network_stack_utils_jni.cpp +65 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #define LOG_TAG "NetworkStackUtils-JNI" #include <dlfcn.h> #include <errno.h> #include <jni.h> #include <linux/filter.h> Loading @@ -27,11 +28,11 @@ #include <netinet/ip6.h> #include <netinet/udp.h> #include <stdlib.h> #include <sys/system_properties.h> #include <string> #include <nativehelper/JNIHelp.h> #include <nativehelper/JNIHelpCompat.h> #include <android/log.h> Loading @@ -57,6 +58,65 @@ static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* return true; } static int getNativeFileDescriptorWithoutNdk(JNIEnv* env, jobject javaFd) { // Prior to Android S, we need to find the descriptor field in the FileDescriptor class. The // symbol name has been stable in libcore, but is a private implementation detail. static jfieldID descriptorFieldID = nullptr; if (descriptorFieldID == nullptr) { jclass fileDescriptorClass = env->FindClass("java/io/FileDescriptor"); descriptorFieldID = env->GetFieldID(fileDescriptorClass, "descriptor", "I"); env->DeleteLocalRef(fileDescriptorClass); if (descriptorFieldID == nullptr) { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Failed to get descriptor field."); return -1; } } return javaFd != nullptr ? env->GetIntField(javaFd, descriptorFieldID) : -1; } static int getNativeFileDescriptorWithNdk(JNIEnv* env, jobject javaFd) { // Since Android S, there is an NDK API to get a file descriptor present in libnativehelper.so. // libnativehelper is loaded into all processes by the zygote since the zygote uses it // to load the Android Runtime and is also a public library (because of the NDK API). static int (*ndkGetFD)(JNIEnv*, jobject) = nullptr; if (ndkGetFD == nullptr) { void* handle = dlopen("libnativehelper.so", RTLD_NOLOAD | RTLD_NODELETE); ndkGetFD = reinterpret_cast<decltype(ndkGetFD)>(dlsym(handle, "AFileDescriptor_getFD")); if (ndkGetFD == nullptr) { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Failed to dlsym(AFileDescriptor_getFD): %s", dlerror()); dlclose(handle); return -1; } dlclose(handle); } return javaFd != nullptr ? ndkGetFD(env, javaFd) : -1; } static int getNativeFileDescriptor(JNIEnv* env, jobject javaFd) { static bool preferNdkFileDescriptorApi = false; static bool probeNdkFileDescriptorApi = true; if (probeNdkFileDescriptorApi) { // Check if we should use the NDK File Descriptor API introduced in S. preferNdkFileDescriptorApi = (android_get_device_api_level() >= __ANDROID_API_S__); if (!preferNdkFileDescriptorApi) { // Check if this is a device with Android S dogfood build. static constexpr const char* kCodenameProperty = "ro.build.version.codename"; char codename[PROP_VALUE_MAX] = { 0 }; preferNdkFileDescriptorApi = (__system_property_get(kCodenameProperty, codename) > 0 && strncmp(codename, "S", 2) == 0); } probeNdkFileDescriptorApi = false; } if (preferNdkFileDescriptorApi) { return getNativeFileDescriptorWithNdk(env, javaFd); } else { return getNativeFileDescriptorWithoutNdk(env, javaFd); } } static void network_stack_utils_addArpEntry(JNIEnv *env, jobject thiz, jbyteArray ethAddr, jbyteArray ipv4Addr, jstring ifname, jobject javaFd) { arpreq req = {}; Loading Loading @@ -84,7 +144,7 @@ static void network_stack_utils_addArpEntry(JNIEnv *env, jobject thiz, jbyteArra env->GetStringUTFRegion(ifname, 0, ifLen, req.arp_dev); req.arp_flags = ATF_COM; // Completed entry (ha valid) int fd = jniGetFDFromFileDescriptor(env, javaFd); int fd = getNativeFileDescriptor(env, javaFd); if (fd < 0) { jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor"); return; Loading Loading @@ -122,7 +182,7 @@ static void network_stack_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, job filter_code, }; int fd = jniGetFDFromFileDescriptor(env, javaFd); int fd = getNativeFileDescriptor(env, javaFd); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { jniThrowExceptionFmt(env, "java/net/SocketException", "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); Loading Loading @@ -155,7 +215,7 @@ static void network_stack_utils_attachRaFilter(JNIEnv *env, jobject clazz, jobje filter_code, }; int fd = jniGetFDFromFileDescriptor(env, javaFd); int fd = getNativeFileDescriptor(env, javaFd); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { jniThrowExceptionFmt(env, "java/net/SocketException", "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); Loading Loading @@ -230,7 +290,7 @@ static void network_stack_utils_attachControlPacketFilter( filter_code, }; int fd = jniGetFDFromFileDescriptor(env, javaFd); int fd = getNativeFileDescriptor(env, javaFd); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { jniThrowExceptionFmt(env, "java/net/SocketException", "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); Loading