【问题标题】:Using a file descriptor from Android VpnService.Builder in native code在本机代码中使用来自 Android VpnService.Builder 的文件描述符
【发布时间】:2022-01-01 03:36:39
【问题描述】:

我正在 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...

}

现在,我想用本机代码进行 VPN 处理。所以我试图用这样的东西替换线程:

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

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

这似乎也有效。

但是:如果我从 Java 中关闭文件描述符,例如:

  lt.close()

然后在纯 Java 代码中,read 调用立即抛出一个看起来合理的 InterruptedIOException。

但在本机代码中,读取调用通常似乎需要很长时间才能报告错误 - 它只是一直阻塞。此外,如果我通过单击 Android UI 关闭 VPN 并要求 Android 忘记 VPN(这会触发对 VpnService.OnRevoke 的调用),那么本机读取调用似乎会永远阻塞。我怀疑 read 调用会阻塞,直到返回与错误不同的东西,然后再返回错误。这可以解释这两种观察结果。

关于如何解决此问题或发生了什么的任何想法?我真的不希望从 Java 代码中读取文件描述符。

【问题讨论】:

    标签: android native vpn android-vpn-service


    【解决方案1】:

    事实证明,您不应该在读取文件描述符时关闭它。 C: blocking read should return, if filedescriptor is deleted 描述了解决此问题的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-03
      • 2015-02-06
      • 2017-02-11
      • 2023-02-03
      相关资源
      最近更新 更多