feat(bridge): 桥接 API 面 Channel 契约 + 三端原生骨架 [tsk_iP5-_Ztq5THx]
MethodChannel pangolin/vpn (start/stop/getStatus/selectOutbound/ getActiveOutbound/setKillSwitch) + 双 EventChannel (status/stats) 契约定义在 lib/bridge/vpn_bridge.dart 头部注释冻结。 - Dart: VpnBridge 抽象接口 + VpnNativeBridge 原生实现 + VpnBridgeMock 假内核(含 connectDelay/statsInterval 可配参数) - Dart 桌面: kernel_process.dart KernelProcess/ClashApiClient 接口骨架 - Android: PangolinVpnService (VpnService 骨架 + 前台通知占位) + VpnEventBus 解耦总线 + MainActivity MethodChannel/EventChannel 注册 - iOS: PacketTunnelProvider (NEPacketTunnelProvider 骨架) + VpnManager (NETunnelProviderManager + NEVPNStatus 订阅) + AppDelegate 通道注册 + Xcode project.pbxproj 添加 PacketTunnel extension target - Widget: VpnStatus enum 迁移至 bridge/vpn_bridge.dart (+error 态), connect_button/home_shell 更新穷举匹配 - Test: test/bridge/vpn_bridge_mock_test.dart 覆盖 start→connecting→on →stop→off 序列、stats 周期推帧、selectOutbound 重连序列、schema 回测 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>穿山甲 Tunnel</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>PacketTunnel</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.networkextension.packet-tunnel</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,78 @@
|
||||
// PacketTunnelProvider.swift — Packet Tunnel Extension 骨架
|
||||
//
|
||||
// Target: PacketTunnel (NEPacketTunnelProvider extension)
|
||||
// Bundle: com.pangolin.pangolinVpn.PacketTunnel
|
||||
//
|
||||
// 生命周期占位日志;实际 TUN 隧道建立与 libbox 接线由 11F 完成。
|
||||
//
|
||||
// 与主 App 通信:
|
||||
// 主 App → extension: NETunnelProviderSession.sendProviderMessage(_:responseHandler:)
|
||||
// extension → 主 App: 通过 NEProvider.cancelTunnelWithError 或 NETunnelProviderSession 回调
|
||||
//
|
||||
// TODO(11F): 在 startTunnel 中初始化 sing-box/libbox TUN 模式内核。
|
||||
|
||||
import NetworkExtension
|
||||
import os.log
|
||||
|
||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
|
||||
private let log = OSLog(
|
||||
subsystem: "com.pangolin.pangolinVpn.PacketTunnel",
|
||||
category: "VPN"
|
||||
)
|
||||
|
||||
// ── NEPacketTunnelProvider 生命周期 ──────────────────────────
|
||||
|
||||
override func startTunnel(
|
||||
options: [String: NSObject]?,
|
||||
completionHandler: @escaping (Error?) -> Void
|
||||
) {
|
||||
os_log(
|
||||
"PacketTunnelProvider: startTunnel — options keys: %{public}@",
|
||||
log: log, type: .info,
|
||||
(options?.keys.joined(separator: ", ") ?? "(none)")
|
||||
)
|
||||
// TODO(11F): 解析 options["config"] → 写入临时配置文件
|
||||
// → 调用 libbox/sing-box TUN 启动 API
|
||||
// → 配置 packetFlow / setTunnelNetworkSettings
|
||||
completionHandler(nil)
|
||||
}
|
||||
|
||||
override func stopTunnel(
|
||||
with reason: NEProviderStopReason,
|
||||
completionHandler: @escaping () -> Void
|
||||
) {
|
||||
os_log(
|
||||
"PacketTunnelProvider: stopTunnel — reason=%d",
|
||||
log: log, type: .info,
|
||||
reason.rawValue
|
||||
)
|
||||
// TODO(11F): 停止 sing-box 内核 → 关闭 TUN fd
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
override func handleAppMessage(
|
||||
_ messageData: Data,
|
||||
completionHandler: ((Data?) -> Void)?
|
||||
) {
|
||||
os_log(
|
||||
"PacketTunnelProvider: handleAppMessage — %d bytes",
|
||||
log: log, type: .debug,
|
||||
messageData.count
|
||||
)
|
||||
// TODO(11F): 解码 messageData(JSON 或 protobuf)→ 路由到内核命令
|
||||
// 如 selectOutbound / getStats 等
|
||||
completionHandler?(nil)
|
||||
}
|
||||
|
||||
override func sleep(completionHandler: @escaping () -> Void) {
|
||||
os_log("PacketTunnelProvider: sleep", log: log, type: .debug)
|
||||
// TODO(11F): 挂起内核流量处理
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
override func wake() {
|
||||
os_log("PacketTunnelProvider: wake", log: log, type: .debug)
|
||||
// TODO(11F): 恢复内核流量处理
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user