Files
KKRainbow 021f523431 refactor(core): separate portable core from native runtime (#2451)
Create easytier-core as the portable owner of configuration,
connectivity, tunnels, peer and routing state, gateways, management,
the data plane, and instance lifecycle. Keep operating-system
integration, native protocol engines, process startup, and presentation
in easytier behind explicit Host capability adapters.

Create easytier-proto to own schemas, generated RPC types, descriptors,
and feature-scoped protocol slices. Remove runtime protobuf reflection
from core while preserving unknown route-peer fields across forwarding.

Normalize instance construction through CoreInstance, CoreHostAdapters,
CoreProcessRuntime, and InstanceManager. Make the runtime config store
the only authoritative mutable configuration after startup.

Move the portable TCP/UDP data plane into core and extract a generic
OperationBroker for completion, cancellation, disposal, and capacity
accounting. Expose the session-based FFI v2 completion API and keep the
WASI guest ABI, wire schemas, and adapters with core.

Migrate CLI, GUI, web, FFI, Android JNI, OHOS, uptime, and mobile
consumers to the shared manager and core state. Add explicit user/web
config ownership and revision-aware web reconciliation.

Preserve configuration, wire, and management behavior while fixing
regressions discovered by the full platform and integration matrix:

- inherit advertised relay capabilities in foreign networks;
- refresh OSPF peer state immediately after runtime config changes;
- restore CLI GlobalCtx event output without forcing GUI logging;
- retain legacy encryption names and standalone RPC tunnel metadata;
- restore ICMP host composition and fragmented UDP handling;
- use portable 64-bit atomics on 32-bit MIPS targets; and
- retain discarded operations until late cancellation completes.

Validate the refactor across 45 GitHub checks, including Linux, macOS,
Windows, FreeBSD, web, GUI, Android, OHOS, feature profiles, and
three-node and subnet-proxy integration tests.

BREAKING CHANGE: internal Rust module paths are not preserved. Legacy
native data-plane APIs are replaced by the session-based FFI v2 API.
The dedicated Android data-plane wrapper is removed.
2026-07-26 15:41:55 +08:00
..
2025-10-19 15:46:51 +08:00
2025-09-06 13:49:42 +08:00
2026-06-07 17:48:58 +08:00

EasyTier Android JNI

这是 EasyTier 的 Android JNI 绑定库,允许 Android 应用程序调用 EasyTier 的网络功能。

功能特性

  • 🚀 完整的 EasyTier FFI 接口封装
  • 📱 原生 Android JNI 支持
  • 🔧 支持多种 Android 架构 (arm64-v8a, armeabi-v7a, x86, x86_64)
  • 🛡️ 类型安全的 Java 接口
  • 🔌 支持通过 JSON 调用已暴露的 EasyTier RPC 查询/管理接口
  • 📝 详细的错误处理和日志记录

支持的架构

  • arm64-v8a (aarch64-linux-android)
  • armeabi-v7a (armv7-linux-androideabi)
  • x86 (i686-linux-android)
  • x86_64 (x86_64-linux-android)

构建要求

系统要求

  • Rust 1.70+
  • Android NDK r21+
  • Linux/macOS 开发环境

环境设置

  1. 安装 Rust

    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    source ~/.cargo/env
    
  2. 安装 Android NDK

  3. 添加 Android 目标

    rustup target add aarch64-linux-android
    rustup target add armv7-linux-androideabi
    rustup target add i686-linux-android
    rustup target add x86_64-linux-android
    

构建步骤

  1. 克隆项目并进入目录

    cd /path/to/EasyTier/easytier-contrib/easytier-android-jni
    
  2. 运行构建脚本

    ./build.sh
    
  3. 构建完成后,库文件将生成在 target/android/ 目录下

    target/android/
    ├── arm64-v8a/
    │   └── libeasytier_android_jni.so
    ├── armeabi-v7a/
    │   └── libeasytier_android_jni.so
    ├── x86/
    │   └── libeasytier_android_jni.so
    └── x86_64/
        └── libeasytier_android_jni.so
    

Android 项目集成

1. 复制库文件

将生成的 .so 文件复制到您的 Android 项目中:

your-android-project/
└── src/main/
    ├── jniLibs/
    │   ├── arm64-v8a/
    │   │   └── libeasytier_android_jni.so
    │   ├── armeabi-v7a/
    │   │   └── libeasytier_android_jni.so
    │   ├── x86/
    │   │   └── libeasytier_android_jni.so
    │   └── x86_64/
    │       └── libeasytier_android_jni.so
    └── java/
        └── com/easytier/jni/
            └── EasyTierJNI.java

2. 复制 Java 接口

java/com/easytier/jni/EasyTierJNI.java 复制到您的 Android 项目的相应包路径下。

3. 添加权限

AndroidManifest.xml 中添加必要的权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

使用示例

基本使用

import com.easytier.jni.EasyTierJNI;
import java.util.Map;

public class EasyTierManager {
    
    // 初始化网络实例
    public void startNetwork() {
        String config = """
            inst_name = "my_instance"
            network = "my_network"
            """; 
        
        try {
            // 解析配置
            int result = EasyTierJNI.parseConfig(config);
            if (result != 0) {
                String error = EasyTierJNI.getLastError();
                throw new RuntimeException("配置解析失败: " + error);
            }
            
            // 启动网络实例
            result = EasyTierJNI.runNetworkInstance(config);
            if (result != 0) {
                String error = EasyTierJNI.getLastError();
                throw new RuntimeException("网络实例启动失败: " + error);
            }
            
            System.out.println("EasyTier 网络实例启动成功");
            
        } catch (RuntimeException e) {
            System.err.println("启动失败: " + e.getMessage());
        }
    }
    
    // 获取网络信息
    public void getNetworkInfo() {
        try {
            Map<String, String> infos = EasyTierJNI.collectNetworkInfosAsMap(10);
            for (Map.Entry<String, String> entry : infos.entrySet()) {
                System.out.println(entry.getKey() + ": " + entry.getValue());
            }
        } catch (RuntimeException e) {
            System.err.println("获取网络信息失败: " + e.getMessage());
        }
    }
    
    // 停止所有实例
    public void stopNetwork() {
        try {
            int result = EasyTierJNI.stopAllInstances();
            if (result == 0) {
                System.out.println("所有网络实例已停止");
            }
        } catch (RuntimeException e) {
            System.err.println("停止网络失败: " + e.getMessage());
        }
    }
}

通用 JSON RPC

EasyTierJNI.callJsonRpc(serviceName, methodName, domainName, payloadJson) 可以调用已暴露的 EasyTier RPC 服务,payload 和返回值均为 protobuf JSON。该接口不支持 api.manage.WebClientService;实例启动、保留、删除、信息收集仍使用专用 JNI API。

String response = EasyTierJNI.callJsonRpc(
    "api.logger.LoggerRpcService",
    "get_logger_config",
    "{}"
);

VPN 服务集成

如果您要在 Android VPN 服务中使用:

public class EasyTierVpnService extends VpnService {
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 建立 VPN 连接
        ParcelFileDescriptor vpnInterface = establishVpnInterface();
        
        if (vpnInterface != null) {
            int fd = vpnInterface.getFd();
            
            // 设置 TUN 文件描述符
            try {
                EasyTierJNI.setTunFd("my_instance", fd);
            } catch (RuntimeException e) {
                Log.e("EasyTier", "设置 TUN FD 失败", e);
            }
        }
        
        return START_STICKY;
    }
    
    private ParcelFileDescriptor establishVpnInterface() {
        Builder builder = new Builder();
        builder.setMtu(1500);
        builder.addAddress("10.0.0.2", 24);
        builder.addRoute("0.0.0.0", 0);
        builder.setSession("EasyTier VPN");
        
        return builder.establish();
    }
}

API 参考

EasyTierJNI 类方法

方法 描述 参数 返回值
parseConfig(String config) 解析 TOML 配置 config: 配置字符串 0=成功, -1=失败
runNetworkInstance(String config) 启动网络实例 config: 配置字符串 0=成功, -1=失败
setTunFd(String instanceName, int fd) 设置 TUN 文件描述符 instanceName: 实例名, fd: 文件描述符 0=成功, -1=失败
retainNetworkInstance(String[] names) 保留指定实例 names: 实例名数组 0=成功, -1=失败
collectNetworkInfos(int maxLength) 收集网络信息 maxLength: 最大条目数 信息字符串数组
collectNetworkInfosAsMap(int maxLength) 收集网络信息为 Map maxLength: 最大条目数 Map<String, String>
getLastError() 获取最后错误 错误消息字符串
stopAllInstances() 停止所有实例 0=成功, -1=失败
retainSingleInstance(String name) 保留单个实例 name: 实例名 0=成功, -1=失败

故障排除

常见问题

  1. 构建失败: "Android NDK not found"

    • 确保设置了 ANDROID_NDK_ROOT 环境变量
    • 检查 NDK 路径是否正确
  2. 运行时错误: "java.lang.UnsatisfiedLinkError"

    • 确保 .so 文件放在正确的 jniLibs 目录下
    • 检查目标架构是否匹配
  3. 配置解析失败

    • 检查 TOML 配置格式是否正确
    • 使用 getLastError() 获取详细错误信息

调试技巧

  • 启用 Android 日志查看 JNI 层的日志输出
  • 使用 adb logcat -s EasyTier-JNI 查看相关日志
  • 检查 getLastError() 返回的错误信息

许可证

本项目遵循与 EasyTier 主项目相同的许可证。

贡献

欢迎提交 Issue 和 Pull Request 来改进这个项目。

相关链接