简体   繁体   English

在本机代码中使用来自 Android VpnService.Builder 的文件描述符

[英]Using a file descriptor from Android VpnService.Builder in native code

I am making a VPN application in Android and the following seems to work great:我正在 Android 中制作一个 VPN 应用程序,以下似乎效果很好:

public class MyVpnService extends VpnService  {

    private ParcelFileDescriptor lt;

    public int onStartCommand(Intent intent,
                               int flags,
                               int startId) {
        VpnService.Builder builder = new VpnService.Builder();

        builder=builder.addAddress("192.168.2.2", 0)
                .addDnsServer("192.168.2.1")
                .setBlocking(true);

        lt = builder.establish();
        if(lt==null) {
            Log.i(logTag,"We are not prepared");
            return START_STICKY;
        }

        new Thread() {
            @Override
            public void run() {
                FileInputStream in = new FileInputStream(lt.getFileDescriptor());
                byte[] buffer = new byte[2000];
                for (;;) {
                    int len;
                    try {
                        len = in.read(buffer);
                    } catch (IOException e) {
                        Log.i(logTag, "Got exception " + e);
                        break;
                    }
                    if (len <= 0) {
                        Log.i(logTag, "No more packets; exits");
                        break;
                    }
                    Log.i(logTag, "Got a packet with length " + len);
                }

                try {
                    lt.close();
                } catch(IOException e) {
                    Log.i(logTag,"Exception when closing fd - likely it was closed already "+e);
                }
            }
        }.start();

        return START_STICKY;
    }

    // ... other methods omitted...

}

Now, I want to do the VPN handling in native code instead.现在,我想用本机代码进行 VPN 处理。 So I have tried to replace the thread with something like this:所以我试图用这样的东西替换线程:

new Thread() {
    @Override
    public void run() {
        int fd = lt.getFd();
        Jni.doVPN(fd);
        try {
            lt.close();
        } catch(IOException e) {
            Log.i(logTag,"Exception when closing fd - likely it was closed already "+e);
        }
    }
}.start();

Where the JNI code looks something like this: JNI 代码如下所示:

#include "unistd.h"
#include <android/log.h>

JNIEXPORT void JNICALL Java_com_example_Jni_doVPN(JNIEnv *env, jclass cls, jint fd) {
    char buf[2000];
    for(;;) {
        int n=read(fd,&buf,sizeof(buf));
        __android_log_print(ANDROID_LOG_VERBOSE, "foo", "Got packet with length %i",n);        
        if(n<=0) {
            break;
        }
    }
}

This also seems to work.这似乎也有效。

HOWEVER: If I close the file descriptor from Java with something like:但是:如果我从 Java 关闭文件描述符,例如:

  lt.close()

Then in the pure Java code, the read call immediately throws an InterruptedIOException which seems reasonable.然后在纯 Java 代码中,读取调用立即抛出看起来合理的 InterruptedIOException。

But in the native code, it often seems to take a long time before the read call reports an error - it just keeps blocking.但在本机代码中,read 调用通常似乎需要很长时间才能报告错误——它只是一直阻塞。 Further, if I close the VPN by clicking in the Android UI and ask Android to forget the VPN (which triggers a call to VpnService.OnRevoke), then the native read call seems to block forever.此外,如果我通过单击 Android UI 关闭 VPN 并要求 Android 忘记 VPN(这会触发对 VpnService.OnRevoke 的调用),那么本机读取调用似乎会永远阻塞。 I suspect that the read call blocks until there is something different from an error to return, and then it returns an error afterwards.我怀疑 read 调用会阻塞,直到返回与错误不同的东西,然后再返回错误。 This would explain both observations.这将解释这两个观察结果。

Any ideas of how I can fix this or what is going on?关于如何解决这个问题或发生了什么的任何想法? I really would prefer not read from the file descriptor from Java code.我真的不希望从 Java 代码中读取文件描述符。

It turns out you are not supposed to close a file descriptor while reading of it.事实证明,您不应该在读取文件描述符时关闭它。 Ways to work around this are described at C: blocking read should return, if filedescriptor is deleted . C 中描述了解决此问题的方法:如果删除了文件描述符,则应返回阻塞读取

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM