add android icon
This commit is contained in:
1 parent
f22d954105
commit
55a69c743a
521 files changed
+8146
-14086
No files matched your search
@@ -4,7 +4,6 @@ version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
# Имя выходной библиотеки libramshare_ui.so совпадает с System.loadLibrary("ramshare_ui") в Kotlin
|
||||
name = "ramshare_ui"
|
||||
path = "src/lib.rs"
|
||||
crate-type = ["cdylib"]
|
||||
@@ -12,7 +11,8 @@ crate-type = ["cdylib"]
|
||||
[dependencies]
|
||||
ramshare-core = { path = "../core" }
|
||||
tokio.workspace = true
|
||||
tokio-util.workspace = true
|
||||
tokio-rustls.workspace = true
|
||||
axum.workspace = true
|
||||
parking_lot.workspace = true
|
||||
serde_json.workspace = true
|
||||
jni = "0.21"
|
||||
+354
-47
@@ -1,18 +1,18 @@
|
||||
use axum::Router;
|
||||
use jni::{
|
||||
objects::{JClass, JString},
|
||||
sys::{jboolean, jint, JNI_FALSE, JNI_TRUE},
|
||||
sys::{jboolean, jint, jlong, jstring, JNI_FALSE, JNI_TRUE},
|
||||
JNIEnv,
|
||||
};
|
||||
use parking_lot::Mutex as PlMutex;
|
||||
use ramshare_core::{api, init_app_state, models::AppState, net, p2p, storage, tls};
|
||||
use ramshare_core::{api, init_app_state, models::*, net, p2p, storage, tls, transfer};
|
||||
use std::{
|
||||
net::Ipv4Addr,
|
||||
net::IpAddr,
|
||||
path::Path,
|
||||
sync::{atomic::Ordering, Arc, OnceLock},
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::sync::Notify;
|
||||
use tokio::{sync::Notify, task::JoinHandle};
|
||||
use tokio_util::io::ReaderStream;
|
||||
|
||||
struct RunningNode {
|
||||
state: Arc<AppState>,
|
||||
@@ -33,40 +33,42 @@ fn rt() -> &'static tokio::runtime::Runtime {
|
||||
})
|
||||
}
|
||||
|
||||
fn jstr<'a>(env: &JNIEnv<'a>, s: &str) -> jstring {
|
||||
env.new_string(s)
|
||||
.map(|v| v.into_raw())
|
||||
.unwrap_or(std::ptr::null_mut())
|
||||
}
|
||||
|
||||
fn get_state() -> Result<Arc<AppState>, String> {
|
||||
NODE.lock()
|
||||
.as_ref()
|
||||
.map(|n| n.state.clone())
|
||||
.ok_or_else(|| "Локальное ядро остановлено".to_string())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_ramshare_app_RamshareNative_startServer<'local>(
|
||||
pub extern "system" fn Java_com_ramshare_app_RamshareNative_startNode<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
base_dir: JString<'local>,
|
||||
device_name: JString<'local>,
|
||||
p2p_port: jint,
|
||||
ui_port: jint,
|
||||
) -> jboolean {
|
||||
) -> jstring {
|
||||
if NODE.lock().is_some() {
|
||||
return JNI_TRUE;
|
||||
return jstr(&env, "");
|
||||
}
|
||||
let base_str: String = match env.get_string(&base_dir) {
|
||||
Ok(s) => s.into(),
|
||||
Err(_) => return JNI_FALSE,
|
||||
Err(e) => return jstr(&env, &format!("base_dir: {}", e)),
|
||||
};
|
||||
let dev_name: String = match env.get_string(&device_name) {
|
||||
Ok(s) => s.into(),
|
||||
Err(_) => "Android-Device".to_string(),
|
||||
};
|
||||
let p2p_port = (p2p_port as u16).max(1024);
|
||||
let ui_port = (ui_port as u16).max(1024);
|
||||
|
||||
let dirs = match storage::resolve_dirs(Some(Path::new(&base_str)), p2p_port) {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
eprintln!("[ANDROID ERR] resolve_dirs: {}", e);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
};
|
||||
|
||||
let (state, bind_ip) = match init_app_state(
|
||||
dirs.config,
|
||||
dirs.media,
|
||||
Some(Path::new(&base_str)),
|
||||
None,
|
||||
&dev_name,
|
||||
p2p_port,
|
||||
@@ -76,10 +78,7 @@ pub extern "system" fn Java_com_ramshare_app_RamshareNative_startServer<'local>(
|
||||
false,
|
||||
) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
eprintln!("[ANDROID ERR] init_app_state: {}", e);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
Err(e) => return jstr(&env, &e),
|
||||
};
|
||||
|
||||
let stop = Arc::new(Notify::new());
|
||||
@@ -89,17 +88,21 @@ pub extern "system" fn Java_com_ramshare_app_RamshareNative_startServer<'local>(
|
||||
let started = rt().block_on(async move {
|
||||
let server_cfg = tls::server_config(&st.cert_der, &st.key_der)?;
|
||||
let acceptor = tokio_rustls::TlsAcceptor::from(server_cfg);
|
||||
let p2p_listener = tokio::net::TcpListener::bind((bind_ip, p2p_port))
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let ui_listener = tokio::net::TcpListener::bind((Ipv4Addr::LOCALHOST, ui_port))
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let p2p_listener = net::bind_p2p_listener(bind_ip, p2p_port)
|
||||
.map_err(|e| format!("Порт {} занят или недоступен: {}", p2p_port, e))?;
|
||||
|
||||
net::start_discovery(st.clone());
|
||||
st.add_log(format!(
|
||||
"Ядро запущено: '{}' ({}:{}, ID: {})",
|
||||
st.device_name,
|
||||
st.current_ip(),
|
||||
p2p_port,
|
||||
short_id(&st.my_fingerprint)
|
||||
));
|
||||
|
||||
let mut tasks: Vec<JoinHandle<()>> = net::start_discovery(st.clone());
|
||||
|
||||
let st_ping = st.clone();
|
||||
let ping_task = tokio::spawn(async move {
|
||||
tasks.push(tokio::spawn(async move {
|
||||
net::ping_all_peers(&st_ping, false).await;
|
||||
st_ping.maintenance();
|
||||
loop {
|
||||
@@ -111,17 +114,19 @@ pub extern "system" fn Java_com_ramshare_app_RamshareNative_startServer<'local>(
|
||||
net::ping_all_peers(&st_ping, false).await;
|
||||
st_ping.maintenance();
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
let ui_router = api::control_router(Router::new(), st.clone(), ui_port);
|
||||
let ui_task = tokio::spawn(async move { axum::serve(ui_listener, ui_router).await });
|
||||
let p2p_task = tokio::spawn(p2p::serve(p2p_listener, acceptor, p2p::router(st.clone())));
|
||||
tasks.push(tokio::spawn(p2p::serve(
|
||||
p2p_listener,
|
||||
acceptor,
|
||||
p2p::router(st.clone()),
|
||||
)));
|
||||
|
||||
tokio::spawn(async move {
|
||||
stop_wait.notified().await;
|
||||
ping_task.abort();
|
||||
ui_task.abort();
|
||||
p2p_task.abort();
|
||||
for t in tasks {
|
||||
t.abort();
|
||||
}
|
||||
});
|
||||
|
||||
Ok::<(), String>(())
|
||||
@@ -130,24 +135,326 @@ pub extern "system" fn Java_com_ramshare_app_RamshareNative_startServer<'local>(
|
||||
match started {
|
||||
Ok(()) => {
|
||||
*NODE.lock() = Some(RunningNode { state, stop });
|
||||
JNI_TRUE
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[ANDROID ERR] bind/start: {}", e);
|
||||
JNI_FALSE
|
||||
jstr(&env, "")
|
||||
}
|
||||
Err(e) => jstr(&env, &e),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_ramshare_app_RamshareNative_stopServer<'local>(
|
||||
pub extern "system" fn Java_com_ramshare_app_RamshareNative_stopNode<'local>(
|
||||
_env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
) {
|
||||
if let Some(node) = NODE.lock().take() {
|
||||
node.state.sync_trust_from_disk();
|
||||
node.state.persist_trust();
|
||||
storage::cleanup_partials(&node.state.media_dir);
|
||||
storage::cleanup_partials(&node.state.current_media_dir());
|
||||
node.stop.notify_waiters();
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_ramshare_app_RamshareNative_isNodeRunning<'local>(
|
||||
_env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
) -> jboolean {
|
||||
if NODE.lock().is_some() {
|
||||
JNI_TRUE
|
||||
} else {
|
||||
JNI_FALSE
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_ramshare_app_RamshareNative_getStateJson<'local>(
|
||||
env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
) -> jstring {
|
||||
let Ok(state) = get_state() else {
|
||||
return jstr(&env, "");
|
||||
};
|
||||
let val = api::state_snapshot_json(&state);
|
||||
jstr(&env, &val.to_string())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_ramshare_app_RamshareNative_pingAll<'local>(
|
||||
env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
) -> jstring {
|
||||
let state = match get_state() {
|
||||
Ok(s) => s,
|
||||
Err(e) => return jstr(&env, &e),
|
||||
};
|
||||
rt().block_on(async move {
|
||||
net::ping_all_peers(&state, true).await;
|
||||
});
|
||||
jstr(&env, "")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_ramshare_app_RamshareNative_pingPeer<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
peer_id: JString<'local>,
|
||||
) -> jboolean {
|
||||
let Ok(state) = get_state() else {
|
||||
return JNI_FALSE;
|
||||
};
|
||||
let Ok(id_str) = env.get_string(&peer_id) else {
|
||||
return JNI_FALSE;
|
||||
};
|
||||
let id: String = id_str.into();
|
||||
let online = rt()
|
||||
.block_on(async move { net::ping_peer_by_id(&state, &id).await })
|
||||
.unwrap_or(false);
|
||||
if online {
|
||||
JNI_TRUE
|
||||
} else {
|
||||
JNI_FALSE
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_ramshare_app_RamshareNative_addManualPeer<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
ip: JString<'local>,
|
||||
port: jint,
|
||||
) -> jstring {
|
||||
let state = match get_state() {
|
||||
Ok(s) => s,
|
||||
Err(e) => return jstr(&env, &e),
|
||||
};
|
||||
let ip_raw: String = match env.get_string(&ip) {
|
||||
Ok(s) => s.into(),
|
||||
Err(_) => return jstr(&env, "Некорректная строка IP"),
|
||||
};
|
||||
let parsed_ip: IpAddr = match ip_raw.trim().parse() {
|
||||
Ok(a) => a,
|
||||
Err(_) => return jstr(&env, "Некорректный IP-адрес"),
|
||||
};
|
||||
match parsed_ip {
|
||||
IpAddr::V4(v4) if is_lan_v4(v4) => {}
|
||||
_ => return jstr(&env, "Разрешены только локальные IPv4-адреса"),
|
||||
}
|
||||
let target_port = if port > 0 && port <= 65535 {
|
||||
port as u16
|
||||
} else {
|
||||
state.p2p_port
|
||||
};
|
||||
let res = rt().block_on(async move { net::probe_and_register(&state, parsed_ip, target_port).await });
|
||||
match res {
|
||||
Ok(_) => jstr(&env, ""),
|
||||
Err(e) => jstr(&env, &format!("Узел {}: {}", parsed_ip, e)),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_ramshare_app_RamshareNative_scanRange<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
range: JString<'local>,
|
||||
) -> jstring {
|
||||
let state = match get_state() {
|
||||
Ok(s) => s,
|
||||
Err(e) => return jstr(&env, &e),
|
||||
};
|
||||
let range_str: String = match env.get_string(&range) {
|
||||
Ok(s) => s.into(),
|
||||
Err(_) => "all".to_string(),
|
||||
};
|
||||
if let Some(ip) = net::detect_lan_ip() {
|
||||
state.update_my_ip(ip);
|
||||
}
|
||||
let my_ip = state.current_ip();
|
||||
let (label, ips) = match parse_ip_range(&range_str, &my_ip) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return jstr(&env, &e),
|
||||
};
|
||||
let total = ips.len();
|
||||
let port = state.p2p_port;
|
||||
|
||||
{
|
||||
let mut s = state.scan_status.write();
|
||||
if s.active {
|
||||
return jstr(&env, "Сканирование уже выполняется");
|
||||
}
|
||||
*s = ScanProgress {
|
||||
active: true,
|
||||
range: label.clone(),
|
||||
scanned: 0,
|
||||
total,
|
||||
found: 0,
|
||||
};
|
||||
}
|
||||
state.add_log(format!("Запущено сканирование {} ({} адресов)", label, total));
|
||||
|
||||
let st = state.clone();
|
||||
rt().spawn(async move {
|
||||
let worker = tokio::spawn(net::run_scan(st.clone(), ips, port));
|
||||
let _ = worker.await;
|
||||
let found = {
|
||||
let mut s = st.scan_status.write();
|
||||
s.active = false;
|
||||
s.found
|
||||
};
|
||||
st.add_log(format!("Сканирование {} завершено. Найдено: {}", label, found));
|
||||
});
|
||||
|
||||
jstr(&env, "")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_ramshare_app_RamshareNative_setTrust<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
peer_id: JString<'local>,
|
||||
trust: jboolean,
|
||||
) -> jstring {
|
||||
let state = match get_state() {
|
||||
Ok(s) => s,
|
||||
Err(e) => return jstr(&env, &e),
|
||||
};
|
||||
let id: String = match env.get_string(&peer_id) {
|
||||
Ok(s) => s.into(),
|
||||
Err(_) => return jstr(&env, "Некорректный ID"),
|
||||
};
|
||||
let trust_bool = trust == JNI_TRUE;
|
||||
let msg = {
|
||||
let mut peers = state.peers.write();
|
||||
let Some(p) = peers.get_mut(&id) else {
|
||||
return jstr(&env, "Устройство не найдено");
|
||||
};
|
||||
p.verified = trust_bool;
|
||||
if trust_bool {
|
||||
p.fail_count = 0;
|
||||
format!("Устройство '{}' [{}] ЗАВЕРЕНО", p.name, p.pin_code)
|
||||
} else {
|
||||
format!("Доверие устройству '{}' [{}] ОТОЗВАНО", p.name, p.pin_code)
|
||||
}
|
||||
};
|
||||
state.persist_trust();
|
||||
state.add_log(msg);
|
||||
let st = state.clone();
|
||||
rt().spawn(async move {
|
||||
net::push_trust(&st, &id).await;
|
||||
});
|
||||
jstr(&env, "")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_ramshare_app_RamshareNative_setPingInterval<'local>(
|
||||
env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
sec: jint,
|
||||
) -> jstring {
|
||||
let state = match get_state() {
|
||||
Ok(s) => s,
|
||||
Err(e) => return jstr(&env, &e),
|
||||
};
|
||||
match state.set_ping_interval(sec.max(0) as u64) {
|
||||
Ok(()) => jstr(&env, ""),
|
||||
Err(e) => jstr(&env, &e),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_ramshare_app_RamshareNative_setMediaDir<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
path: JString<'local>,
|
||||
) -> jstring {
|
||||
let state = match get_state() {
|
||||
Ok(s) => s,
|
||||
Err(e) => return jstr(&env, &e),
|
||||
};
|
||||
let raw: String = match env.get_string(&path) {
|
||||
Ok(s) => s.into(),
|
||||
Err(_) => String::new(),
|
||||
};
|
||||
match state.set_media_dir(Some(&raw)) {
|
||||
Ok(_) => jstr(&env, ""),
|
||||
Err(e) => jstr(&env, &e),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_ramshare_app_RamshareNative_clearRam<'local>(
|
||||
_env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
) {
|
||||
if let Ok(state) = get_state() {
|
||||
state.ram_texts.write().clear();
|
||||
state.add_log("Текстовый буфер в ОЗУ очищен");
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_ramshare_app_RamshareNative_sendText<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
peer_id: JString<'local>,
|
||||
text: JString<'local>,
|
||||
) -> jstring {
|
||||
let state = match get_state() {
|
||||
Ok(s) => s,
|
||||
Err(e) => return jstr(&env, &e),
|
||||
};
|
||||
let id: String = match env.get_string(&peer_id) {
|
||||
Ok(s) => s.into(),
|
||||
Err(_) => return jstr(&env, "Не указан получатель"),
|
||||
};
|
||||
let txt: String = match env.get_string(&text) {
|
||||
Ok(s) => s.into(),
|
||||
Err(_) => return jstr(&env, "Некорректный текст"),
|
||||
};
|
||||
let res = rt().block_on(async move { transfer::send_text(&state, &id, txt).await });
|
||||
match res {
|
||||
Ok(_) => jstr(&env, ""),
|
||||
Err(e) => jstr(&env, &e.1),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_ramshare_app_RamshareNative_sendFileFd<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
peer_id: JString<'local>,
|
||||
file_name: JString<'local>,
|
||||
fd: jint,
|
||||
total_size: jlong,
|
||||
) -> jstring {
|
||||
use std::os::unix::io::FromRawFd;
|
||||
|
||||
if fd < 0 {
|
||||
return jstr(&env, "Некорректный файловый дескриптор");
|
||||
}
|
||||
let std_file = unsafe { std::fs::File::from_raw_fd(fd) };
|
||||
|
||||
let state = match get_state() {
|
||||
Ok(s) => s,
|
||||
Err(e) => return jstr(&env, &e),
|
||||
};
|
||||
let id: String = match env.get_string(&peer_id) {
|
||||
Ok(s) => s.into(),
|
||||
Err(_) => return jstr(&env, "Не указан получатель"),
|
||||
};
|
||||
let name: String = match env.get_string(&file_name) {
|
||||
Ok(s) => s.into(),
|
||||
Err(_) => "file.bin".to_string(),
|
||||
};
|
||||
let total = total_size.max(0) as u64;
|
||||
|
||||
let res = rt().block_on(async move {
|
||||
let tokio_file = tokio::fs::File::from_std(std_file);
|
||||
let stream = ReaderStream::with_capacity(tokio_file, 256 * 1024);
|
||||
transfer::send_stream(&state, &id, &name, total, stream).await
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(_) => jstr(&env, ""),
|
||||
Err(e) => jstr(&env, &e.1),
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user