私は 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 を閉じ、VPN を忘れるように Android に依頼すると (VpnService.OnRevoke の呼び出しがトリガーされます)、ネイティブの読み取り呼び出しが永久にブロックされているように見えます。エラーとは異なるものが返されるまで読み取り呼び出しがブロックされ、その後エラーが返されるのではないかと思います。これにより、両方の観察結果が説明されます。
これを修正する方法や何が起こっているのかについてのアイデアはありますか? Javaコードからファイル記述子を読み取らないことを本当に望んでいます。