windows_core_api.dart 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:io';
  4. import 'package:flutter/services.dart';
  5. import 'package:path/path.dart' as path;
  6. import 'package:path_provider/path_provider.dart';
  7. import '../../utils/log/logger.dart';
  8. import '../constants/enums.dart';
  9. import 'base_core_api.dart';
  10. import 'windows/vpn_windows_service.dart';
  11. /// Windows 实现
  12. class WindowsCoreApi implements BaseCoreApi {
  13. static const _tag = 'WindowsCoreApi';
  14. WindowsCoreApi._() {
  15. _initEventChannel();
  16. // 初始化vpn服务
  17. _initVpnService();
  18. }
  19. // 创建vpn服务
  20. static final _vpn = VpnWindowsService();
  21. StreamSubscription? _ssStatus;
  22. bool _isVpnInited = false;
  23. /// 内部构造方法,供 BaseCoreApi 工厂使用
  24. factory WindowsCoreApi.create() => WindowsCoreApi._();
  25. // Windows Method Channel
  26. static const MethodChannel _channel = MethodChannel('app.xixi.nomo/core_api');
  27. // Windows 事件流控制器
  28. static final StreamController<String> _eventController =
  29. StreamController<String>.broadcast();
  30. // Windows 事件流
  31. static Stream<String> get eventStream => _eventController.stream;
  32. // 初始化事件监听
  33. void _initEventChannel() {
  34. // 监听来自 Windows 原生端的方法调用
  35. _channel.setMethodCallHandler(_handleMethodCall);
  36. }
  37. // 处理来自 Windows 原生端的方法调用
  38. Future<dynamic> _handleMethodCall(MethodCall call) async {
  39. switch (call.method) {
  40. case 'onEventChange':
  41. // 原生端发送事件,转发到事件流
  42. final String event = call.arguments as String;
  43. _eventController.add(event);
  44. return null;
  45. default:
  46. throw PlatformException(
  47. code: 'Unimplemented',
  48. message: 'Method ${call.method} not implemented',
  49. );
  50. }
  51. }
  52. void _initVpnService() {
  53. if (_isVpnInited) {
  54. return;
  55. }
  56. _isVpnInited = true;
  57. // 初始化vpn服务 10秒超时
  58. _vpn.initialize(10, false);
  59. // 监听VPN服务状态
  60. _ssStatus = _vpn.onStatusChanged.listen((event) async {
  61. switch (event) {
  62. case ConnectionState.connecting:
  63. // 正在连接
  64. _eventController.add(
  65. '{"type":"vpn_status","status":1,"code":0,"message":""}',
  66. );
  67. break;
  68. // 已经连接
  69. case ConnectionState.connected:
  70. _eventController.add(
  71. '{"type":"vpn_status","status":2,"code":0,"message":""}',
  72. );
  73. break;
  74. // 连接错误
  75. case ConnectionState.error:
  76. _eventController.add(
  77. '{"type":"vpn_status","status":3,"code":0,"message":""}',
  78. );
  79. _vpn.stop();
  80. break;
  81. // 已经断开
  82. case ConnectionState.disconnected:
  83. _eventController.add(
  84. '{"type":"vpn_status","status":0,"code":0,"message":""}',
  85. );
  86. break;
  87. }
  88. // TODO: 通知状态变更
  89. });
  90. }
  91. @override
  92. Future<String?> getApps() async {
  93. // Windows 不需要获取应用列表
  94. return null;
  95. }
  96. @override
  97. Future<String?> getSystemLocale() async {
  98. return Platform.localeName;
  99. }
  100. @override
  101. Future<bool?> connect(
  102. String sessionId,
  103. int socksPort,
  104. String tunnelConfig,
  105. String configJson,
  106. int remainTime,
  107. bool isCountdown,
  108. List<String> allowVpnApps,
  109. List<String> disallowVpnApps,
  110. String accessToken,
  111. String aesKey,
  112. String aesIv,
  113. int locationId,
  114. String locationCode,
  115. List<String> baseUrls,
  116. String params,
  117. int peekTimeInterval,
  118. ) async {
  119. try {
  120. String geoPath = await _getGeoDirectory();
  121. log(_tag, 'geoPath: $geoPath');
  122. final selfExecutable = Platform.resolvedExecutable;
  123. //List<String> apps = isSplitTunnelEnabled ? splitTunnelApps : [];
  124. List<String> allowExes = [];
  125. List<String> disallowExes = [selfExecutable];
  126. // 连接vpn
  127. Map<String, dynamic> params = {
  128. 'sessionId': sessionId,
  129. 'connectOptions': jsonEncode({
  130. 'geoPath': geoPath,
  131. 'nodesConfig': configJson,
  132. }),
  133. 'allowExes': allowExes,
  134. 'disallowExes': disallowExes,
  135. };
  136. log(_tag, jsonEncode(params));
  137. // 连接vpn
  138. _vpn.start(params);
  139. return true;
  140. } catch (e) {
  141. // 通知连接出错
  142. _eventController.add(
  143. '{"type":"vpn_status","status":3,"code":0,"message":""}',
  144. );
  145. //logger.e('get nodes error: $e');
  146. return false;
  147. }
  148. }
  149. @override
  150. Future<bool?> disconnect() async {
  151. // 实现 Windows 断开连接逻辑
  152. await _vpn.stop();
  153. return true;
  154. }
  155. @override
  156. Future<String?> getRemoteIp() async {
  157. // 实现 Windows 获取远程 IP
  158. return await _vpn.getRemoteAddress();
  159. }
  160. @override
  161. Future<String?> getAdvertisingId() async {
  162. // Windows 不支持广告 ID
  163. return null;
  164. }
  165. @override
  166. Future<bool?> moveTaskToBack() async {
  167. // Windows 不需要此功能
  168. return true;
  169. }
  170. @override
  171. Future<bool?> isConnected() async {
  172. return _vpn.isOnline && _vpn.status == ConnectionState.connected;
  173. }
  174. @override
  175. Future<String?> getSimInfo() async {
  176. // Windows 不支持 SIM 卡信息
  177. return null;
  178. }
  179. @override
  180. Future<String?> getChannel() async {
  181. // TODO: 实现 Windows 渠道获取
  182. return 'windows';
  183. }
  184. /// 发送事件(供 Windows 实现内部使用)
  185. ///
  186. /// Windows 原生端可以通过 MethodChannel 发送事件:
  187. /// ```cpp
  188. /// // C++ 示例
  189. /// flutter::MethodChannel<flutter::EncodableValue> channel(
  190. /// messenger, "app.xixi.nomo/core_api",
  191. /// &flutter::StandardMethodCodec::GetInstance());
  192. ///
  193. /// // 发送 VPN 状态变化
  194. /// channel.InvokeMethod("onEventChange",
  195. /// flutter::EncodableValue("{\"type\":\"vpn_status\",\"status\":2}"));
  196. /// ```
  197. ///
  198. /// 事件 JSON 格式:
  199. /// - vpn_status: {"type":"vpn_status","status":0|1|2|3,"code":0,"message":""}
  200. /// - status: 0=idle, 1=connecting, 2=connected, 3=error
  201. /// - timer_update: {"type":"timer_update","currentTime":123,"mode":"countdown"}
  202. /// - boost_result: {"type":"boost_result","locationCode":"US","nodeId":"xxx","success":true}
  203. static void sendEvent(String event) {
  204. _eventController.add(event);
  205. }
  206. /// 释放资源
  207. static void dispose() {
  208. _eventController.close();
  209. }
  210. /// 获取 geo 文件目录
  211. Future<String> _getGeoDirectory() async {
  212. try {
  213. final appDir = await getApplicationSupportDirectory();
  214. final geoDir = Directory(path.join(appDir.path, 'geo'));
  215. return geoDir.path;
  216. } catch (_) {
  217. return '';
  218. }
  219. }
  220. }