This commit is contained in:
2026-03-10 17:48:22 +07:00
parent 7240bcf94c
commit 8d0eccdbc2
16 changed files with 202 additions and 25 deletions
+4
View File
@@ -11,7 +11,11 @@ name = "netrunner_app"
tauri-build = { version = "2.0.0", features = [] }
[dependencies]
jni = "0.21"
netrunner-client = { path = "../../client" }
netrunner-core = { path = "../../core" }
tauri = { version = "2.0.0", features = [] }
serde = { version = "1.0", features = ["derive"] }
ndk-context = "0.1"
log = "0.4"
android_logger = "0.14"
+12
View File
@@ -1,3 +1,15 @@
fn main() {
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
println!("cargo:warning=MANIFEST_DIR is: {}", manifest_dir);
let path = std::path::Path::new(&manifest_dir).join("../../gen/arm64-v8a");
println!(
"cargo:warning=LINKER_SEARCH_PATH is: {:?}",
path.canonicalize()
);
println!("cargo:rustc-link-search=native={}", path.display());
println!("cargo:rustc-link-lib=dylib=netrunner_client");
tauri_build::build();
}
+7 -1
View File
@@ -3,5 +3,11 @@
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": ["core:default"]
"permissions": [
"core:default",
{
"identifier": "core:app:default",
"allow": ["start_vpn_android"]
}
]
}
+1
View File
@@ -0,0 +1 @@
pub mod start_vpn_android;
@@ -0,0 +1,56 @@
use jni::errors::Error as JniError;
use jni::objects::{JObject, JString, JValue};
use tauri::command;
#[command]
pub fn start_vpn_android(remote_address: String) -> Result<i32, String> {
#[cfg(target_os = "android")]
{
let ctx = ndk_context::android_context();
let vm_ptr = ctx.vm() as *mut jni::sys::JavaVM;
let vm = unsafe { jni::JavaVM::from_raw(vm_ptr) }.map_err(|e: JniError| e.to_string())?;
let mut env = vm
.attach_current_thread()
.map_err(|e: JniError| e.to_string())?;
let class_name = "com/netrunner/vpn/NetrunnerVpnService";
let class = env
.find_class(class_name)
.map_err(|e: JniError| e.to_string())?;
let companion = env
.get_static_field(
class,
"Companion",
"Lcom/netrunner/vpn/NetrunnerVpnService$Companion;",
)
.map_err(|e: JniError| e.to_string())?
.l()
.map_err(|e: JniError| e.to_string())?;
let j_remote_addr = env
.new_string(remote_address)
.map_err(|e: JniError| e.to_string())?;
// Явное приведение контекста к JObject
let context_obj = unsafe { JObject::from_raw(ctx.context() as *mut _) };
let result = env
.call_method(
companion,
"startVpn",
"(Landroid/app/Activity;Ljava/lang/String;)I",
&[JValue::Object(&context_obj), JValue::Object(&j_remote_addr)],
)
.map_err(|e: JniError| e.to_string())?;
result.i().map_err(|e: JniError| e.to_string())
}
#[cfg(not(target_os = "android"))]
{
Err("VPN доступен только на Android".into())
}
}
+9
View File
@@ -1,6 +1,15 @@
mod core;
use core::start_vpn_android::start_vpn_android;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
#[cfg(target_os = "android")]
android_logger::init_once(
android_logger::Config::default()
.with_max_level(log::LevelFilter::Debug)
.with_tag("NetrunnerRust"),
);
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![start_vpn_android])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
@@ -0,0 +1,64 @@
package com.netrunner.vpn
import android.app.Activity
import android.content.Intent
import android.net.VpnService
import android.os.ParcelFileDescriptor
import uniffi.netrunner_client.SessionManager
class NetrunnerVpnService : VpnService() {
companion object {
private var vpnInterface: ParcelFileDescriptor? = null
fun start(activity: Activity): Int {
val intent = VpnService.prepare(activity)
if (intent != null) {
activity.startActivityForResult(intent, 1001)
return -1
}
return startVpn(activity)
}
@JvmStatic
fun startVpn(activity: Activity, remoteAddress: String): Int {
val builder = Builder()
builder.setSession("NetrunnerVPN")
.addAddress("10.0.0.1", 32)
.addRoute("0.0.0.0", 0)
.setMtu(1500)
vpnInterface = builder.establish()
val fd = vpnInterface?.fd ?: -1
if (fd != -1) {
try {
// 1. Создаем менеджер сессий
val sessionManager = uniffi.netrunner_client.SessionManager()
// 2. Вызываем метод start, прокидывая адрес и дескриптор
// Обратите внимание: метод .start() возвращает объект Session
sessionManager.start(
remoteAddress = remoteAddress, // Ваша пустая строка или адрес
tunFd = fd
)
Log.d("NetrunnerVPN", "Сессия успешно запущена с FD: $fd")
} catch (e: Exception) {
Log.e("NetrunnerVPN", "Ошибка при запуске Rust-сессии: ${e.message}")
stop() // Закрываем интерфейс, если нативная часть не стартанула
return -1
}
}
return fd
}
fun stop() {
vpnInterface?.close()
vpnInterface = null
}
}
}