core_controller.dart 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:io';
  4. import 'package:device_info_plus/device_info_plus.dart';
  5. import 'package:dio/dio.dart';
  6. import 'package:get/get.dart';
  7. import 'package:nomo/app/constants/api_domains.dart';
  8. import 'package:nomo/app/constants/keys.dart';
  9. import 'package:package_info_plus/package_info_plus.dart';
  10. import 'package:uuid/uuid.dart';
  11. import '../../config/translations/strings_enum.dart';
  12. import 'base_core_api.dart';
  13. import '../../utils/boost_report_manager.dart';
  14. import '../../utils/haptic_feedback_manager.dart';
  15. import '../../utils/log/logger.dart';
  16. import '../../utils/network_helper.dart';
  17. import '../components/ix_snackbar.dart';
  18. import '../constants/enums.dart';
  19. import '../constants/errors.dart';
  20. import '../data/models/api_exception.dart';
  21. import '../data/models/failure.dart';
  22. import '../data/models/vpn_message.dart';
  23. import '../data/sp/ix_sp.dart';
  24. import '../constants/sp_keys.dart';
  25. import '../dialog/error_dialog.dart';
  26. import 'api_controller.dart';
  27. class CoreController extends GetxService {
  28. final TAG = 'CoreController';
  29. final _apiController = Get.find<ApiController>();
  30. final _state = ConnectionState.disconnected.obs;
  31. ConnectionState get state => _state.value;
  32. set state(ConnectionState value) => _state.value = value;
  33. // 公开状态流供外部监听
  34. Rx<ConnectionState> get stateStream => _state;
  35. final _timer = "00:00:00".obs;
  36. String get timer => _timer.value;
  37. set timer(String value) => _timer.value = value;
  38. // VPN 事件流订阅
  39. StreamSubscription<String>? _eventSubscription;
  40. CancelToken? _cancelToken;
  41. //全局uuid
  42. final _globalUuid = Uuid().v4();
  43. String locationSelectionType = 'auto';
  44. // 标记 VPN 逻辑是否已开启(调用了 BaseCoreApi().connect())
  45. bool _isVpnStarted = false;
  46. // 标记是否有待处理的断开请求
  47. bool _pendingDisconnect = false;
  48. // 用于等待断开完成的 Completer
  49. Completer<void>? _disconnectCompleter;
  50. @override
  51. void onInit() {
  52. super.onInit();
  53. _initCheckConnect();
  54. _startListeningToEvents();
  55. }
  56. @override
  57. void onClose() {
  58. super.onClose();
  59. // 取消事件流订阅
  60. _eventSubscription?.cancel();
  61. _eventSubscription = null;
  62. }
  63. void _initCheckConnect() {
  64. BaseCoreApi().isConnected().then((value) {
  65. if (value == true) {
  66. state = ConnectionState.connected;
  67. _isVpnStarted = true;
  68. } else {
  69. state = ConnectionState.disconnected;
  70. _isVpnStarted = false;
  71. }
  72. });
  73. }
  74. void handleConnection() async {
  75. // 如果正在断开中,忽略操作
  76. if (state == ConnectionState.disconnecting) {
  77. log(TAG, '正在断开中,忽略操作');
  78. return;
  79. }
  80. if (state == ConnectionState.disconnected) {
  81. // 开始连接 - 轻微震动
  82. state = ConnectionState.connectingVirtual;
  83. _isVpnStarted = false;
  84. _pendingDisconnect = false;
  85. HapticFeedbackManager.connectionStart();
  86. getDispatchInfo();
  87. } else if (state == ConnectionState.connectingVirtual) {
  88. // 虚拟连接中点击断开
  89. if (_isVpnStarted) {
  90. // VPN 逻辑已开启,标记待断开,等待 _onVpnConnecting 返回后执行断开
  91. log(TAG, 'VPN 已启动,标记待断开');
  92. _pendingDisconnect = true;
  93. } else {
  94. // VPN 逻辑还没开启,取消请求并初始化状态
  95. log(TAG, 'VPN 未启动,取消请求并初始化状态');
  96. _cancelToken?.cancel('用户取消连接');
  97. _cancelToken = null;
  98. _uninitState();
  99. }
  100. } else if (state == ConnectionState.connecting) {
  101. // 真实连接中点击断开 - 执行断开逻辑
  102. log(TAG, '真实连接中,执行断开逻辑');
  103. await _performDisconnect();
  104. } else if (state == ConnectionState.connected) {
  105. // 已连接状态,执行断开逻辑
  106. await _performDisconnect();
  107. }
  108. }
  109. /// 执行断开逻辑,等待断开完成
  110. Future<void> _performDisconnect() async {
  111. // 如果已经在断开中,等待断开完成(带超时)
  112. if (_disconnectCompleter != null) {
  113. log(TAG, '已经在断开中,等待完成');
  114. try {
  115. await _disconnectCompleter!.future.timeout(
  116. const Duration(seconds: 10),
  117. onTimeout: () {
  118. log(TAG, '等待断开超时,强制清理');
  119. _forceCleanupDisconnect();
  120. },
  121. );
  122. } catch (e) {
  123. log(TAG, '等待断开异常: $e');
  124. _forceCleanupDisconnect();
  125. }
  126. return;
  127. }
  128. log(TAG, '开始执行断开逻辑, _isVpnStarted=$_isVpnStarted');
  129. // 如果 VPN 根本没启动,直接清理状态即可
  130. if (!_isVpnStarted) {
  131. log(TAG, 'VPN 未启动,直接清理状态');
  132. _uninitState();
  133. return;
  134. }
  135. state = ConnectionState.disconnecting;
  136. _disconnectCompleter = Completer<void>();
  137. try {
  138. BaseCoreApi().disconnect();
  139. } catch (e) {
  140. log(TAG, 'disconnect 调用异常: $e');
  141. _forceCleanupDisconnect();
  142. return;
  143. }
  144. // 等待断开完成(带超时)
  145. try {
  146. await _disconnectCompleter!.future.timeout(
  147. const Duration(seconds: 10),
  148. onTimeout: () {
  149. log(TAG, '断开超时,强制清理');
  150. _forceCleanupDisconnect();
  151. },
  152. );
  153. } catch (e) {
  154. log(TAG, '断开等待异常: $e');
  155. _forceCleanupDisconnect();
  156. }
  157. }
  158. /// 强制清理断开状态
  159. void _forceCleanupDisconnect() {
  160. if (_disconnectCompleter != null && !_disconnectCompleter!.isCompleted) {
  161. _disconnectCompleter!.complete();
  162. }
  163. _disconnectCompleter = null;
  164. _isVpnStarted = false;
  165. _pendingDisconnect = false;
  166. _uninitState();
  167. }
  168. void selectLocationConnect() async {
  169. if (state == ConnectionState.disconnecting) {
  170. log(TAG, '正在断开中,等待断开完成');
  171. if (_disconnectCompleter != null) {
  172. await _disconnectCompleter!.future;
  173. }
  174. }
  175. if (state != ConnectionState.disconnected) {
  176. await _performDisconnect();
  177. // 延迟300ms
  178. await Future.delayed(const Duration(milliseconds: 300));
  179. log(TAG, 'selectLocationConnect disconnected = $state');
  180. state = ConnectionState.connectingVirtual;
  181. _isVpnStarted = false;
  182. _pendingDisconnect = false;
  183. getDispatchInfo();
  184. } else {
  185. handleConnection();
  186. }
  187. }
  188. Future<void> getDispatchInfo() async {
  189. // 如果正在请求中,取消当前请求
  190. if (_cancelToken != null) {
  191. log(TAG, '取消当前请求,重新发起新请求');
  192. _cancelToken?.cancel('取消旧请求,发起新请求');
  193. }
  194. // 创建新的 CancelToken
  195. final currentToken = CancelToken();
  196. _cancelToken = currentToken;
  197. // 创建一条加速日志
  198. await createBoostLog();
  199. try {
  200. final locationId = IXSP.getSelectedLocation()?['id'];
  201. final locationCode = IXSP.getSelectedLocation()?['code'];
  202. final launch = await _apiController.getDispatchInfo(
  203. locationId,
  204. locationCode,
  205. cancelToken: currentToken,
  206. );
  207. // 只有当前 token 没有被替换时才清空
  208. if (_cancelToken == currentToken) {
  209. _cancelToken = null;
  210. }
  211. final remainTime = launch.userConfig?.remainTime ?? 0;
  212. if (remainTime <= 0) {
  213. _onVpnError(Errors.ERROR_REMAIN_TIME, Strings.remainTimeEnded.tr);
  214. return;
  215. }
  216. if (state == ConnectionState.connectingVirtual) {
  217. // 标记 VPN 逻辑已开启
  218. _isVpnStarted = true;
  219. final sessionId = Uuid().v4();
  220. final socksPort = launch.nodesConfig!.socketPort!;
  221. final tunnelConfig = launch.nodesConfig!.tunnelConfig!;
  222. final configJson = jsonEncode(launch.nodesConfig!);
  223. // 根据分流隧道设置获取 allowVpnApps 和 disallowVpnApps
  224. final splitTunnelingApps = _getSplitTunnelingApps();
  225. final allowVpnApps = splitTunnelingApps['allowVpnApps']!;
  226. final disallowVpnApps = splitTunnelingApps['disallowVpnApps']!;
  227. final accessToken = launch.userConfig?.accessToken ?? '';
  228. final peekTimeInterval = launch.appConfig?.peekTimeInterval ?? 600;
  229. final aesKey = Keys.aesKey;
  230. final aesIv = Keys.aesIv;
  231. final baseUrls = ApiDomains.instance.getAllLogUrls();
  232. final params = jsonEncode(_apiController.fp);
  233. BaseCoreApi().connect(
  234. sessionId,
  235. socksPort,
  236. tunnelConfig,
  237. configJson,
  238. remainTime,
  239. false,
  240. allowVpnApps,
  241. disallowVpnApps,
  242. accessToken,
  243. aesKey,
  244. aesIv,
  245. locationId,
  246. locationCode,
  247. baseUrls,
  248. params,
  249. peekTimeInterval,
  250. );
  251. }
  252. } on DioException catch (e, s) {
  253. // 只有当前 token 没有被替换时才清空
  254. if (_cancelToken == currentToken) {
  255. _cancelToken = null;
  256. }
  257. // 如果是取消错误,不处理
  258. if (e.type == DioExceptionType.cancel) {
  259. log(TAG, '请求已取消');
  260. return;
  261. }
  262. if (state == ConnectionState.connectingVirtual) {
  263. state = ConnectionState.disconnected;
  264. }
  265. handleErrorDialog(e, s);
  266. log(TAG, 'getDispatchInfo error: $e');
  267. } catch (e, s) {
  268. // 只有当前 token 没有被替换时才清空
  269. if (_cancelToken == currentToken) {
  270. _cancelToken = null;
  271. }
  272. if (state == ConnectionState.connectingVirtual) {
  273. state = ConnectionState.disconnected;
  274. }
  275. handleErrorDialog(e, s);
  276. log(TAG, 'getDispatchInfo error: $e');
  277. }
  278. }
  279. /// 根据分流隧道设置获取 allowVpnApps 和 disallowVpnApps
  280. /// 直接从 IXSP 读取,参考 SplittunnelingController 的逻辑
  281. Map<String, List<String>> _getSplitTunnelingApps() {
  282. List<String> allowVpnApps = [];
  283. List<String> disallowVpnApps = [];
  284. try {
  285. // 读取选中的模式
  286. final modeString = IXSP.getString(SPKeys.splittunnelingSelectedMode);
  287. if (modeString == null) {
  288. log(TAG, '分流隧道未设置模式');
  289. return {
  290. 'allowVpnApps': allowVpnApps,
  291. 'disallowVpnApps': disallowVpnApps,
  292. };
  293. }
  294. // 判断模式类型
  295. final isExcludeMode = modeString.contains('exclude');
  296. final isIncludeMode = modeString.contains('include');
  297. if (!isExcludeMode && !isIncludeMode) {
  298. log(TAG, '分流隧道模式为 none');
  299. return {
  300. 'allowVpnApps': allowVpnApps,
  301. 'disallowVpnApps': disallowVpnApps,
  302. };
  303. }
  304. // 根据模式获取对应的应用列表
  305. final key = isExcludeMode
  306. ? SPKeys.splittunnelingExcludeSelectedApps
  307. : SPKeys.splittunnelingIncludeSelectedApps;
  308. final selectedAppsJson = IXSP.getString(key);
  309. if (selectedAppsJson != null) {
  310. final selectedPackageNames =
  311. (jsonDecode(selectedAppsJson) as List<dynamic>).cast<String>();
  312. if (isIncludeMode) {
  313. // include 模式:只有选中的应用走 VPN
  314. allowVpnApps = selectedPackageNames;
  315. log(TAG, '分流隧道 include 模式,允许的应用: $allowVpnApps');
  316. } else {
  317. // exclude 模式:选中的应用不走 VPN
  318. disallowVpnApps = selectedPackageNames;
  319. log(TAG, '分流隧道 exclude 模式,排除的应用: $disallowVpnApps');
  320. }
  321. }
  322. } catch (e) {
  323. log(TAG, '获取分流隧道设置失败: $e');
  324. }
  325. return {'allowVpnApps': allowVpnApps, 'disallowVpnApps': disallowVpnApps};
  326. }
  327. /// 开始监听来自 Android 的事件
  328. void _startListeningToEvents() {
  329. _eventSubscription = onEventChange().listen(
  330. _handleEventChange,
  331. onError: (error) {
  332. log(TAG, '事件流错误: $error');
  333. },
  334. );
  335. }
  336. // 处理从原生端接收到的消息
  337. void _handleEventChange(String message) {
  338. try {
  339. final Map<String, dynamic> json = jsonDecode(message);
  340. final String type = json['type'] ?? '';
  341. switch (type) {
  342. case 'vpn_status':
  343. _handleVpnStatus(VpnStatusMessage.fromJson(json));
  344. break;
  345. case 'timer_update':
  346. _handleTimerUpdate(TimerUpdateMessage.fromJson(json));
  347. break;
  348. case 'boost_result':
  349. _handleBoostResult(BoostResultMessage.fromJson(json));
  350. break;
  351. default:
  352. log(TAG, '未知消息类型: $type');
  353. }
  354. } catch (e) {
  355. log(TAG, '解析消息失败: $e');
  356. }
  357. }
  358. void _handleVpnStatus(VpnStatusMessage message) {
  359. final vpnError = VpnStatus.fromValue(message.status);
  360. log(
  361. TAG,
  362. 'VPN状态变化: ${vpnError.label}, status=${message.status}, code=${message.code}, message=${message.message}',
  363. );
  364. // 根据状态码处理不同的VPN状态
  365. switch (vpnError) {
  366. case VpnStatus.idle:
  367. // disconnected
  368. _onVpnDisconnected();
  369. break;
  370. case VpnStatus.connecting:
  371. // connecting
  372. _onVpnConnecting();
  373. break;
  374. case VpnStatus.connected:
  375. // connected
  376. _onVpnConnected();
  377. break;
  378. case VpnStatus.error:
  379. // error
  380. _onVpnError(message.code, message.message);
  381. break;
  382. }
  383. }
  384. void _handleTimerUpdate(TimerUpdateMessage message) {
  385. log(TAG, '计时更新: time=${message.currentTime}, mode=${message.mode}');
  386. timer = _formatTime(message.currentTime);
  387. }
  388. void _handleBoostResult(BoostResultMessage message) async {
  389. log(
  390. TAG,
  391. '加速结果: locationCode=${message.locationCode}, nodeId=${message.nodeId}, success=${message.success}',
  392. );
  393. if (message.success) {
  394. try {
  395. await _apiController.connected({
  396. 'locationCode': message.locationCode,
  397. 'instanceId': message.nodeId,
  398. });
  399. } catch (e) {
  400. log('handleRouterConnected error: $e');
  401. }
  402. }
  403. try {
  404. final json = jsonDecode(message.param);
  405. await _apiController.uploadLogs(json);
  406. } catch (e) {
  407. log('handleBoostResult error: $e');
  408. }
  409. }
  410. // VPN状态处理方法
  411. void _onVpnDisconnected() {
  412. log(TAG, 'VPN已断开连接');
  413. // 完成断开 Completer
  414. if (_disconnectCompleter != null && !_disconnectCompleter!.isCompleted) {
  415. _disconnectCompleter!.complete();
  416. }
  417. _disconnectCompleter = null;
  418. // 重置标志位
  419. _isVpnStarted = false;
  420. _pendingDisconnect = false;
  421. // 更新UI状态
  422. _uninitState();
  423. // 上传 Boost 日志(不阻塞)
  424. _apiController.uploadBoostLog();
  425. // FeedbackBottomSheet.show();
  426. }
  427. void _onVpnConnecting() {
  428. log(TAG, 'VPN正在连接');
  429. // 检查是否有待处理的断开请求
  430. if (_pendingDisconnect) {
  431. log(TAG, '检测到待处理的断开请求,执行断开');
  432. _pendingDisconnect = false;
  433. _performDisconnect();
  434. return;
  435. }
  436. // 显示连接中状态
  437. state = ConnectionState.connecting;
  438. }
  439. void _onVpnConnected() {
  440. log(TAG, 'VPN已连接');
  441. // 显示已连接状态
  442. state = ConnectionState.connected;
  443. HapticFeedbackManager.connectionSuccess();
  444. }
  445. void _onVpnError(int code, String message) {
  446. log(TAG, 'VPN连接错误: code=$code, message=$message');
  447. // 完成断开 Completer(如果有)
  448. if (_disconnectCompleter != null && !_disconnectCompleter!.isCompleted) {
  449. _disconnectCompleter!.complete();
  450. }
  451. _disconnectCompleter = null;
  452. // 重置标志位
  453. _isVpnStarted = false;
  454. _pendingDisconnect = false;
  455. // 显示错误信息
  456. _uninitState();
  457. showErrorDialog(code, message);
  458. // 上传 Boost 日志(不阻塞)
  459. _apiController.uploadBoostLog();
  460. }
  461. void showErrorDialog(int code, String message) {
  462. var errorMessage = message;
  463. switch (code) {
  464. case Errors.ERROR_NODE_TIMEOUT:
  465. errorMessage = Strings.vpnConnectionTimeoutError.tr;
  466. break;
  467. case Errors.ERROR_NO_NODE:
  468. errorMessage = Strings.vpnNoNodeError.tr;
  469. break;
  470. case Errors.ERROR_INIT:
  471. errorMessage = Strings.vpnInitError.tr;
  472. break;
  473. case Errors.ERROR_KILL:
  474. errorMessage = Strings.vpnKillError.tr;
  475. break;
  476. case Errors.ERROR_REVOKE:
  477. errorMessage = Strings.vpnRevokeError.tr;
  478. break;
  479. case Errors.ERROR_SERVICE_EMPTY:
  480. errorMessage = Strings.vpnServiceEmptyError.tr;
  481. break;
  482. case Errors.ERROR_ROUTER:
  483. errorMessage = Strings.vpnRouterError.tr;
  484. break;
  485. case Errors.ERROR_PERMISSION_DENIED:
  486. errorMessage = Strings.vpnPermissionDeniedError.tr;
  487. break;
  488. case Errors.ERROR_REMAIN_TIME:
  489. errorMessage = Strings.remainTimeEnded.tr;
  490. break;
  491. }
  492. if (errorMessage.isNotEmpty) {
  493. ErrorDialog.show(message: errorMessage);
  494. }
  495. }
  496. void _uninitState() {
  497. if (state != ConnectionState.disconnected) {
  498. state = ConnectionState.disconnected;
  499. timer = "00:00:00";
  500. HapticFeedbackManager.connectionDisconnected();
  501. }
  502. }
  503. // 格式化时间显示
  504. String _formatTime(int timeMs) {
  505. final totalSeconds = (timeMs / 1000).abs().round();
  506. final hours = totalSeconds ~/ 3600;
  507. final minutes = (totalSeconds % 3600) ~/ 60;
  508. final seconds = totalSeconds % 60;
  509. return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
  510. }
  511. void handleSnackBarError(dynamic error, StackTrace stackTrace) {
  512. if (error is ApiException) {
  513. IXSnackBar.showIXErrorSnackBar(
  514. title: Strings.error.tr,
  515. message: error.message,
  516. );
  517. } else if (error is Failure) {
  518. IXSnackBar.showIXErrorSnackBar(
  519. title: Strings.error.tr,
  520. message: error.message ?? Strings.unknownError.tr,
  521. );
  522. } else if (error is DioException) {
  523. switch (error.type) {
  524. case DioExceptionType.connectionError:
  525. case DioExceptionType.connectionTimeout:
  526. case DioExceptionType.receiveTimeout:
  527. case DioExceptionType.sendTimeout:
  528. IXSnackBar.showIXErrorSnackBar(
  529. title: Strings.error.tr,
  530. message: Strings.unableToConnectNetwork.tr,
  531. );
  532. break;
  533. default:
  534. IXSnackBar.showIXErrorSnackBar(
  535. title: Strings.error.tr,
  536. message: Strings.unableToConnectServer.tr,
  537. );
  538. }
  539. } else {
  540. IXSnackBar.showIXErrorSnackBar(
  541. title: Strings.error.tr,
  542. message: error.toString(),
  543. );
  544. }
  545. }
  546. void handleErrorDialog(dynamic error, StackTrace stackTrace) {
  547. if (error is ApiException) {
  548. ErrorDialog.show(title: Strings.error.tr, message: error.message);
  549. } else if (error is Failure) {
  550. ErrorDialog.show(
  551. title: Strings.error.tr,
  552. message: error.message ?? Strings.unknownError.tr,
  553. );
  554. } else if (error is DioException) {
  555. switch (error.type) {
  556. case DioExceptionType.connectionError:
  557. case DioExceptionType.connectionTimeout:
  558. case DioExceptionType.receiveTimeout:
  559. case DioExceptionType.sendTimeout:
  560. ErrorDialog.show(
  561. title: Strings.error.tr,
  562. message: Strings.unableToConnectNetwork.tr,
  563. );
  564. break;
  565. default:
  566. ErrorDialog.show(
  567. title: Strings.error.tr,
  568. message: Strings.unableToConnectServer.tr,
  569. );
  570. }
  571. } else {
  572. ErrorDialog.show(title: Strings.error.tr, message: error.toString());
  573. }
  574. }
  575. // 创建一条加速日志
  576. Future<void> createBoostLog() async {
  577. await initLog();
  578. await setSessionInfoLog();
  579. await setTargetInfoLog();
  580. }
  581. // 初始化日志
  582. Future<void> initLog() async {
  583. await BoostReportManager().init();
  584. }
  585. // 读取历史日志
  586. Future<void> readHistoryLog() async {
  587. await BoostReportManager().readHistoryLog();
  588. }
  589. // 初始化会话日志
  590. Future<void> setSessionInfoLog() async {
  591. final deviceInfoPlugin = DeviceInfoPlugin();
  592. final appVersion = await PackageInfo.fromPlatform().then(
  593. (value) => value.version,
  594. );
  595. final networkType = await NetworkHelper.instance.getNetworkType();
  596. Map<String, String> deviceInfo = {};
  597. if (Platform.isIOS) {
  598. final iosOsInfo = await deviceInfoPlugin.iosInfo;
  599. deviceInfo = {
  600. 'deviceModel': iosOsInfo.model,
  601. 'osVersion': iosOsInfo.systemVersion,
  602. 'appVersion': appVersion,
  603. 'networkType': networkType,
  604. 'deviceBrand': iosOsInfo.utsname.machine,
  605. };
  606. } else if (Platform.isAndroid) {
  607. final androidOsInfo = await deviceInfoPlugin.androidInfo;
  608. deviceInfo = {
  609. 'deviceModel': androidOsInfo.model,
  610. 'osVersion': androidOsInfo.version.release,
  611. 'appVersion': appVersion,
  612. 'networkType': networkType,
  613. 'deviceBrand': androidOsInfo.brand,
  614. };
  615. }
  616. final boostSessionId = Uuid().v4();
  617. await BoostReportManager().initSessionInfo(
  618. appSessionId: _globalUuid,
  619. boostSessionId: boostSessionId,
  620. deviceInfo: deviceInfo,
  621. );
  622. }
  623. // 初始化目标信息
  624. Future<void> setTargetInfoLog() async {
  625. await BoostReportManager().addTargetInfo(
  626. locationSelectionType: locationSelectionType,
  627. location: IXSP.getSelectedLocation(),
  628. );
  629. }
  630. }