Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0c455664 authored by Orion Hodson's avatar Orion Hodson
Browse files

Remove dependency on legacy jniGetFDFromFileDescriptor

On S, use the NDK API to get the native file descriptor from a
FileDescriptor. Pre-S, fallback to implementation knowledge.

Bug: 158749603
Test: manual
Change-Id: I02b261ee2747df2637bd55b0c1ade3c62c73b17e
parent 9f48429f
Loading
Loading
Loading
Loading
+65 −5
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

#define LOG_TAG "NetworkStackUtils-JNI"

#include <dlfcn.h>
#include <errno.h>
#include <jni.h>
#include <linux/filter.h>
@@ -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>

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