hi-client/lib/app/services/kr_device_info_service.dart

503 lines
17 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_udid/flutter_udid.dart';
import 'package:flutter_keychain/flutter_keychain.dart';
import '../utils/kr_secure_storage.dart';
import '../utils/kr_log_util.dart';
import 'package:crypto/crypto.dart';
import 'dart:convert';
/// 设备信息服务
/// 用于获取设备唯一标识和其他设备信息
class KRDeviceInfoService {
static final KRDeviceInfoService _instance = KRDeviceInfoService._internal();
factory KRDeviceInfoService() => _instance;
KRDeviceInfoService._internal();
final DeviceInfoPlugin _deviceInfo = DeviceInfoPlugin();
String? _deviceId;
Map<String, dynamic>? _deviceDetails;
// 获取设备唯一标识
String? get deviceId => _deviceId;
// 获取设备详细信息
Map<String, dynamic>? get deviceDetails => _deviceDetails;
/// 初始化设备信息
Future<void> initialize() async {
try {
if (kDebugMode) {
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
print('📱 开始初始化设备信息服务');
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
}
KRLogUtil.kr_i('📱 开始初始化设备信息', tag: 'KRDeviceInfoService');
_deviceId = await _getDeviceId();
_deviceDetails = await _getDeviceDetails();
if (kDebugMode) {
print('✅ 设备信息初始化成功');
print('📱 设备ID: $_deviceId');
print('📱 设备平台: ${getPlatformName()}');
print('📱 设备型号: ${getDeviceModel()}');
print('📱 系统版本: ${getOSVersion()}');
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
}
KRLogUtil.kr_i('✅ 设备信息初始化成功', tag: 'KRDeviceInfoService');
KRLogUtil.kr_i('📱 设备ID - $_deviceId', tag: 'KRDeviceInfoService');
KRLogUtil.kr_i('📱 设备详情 - $_deviceDetails', tag: 'KRDeviceInfoService');
} catch (e) {
if (kDebugMode) {
print('❌ 设备信息初始化失败: $e');
}
KRLogUtil.kr_e('❌ 设备信息初始化失败 - $e', tag: 'KRDeviceInfoService');
}
}
/// 获取设备唯一标识
/// 使用多因子组合策略确保唯一性和稳定性
Future<String> _getDeviceId() async {
try {
String identifier;
if (Platform.isAndroid) {
identifier = await _getAndroidDeviceId();
} else if (Platform.isIOS) {
identifier = await _getIOSDeviceId();
} else if (Platform.isMacOS) {
identifier = await _getMacOSDeviceId();
} else if (Platform.isWindows) {
identifier = await _getWindowsDeviceId();
} else if (Platform.isLinux) {
identifier = await _getLinuxDeviceId();
} else {
// Web或其他平台,使用生成的UUID
identifier = await _getOrCreateStoredDeviceId();
}
// 如果获取失败,使用存储的或生成新的ID
if (identifier.isEmpty) {
identifier = await _getOrCreateStoredDeviceId();
}
return identifier;
} catch (e) {
if (kDebugMode) {
print('❌ 获取设备ID失败: $e');
}
KRLogUtil.kr_e('❌ 获取设备ID失败 - $e', tag: 'KRDeviceInfoService');
// 如果获取失败,返回存储的或生成新的ID
return await _getOrCreateStoredDeviceId();
}
}
/// Android设备ID - 多因子组合
/// 组合: AndroidID + 设备型号 + 主板信息 + 硬件信息
Future<String> _getAndroidDeviceId() async {
try {
final androidInfo = await _deviceInfo.androidInfo;
// 优先使用 flutter_udid (封装了多种Android标识获取方式)
String udid = await FlutterUdid.consistentUdid;
// 构建多因子字符串
final factors = [
udid,
androidInfo.id, // Android ID
androidInfo.board, // 主板
androidInfo.bootloader, // Bootloader
androidInfo.brand, // 品牌
androidInfo.device, // 设备名
androidInfo.fingerprint, // 系统指纹
androidInfo.hardware, // 硬件名
androidInfo.manufacturer, // 制造商
androidInfo.model, // 型号
androidInfo.product, // 产品名
];
// 过滤空值并组合
final combined = factors
.where((f) => f != null && f.isNotEmpty)
.join('|');
// 生成SHA256哈希
final bytes = utf8.encode(combined);
final hash = sha256.convert(bytes);
if (kDebugMode) {
print('📱 Android多因子ID生成 - 因子数: ${factors.where((f) => f != null && f.isNotEmpty).length}');
}
KRLogUtil.kr_i('📱 Android多因子ID - $hash', tag: 'KRDeviceInfoService');
return hash.toString();
} catch (e) {
if (kDebugMode) {
print('❌ Android设备ID获取失败: $e');
}
KRLogUtil.kr_e('❌ Android设备ID获取失败 - $e', tag: 'KRDeviceInfoService');
return '';
}
}
/// iOS设备ID - 优先使用Keychain存储的UUID卸载重装后不变
/// iOS的identifierForVendor在所有同供应商应用卸载后会重置
/// 使用flutter_keychain存储到iOS Keychain保证即使卸载重装设备ID也不会变化
Future<String> _getIOSDeviceId() async {
try {
const keychainKey = 'kr_ios_device_persistent_id';
// 1. 优先从iOS Keychain读取已存储的设备ID卸载后依然存在
String? storedId = await FlutterKeychain.get(key: keychainKey);
if (storedId != null && storedId.isNotEmpty) {
if (kDebugMode) {
print('📱 iOS: 从Keychain读取已存储的设备ID');
}
KRLogUtil.kr_i('📱 iOS: 使用Keychain中的持久化设备ID', tag: 'KRDeviceInfoService');
return storedId;
}
// 2. 如果Keychain中没有生成新的设备ID
final iosInfo = await _deviceInfo.iosInfo;
// 使用硬件级别的信息生成唯一标识(作为备用方案)
String udid = await FlutterUdid.consistentUdid;
// 构建多因子字符串不使用会变化的identifierForVendor作为主要因子
final factors = [
udid,
iosInfo.utsname.machine, // 硬件机器类型(如"iPhone14,3"
iosInfo.model, // 型号
iosInfo.systemName, // 系统名
// 移除: iosInfo.identifierForVendor - 因为它会在卸载后变化
// 移除: iosInfo.name - 用户可以修改设备名
// 移除: iosInfo.systemVersion - 系统升级会变化
];
final combined = factors
.where((f) => f.isNotEmpty)
.join('|');
final bytes = utf8.encode(combined);
final hash = sha256.convert(bytes);
final newDeviceId = hash.toString();
// 3. 保存到iOS Keychain即使应用卸载也会保留
await FlutterKeychain.put(key: keychainKey, value: newDeviceId);
if (kDebugMode) {
print('📱 iOS: 生成新设备ID并保存到Keychain - 因子数: ${factors.where((f) => f.isNotEmpty).length}');
}
KRLogUtil.kr_i('📱 iOS: 生成并持久化新设备ID到Keychain', tag: 'KRDeviceInfoService');
return newDeviceId;
} catch (e) {
if (kDebugMode) {
print('❌ iOS设备ID获取失败: $e');
}
KRLogUtil.kr_e('❌ iOS设备ID获取失败 - $e', tag: 'KRDeviceInfoService');
return '';
}
}
/// macOS设备ID - 使用硬件UUID
Future<String> _getMacOSDeviceId() async {
try {
final macInfo = await _deviceInfo.macOsInfo;
// 优先使用 flutter_udid
String udid = await FlutterUdid.consistentUdid;
// 构建多因子字符串
final factors = [
udid,
macInfo.systemGUID ?? '', // 系统GUID (最稳定)
macInfo.model, // 型号
macInfo.hostName, // 主机名
macInfo.arch, // 架构
macInfo.kernelVersion, // 内核版本
];
final combined = factors
.where((f) => f.isNotEmpty)
.join('|');
final bytes = utf8.encode(combined);
final hash = sha256.convert(bytes);
if (kDebugMode) {
print('📱 macOS多因子ID生成 - 因子数: ${factors.where((f) => f.isNotEmpty).length}');
}
KRLogUtil.kr_i('📱 macOS多因子ID - $hash', tag: 'KRDeviceInfoService');
return hash.toString();
} catch (e) {
if (kDebugMode) {
print('❌ macOS设备ID获取失败: $e');
}
KRLogUtil.kr_e('❌ macOS设备ID获取失败 - $e', tag: 'KRDeviceInfoService');
return '';
}
}
/// Windows设备ID - 使用机器GUID
/// 🔧 修复:不使用 flutter_udid因为它会调用 wmic 命令弹出黑窗口
Future<String> _getWindowsDeviceId() async {
try {
final windowsInfo = await _deviceInfo.windowsInfo;
// 🔧 修复:不使用 FlutterUdid.consistentUdid
// 因为它在 Windows 上会调用 "cmd.exe /c wmic csproduct get UUID"
// 这个调用没有使用 CREATE_NO_WINDOW 标志,会弹出黑色命令行窗口
// 直接使用 device_info_plus 提供的信息构建唯一标识
// windowsInfo.deviceId 已经是一个稳定的设备标识
// 构建多因子字符串
final factors = [
windowsInfo.deviceId, // 设备ID (最稳定,来自注册表 MachineGuid)
windowsInfo.computerName, // 计算机名
windowsInfo.productName, // 产品名
windowsInfo.numberOfCores.toString(), // CPU核心数
windowsInfo.systemMemoryInMegabytes.toString(), // 内存大小
];
final combined = factors
.where((f) => f.isNotEmpty)
.join('|');
final bytes = utf8.encode(combined);
final hash = sha256.convert(bytes);
if (kDebugMode) {
print('📱 Windows多因子ID生成 - 因子数: ${factors.where((f) => f.isNotEmpty).length}');
}
KRLogUtil.kr_i('📱 Windows多因子ID - $hash', tag: 'KRDeviceInfoService');
return hash.toString();
} catch (e) {
if (kDebugMode) {
print('❌ Windows设备ID获取失败: $e');
}
KRLogUtil.kr_e('❌ Windows设备ID获取失败 - $e', tag: 'KRDeviceInfoService');
return '';
}
}
/// Linux设备ID - 使用machine-id
Future<String> _getLinuxDeviceId() async {
try {
final linuxInfo = await _deviceInfo.linuxInfo;
// 优先使用 flutter_udid
String udid = await FlutterUdid.consistentUdid;
// 构建多因子字符串
final factors = [
udid,
linuxInfo.machineId ?? '', // Machine ID (最稳定)
linuxInfo.id, // 发行版ID
linuxInfo.name, // 发行版名称
linuxInfo.version ?? '', // 版本
linuxInfo.variant ?? '', // 变体
];
final combined = factors
.where((f) => f.isNotEmpty)
.join('|');
final bytes = utf8.encode(combined);
final hash = sha256.convert(bytes);
if (kDebugMode) {
print('📱 Linux多因子ID生成 - 因子数: ${factors.where((f) => f.isNotEmpty).length}');
}
KRLogUtil.kr_i('📱 Linux多因子ID - $hash', tag: 'KRDeviceInfoService');
return hash.toString();
} catch (e) {
if (kDebugMode) {
print('❌ Linux设备ID获取失败: $e');
}
KRLogUtil.kr_e('❌ Linux设备ID获取失败 - $e', tag: 'KRDeviceInfoService');
return '';
}
}
/// 获取或创建存储的设备ID
Future<String> _getOrCreateStoredDeviceId() async {
try {
const key = 'kr_device_unique_id';
final storage = KRSecureStorage();
String? storedId = await storage.kr_readData(key: key);
if (storedId == null || storedId.isEmpty) {
// 生成新的UUID
storedId = _generateUniqueId();
await storage.kr_saveData(key: key, value: storedId);
if (kDebugMode) {
print('📱 生成新的设备ID: $storedId');
}
KRLogUtil.kr_i('📱 生成新的设备ID - $storedId', tag: 'KRDeviceInfoService');
} else {
if (kDebugMode) {
print('📱 使用存储的设备ID: $storedId');
}
KRLogUtil.kr_i('📱 使用存储的设备ID - $storedId', tag: 'KRDeviceInfoService');
}
return storedId;
} catch (e) {
if (kDebugMode) {
print('❌ 获取存储的设备ID失败: $e');
}
KRLogUtil.kr_e('❌ 获取存储的设备ID失败 - $e', tag: 'KRDeviceInfoService');
return _generateUniqueId();
}
}
/// 生成唯一ID
String _generateUniqueId() {
final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
final random = DateTime.now().microsecondsSinceEpoch.toString();
final combined = '$timestamp-$random';
// 使用MD5生成唯一标识
final bytes = utf8.encode(combined);
final digest = md5.convert(bytes);
return digest.toString();
}
/// 获取设备详细信息
Future<Map<String, dynamic>> _getDeviceDetails() async {
try {
if (Platform.isAndroid) {
final androidInfo = await _deviceInfo.androidInfo;
return {
'platform': 'android',
'device': androidInfo.device,
'model': androidInfo.model,
'brand': androidInfo.brand,
'manufacturer': androidInfo.manufacturer,
'androidId': androidInfo.id,
'version': androidInfo.version.release,
'sdkInt': androidInfo.version.sdkInt,
};
} else if (Platform.isIOS) {
final iosInfo = await _deviceInfo.iosInfo;
return {
'platform': 'ios',
'name': iosInfo.name,
'model': iosInfo.model,
'systemName': iosInfo.systemName,
'systemVersion': iosInfo.systemVersion,
'identifierForVendor': iosInfo.identifierForVendor,
'isPhysicalDevice': iosInfo.isPhysicalDevice,
};
} else if (Platform.isMacOS) {
final macInfo = await _deviceInfo.macOsInfo;
return {
'platform': 'macos',
'computerName': macInfo.computerName,
'model': macInfo.model,
'hostName': macInfo.hostName,
'arch': macInfo.arch,
'systemGUID': macInfo.systemGUID,
};
} else if (Platform.isWindows) {
final windowsInfo = await _deviceInfo.windowsInfo;
return {
'platform': 'windows',
'computerName': windowsInfo.computerName,
'numberOfCores': windowsInfo.numberOfCores,
'systemMemoryInMegabytes': windowsInfo.systemMemoryInMegabytes,
};
} else if (Platform.isLinux) {
final linuxInfo = await _deviceInfo.linuxInfo;
return {
'platform': 'linux',
'name': linuxInfo.name,
'version': linuxInfo.version,
'id': linuxInfo.id,
'machineId': linuxInfo.machineId,
};
} else if (kIsWeb) {
final webInfo = await _deviceInfo.webBrowserInfo;
return {
'platform': 'web',
'browserName': webInfo.browserName.toString(),
'userAgent': webInfo.userAgent,
'vendor': webInfo.vendor,
};
}
return {
'platform': 'unknown',
};
} catch (e) {
if (kDebugMode) {
print('❌ 获取设备详情失败: $e');
}
KRLogUtil.kr_e('❌ 获取设备详情失败 - $e', tag: 'KRDeviceInfoService');
return {
'platform': 'unknown',
'error': e.toString(),
};
}
}
/// 获取平台名称
String getPlatformName() {
if (Platform.isAndroid) return 'Android';
if (Platform.isIOS) return 'iOS';
if (Platform.isMacOS) return 'macOS';
if (Platform.isWindows) return 'Windows';
if (Platform.isLinux) return 'Linux';
if (kIsWeb) return 'Web';
return 'Unknown';
}
/// 获取设备型号
String getDeviceModel() {
if (_deviceDetails == null) return 'Unknown';
if (Platform.isAndroid) {
return '${_deviceDetails!['brand']} ${_deviceDetails!['model']}';
} else if (Platform.isIOS) {
return _deviceDetails!['model'] ?? 'Unknown';
} else if (Platform.isMacOS) {
return _deviceDetails!['model'] ?? 'Unknown';
}
return 'Unknown';
}
/// 获取操作系统版本
String getOSVersion() {
if (_deviceDetails == null) return 'Unknown';
if (Platform.isAndroid) {
return _deviceDetails!['version'] ?? 'Unknown';
} else if (Platform.isIOS) {
return _deviceDetails!['systemVersion'] ?? 'Unknown';
}
return 'Unknown';
}
/// 获取User-Agent信息
String getUserAgent() {
final platform = getPlatformName();
final model = getDeviceModel();
final osVersion = getOSVersion();
return 'HiVPN/1.0.0 ($platform; $model; $osVersion) Flutter';
}
}