import 'dart:math'; import 'package:fl_clash/common/boot_record.dart'; import 'package:fl_clash/common/preferences.dart'; import 'package:fl_clash/common/print.dart'; import 'package:fl_clash/common/system.dart'; import 'package:fl_clash/enum/enum.dart'; class BootGuard { final bool _supported; final Future Function() _readRecord; final Future Function(BootRecord record) _writeRecord; final Future Function() _readExitInfo; final int Function() _now; BootDecision _decision = const BootDecision(); BootGuard({ bool? supported, Future Function()? readRecord, Future Function(BootRecord record)? writeRecord, Future Function()? readExitInfo, int Function()? now, }) : _supported = supported ?? system.isAndroid, _readRecord = readRecord ?? preferences.getBootRecord, _writeRecord = writeRecord ?? preferences.saveBootRecord, _readExitInfo = readExitInfo ?? system.lastExitInfo, _now = now ?? _currentMilliseconds; static int _currentMilliseconds() => DateTime.now().millisecondsSinceEpoch; BootDecision get decision => _decision; Future evaluate({required int? profileId}) async { if (!_supported) { return _decision; } final record = await _readRecord(); final exitInfo = await _readExitInfo(); final decision = resolveBootDecision(record: record, exitInfo: exitInfo); if (decision.isDegraded) { commonPrint.log( 'Previous launch did not finish: $decision', logLevel: LogLevel.warning, ); } await _writeRecord( BootRecord( stage: BootStage.starting, profileId: decision.recovery == BootRecovery.clearProfile ? null : profileId, startedAt: _now(), failureCount: decision.failureCount, lastFailedProfileId: decision.failedProfileId ?? record?.lastFailedProfileId, handledExitAt: max( record?.handledExitAt ?? 0, exitInfo?.timestamp ?? 0, ), ), ); _decision = decision; return decision; } Future markRunning() async { if (!_supported) { return; } final record = await _readRecord(); if (record == null) { return; } await _writeRecord( BootRecord( stage: BootStage.running, profileId: record.profileId, startedAt: record.startedAt, failureCount: _decision.isDegraded ? record.failureCount : 0, lastFailedProfileId: record.lastFailedProfileId, handledExitAt: record.handledExitAt, ), ); } Future markClosed() async { if (!_supported) { return; } final record = await _readRecord(); if (record == null) { return; } await _writeRecord( BootRecord( profileId: record.profileId, startedAt: record.startedAt, lastFailedProfileId: record.lastFailedProfileId, handledExitAt: record.handledExitAt, ), ); } } final bootGuard = BootGuard();