From b987c2376a9058151fc6f5d4dbe22cef716d8fd9 Mon Sep 17 00:00:00 2001 From: chen08209 Date: Thu, 31 Jul 2025 17:09:18 +0800 Subject: [PATCH] Add android separates the core process Support core status check and force restart Optimize proxies page and access page Update flutter and pub dependencies --- README.md | 2 +- README_zh_CN.md | 2 +- analysis_options.yaml | 2 + android/app/build.gradle.kts | 37 +- android/app/proguard-rules.pro | 4 +- android/app/src/debug/AndroidManifest.xml | 2 +- android/app/src/main/AndroidManifest.xml | 75 +- .../kotlin/com/follow/clash/Application.kt | 13 + .../com/follow/clash/BroadcastReceiver.kt | 34 + .../src/main/kotlin/com/follow/clash/Ext.kt | 121 + .../com/follow/clash/FlClashApplication.kt | 18 - .../kotlin/com/follow/clash/GlobalState.kt | 125 - .../kotlin/com/follow/clash/MainActivity.kt | 24 +- .../main/kotlin/com/follow/clash/Service.kt | 72 + .../src/main/kotlin/com/follow/clash/State.kt | 148 + .../kotlin/com/follow/clash/TempActivity.kt | 28 +- .../kotlin/com/follow/clash/TileService.kt | 61 + .../kotlin/com/follow/clash/extensions/Ext.kt | 200 - .../kotlin/com/follow/clash/models/Process.kt | 15 - .../kotlin/com/follow/clash/models/Props.kt | 34 - .../kotlin/com/follow/clash/models/State.kt | 8 + .../com/follow/clash/plugins/AppPlugin.kt | 263 +- .../com/follow/clash/plugins/ServicePlugin.kt | 144 +- .../com/follow/clash/plugins/TilePlugin.kt | 14 +- .../com/follow/clash/plugins/VpnPlugin.kt | 276 - .../clash/services/BaseServiceInterface.kt | 96 - .../follow/clash/services/FlClashService.kt | 67 - .../clash/services/FlClashTileService.kt | 77 - .../clash/services/FlClashVpnService.kt | 193 - android/build.gradle.kts | 16 +- android/common/.gitignore | 1 + android/common/build.gradle.kts | 42 + android/common/src/main/AndroidManifest.xml | 9 + .../com/follow/clash/common/Components.kt | 16 + .../java/com/follow/clash/common/Enums.kt | 24 + .../main/java/com/follow/clash/common/Ext.kt | 238 + .../com/follow/clash/common/GlobalState.kt | 34 + .../java/com/follow/clash/common/Service.kt | 72 + .../java/com/follow/clash/common/Utils.kt | 14 + .../src/main/res/values/strings.xml | 2 +- android/core/build.gradle.kts | 52 +- android/core/proguard-rules.pro | 21 - android/core/src/main/AndroidManifest.xml | 1 + android/core/src/main/cpp/CMakeLists.txt | 4 +- android/core/src/main/cpp/core.cpp | 148 +- android/core/src/main/cpp/jni_helper.h | 2 +- .../main/java/com/follow/clash/core/Core.kt | 100 +- .../com/follow/clash/core/InvokeInterface.kt | 8 + android/gradle.properties | 2 - android/gradle/libs.versions.toml | 20 + .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/service/.gitignore | 1 + android/service/build.gradle.kts | 48 + android/service/src/main/AndroidManifest.xml | 49 + .../clash/service/ICallbackInterface.aidl | 6 + .../clash/service/IMessageInterface.aidl | 6 + .../clash/service/IRemoteInterface.aidl | 15 + .../clash/service/models/AccessControl.aidl | 4 + .../service/models/NotificationParams.aidl | 4 + .../clash/service/models/VpnOptions.aidl | 6 + .../com/follow/clash/service/CommonService.kt | 55 + .../follow/clash/service}/FilesProvider.kt | 54 +- .../com/follow/clash/service/IBaseService.kt | 18 + .../com/follow/clash/service/RemoteService.kt | 99 + .../java/com/follow/clash/service/State.kt | 13 + .../com/follow/clash/service/VpnService.kt | 250 + .../service/models/NotificationParams.kt | 11 + .../follow/clash/service/models/Traffic.kt | 19 + .../follow/clash/service/models/VpnOptions.kt | 83 + .../follow/clash/service/modules/Module.kt | 19 + .../clash/service/modules/ModuleLoader.kt | 51 + .../service/modules/NetworkObserveModule.kt | 149 + .../service/modules/NotificationModule.kt | 124 + .../clash/service/modules/SuspendModule.kt | 61 + .../src/main/res/drawable/ic.xml | 0 .../src/main/res/drawable/ic_service.xml | 17 + android/settings.gradle.kts | 8 +- arb/intl_en.arb | 9 +- arb/intl_ja.arb | 9 +- arb/intl_ru.arb | 9 +- arb/intl_zh_CN.arb | 9 +- core/Clash.Meta | 2 +- core/action.go | 27 +- core/android_bride.go | 77 - core/bride.c | 31 + core/bride.go | 35 + core/bride.h | 23 + core/common.go | 22 +- core/constant.go | 7 +- core/dart-bridge/include/dart_api.h | 4185 ------- core/dart-bridge/include/dart_api_dl.c | 59 - core/dart-bridge/include/dart_api_dl.h | 156 - core/dart-bridge/include/dart_native_api.h | 207 - core/dart-bridge/include/dart_tools_api.h | 658 -- core/dart-bridge/include/dart_version.h | 16 - .../include/internal/dart_api_dl_impl.h | 21 - core/dart-bridge/lib.go | 42 - core/dart-bridge/lib_common.go | 7 - core/go.mod | 43 +- core/go.sum | 92 +- core/hub.go | 52 +- core/lib.go | 249 +- core/lib_android.go | 267 - core/lib_no_android.go | 7 - core/platform/procfs.go | 1 - core/state/state.go | 59 - core/tun/tun.go | 62 +- lib/application.dart | 143 +- lib/clash/core.dart | 284 - lib/clash/generated/clash_ffi.dart | 5363 --------- lib/clash/interface.dart | 421 - lib/clash/lib.dart | 292 - lib/clash/message.dart | 55 - lib/clash/service.dart | 160 - lib/common/android.dart | 7 +- lib/common/archive.dart | 11 +- lib/common/cache.dart | 190 + lib/common/common.dart | 2 + lib/common/compute.dart | 114 + lib/common/constant.dart | 6 +- lib/common/function.dart | 34 +- lib/common/future.dart | 37 +- lib/common/http.dart | 2 +- lib/common/mixin.dart | 56 +- lib/common/navigation.dart | 55 +- lib/common/navigator.dart | 85 +- lib/common/num.dart | 16 + lib/common/path.dart | 37 +- lib/common/picker.dart | 7 +- lib/common/preferences.dart | 7 +- lib/common/request.dart | 132 +- lib/common/tray.dart | 5 +- lib/common/utils.dart | 127 +- lib/controller.dart | 519 +- lib/core/controller.dart | 281 + lib/{clash/clash.dart => core/core.dart} | 3 +- lib/core/event.dart | 68 + lib/core/interface.dart | 327 + lib/core/lib.dart | 62 + lib/core/service.dart | 185 + lib/enum/enum.dart | 235 +- lib/l10n/intl/messages_en.dart | 11 + lib/l10n/intl/messages_ja.dart | 9 + lib/l10n/intl/messages_ru.dart | 11 + lib/l10n/intl/messages_zh_CN.dart | 7 + lib/l10n/l10n.dart | 67 +- lib/main.dart | 120 +- lib/manager/android_manager.dart | 44 +- lib/manager/app_manager.dart | 140 +- .../{clash_manager.dart => core_manager.dart} | 74 +- lib/manager/manager.dart | 2 +- lib/manager/message_manager.dart | 94 +- lib/manager/tray_manager.dart | 1 + lib/manager/vpn_manager.dart | 21 +- lib/manager/window_manager.dart | 92 +- lib/models/app.dart | 6 +- lib/models/clash_config.dart | 114 +- lib/models/common.dart | 365 +- lib/models/config.dart | 35 +- lib/models/core.dart | 134 +- lib/models/generated/app.freezed.dart | 962 +- .../generated/clash_config.freezed.dart | 7333 ++++++------- lib/models/generated/clash_config.g.dart | 563 +- lib/models/generated/common.freezed.dart | 9026 +++++++++------ lib/models/generated/common.g.dart | 339 +- lib/models/generated/config.freezed.dart | 5440 ++++----- lib/models/generated/config.g.dart | 314 +- lib/models/generated/core.freezed.dart | 6426 ++++++----- lib/models/generated/core.g.dart | 279 +- lib/models/generated/profile.freezed.dart | 1992 ++-- lib/models/generated/profile.g.dart | 118 +- lib/models/generated/selector.freezed.dart | 9742 ++++++++++------- lib/models/generated/widget.freezed.dart | 1864 ++-- lib/models/profile.dart | 26 +- lib/models/selector.dart | 128 +- lib/models/widget.dart | 10 +- lib/pages/editor.dart | 218 +- lib/pages/home.dart | 84 +- lib/pages/scan.dart | 36 +- lib/plugins/app.dart | 47 +- lib/plugins/service.dart | 107 +- lib/plugins/tile.dart | 3 +- lib/plugins/vpn.dart | 70 - lib/providers/app.dart | 176 +- lib/providers/config.dart | 102 +- lib/providers/generated/app.g.dart | 1675 ++- lib/providers/generated/config.g.dart | 819 +- lib/providers/generated/state.g.dart | 3868 +++---- lib/providers/state.dart | 408 +- lib/state.dart | 198 +- lib/views/about.dart | 126 +- lib/views/access.dart | 345 +- lib/views/application_setting.dart | 95 +- lib/views/backup_and_recovery.dart | 143 +- lib/views/config/config.dart | 120 +- lib/views/config/dns.dart | 350 +- lib/views/config/general.dart | 309 +- lib/views/config/network.dart | 226 +- lib/views/connection/connections.dart | 25 +- lib/views/connection/item.dart | 83 +- lib/views/connection/requests.dart | 94 +- lib/views/dashboard/dashboard.dart | 290 +- lib/views/dashboard/widgets/intranet_ip.dart | 13 +- lib/views/dashboard/widgets/memory_info.dart | 108 +- .../dashboard/widgets/network_detection.dart | 68 +- .../dashboard/widgets/network_speed.dart | 22 +- .../dashboard/widgets/outbound_mode.dart | 153 +- .../dashboard/widgets/quick_options.dart | 104 +- lib/views/dashboard/widgets/start_button.dart | 56 +- .../dashboard/widgets/traffic_usage.dart | 75 +- lib/views/developer.dart | 45 +- lib/views/hotkey.dart | 70 +- lib/views/logs.dart | 99 +- lib/views/profiles/edit_profile.dart | 105 +- lib/views/profiles/override_profile.dart | 698 +- lib/views/profiles/profiles.dart | 138 +- lib/views/profiles/scripts.dart | 217 +- lib/views/proxies/card.dart | 118 +- lib/views/proxies/common.dart | 46 +- lib/views/proxies/list.dart | 466 +- lib/views/proxies/providers.dart | 128 +- lib/views/proxies/proxies.dart | 89 +- lib/views/proxies/setting.dart | 95 +- lib/views/proxies/tab.dart | 219 +- lib/views/resources.dart | 114 +- lib/views/theme.dart | 305 +- lib/views/tools.dart | 23 +- lib/widgets/builder.dart | 7 +- lib/widgets/card.dart | 90 +- lib/widgets/fade_box.dart | 133 +- lib/widgets/icon.dart | 84 +- lib/widgets/input.dart | 171 +- lib/widgets/line_chart.dart | 85 +- lib/widgets/list.dart | 14 +- lib/widgets/palette.dart | 125 +- lib/widgets/pop_scope.dart | 12 +- lib/widgets/popup.dart | 122 +- lib/widgets/scaffold.dart | 103 +- lib/widgets/scroll.dart | 29 +- lib/widgets/subscription_info_view.dart | 4 +- lib/widgets/super_grid.dart | 202 +- macos/Podfile.lock | 8 +- macos/Runner.xcodeproj/project.pbxproj | 10 +- macos/Runner/DebugProfile.entitlements | 2 + macos/Runner/Release.entitlements | 2 + pubspec.lock | 408 +- pubspec.yaml | 37 +- setup.dart | 218 +- 248 files changed, 38107 insertions(+), 43183 deletions(-) create mode 100644 android/app/src/main/kotlin/com/follow/clash/Application.kt create mode 100644 android/app/src/main/kotlin/com/follow/clash/BroadcastReceiver.kt create mode 100644 android/app/src/main/kotlin/com/follow/clash/Ext.kt delete mode 100644 android/app/src/main/kotlin/com/follow/clash/FlClashApplication.kt delete mode 100644 android/app/src/main/kotlin/com/follow/clash/GlobalState.kt create mode 100644 android/app/src/main/kotlin/com/follow/clash/Service.kt create mode 100644 android/app/src/main/kotlin/com/follow/clash/State.kt create mode 100644 android/app/src/main/kotlin/com/follow/clash/TileService.kt delete mode 100644 android/app/src/main/kotlin/com/follow/clash/extensions/Ext.kt delete mode 100644 android/app/src/main/kotlin/com/follow/clash/models/Process.kt delete mode 100644 android/app/src/main/kotlin/com/follow/clash/models/Props.kt create mode 100644 android/app/src/main/kotlin/com/follow/clash/models/State.kt delete mode 100644 android/app/src/main/kotlin/com/follow/clash/plugins/VpnPlugin.kt delete mode 100644 android/app/src/main/kotlin/com/follow/clash/services/BaseServiceInterface.kt delete mode 100644 android/app/src/main/kotlin/com/follow/clash/services/FlClashService.kt delete mode 100644 android/app/src/main/kotlin/com/follow/clash/services/FlClashTileService.kt delete mode 100644 android/app/src/main/kotlin/com/follow/clash/services/FlClashVpnService.kt create mode 100644 android/common/.gitignore create mode 100644 android/common/build.gradle.kts create mode 100644 android/common/src/main/AndroidManifest.xml create mode 100644 android/common/src/main/java/com/follow/clash/common/Components.kt create mode 100644 android/common/src/main/java/com/follow/clash/common/Enums.kt create mode 100644 android/common/src/main/java/com/follow/clash/common/Ext.kt create mode 100644 android/common/src/main/java/com/follow/clash/common/GlobalState.kt create mode 100644 android/common/src/main/java/com/follow/clash/common/Service.kt create mode 100644 android/common/src/main/java/com/follow/clash/common/Utils.kt rename android/{app => common}/src/main/res/values/strings.xml (58%) delete mode 100644 android/core/proguard-rules.pro create mode 100644 android/core/src/main/java/com/follow/clash/core/InvokeInterface.kt create mode 100644 android/gradle/libs.versions.toml create mode 100644 android/service/.gitignore create mode 100644 android/service/build.gradle.kts create mode 100644 android/service/src/main/AndroidManifest.xml create mode 100644 android/service/src/main/aidl/com/follow/clash/service/ICallbackInterface.aidl create mode 100644 android/service/src/main/aidl/com/follow/clash/service/IMessageInterface.aidl create mode 100644 android/service/src/main/aidl/com/follow/clash/service/IRemoteInterface.aidl create mode 100644 android/service/src/main/aidl/com/follow/clash/service/models/AccessControl.aidl create mode 100644 android/service/src/main/aidl/com/follow/clash/service/models/NotificationParams.aidl create mode 100644 android/service/src/main/aidl/com/follow/clash/service/models/VpnOptions.aidl create mode 100644 android/service/src/main/java/com/follow/clash/service/CommonService.kt rename android/{app/src/main/kotlin/com/follow/clash => service/src/main/java/com/follow/clash/service}/FilesProvider.kt (58%) create mode 100644 android/service/src/main/java/com/follow/clash/service/IBaseService.kt create mode 100644 android/service/src/main/java/com/follow/clash/service/RemoteService.kt create mode 100644 android/service/src/main/java/com/follow/clash/service/State.kt create mode 100644 android/service/src/main/java/com/follow/clash/service/VpnService.kt create mode 100644 android/service/src/main/java/com/follow/clash/service/models/NotificationParams.kt create mode 100644 android/service/src/main/java/com/follow/clash/service/models/Traffic.kt create mode 100644 android/service/src/main/java/com/follow/clash/service/models/VpnOptions.kt create mode 100644 android/service/src/main/java/com/follow/clash/service/modules/Module.kt create mode 100644 android/service/src/main/java/com/follow/clash/service/modules/ModuleLoader.kt create mode 100644 android/service/src/main/java/com/follow/clash/service/modules/NetworkObserveModule.kt create mode 100644 android/service/src/main/java/com/follow/clash/service/modules/NotificationModule.kt create mode 100644 android/service/src/main/java/com/follow/clash/service/modules/SuspendModule.kt rename android/{app => service}/src/main/res/drawable/ic.xml (100%) create mode 100644 android/service/src/main/res/drawable/ic_service.xml delete mode 100644 core/android_bride.go create mode 100644 core/bride.c create mode 100644 core/bride.go create mode 100644 core/bride.h delete mode 100644 core/dart-bridge/include/dart_api.h delete mode 100644 core/dart-bridge/include/dart_api_dl.c delete mode 100644 core/dart-bridge/include/dart_api_dl.h delete mode 100644 core/dart-bridge/include/dart_native_api.h delete mode 100644 core/dart-bridge/include/dart_tools_api.h delete mode 100644 core/dart-bridge/include/dart_version.h delete mode 100644 core/dart-bridge/include/internal/dart_api_dl_impl.h delete mode 100644 core/dart-bridge/lib.go delete mode 100644 core/dart-bridge/lib_common.go delete mode 100644 core/lib_android.go delete mode 100644 core/lib_no_android.go delete mode 100644 core/state/state.go delete mode 100644 lib/clash/core.dart delete mode 100644 lib/clash/generated/clash_ffi.dart delete mode 100644 lib/clash/interface.dart delete mode 100644 lib/clash/lib.dart delete mode 100644 lib/clash/message.dart delete mode 100644 lib/clash/service.dart create mode 100644 lib/common/cache.dart create mode 100644 lib/common/compute.dart create mode 100644 lib/core/controller.dart rename lib/{clash/clash.dart => core/core.dart} (56%) create mode 100644 lib/core/event.dart create mode 100644 lib/core/interface.dart create mode 100644 lib/core/lib.dart create mode 100644 lib/core/service.dart rename lib/manager/{clash_manager.dart => core_manager.dart} (57%) delete mode 100644 lib/plugins/vpn.dart mode change 100755 => 100644 macos/Runner.xcodeproj/project.pbxproj diff --git a/README.md b/README.md index 4255f2c..09a650a 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Support the following actions com.follow.clash.action.STOP - com.follow.clash.action.CHANGE + com.follow.clash.action.TOGGLE ``` ## Download diff --git a/README_zh_CN.md b/README_zh_CN.md index 54d60af..f762416 100644 --- a/README_zh_CN.md +++ b/README_zh_CN.md @@ -54,7 +54,7 @@ on Mobile: com.follow.clash.action.STOP - com.follow.clash.action.CHANGE + com.follow.clash.action.TOGGLE ``` ## Download diff --git a/analysis_options.yaml b/analysis_options.yaml index 1bb9e87..5439011 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -2,6 +2,8 @@ include: package:flutter_lints/flutter.yaml analyzer: exclude: - lib/l10n/intl/** + errors: + invalid_annotation_target: ignore linter: rules: diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 337dedf..ec5f22d 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -1,3 +1,4 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget import java.util.Properties plugins { @@ -24,22 +25,20 @@ val isRelease = mStoreFile.exists() android { namespace = "com.follow.clash" - compileSdk = 35 - ndkVersion = "28.0.13004108" + compileSdk = libs.versions.compileSdk.get().toInt() + ndkVersion = libs.versions.ndkVersion.get() + + compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = JavaVersion.VERSION_17.toString() - } - defaultConfig { applicationId = "com.follow.clash" minSdk = flutter.minSdkVersion - targetSdk = flutter.targetSdkVersion + targetSdk = libs.versions.targetSdk.get().toInt() versionCode = flutter.versionCode versionName = flutter.versionName } @@ -55,6 +54,12 @@ android { } } + packaging { + jniLibs { + useLegacyPackaging = true + } + } + buildTypes { debug { isMinifyEnabled = false @@ -63,8 +68,7 @@ android { release { isMinifyEnabled = true - isDebuggable = false - + isShrinkResources = true signingConfig = if (isRelease) { signingConfigs.getByName("release") } else { @@ -79,15 +83,22 @@ android { } } +kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_17) + } +} + flutter { source = "../.." } dependencies { - implementation(project(":core")) - implementation("androidx.core:core-splashscreen:1.0.1") - implementation("com.google.code.gson:gson:2.10.1") - implementation("com.android.tools.smali:smali-dexlib2:3.0.9") { + implementation(project(":service")) + implementation(project(":common")) + implementation(libs.core.splashscreen) + implementation(libs.gson) + implementation(libs.smali.dexlib2) { exclude(group = "com.google.guava", module = "guava") } } \ No newline at end of file diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro index 17e22a6..0b65f1f 100644 --- a/android/app/proguard-rules.pro +++ b/android/app/proguard-rules.pro @@ -1,2 +1,4 @@ --keep class com.follow.clash.models.**{ *; } \ No newline at end of file +-keep class com.follow.clash.models.**{ *; } + +-keep class com.follow.clash.service.models.**{ *; } \ No newline at end of file diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index 8fe3c0a..61ad146 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -9,7 +9,7 @@ android:label="FlClash Debug" tools:replace="android:label"> diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index dad0f94..b1e136b 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,3 +1,4 @@ + @@ -13,40 +14,36 @@ - - - + + - + @@ -67,12 +64,9 @@ - - @@ -85,17 +79,16 @@ - + + android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"> @@ -104,49 +97,17 @@ android:value="true" /> - + android:permission="${applicationId}.permission.RECEIVE_BROADCASTS"> - + + + - - - - - - - - - - - - - - - - + { + launch { + State.handleStartServiceAction() + } + } + + BroadcastAction.STOP.action -> { + State.handleStopServiceAction() + } + + BroadcastAction.TOGGLE.action -> { + launch { + State.handleToggleAction() + } + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/follow/clash/Ext.kt b/android/app/src/main/kotlin/com/follow/clash/Ext.kt new file mode 100644 index 0000000..d1ba230 --- /dev/null +++ b/android/app/src/main/kotlin/com/follow/clash/Ext.kt @@ -0,0 +1,121 @@ +package com.follow.clash + +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.os.Build +import android.os.Handler +import android.os.Looper +import androidx.core.graphics.drawable.toBitmap +import com.follow.clash.common.GlobalState +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.MethodChannel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withContext +import java.io.File +import java.io.FileOutputStream +import java.util.concurrent.TimeUnit +import kotlin.coroutines.resume + +private const val ICON_TTL_DAYS = 1L + +suspend fun PackageManager.getPackageIconPath(packageName: String): String = + withContext(Dispatchers.IO) { + val cacheDir = GlobalState.application.cacheDir + val iconDir = File(cacheDir, "icons").apply { mkdirs() } + return@withContext try { + val pkgInfo = getPackageInfo(packageName, 0) + val lastUpdateTime = pkgInfo.lastUpdateTime + val iconFile = File(iconDir, "${packageName}_${lastUpdateTime}.webp") + if (iconFile.exists() && !isExpired(iconFile)) { + return@withContext iconFile.absolutePath + } + iconDir.listFiles()?.forEach { file -> + if (file.name.startsWith(packageName + "_")) file.delete() + } + val icon = getApplicationIcon(packageName) + saveDrawableToFile(icon, iconFile) + iconFile.absolutePath + } catch (_: Exception) { + val defaultIconFile = File(iconDir, "default_icon.webp") + if (!defaultIconFile.exists()) { + saveDrawableToFile(defaultActivityIcon, defaultIconFile) + } + defaultIconFile.absolutePath + } + } + +private fun saveDrawableToFile(drawable: Drawable, file: File) { + val bitmap = drawable.toBitmap() + try { + val format = when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { + Bitmap.CompressFormat.WEBP_LOSSY + } + + else -> { + Bitmap.CompressFormat.WEBP + } + } + FileOutputStream(file).use { fos -> + bitmap.compress(format, 90, fos) + } + } finally { + if (!bitmap.isRecycled) bitmap.recycle() + } +} + +private fun isExpired(file: File): Boolean { + val now = System.currentTimeMillis() + val age = now - file.lastModified() + return age > TimeUnit.DAYS.toMillis(ICON_TTL_DAYS) +} + +suspend fun MethodChannel.awaitResult( + method: String, arguments: Any? = null +): T? = withContext(Dispatchers.Main) { + suspendCancellableCoroutine { continuation -> + invokeMethod(method, arguments, object : MethodChannel.Result { + override fun success(result: Any?) { + @Suppress("UNCHECKED_CAST") continuation.resume(result as T?) + } + + override fun error(code: String, message: String?, details: Any?) { + continuation.resume(null) + } + + override fun notImplemented() { + continuation.resume(null) + } + }) + } +} + +inline fun FlutterEngine.plugin(): T? { + return plugins.get(T::class.java) as T? +} + + +fun MethodChannel.invokeMethodOnMainThread( + method: String, arguments: Any? = null, callback: ((Result) -> Unit)? = null +) { + Handler(Looper.getMainLooper()).post { + invokeMethod(method, arguments, object : MethodChannel.Result { + override fun success(result: Any?) { + @Suppress("UNCHECKED_CAST") callback?.invoke(Result.success(result as T)) + } + + override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { + val exception = Exception("MethodChannel error: $errorCode - $errorMessage") + callback?.invoke(Result.failure(exception)) + } + + override fun notImplemented() { + val exception = NotImplementedError("Method not implemented: $method") + callback?.invoke(Result.failure(exception)) + } + }) + } +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/follow/clash/FlClashApplication.kt b/android/app/src/main/kotlin/com/follow/clash/FlClashApplication.kt deleted file mode 100644 index e2b2783..0000000 --- a/android/app/src/main/kotlin/com/follow/clash/FlClashApplication.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.follow.clash; - -import android.app.Application -import android.content.Context - -class FlClashApplication : Application() { - companion object { - private lateinit var instance: FlClashApplication - fun getAppContext(): Context { - return instance.applicationContext - } - } - - override fun onCreate() { - super.onCreate() - instance = this - } -} diff --git a/android/app/src/main/kotlin/com/follow/clash/GlobalState.kt b/android/app/src/main/kotlin/com/follow/clash/GlobalState.kt deleted file mode 100644 index f47ff57..0000000 --- a/android/app/src/main/kotlin/com/follow/clash/GlobalState.kt +++ /dev/null @@ -1,125 +0,0 @@ -package com.follow.clash - -import androidx.lifecycle.MutableLiveData -import com.follow.clash.plugins.AppPlugin -import com.follow.clash.plugins.TilePlugin -import com.follow.clash.plugins.VpnPlugin -import io.flutter.FlutterInjector -import io.flutter.embedding.engine.FlutterEngine -import io.flutter.embedding.engine.dart.DartExecutor -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import java.util.concurrent.locks.ReentrantLock -import kotlin.concurrent.withLock - -enum class RunState { - START, - PENDING, - STOP -} - - -object GlobalState { - val runLock = ReentrantLock() - - const val NOTIFICATION_CHANNEL = "FlClash" - - const val NOTIFICATION_ID = 1 - - val runState: MutableLiveData = MutableLiveData(RunState.STOP) - var flutterEngine: FlutterEngine? = null - private var serviceEngine: FlutterEngine? = null - - fun getCurrentAppPlugin(): AppPlugin? { - val currentEngine = if (flutterEngine != null) flutterEngine else serviceEngine - return currentEngine?.plugins?.get(AppPlugin::class.java) as AppPlugin? - } - - fun syncStatus() { - CoroutineScope(Dispatchers.Default).launch { - val status = getCurrentVPNPlugin()?.getStatus() ?: false - withContext(Dispatchers.Main){ - runState.value = if (status) RunState.START else RunState.STOP - } - } - } - - suspend fun getText(text: String): String { - return getCurrentAppPlugin()?.getText(text) ?: "" - } - - fun getCurrentTilePlugin(): TilePlugin? { - val currentEngine = if (flutterEngine != null) flutterEngine else serviceEngine - return currentEngine?.plugins?.get(TilePlugin::class.java) as TilePlugin? - } - - fun getCurrentVPNPlugin(): VpnPlugin? { - return serviceEngine?.plugins?.get(VpnPlugin::class.java) as VpnPlugin? - } - - fun handleToggle() { - val starting = handleStart() - if (!starting) { - handleStop() - } - } - - fun handleStart(): Boolean { - if (runState.value == RunState.STOP) { - runState.value = RunState.PENDING - runLock.lock() - val tilePlugin = getCurrentTilePlugin() - if (tilePlugin != null) { - tilePlugin.handleStart() - } else { - initServiceEngine() - } - return true - } - return false - } - - fun handleStop() { - if (runState.value == RunState.START) { - runState.value = RunState.PENDING - runLock.lock() - getCurrentTilePlugin()?.handleStop() - } - } - - fun handleTryDestroy() { - if (flutterEngine == null) { - destroyServiceEngine() - } - } - - fun destroyServiceEngine() { - runLock.withLock { - serviceEngine?.destroy() - serviceEngine = null - } - } - - fun initServiceEngine() { - if (serviceEngine != null) return - destroyServiceEngine() - runLock.withLock { - serviceEngine = FlutterEngine(FlClashApplication.getAppContext()) - serviceEngine?.plugins?.add(VpnPlugin) - serviceEngine?.plugins?.add(AppPlugin()) - serviceEngine?.plugins?.add(TilePlugin()) - val vpnService = DartExecutor.DartEntrypoint( - FlutterInjector.instance().flutterLoader().findAppBundlePath(), - "_service" - ) - serviceEngine?.dartExecutor?.executeDartEntrypoint( - vpnService, - if (flutterEngine == null) listOf("quick") else null - ) - } - } -} - - diff --git a/android/app/src/main/kotlin/com/follow/clash/MainActivity.kt b/android/app/src/main/kotlin/com/follow/clash/MainActivity.kt index de930ca..08e9969 100644 --- a/android/app/src/main/kotlin/com/follow/clash/MainActivity.kt +++ b/android/app/src/main/kotlin/com/follow/clash/MainActivity.kt @@ -1,23 +1,37 @@ package com.follow.clash +import android.os.Bundle +import androidx.lifecycle.lifecycleScope import com.follow.clash.plugins.AppPlugin import com.follow.clash.plugins.ServicePlugin import com.follow.clash.plugins.TilePlugin import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch + +class MainActivity : FlutterActivity(), + CoroutineScope by CoroutineScope(SupervisorJob() + Dispatchers.Default) { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + lifecycleScope.launch { + State.destroyServiceEngine() + } + } -class MainActivity : FlutterActivity() { override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) flutterEngine.plugins.add(AppPlugin()) - flutterEngine.plugins.add(ServicePlugin) + flutterEngine.plugins.add(ServicePlugin()) flutterEngine.plugins.add(TilePlugin()) - GlobalState.flutterEngine = flutterEngine + State.flutterEngine = flutterEngine } override fun onDestroy() { - GlobalState.flutterEngine = null - GlobalState.runState.value = RunState.STOP + State.flutterEngine = null super.onDestroy() } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/follow/clash/Service.kt b/android/app/src/main/kotlin/com/follow/clash/Service.kt new file mode 100644 index 0000000..46ed228 --- /dev/null +++ b/android/app/src/main/kotlin/com/follow/clash/Service.kt @@ -0,0 +1,72 @@ +package com.follow.clash + +import com.follow.clash.common.ServiceDelegate +import com.follow.clash.common.intent +import com.follow.clash.service.ICallbackInterface +import com.follow.clash.service.IMessageInterface +import com.follow.clash.service.IRemoteInterface +import com.follow.clash.service.RemoteService +import com.follow.clash.service.models.NotificationParams +import com.follow.clash.service.models.VpnOptions + +object Service { + private val delegate by lazy { + ServiceDelegate( + RemoteService::class.intent, ::handleServiceDisconnected + ) { + IRemoteInterface.Stub.asInterface(it) + } + } + + var onServiceDisconnected: ((String) -> Unit)? = null + + private fun handleServiceDisconnected(message: String) { + onServiceDisconnected?.let { + it(message) + } + } + + fun bind() { + delegate.bind() + } + + suspend fun invokeAction( + data: String, cb: (result: ByteArray?, isSuccess: Boolean) -> Unit + ): Result { + return delegate.useService { + it.invokeAction(data, object : ICallbackInterface.Stub() { + override fun onResult(result: ByteArray?, isSuccess: Boolean) { + cb(result, isSuccess) + } + }) + } + } + + suspend fun updateNotificationParams( + params: NotificationParams + ): Result { + return delegate.useService { + it.updateNotificationParams(params) + } + } + + suspend fun setMessageCallback( + cb: (result: String?) -> Unit + ): Result { + return delegate.useService { + it.setMessageCallback(object : IMessageInterface.Stub() { + override fun onResult(result: String?) { + cb(result) + } + }) + } + } + + suspend fun startService(options: VpnOptions, inApp: Boolean) { + delegate.useService { it.startService(options, inApp) } + } + + suspend fun stopService() { + delegate.useService { it.stopService() } + } +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/follow/clash/State.kt b/android/app/src/main/kotlin/com/follow/clash/State.kt new file mode 100644 index 0000000..1886a63 --- /dev/null +++ b/android/app/src/main/kotlin/com/follow/clash/State.kt @@ -0,0 +1,148 @@ +package com.follow.clash + +import com.follow.clash.common.GlobalState +import com.follow.clash.plugins.AppPlugin +import com.follow.clash.plugins.ServicePlugin +import com.follow.clash.plugins.TilePlugin +import io.flutter.FlutterInjector +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.embedding.engine.dart.DartExecutor +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.withContext + +enum class RunState { + START, PENDING, STOP +} + + +object State { + + val runLock = Mutex() + + var runTime: Long = 0 + + val runStateFlow: MutableStateFlow = MutableStateFlow(RunState.STOP) + var flutterEngine: FlutterEngine? = null + var serviceFlutterEngine: FlutterEngine? = null + + val appPlugin: AppPlugin? + get() = flutterEngine?.plugin() ?: serviceFlutterEngine?.plugin() + + val servicePlugin: ServicePlugin? + get() = flutterEngine?.plugin() + ?: serviceFlutterEngine?.plugin() + + val tilePlugin: TilePlugin? + get() = flutterEngine?.plugin() ?: serviceFlutterEngine?.plugin() + + suspend fun handleToggleAction() { + var action: (suspend () -> Unit)? + runLock.withLock { + action = when (runStateFlow.value) { + RunState.PENDING -> null + RunState.START -> ::handleStopServiceAction + RunState.STOP -> ::handleStartServiceAction + } + } + action?.invoke() + } + + suspend fun handleStartServiceAction() { + tilePlugin?.handleStart() + if (flutterEngine != null) { + return + } + startServiceWithEngine() + } + + fun handleStopServiceAction() { + tilePlugin?.handleStop() + if (flutterEngine != null || serviceFlutterEngine != null) { + return + } + handleStopService() + } + + fun handleStartService() { + if (appPlugin != null) { + appPlugin?.requestNotificationsPermission { + startService() + } + return + } + startService() + } + + suspend fun destroyServiceEngine() { + runLock.withLock { + withContext(Dispatchers.Main) { + runCatching { + serviceFlutterEngine?.destroy() + serviceFlutterEngine = null + } + } + } + } + + suspend fun startServiceWithEngine() { + runLock.withLock { + withContext(Dispatchers.Main) { + serviceFlutterEngine = FlutterEngine(GlobalState.application) + serviceFlutterEngine?.plugins?.add(ServicePlugin()) + serviceFlutterEngine?.plugins?.add(AppPlugin()) + serviceFlutterEngine?.plugins?.add(TilePlugin()) + val dartEntrypoint = DartExecutor.DartEntrypoint( + FlutterInjector.instance().flutterLoader().findAppBundlePath(), "_service" + ) + serviceFlutterEngine?.dartExecutor?.executeDartEntrypoint(dartEntrypoint) + } + + } + } + + private fun startService() { + GlobalState.launch { + runLock.withLock { + if (runStateFlow.value == RunState.PENDING || runStateFlow.value == RunState.START) { + return@launch + } + runStateFlow.tryEmit(RunState.PENDING) + if (servicePlugin == null) { + return@launch + } + val options = servicePlugin?.handleGetVpnOptions() + if (options == null) { + return@launch + } + appPlugin?.prepare(options.enable) { + runTime = System.currentTimeMillis() + Service.startService(options, true) + runStateFlow.tryEmit(RunState.START) + } + } + } + + } + + fun handleStopService() { + GlobalState.launch { + runLock.withLock { + if (runStateFlow.value == RunState.PENDING || runStateFlow.value == RunState.STOP) { + return@launch + } + runStateFlow.tryEmit(RunState.PENDING) + Service.stopService() + runStateFlow.tryEmit(RunState.STOP) + runTime = 0 + } + destroyServiceEngine() + } + + } +} + + diff --git a/android/app/src/main/kotlin/com/follow/clash/TempActivity.kt b/android/app/src/main/kotlin/com/follow/clash/TempActivity.kt index 33f631a..1e8a519 100644 --- a/android/app/src/main/kotlin/com/follow/clash/TempActivity.kt +++ b/android/app/src/main/kotlin/com/follow/clash/TempActivity.kt @@ -2,24 +2,34 @@ package com.follow.clash import android.app.Activity import android.os.Bundle -import com.follow.clash.extensions.wrapAction +import com.follow.clash.common.QuickAction +import com.follow.clash.common.action +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch -class TempActivity : Activity() { +class TempActivity : Activity(), + CoroutineScope by CoroutineScope(SupervisorJob() + Dispatchers.Default) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) when (intent.action) { - wrapAction("START") -> { - GlobalState.handleStart() + QuickAction.START.action -> { + launch { + State.handleStartServiceAction() + } } - wrapAction("STOP") -> { - GlobalState.handleStop() + QuickAction.STOP.action -> { + State.handleStopServiceAction() } - wrapAction("CHANGE") -> { - GlobalState.handleToggle() + QuickAction.TOGGLE.action -> { + launch { + State.handleToggleAction() + } } } - finishAndRemoveTask() + finish() } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/follow/clash/TileService.kt b/android/app/src/main/kotlin/com/follow/clash/TileService.kt new file mode 100644 index 0000000..907732d --- /dev/null +++ b/android/app/src/main/kotlin/com/follow/clash/TileService.kt @@ -0,0 +1,61 @@ +package com.follow.clash + +import android.annotation.SuppressLint +import android.os.Build +import android.service.quicksettings.Tile +import android.service.quicksettings.TileService +import com.follow.clash.common.QuickAction +import com.follow.clash.common.quickIntent +import com.follow.clash.common.toPendingIntent +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch + +class TileService : TileService() { + private var scope: CoroutineScope? = null + private fun updateTile(runState: RunState) { + if (qsTile != null) { + qsTile.state = when (runState) { + RunState.START -> Tile.STATE_ACTIVE + RunState.PENDING -> Tile.STATE_UNAVAILABLE + RunState.STOP -> Tile.STATE_INACTIVE + } + qsTile.updateTile() + } + } + + override fun onStartListening() { + super.onStartListening() + scope?.cancel() + scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) + scope?.launch { + State.runStateFlow.collect { + updateTile(it) + } + } + } + + @SuppressLint("StartActivityAndCollapseDeprecated") + private fun handleToggle() { + val intent = QuickAction.TOGGLE.quickIntent + val pendingIntent = intent.toPendingIntent + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + startActivityAndCollapse(pendingIntent) + } else { + @Suppress("DEPRECATION") + startActivityAndCollapse(intent) + } + } + + override fun onClick() { + super.onClick() + handleToggle() + } + + override fun onStopListening() { + scope?.cancel() + super.onStopListening() + } +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/follow/clash/extensions/Ext.kt b/android/app/src/main/kotlin/com/follow/clash/extensions/Ext.kt deleted file mode 100644 index 10e0536..0000000 --- a/android/app/src/main/kotlin/com/follow/clash/extensions/Ext.kt +++ /dev/null @@ -1,200 +0,0 @@ -package com.follow.clash.extensions - -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import android.graphics.Bitmap -import android.graphics.drawable.Drawable -import android.net.ConnectivityManager -import android.net.Network -import android.os.Build -import android.system.OsConstants.IPPROTO_TCP -import android.system.OsConstants.IPPROTO_UDP -import android.util.Base64 -import androidx.core.graphics.drawable.toBitmap -import com.follow.clash.TempActivity -import com.follow.clash.models.CIDR -import com.follow.clash.models.Metadata -import com.follow.clash.models.VpnOptions -import io.flutter.plugin.common.MethodChannel -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import java.io.ByteArrayOutputStream -import java.net.Inet4Address -import java.net.Inet6Address -import java.net.InetAddress -import java.util.concurrent.locks.ReentrantLock -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine - -suspend fun Drawable.getBase64(): String { - val drawable = this - return withContext(Dispatchers.IO) { - val bitmap = drawable.toBitmap() - val byteArrayOutputStream = ByteArrayOutputStream() - bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream) - Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.NO_WRAP) - } -} - -fun Metadata.getProtocol(): Int? { - if (network.startsWith("tcp")) return IPPROTO_TCP - if (network.startsWith("udp")) return IPPROTO_UDP - return null -} - -fun VpnOptions.getIpv4RouteAddress(): List { - return routeAddress.filter { - it.isIpv4() - }.map { - it.toCIDR() - } -} - -fun VpnOptions.getIpv6RouteAddress(): List { - return routeAddress.filter { - it.isIpv6() - }.map { - it.toCIDR() - } -} - -fun String.isIpv4(): Boolean { - val parts = split("/") - if (parts.size != 2) { - throw IllegalArgumentException("Invalid CIDR format") - } - val address = InetAddress.getByName(parts[0]) - return address.address.size == 4 -} - -fun String.isIpv6(): Boolean { - val parts = split("/") - if (parts.size != 2) { - throw IllegalArgumentException("Invalid CIDR format") - } - val address = InetAddress.getByName(parts[0]) - return address.address.size == 16 -} - -fun String.toCIDR(): CIDR { - val parts = split("/") - if (parts.size != 2) { - throw IllegalArgumentException("Invalid CIDR format") - } - val ipAddress = parts[0] - val prefixLength = parts[1].toIntOrNull() - ?: throw IllegalArgumentException("Invalid prefix length") - - val address = InetAddress.getByName(ipAddress) - - val maxPrefix = if (address.address.size == 4) 32 else 128 - if (prefixLength < 0 || prefixLength > maxPrefix) { - throw IllegalArgumentException("Invalid prefix length for IP version") - } - - return CIDR(address, prefixLength) -} - -fun ConnectivityManager.resolveDns(network: Network?): List { - val properties = getLinkProperties(network) ?: return listOf() - return properties.dnsServers.map { it.asSocketAddressText(53) } -} - -fun InetAddress.asSocketAddressText(port: Int): String { - return when (this) { - is Inet6Address -> - "[${numericToTextFormat(this)}]:$port" - - is Inet4Address -> - "${this.hostAddress}:$port" - - else -> throw IllegalArgumentException("Unsupported Inet type ${this.javaClass}") - } -} - -fun Context.wrapAction(action: String): String { - return "${this.packageName}.action.$action" -} - -fun Context.getActionIntent(action: String): Intent { - val actionIntent = Intent(this, TempActivity::class.java) - actionIntent.action = wrapAction(action) - return actionIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK) -} - -fun Context.getActionPendingIntent(action: String): PendingIntent { - return if (Build.VERSION.SDK_INT >= 31) { - PendingIntent.getActivity( - this, - 0, - getActionIntent(action), - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT - ) - } else { - PendingIntent.getActivity( - this, - 0, - getActionIntent(action), - PendingIntent.FLAG_UPDATE_CURRENT - ) - } -} - -private fun numericToTextFormat(address: Inet6Address): String { - val src = address.address - val sb = StringBuilder(39) - for (i in 0 until 8) { - sb.append( - Integer.toHexString( - src[i shl 1].toInt() shl 8 and 0xff00 - or (src[(i shl 1) + 1].toInt() and 0xff) - ) - ) - if (i < 7) { - sb.append(":") - } - } - if (address.scopeId > 0) { - sb.append("%") - sb.append(address.scopeId) - } - return sb.toString() -} - -suspend fun MethodChannel.awaitResult( - method: String, - arguments: Any? = null -): T? = withContext(Dispatchers.Main) { // 切换到主线程 - suspendCoroutine { continuation -> - invokeMethod(method, arguments, object : MethodChannel.Result { - override fun success(result: Any?) { - @Suppress("UNCHECKED_CAST") - continuation.resume(result as T) - } - - override fun error(code: String, message: String?, details: Any?) { - continuation.resume(null) - } - - override fun notImplemented() { - continuation.resume(null) - } - }) - } -} - -fun ReentrantLock.safeLock() { - if (this.isLocked) { - return - } - this.lock() -} - -fun ReentrantLock.safeUnlock() { - if (!this.isLocked) { - return - } - - this.unlock() -} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/follow/clash/models/Process.kt b/android/app/src/main/kotlin/com/follow/clash/models/Process.kt deleted file mode 100644 index a889564..0000000 --- a/android/app/src/main/kotlin/com/follow/clash/models/Process.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.follow.clash.models - -data class Process( - val id: String, - val metadata: Metadata, -) - -data class Metadata( - val network: String, - val sourceIP: String, - val sourcePort: Int, - val destinationIP: String, - val destinationPort: Int, - val host: String -) diff --git a/android/app/src/main/kotlin/com/follow/clash/models/Props.kt b/android/app/src/main/kotlin/com/follow/clash/models/Props.kt deleted file mode 100644 index 9a7ed7b..0000000 --- a/android/app/src/main/kotlin/com/follow/clash/models/Props.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.follow.clash.models - -import java.net.InetAddress - -enum class AccessControlMode { - acceptSelected, rejectSelected, -} - -data class AccessControl( - val enable: Boolean, - val mode: AccessControlMode, - val acceptList: List, - val rejectList: List, -) - -data class CIDR(val address: InetAddress, val prefixLength: Int) - -data class VpnOptions( - val enable: Boolean, - val port: Int, - val accessControl: AccessControl, - val allowBypass: Boolean, - val systemProxy: Boolean, - val bypassDomain: List, - val routeAddress: List, - val ipv4Address: String, - val ipv6Address: String, - val dnsServerAddress: String, -) - -data class StartForegroundParams( - val title: String, - val content: String, -) \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/follow/clash/models/State.kt b/android/app/src/main/kotlin/com/follow/clash/models/State.kt new file mode 100644 index 0000000..7df9850 --- /dev/null +++ b/android/app/src/main/kotlin/com/follow/clash/models/State.kt @@ -0,0 +1,8 @@ +package com.follow.clash.models + + +data class AppState( + val currentProfileName: String, + val stopText: String, + val onlyStatisticsProxy: Boolean, +) diff --git a/android/app/src/main/kotlin/com/follow/clash/plugins/AppPlugin.kt b/android/app/src/main/kotlin/com/follow/clash/plugins/AppPlugin.kt index 757c193..6b757f2 100644 --- a/android/app/src/main/kotlin/com/follow/clash/plugins/AppPlugin.kt +++ b/android/app/src/main/kotlin/com/follow/clash/plugins/AppPlugin.kt @@ -13,17 +13,16 @@ import android.widget.Toast import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat.getSystemService -import androidx.core.content.FileProvider import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.graphics.drawable.IconCompat import com.android.tools.smali.dexlib2.dexbacked.DexBackedDexFile -import com.follow.clash.FlClashApplication -import com.follow.clash.GlobalState import com.follow.clash.R -import com.follow.clash.extensions.awaitResult -import com.follow.clash.extensions.getActionIntent -import com.follow.clash.extensions.getBase64 +import com.follow.clash.common.Components +import com.follow.clash.common.GlobalState +import com.follow.clash.common.QuickAction +import com.follow.clash.common.quickIntent +import com.follow.clash.getPackageIconPath import com.follow.clash.models.Package import com.google.gson.Gson import io.flutter.embedding.android.FlutterActivity @@ -44,13 +43,20 @@ import java.util.zip.ZipFile class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware { + companion object { + const val VPN_PERMISSION_REQUEST_CODE = 1001 + const val NOTIFICATION_PERMISSION_REQUEST_CODE = 1002 + } + private var activityRef: WeakReference? = null private lateinit var channel: MethodChannel private lateinit var scope: CoroutineScope - private var vpnCallBack: (() -> Unit)? = null + private var vpnPrepareCallback: (suspend () -> Unit)? = null + + private var requestNotificationCallback: (() -> Unit)? = null private val iconMap = mutableMapOf() @@ -111,46 +117,8 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware ("(" + chinaAppPrefixList.joinToString("|").replace(".", "\\.") + ").*").toRegex() } - val VPN_PERMISSION_REQUEST_CODE = 1001 - - val NOTIFICATION_PERMISSION_REQUEST_CODE = 1002 - private var isBlockNotification: Boolean = false - override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - scope = CoroutineScope(Dispatchers.Default) - channel = MethodChannel(flutterPluginBinding.binaryMessenger, "app") - channel.setMethodCallHandler(this) - } - - private fun initShortcuts(label: String) { - val shortcut = ShortcutInfoCompat.Builder(FlClashApplication.getAppContext(), "toggle") - .setShortLabel(label) - .setIcon( - IconCompat.createWithResource( - FlClashApplication.getAppContext(), - R.mipmap.ic_launcher_round - ) - ) - .setIntent(FlClashApplication.getAppContext().getActionIntent("CHANGE")) - .build() - ShortcutManagerCompat.setDynamicShortcuts( - FlClashApplication.getAppContext(), - listOf(shortcut) - ) - } - - override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { - channel.setMethodCallHandler(null) - scope.cancel() - } - - private fun tip(message: String?) { - if (GlobalState.flutterEngine == null) { - Toast.makeText(FlClashApplication.getAppContext(), message, Toast.LENGTH_LONG).show() - } - } - override fun onMethodCall(call: MethodCall, result: Result) { when (call.method) { "moveTaskToBack" -> { @@ -182,26 +150,7 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware } "getPackageIcon" -> { - scope.launch { - val packageName = call.argument("packageName") - if (packageName == null) { - result.success(null) - return@launch - } - val packageIcon = getPackageIcon(packageName) - packageIcon.let { - if (it != null) { - result.success(it) - return@launch - } - if (iconMap["default"] == null) { - iconMap["default"] = - FlClashApplication.getAppContext().packageManager?.defaultActivityIcon?.getBase64() - } - result.success(iconMap["default"]) - return@launch - } - } + handleGetPackageIcon(call, result) } "tip" -> { @@ -210,56 +159,49 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware result.success(true) } - "openFile" -> { - val path = call.argument("path")!! - openFile(path) - result.success(true) - } - else -> { result.notImplemented() } } } - private fun openFile(path: String) { - val file = File(path) - val uri = FileProvider.getUriForFile( - FlClashApplication.getAppContext(), - "${FlClashApplication.getAppContext().packageName}.fileProvider", - file - ) - - val intent = Intent(Intent.ACTION_VIEW).setDataAndType( - uri, - "text/plain" - ) - - val flags = - Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION - - val resInfoList = FlClashApplication.getAppContext().packageManager.queryIntentActivities( - intent, PackageManager.MATCH_DEFAULT_ONLY - ) - - for (resolveInfo in resInfoList) { - val packageName = resolveInfo.activityInfo.packageName - FlClashApplication.getAppContext().grantUriPermission( - packageName, - uri, - flags - ) - } - - try { - activityRef?.get()?.startActivity(intent) - } catch (e: Exception) { - println(e) + private fun handleGetPackageIcon(call: MethodCall, result: Result) { + scope.launch { + val packageName = call.argument("packageName") + if (packageName == null) { + result.success("") + return@launch + } + val path = + GlobalState.application.packageManager.getPackageIconPath(packageName) + result.success(path) } } + private fun initShortcuts(label: String) { + val shortcut = with(ShortcutInfoCompat.Builder(GlobalState.application, "toggle")) { + setShortLabel(label) + setIcon( + IconCompat.createWithResource( + GlobalState.application, + R.mipmap.ic_launcher_round, + ) + ) + setIntent(QuickAction.TOGGLE.quickIntent) + build() + } + ShortcutManagerCompat.setDynamicShortcuts( + GlobalState.application, listOf(shortcut) + ) + } + + private fun tip(message: String?) { + Toast.makeText(GlobalState.application, message, Toast.LENGTH_LONG).show() + } + + @Suppress("DEPRECATION") private fun updateExcludeFromRecents(value: Boolean?) { - val am = getSystemService(FlClashApplication.getAppContext(), ActivityManager::class.java) + val am = getSystemService(GlobalState.application, ActivityManager::class.java) val task = am?.appTasks?.firstOrNull { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { it.taskInfo.taskId == activityRef?.get()?.taskId @@ -275,25 +217,13 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware } } - private suspend fun getPackageIcon(packageName: String): String? { - val packageManager = FlClashApplication.getAppContext().packageManager - if (iconMap[packageName] == null) { - iconMap[packageName] = try { - packageManager?.getApplicationIcon(packageName)?.getBase64() - } catch (_: Exception) { - null - } - - } - return iconMap[packageName] - } private fun getPackages(): List { - val packageManager = FlClashApplication.getAppContext().packageManager + val packageManager = GlobalState.application.packageManager if (packages.isNotEmpty()) return packages packageManager?.getInstalledPackages(PackageManager.GET_META_DATA or PackageManager.GET_PERMISSIONS) ?.filter { - it.packageName != FlClashApplication.getAppContext().packageName || it.packageName == "android" + it.packageName != GlobalState.application.packageName || it.packageName == "android" }?.map { Package( @@ -321,52 +251,66 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware } } - fun requestVpnPermission(callBack: () -> Unit) { - vpnCallBack = callBack - val intent = VpnService.prepare(FlClashApplication.getAppContext()) + fun requestNotificationsPermission(callBack: () -> Unit) { + requestNotificationCallback = callBack + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + val permission = ContextCompat.checkSelfPermission( + GlobalState.application, Manifest.permission.POST_NOTIFICATIONS + ) + if (permission == PackageManager.PERMISSION_GRANTED || isBlockNotification) { + invokeRequestNotificationCallback() + return + } + activityRef?.get()?.let { + ActivityCompat.requestPermissions( + it, + arrayOf(Manifest.permission.POST_NOTIFICATIONS), + NOTIFICATION_PERMISSION_REQUEST_CODE + ) + } + return + } else { + invokeRequestNotificationCallback() + } + + } + + fun invokeRequestNotificationCallback() { + requestNotificationCallback?.invoke() + requestNotificationCallback = null + } + + fun prepare(needPrepare: Boolean, callBack: (suspend () -> Unit)) { + vpnPrepareCallback = callBack + if (!needPrepare) { + invokeVpnPrepareCallback() + return + } + val intent = VpnService.prepare(GlobalState.application) if (intent != null) { activityRef?.get()?.startActivityForResult(intent, VPN_PERMISSION_REQUEST_CODE) return } - vpnCallBack?.invoke() + invokeVpnPrepareCallback() } - fun requestNotificationsPermission() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - val permission = ContextCompat.checkSelfPermission( - FlClashApplication.getAppContext(), - Manifest.permission.POST_NOTIFICATIONS - ) - if (permission != PackageManager.PERMISSION_GRANTED) { - if (isBlockNotification) return - if (activityRef?.get() == null) return - activityRef?.get()?.let { - ActivityCompat.requestPermissions( - it, - arrayOf(Manifest.permission.POST_NOTIFICATIONS), - NOTIFICATION_PERMISSION_REQUEST_CODE - ) - return - } - } + fun invokeVpnPrepareCallback() { + GlobalState.launch { + vpnPrepareCallback?.invoke() + vpnPrepareCallback = null } } - suspend fun getText(text: String): String? { - return withContext(Dispatchers.Default) { - channel.awaitResult("getText", text) - } - } + @Suppress("DEPRECATION") private fun isChinaPackage(packageName: String): Boolean { - val packageManager = FlClashApplication.getAppContext().packageManager ?: return false + val packageManager = GlobalState.application.packageManager ?: return false skipPrefixList.forEach { if (packageName == it || packageName.startsWith("$it.")) return false } val packageManagerFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { PackageManager.MATCH_UNINSTALLED_PACKAGES or PackageManager.GET_ACTIVITIES or PackageManager.GET_SERVICES or PackageManager.GET_RECEIVERS or PackageManager.GET_PROVIDERS } else { - @Suppress("DEPRECATION") PackageManager.GET_UNINSTALLED_PACKAGES or PackageManager.GET_ACTIVITIES or PackageManager.GET_SERVICES or PackageManager.GET_RECEIVERS or PackageManager.GET_PROVIDERS } if (packageName.matches(chinaAppRegex)) { @@ -375,8 +319,7 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware try { val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { packageManager.getPackageInfo( - packageName, - PackageManager.PackageInfoFlags.of(packageManagerFlags.toLong()) + packageName, PackageManager.PackageInfoFlags.of(packageManagerFlags.toLong()) ) } else { packageManager.getPackageInfo( @@ -427,6 +370,18 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware return false } + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + scope = CoroutineScope(Dispatchers.Default) + channel = + MethodChannel(flutterPluginBinding.binaryMessenger, "${Components.PACKAGE_NAME}/app") + channel.setMethodCallHandler(this) + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) + scope.cancel() + } + override fun onAttachedToActivity(binding: ActivityPluginBinding) { activityRef = WeakReference(binding.activity) binding.addActivityResultListener(::onActivityResult) @@ -449,21 +404,19 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware private fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { if (requestCode == VPN_PERMISSION_REQUEST_CODE) { if (resultCode == FlutterActivity.RESULT_OK) { - GlobalState.initServiceEngine() - vpnCallBack?.invoke() + invokeVpnPrepareCallback() } } return true } private fun onRequestPermissionsResultListener( - requestCode: Int, - permissions: Array, - grantResults: IntArray + requestCode: Int, permissions: Array, grantResults: IntArray ): Boolean { if (requestCode == NOTIFICATION_PERMISSION_REQUEST_CODE) { isBlockNotification = true } + invokeRequestNotificationCallback() return true } } diff --git a/android/app/src/main/kotlin/com/follow/clash/plugins/ServicePlugin.kt b/android/app/src/main/kotlin/com/follow/clash/plugins/ServicePlugin.kt index 62db45d..76c8adb 100644 --- a/android/app/src/main/kotlin/com/follow/clash/plugins/ServicePlugin.kt +++ b/android/app/src/main/kotlin/com/follow/clash/plugins/ServicePlugin.kt @@ -1,19 +1,34 @@ package com.follow.clash.plugins -import com.follow.clash.GlobalState -import com.follow.clash.models.VpnOptions +import com.follow.clash.RunState +import com.follow.clash.Service +import com.follow.clash.State +import com.follow.clash.awaitResult +import com.follow.clash.common.Components +import com.follow.clash.common.formatString +import com.follow.clash.invokeMethodOnMainThread +import com.follow.clash.models.AppState +import com.follow.clash.service.models.NotificationParams +import com.follow.clash.service.models.VpnOptions import com.google.gson.Gson import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Semaphore +import kotlinx.coroutines.sync.withPermit - -data object ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler { - +class ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler, + CoroutineScope by CoroutineScope(SupervisorJob() + Dispatchers.Default) { private lateinit var flutterMethodChannel: MethodChannel override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - flutterMethodChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "service") + flutterMethodChannel = MethodChannel( + flutterPluginBinding.binaryMessenger, "${Components.PACKAGE_NAME}/service" + ) flutterMethodChannel.setMethodCallHandler(this) } @@ -22,28 +37,28 @@ data object ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler { } override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) = when (call.method) { - "startVpn" -> { - val data = call.argument("data") - val options = Gson().fromJson(data, VpnOptions::class.java) - GlobalState.getCurrentVPNPlugin()?.handleStart(options) - result.success(true) - } - - "stopVpn" -> { - GlobalState.getCurrentVPNPlugin()?.handleStop() - result.success(true) - } - "init" -> { - GlobalState.getCurrentAppPlugin() - ?.requestNotificationsPermission() - GlobalState.initServiceEngine() - result.success(true) + handleInit(result) } - "destroy" -> { - handleDestroy() - result.success(true) + "invokeAction" -> { + handleInvokeAction(call, result) + } + + "getRunTime" -> { + handleGetRunTime(result) + } + + "syncState" -> { + handleSyncState(call, result) + } + + "start" -> { + handleStart(result) + } + + "stop" -> { + handleStop(result) } else -> { @@ -51,8 +66,83 @@ data object ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler { } } + private fun handleInvokeAction(call: MethodCall, result: MethodChannel.Result) { + launch { + val data = call.arguments()!! + val res = mutableListOf() + Service.invokeAction(data) { byteArray, isSuccess -> + res.add(byteArray ?: byteArrayOf()) + if (isSuccess) { + result.success(res.formatString()) + } + } + } + } - private fun handleDestroy() { - GlobalState.destroyServiceEngine() + private fun handleStart(result: MethodChannel.Result) { + State.handleStartService() + result.success(true) + } + + private fun handleStop(result: MethodChannel.Result) { + State.handleStopService() + result.success(true) + } + + suspend fun handleGetVpnOptions(): VpnOptions? { + val res = flutterMethodChannel.awaitResult("getVpnOptions", null) + return Gson().fromJson(res, VpnOptions::class.java) + } + + val semaphore = Semaphore(10) + + fun handleSendEvent(value: String?) { + launch(Dispatchers.Main) { + semaphore.withPermit { + flutterMethodChannel.invokeMethod("event", value) + } + } + } + + private fun onServiceDisconnected(message: String) { + State.runStateFlow.tryEmit(RunState.STOP) + flutterMethodChannel.invokeMethodOnMainThread("crash", message) + } + + private fun handleSyncState(call: MethodCall, result: MethodChannel.Result) { + launch { + val data = call.arguments()!! + val params = Gson().fromJson(data, AppState::class.java) + Service.updateNotificationParams( + NotificationParams( + title = params.currentProfileName, + stopText = params.stopText, + onlyStatisticsProxy = params.onlyStatisticsProxy + ) + ).onSuccess { + result.success("") + }.onFailure { + result.success(it.message) + } + } + } + + fun handleInit(result: MethodChannel.Result) { + Service.bind() + launch { + Service.setMessageCallback { + handleSendEvent(it) + }.onSuccess { + result.success("") + }.onFailure { + result.success(it.message) + } + + } + Service.onServiceDisconnected = ::onServiceDisconnected + } + + private fun handleGetRunTime(result: MethodChannel.Result) { + return result.success(State.runTime) } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/follow/clash/plugins/TilePlugin.kt b/android/app/src/main/kotlin/com/follow/clash/plugins/TilePlugin.kt index 6a6bd97..f7a5732 100644 --- a/android/app/src/main/kotlin/com/follow/clash/plugins/TilePlugin.kt +++ b/android/app/src/main/kotlin/com/follow/clash/plugins/TilePlugin.kt @@ -1,5 +1,7 @@ package com.follow.clash.plugins +import com.follow.clash.common.Components +import com.follow.clash.invokeMethodOnMainThread import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel @@ -9,25 +11,21 @@ class TilePlugin : FlutterPlugin, MethodChannel.MethodCallHandler { private lateinit var channel: MethodChannel override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - channel = MethodChannel(flutterPluginBinding.binaryMessenger, "tile") + channel = + MethodChannel(flutterPluginBinding.binaryMessenger, "${Components.PACKAGE_NAME}/tile") channel.setMethodCallHandler(this) } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { - handleDetached() channel.setMethodCallHandler(null) } fun handleStart() { - channel.invokeMethod("start", null) + channel.invokeMethodOnMainThread("start", null) } fun handleStop() { - channel.invokeMethod("stop", null) - } - - private fun handleDetached() { - channel.invokeMethod("detached", null) + channel.invokeMethodOnMainThread("stop", null) } diff --git a/android/app/src/main/kotlin/com/follow/clash/plugins/VpnPlugin.kt b/android/app/src/main/kotlin/com/follow/clash/plugins/VpnPlugin.kt deleted file mode 100644 index 3d3cc39..0000000 --- a/android/app/src/main/kotlin/com/follow/clash/plugins/VpnPlugin.kt +++ /dev/null @@ -1,276 +0,0 @@ -package com.follow.clash.plugins - -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.content.ServiceConnection -import android.net.ConnectivityManager -import android.net.Network -import android.net.NetworkCapabilities -import android.net.NetworkRequest -import android.os.Build -import android.os.IBinder -import androidx.core.content.getSystemService -import com.follow.clash.FlClashApplication -import com.follow.clash.GlobalState -import com.follow.clash.RunState -import com.follow.clash.core.Core -import com.follow.clash.extensions.awaitResult -import com.follow.clash.extensions.resolveDns -import com.follow.clash.models.StartForegroundParams -import com.follow.clash.models.VpnOptions -import com.follow.clash.services.BaseServiceInterface -import com.follow.clash.services.FlClashService -import com.follow.clash.services.FlClashVpnService -import com.google.gson.Gson -import io.flutter.embedding.engine.plugins.FlutterPlugin -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import java.net.InetSocketAddress -import kotlin.concurrent.withLock - -data object VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler { - private lateinit var flutterMethodChannel: MethodChannel - private var flClashService: BaseServiceInterface? = null - private var options: VpnOptions? = null - private var isBind: Boolean = false - private lateinit var scope: CoroutineScope - private var lastStartForegroundParams: StartForegroundParams? = null - private var timerJob: Job? = null - private val uidPageNameMap = mutableMapOf() - - private val connectivity by lazy { - FlClashApplication.getAppContext().getSystemService() - } - - private val connection = object : ServiceConnection { - override fun onServiceConnected(className: ComponentName, service: IBinder) { - isBind = true - flClashService = when (service) { - is FlClashVpnService.LocalBinder -> service.getService() - is FlClashService.LocalBinder -> service.getService() - else -> throw Exception("invalid binder") - } - handleStartService() - } - - override fun onServiceDisconnected(arg: ComponentName) { - isBind = false - flClashService = null - } - } - - override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - scope = CoroutineScope(Dispatchers.Default) - scope.launch { - registerNetworkCallback() - } - flutterMethodChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "vpn") - flutterMethodChannel.setMethodCallHandler(this) - } - - override fun onDetachedFromEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - unRegisterNetworkCallback() - flutterMethodChannel.setMethodCallHandler(null) - } - - override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { - when (call.method) { - "start" -> { - val data = call.argument("data") - result.success(handleStart(Gson().fromJson(data, VpnOptions::class.java))) - } - - "stop" -> { - handleStop() - result.success(true) - } - - else -> { - result.notImplemented() - } - } - } - - fun handleStart(options: VpnOptions): Boolean { - onUpdateNetwork(); - if (options.enable != this.options?.enable) { - this.flClashService = null - } - this.options = options - when (options.enable) { - true -> handleStartVpn() - false -> handleStartService() - } - return true - } - - private fun handleStartVpn() { - GlobalState.getCurrentAppPlugin()?.requestVpnPermission { - handleStartService() - } - } - - fun requestGc() { - flutterMethodChannel.invokeMethod("gc", null) - } - - val networks = mutableSetOf() - - fun onUpdateNetwork() { - val dns = networks.flatMap { network -> - connectivity?.resolveDns(network) ?: emptyList() - }.toSet().joinToString(",") - scope.launch { - withContext(Dispatchers.Main) { - flutterMethodChannel.invokeMethod("dnsChanged", dns) - } - } - } - - private val callback = object : ConnectivityManager.NetworkCallback() { - override fun onAvailable(network: Network) { - networks.add(network) - onUpdateNetwork() - } - - override fun onLost(network: Network) { - networks.remove(network) - onUpdateNetwork() - } - } - - private val request = NetworkRequest.Builder().apply { - addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) - addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - }.build() - - private fun registerNetworkCallback() { - networks.clear() - connectivity?.registerNetworkCallback(request, callback) - } - - private fun unRegisterNetworkCallback() { - connectivity?.unregisterNetworkCallback(callback) - networks.clear() - onUpdateNetwork() - } - - private suspend fun startForeground() { - GlobalState.runLock.lock() - try { - if (GlobalState.runState.value != RunState.START) return - val data = flutterMethodChannel.awaitResult("getStartForegroundParams") - val startForegroundParams = if (data != null) Gson().fromJson( - data, StartForegroundParams::class.java - ) else StartForegroundParams( - title = "", content = "" - ) - if (lastStartForegroundParams != startForegroundParams) { - lastStartForegroundParams = startForegroundParams - flClashService?.startForeground( - startForegroundParams.title, - startForegroundParams.content, - ) - } - } finally { - GlobalState.runLock.unlock() - } - } - - private fun startForegroundJob() { - stopForegroundJob() - timerJob = CoroutineScope(Dispatchers.Main).launch { - while (isActive) { - startForeground() - delay(1000) - } - } - } - - private fun stopForegroundJob() { - timerJob?.cancel() - timerJob = null - } - - - suspend fun getStatus(): Boolean? { - return withContext(Dispatchers.Default) { - flutterMethodChannel.awaitResult("status", null) - } - } - - private fun handleStartService() { - if (flClashService == null) { - bindService() - return - } - GlobalState.runLock.withLock { - if (GlobalState.runState.value == RunState.START) return - GlobalState.runState.value = RunState.START - val fd = flClashService?.start(options!!) - Core.startTun( - fd = fd ?: 0, - protect = this::protect, - resolverProcess = this::resolverProcess, - ) - startForegroundJob() - } - } - - private fun protect(fd: Int): Boolean { - return (flClashService as? FlClashVpnService)?.protect(fd) == true - } - - private fun resolverProcess( - protocol: Int, - source: InetSocketAddress, - target: InetSocketAddress, - uid: Int, - ): String { - val nextUid = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - connectivity?.getConnectionOwnerUid(protocol, source, target) ?: -1 - } else { - uid - } - if (nextUid == -1) { - return "" - } - if (!uidPageNameMap.containsKey(nextUid)) { - uidPageNameMap[nextUid] = - FlClashApplication.getAppContext().packageManager?.getPackagesForUid(nextUid) - ?.first() ?: "" - } - return uidPageNameMap[nextUid] ?: "" - } - - fun handleStop() { - GlobalState.runLock.withLock { - if (GlobalState.runState.value == RunState.STOP) return - GlobalState.runState.value = RunState.STOP - flClashService?.stop() - stopForegroundJob() - Core.stopTun() - GlobalState.handleTryDestroy() - } - } - - private fun bindService() { - if (isBind) { - FlClashApplication.getAppContext().unbindService(connection) - } - val intent = when (options?.enable == true) { - true -> Intent(FlClashApplication.getAppContext(), FlClashVpnService::class.java) - false -> Intent(FlClashApplication.getAppContext(), FlClashService::class.java) - } - FlClashApplication.getAppContext().bindService(intent, connection, Context.BIND_AUTO_CREATE) - } -} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/follow/clash/services/BaseServiceInterface.kt b/android/app/src/main/kotlin/com/follow/clash/services/BaseServiceInterface.kt deleted file mode 100644 index 287267b..0000000 --- a/android/app/src/main/kotlin/com/follow/clash/services/BaseServiceInterface.kt +++ /dev/null @@ -1,96 +0,0 @@ -package com.follow.clash.services - -import android.annotation.SuppressLint -import android.app.Notification -import android.app.Notification.FOREGROUND_SERVICE_IMMEDIATE -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.PendingIntent -import android.app.Service -import android.content.Intent -import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC -import android.os.Build -import androidx.core.app.NotificationCompat -import com.follow.clash.GlobalState -import com.follow.clash.MainActivity -import com.follow.clash.R -import com.follow.clash.extensions.getActionPendingIntent -import com.follow.clash.models.VpnOptions -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async - -interface BaseServiceInterface { - - fun start(options: VpnOptions): Int - - fun stop() - - suspend fun startForeground(title: String, content: String) -} - -fun Service.createFlClashNotificationBuilder(): Deferred = - CoroutineScope(Dispatchers.Main).async { - val stopText = GlobalState.getText("stop") - val intent = Intent(this@createFlClashNotificationBuilder, MainActivity::class.java) - - val pendingIntent = if (Build.VERSION.SDK_INT >= 31) { - PendingIntent.getActivity( - this@createFlClashNotificationBuilder, - 0, - intent, - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT - ) - } else { - PendingIntent.getActivity( - this@createFlClashNotificationBuilder, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT - ) - } - - with( - NotificationCompat.Builder( - this@createFlClashNotificationBuilder, GlobalState.NOTIFICATION_CHANNEL - ) - ) { - setSmallIcon(R.drawable.ic) - setContentTitle("FlClash") - setContentIntent(pendingIntent) - setCategory(NotificationCompat.CATEGORY_SERVICE) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE - } - setOngoing(true) - addAction( - 0, stopText, getActionPendingIntent("STOP") - ) - setShowWhen(false) - setOnlyAlertOnce(true) - } - } - -@SuppressLint("ForegroundServiceType") -fun Service.startForeground(notification: Notification) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val manager = getSystemService(NotificationManager::class.java) - var channel = manager?.getNotificationChannel(GlobalState.NOTIFICATION_CHANNEL) - if (channel == null) { - channel = NotificationChannel( - GlobalState.NOTIFICATION_CHANNEL, "SERVICE_CHANNEL", NotificationManager.IMPORTANCE_LOW - ) - manager?.createNotificationChannel(channel) - } - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - try { - startForeground( - GlobalState.NOTIFICATION_ID, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC - ) - } catch (_: Exception) { - startForeground(GlobalState.NOTIFICATION_ID, notification) - } - } else { - startForeground(GlobalState.NOTIFICATION_ID, notification) - } -} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/follow/clash/services/FlClashService.kt b/android/app/src/main/kotlin/com/follow/clash/services/FlClashService.kt deleted file mode 100644 index 2f09531..0000000 --- a/android/app/src/main/kotlin/com/follow/clash/services/FlClashService.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.follow.clash.services - -import android.annotation.SuppressLint -import android.app.Service -import android.content.Intent -import android.os.Binder -import android.os.Build -import android.os.IBinder -import androidx.core.app.NotificationCompat -import com.follow.clash.GlobalState -import com.follow.clash.models.VpnOptions - - -class FlClashService : Service(), BaseServiceInterface { - - override fun start(options: VpnOptions) = 0 - - override fun stop() { - stopSelf() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - stopForeground(STOP_FOREGROUND_REMOVE) - } - } - - private var cachedBuilder: NotificationCompat.Builder? = null - - private suspend fun notificationBuilder(): NotificationCompat.Builder { - if (cachedBuilder == null) { - cachedBuilder = createFlClashNotificationBuilder().await() - } - return cachedBuilder!! - } - - @SuppressLint("ForegroundServiceType") - override suspend fun startForeground(title: String, content: String) { - startForeground( - notificationBuilder() - .setContentTitle(title) - .setContentText(content).build() - ) - } - - override fun onTrimMemory(level: Int) { - super.onTrimMemory(level) - GlobalState.getCurrentVPNPlugin()?.requestGc() - } - - - private val binder = LocalBinder() - - inner class LocalBinder : Binder() { - fun getService(): FlClashService = this@FlClashService - } - - override fun onBind(intent: Intent): IBinder { - return binder - } - - override fun onUnbind(intent: Intent?): Boolean { - return super.onUnbind(intent) - } - - override fun onDestroy() { - stop() - super.onDestroy() - } -} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/follow/clash/services/FlClashTileService.kt b/android/app/src/main/kotlin/com/follow/clash/services/FlClashTileService.kt deleted file mode 100644 index 76e4083..0000000 --- a/android/app/src/main/kotlin/com/follow/clash/services/FlClashTileService.kt +++ /dev/null @@ -1,77 +0,0 @@ -package com.follow.clash.services - -import android.annotation.SuppressLint -import android.app.PendingIntent -import android.content.Intent -import android.os.Build -import android.service.quicksettings.Tile -import android.service.quicksettings.TileService -import androidx.annotation.RequiresApi -import androidx.lifecycle.Observer -import com.follow.clash.GlobalState -import com.follow.clash.RunState -import com.follow.clash.TempActivity - - -@RequiresApi(Build.VERSION_CODES.N) -class FlClashTileService : TileService() { - - private val observer = Observer { runState -> - updateTile(runState) - } - - private fun updateTile(runState: RunState) { - if (qsTile != null) { - qsTile.state = when (runState) { - RunState.START -> Tile.STATE_ACTIVE - RunState.PENDING -> Tile.STATE_UNAVAILABLE - RunState.STOP -> Tile.STATE_INACTIVE - } - qsTile.updateTile() - } - } - - override fun onStartListening() { - super.onStartListening() - GlobalState.syncStatus() - GlobalState.runState.value?.let { updateTile(it) } - GlobalState.runState.observeForever(observer) - } - - @SuppressLint("StartActivityAndCollapseDeprecated") - private fun activityTransfer() { - val intent = Intent(this, TempActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK) - val pendingIntent = if (Build.VERSION.SDK_INT >= 31) { - PendingIntent.getActivity( - this, - 0, - intent, - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT - ) - } else { - PendingIntent.getActivity( - this, - 0, - intent, - PendingIntent.FLAG_UPDATE_CURRENT - ) - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - startActivityAndCollapse(pendingIntent) - } else { - startActivityAndCollapse(intent) - } - } - - override fun onClick() { - super.onClick() - activityTransfer() - GlobalState.handleToggle() - } - - override fun onDestroy() { - GlobalState.runState.removeObserver(observer) - super.onDestroy() - } -} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/follow/clash/services/FlClashVpnService.kt b/android/app/src/main/kotlin/com/follow/clash/services/FlClashVpnService.kt deleted file mode 100644 index cb6ab40..0000000 --- a/android/app/src/main/kotlin/com/follow/clash/services/FlClashVpnService.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.follow.clash.services - -import android.annotation.SuppressLint -import android.content.Intent -import android.net.ProxyInfo -import android.net.VpnService -import android.os.Binder -import android.os.Build -import android.os.IBinder -import android.os.Parcel -import android.os.RemoteException -import android.util.Log -import androidx.core.app.NotificationCompat -import com.follow.clash.GlobalState -import com.follow.clash.extensions.getIpv4RouteAddress -import com.follow.clash.extensions.getIpv6RouteAddress -import com.follow.clash.extensions.toCIDR -import com.follow.clash.models.AccessControlMode -import com.follow.clash.models.VpnOptions -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - - -class FlClashVpnService : VpnService(), BaseServiceInterface { - override fun onCreate() { - super.onCreate() - GlobalState.initServiceEngine() - } - - override fun start(options: VpnOptions): Int { - return with(Builder()) { - if (options.ipv4Address.isNotEmpty()) { - val cidr = options.ipv4Address.toCIDR() - addAddress(cidr.address, cidr.prefixLength) - Log.d( - "addAddress", - "address: ${cidr.address} prefixLength:${cidr.prefixLength}" - ) - val routeAddress = options.getIpv4RouteAddress() - if (routeAddress.isNotEmpty()) { - try { - routeAddress.forEach { i -> - Log.d( - "addRoute4", - "address: ${i.address} prefixLength:${i.prefixLength}" - ) - addRoute(i.address, i.prefixLength) - } - } catch (_: Exception) { - addRoute("0.0.0.0", 0) - } - } else { - addRoute("0.0.0.0", 0) - } - } else { - addRoute("0.0.0.0", 0) - } - try { - if (options.ipv6Address.isNotEmpty()) { - val cidr = options.ipv6Address.toCIDR() - Log.d( - "addAddress6", - "address: ${cidr.address} prefixLength:${cidr.prefixLength}" - ) - addAddress(cidr.address, cidr.prefixLength) - val routeAddress = options.getIpv6RouteAddress() - if (routeAddress.isNotEmpty()) { - try { - routeAddress.forEach { i -> - Log.d( - "addRoute6", - "address: ${i.address} prefixLength:${i.prefixLength}" - ) - addRoute(i.address, i.prefixLength) - } - } catch (_: Exception) { - addRoute("::", 0) - } - } else { - addRoute("::", 0) - } - } - }catch (_:Exception){ - Log.d( - "addAddress6", - "IPv6 is not supported." - ) - } - addDnsServer(options.dnsServerAddress) - setMtu(9000) - options.accessControl.let { accessControl -> - if (accessControl.enable) { - when (accessControl.mode) { - AccessControlMode.acceptSelected -> { - (accessControl.acceptList + packageName).forEach { - addAllowedApplication(it) - } - } - - AccessControlMode.rejectSelected -> { - (accessControl.rejectList - packageName).forEach { - addDisallowedApplication(it) - } - } - } - } - } - setSession("FlClash") - setBlocking(false) - if (Build.VERSION.SDK_INT >= 29) { - setMetered(false) - } - if (options.allowBypass) { - allowBypass() - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.systemProxy) { - setHttpProxy( - ProxyInfo.buildDirectProxy( - "127.0.0.1", - options.port, - options.bypassDomain - ) - ) - } - establish()?.detachFd() - ?: throw NullPointerException("Establish VPN rejected by system") - } - } - - override fun stop() { - stopSelf() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - stopForeground(STOP_FOREGROUND_REMOVE) - } - } - - private var cachedBuilder: NotificationCompat.Builder? = null - - private suspend fun notificationBuilder(): NotificationCompat.Builder { - if (cachedBuilder == null) { - cachedBuilder = createFlClashNotificationBuilder().await() - } - return cachedBuilder!! - } - - @SuppressLint("ForegroundServiceType") - override suspend fun startForeground(title: String, content: String) { - startForeground( - notificationBuilder() - .setContentTitle(title) - .setContentText(content).build() - ) - } - - override fun onTrimMemory(level: Int) { - super.onTrimMemory(level) - GlobalState.getCurrentVPNPlugin()?.requestGc() - } - - private val binder = LocalBinder() - - inner class LocalBinder : Binder() { - fun getService(): FlClashVpnService = this@FlClashVpnService - - override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean { - try { - val isSuccess = super.onTransact(code, data, reply, flags) - if (!isSuccess) { - CoroutineScope(Dispatchers.Main).launch { - GlobalState.getCurrentTilePlugin()?.handleStop() - } - } - return isSuccess - } catch (e: RemoteException) { - throw e - } - } - } - - override fun onBind(intent: Intent): IBinder { - return binder - } - - override fun onUnbind(intent: Intent?): Boolean { - return super.onUnbind(intent) - } - - override fun onDestroy() { - stop() - super.onDestroy() - } -} \ No newline at end of file diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 1bfd741..8f89645 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -1,3 +1,12 @@ +buildscript { + dependencies { + classpath(libs.build.kotlin) + } +} + +plugins { + id("com.android.library") apply false +} allprojects { repositories { @@ -5,8 +14,10 @@ allprojects { mavenCentral() } } - -val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() rootProject.layout.buildDirectory.value(newBuildDir) subprojects { @@ -20,3 +31,4 @@ subprojects { tasks.register("clean") { delete(rootProject.layout.buildDirectory) } + diff --git a/android/common/.gitignore b/android/common/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/android/common/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/android/common/build.gradle.kts b/android/common/build.gradle.kts new file mode 100644 index 0000000..4fe775f --- /dev/null +++ b/android/common/build.gradle.kts @@ -0,0 +1,42 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + id("com.android.library") + id("org.jetbrains.kotlin.android") +} + +android { + namespace = "com.follow.clash.common" + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = 21 + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + +} + +kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_17) + } +} + +dependencies { + implementation(libs.androidx.core) + implementation(libs.gson) +} \ No newline at end of file diff --git a/android/common/src/main/AndroidManifest.xml b/android/common/src/main/AndroidManifest.xml new file mode 100644 index 0000000..2935c42 --- /dev/null +++ b/android/common/src/main/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/android/common/src/main/java/com/follow/clash/common/Components.kt b/android/common/src/main/java/com/follow/clash/common/Components.kt new file mode 100644 index 0000000..6d8dd50 --- /dev/null +++ b/android/common/src/main/java/com/follow/clash/common/Components.kt @@ -0,0 +1,16 @@ +package com.follow.clash.common + +import android.content.ComponentName + +object Components { + const val PACKAGE_NAME = "com.follow.clash" + + val MAIN_ACTIVITY = + ComponentName(GlobalState.packageName, "${PACKAGE_NAME}.MainActivity") + + val TEMP_ACTIVITY = + ComponentName(GlobalState.packageName, "${PACKAGE_NAME}.TempActivity") + + val BROADCAST_RECEIVER = + ComponentName(GlobalState.packageName, "${PACKAGE_NAME}.BroadcastReceiver") +} \ No newline at end of file diff --git a/android/common/src/main/java/com/follow/clash/common/Enums.kt b/android/common/src/main/java/com/follow/clash/common/Enums.kt new file mode 100644 index 0000000..c56b2b9 --- /dev/null +++ b/android/common/src/main/java/com/follow/clash/common/Enums.kt @@ -0,0 +1,24 @@ +package com.follow.clash.common + +import com.google.gson.annotations.SerializedName + + +enum class QuickAction { + STOP, + START, + TOGGLE, +} + +enum class BroadcastAction { + START, + STOP, + TOGGLE, +} + +enum class AccessControlMode { + @SerializedName("acceptSelected") + ACCEPT_SELECTED, + + @SerializedName("rejectSelected") + REJECT_SELECTED, +} \ No newline at end of file diff --git a/android/common/src/main/java/com/follow/clash/common/Ext.kt b/android/common/src/main/java/com/follow/clash/common/Ext.kt new file mode 100644 index 0000000..3cf95ed --- /dev/null +++ b/android/common/src/main/java/com/follow/clash/common/Ext.kt @@ -0,0 +1,238 @@ +package com.follow.clash.common + +import android.annotation.SuppressLint +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.app.Service +import android.content.BroadcastReceiver +import android.content.ComponentName +import android.content.Context +import android.content.Context.RECEIVER_NOT_EXPORTED +import android.content.Intent +import android.content.IntentFilter +import android.content.ServiceConnection +import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE +import android.os.Build +import android.os.Handler +import android.os.IBinder +import android.os.Looper +import android.os.RemoteException +import android.util.Log +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.retryWhen +import kotlinx.coroutines.withContext +import java.nio.charset.Charset +import kotlin.reflect.KClass + +//fun Context.startForegroundServiceCompat(intent: Intent?) { +// if (Build.VERSION.SDK_INT >= 26) { +// startForegroundService(intent) +// } else { +// startService(intent) +// } +//} + +val KClass<*>.intent: Intent + get() = Intent(GlobalState.application, this.java) + +fun Service.startForegroundCompat(id: Int, notification: Notification) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + startForeground(id, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE) + } else { + startForeground(id, notification) + } +} + +val ComponentName.intent: Intent + get() = Intent().apply { + setComponent(this@intent) + setPackage(GlobalState.packageName) + } + +val QuickAction.action: String + get() = "${GlobalState.application.packageName}.action.${this.name}" + +val QuickAction.quickIntent: Intent + get() = Components.TEMP_ACTIVITY.intent.apply { + action = this@quickIntent.action + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK) + } + +val BroadcastAction.action: String + get() = "${GlobalState.application.packageName}.intent.action.${this.name}" + +val BroadcastAction.quickIntent: Intent + get() = Components.BROADCAST_RECEIVER.intent.apply { + action = this@quickIntent.action + } + +fun BroadcastAction.sendBroadcast() { + val intent = Intent().apply { + action = this@sendBroadcast.action + Log.d("[sendBroadcast]", "$action") + setPackage(GlobalState.packageName) + } + GlobalState.application.sendBroadcast( + intent, GlobalState.RECEIVE_BROADCASTS_PERMISSIONS + ) +} + + +val Intent.toPendingIntent: PendingIntent + get() = PendingIntent.getActivity( + GlobalState.application, + 0, + this, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + ) + + +fun Service.startForeground(notification: Notification) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val manager = getSystemService(NotificationManager::class.java) + var channel = manager?.getNotificationChannel(GlobalState.NOTIFICATION_CHANNEL) + if (channel == null) { + channel = NotificationChannel( + GlobalState.NOTIFICATION_CHANNEL, + "SERVICE_CHANNEL", + NotificationManager.IMPORTANCE_LOW + ) + manager?.createNotificationChannel(channel) + } + } + startForegroundCompat(GlobalState.NOTIFICATION_ID, notification) +} + +@SuppressLint("UnspecifiedRegisterReceiverFlag") +fun Context.registerReceiverCompat( + receiver: BroadcastReceiver, + filter: IntentFilter, +) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + registerReceiver(receiver, filter, RECEIVER_NOT_EXPORTED) +} else { + registerReceiver(receiver, filter) +} + +fun Context.receiveBroadcastFlow( + configure: IntentFilter.() -> Unit, +): Flow = callbackFlow { + val filter = IntentFilter().apply(configure) + val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (context == null || intent == null) return + trySend(intent) + } + } + registerReceiverCompat(receiver, filter) + awaitClose { unregisterReceiver(receiver) } +} + + +inline fun Context.bindServiceFlow( + intent: Intent, + flags: Int = Context.BIND_AUTO_CREATE, + maxRetries: Int = 5, + retryDelayMillis: Long = 200L +): Flow> = callbackFlow { + val connection = object : ServiceConnection { + override fun onServiceConnected(name: ComponentName?, binder: IBinder?) { + if (binder != null) { + try { + @Suppress("UNCHECKED_CAST") val casted = binder as? T + if (casted != null) { + trySend(Pair(casted, "")) + } else { + trySend(Pair(null, "Binder is not of type ${T::class.java}")) + } + } catch (e: RemoteException) { + trySend(Pair(null, "Failed to link to death: ${e.message}")) + } + } else { + trySend(Pair(null, "Binder empty")) + } + } + + override fun onServiceDisconnected(name: ComponentName?) { + trySend(Pair(null, "Service disconnected")) + } + } + + val success = withContext(Dispatchers.Main) { + bindService(intent, connection, flags) + } + + if (!success) { + throw IllegalStateException("bindService() failed, will retry") + } + + awaitClose { + Handler(Looper.getMainLooper()).post { + unbindService(connection) + } + } +}.retryWhen { cause, attempt -> + if (attempt < maxRetries && cause is Exception) { + delay(retryDelayMillis) + true + } else { + false + } +} + + +val Long.formatBytes: String + get() { + val units = arrayOf("B", "KB", "MB", "GB", "TB") + var size = this.toDouble() + var unitIndex = 0 + + while (size >= 1024 && unitIndex < units.size - 1) { + size /= 1024 + unitIndex++ + } + + return if (unitIndex == 0) { + "${size.toLong()}${units[unitIndex]}" + } else { + "%.1f${units[unitIndex]}".format(size) + } + } + +fun String.chunkedForAidl(charset: Charset = Charsets.UTF_8): List { + val allBytes = toByteArray(charset) + val total = allBytes.size + + val maxBytes = when { + total <= 100 * 1024 -> total + total <= 1024 * 1024 -> 64 * 1024 + total <= 10 * 1024 * 1024 -> 128 * 1024 + else -> 256 * 1024 + } + + val result = mutableListOf() + var index = 0 + while (index < total) { + val end = minOf(index + maxBytes, total) + result.add(allBytes.copyOfRange(index, end)) + index = end + } + return result +} + + +fun > T.formatString(charset: Charset = Charsets.UTF_8): String { + val totalSize = this.sumOf { it.size } + val combined = ByteArray(totalSize) + var offset = 0 + forEach { byteArray -> + byteArray.copyInto(combined, offset) + offset += byteArray.size + } + return String(combined, charset) +} \ No newline at end of file diff --git a/android/common/src/main/java/com/follow/clash/common/GlobalState.kt b/android/common/src/main/java/com/follow/clash/common/GlobalState.kt new file mode 100644 index 0000000..3ef8957 --- /dev/null +++ b/android/common/src/main/java/com/follow/clash/common/GlobalState.kt @@ -0,0 +1,34 @@ +package com.follow.clash.common + +import android.app.Application +import android.util.Log +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers + +object GlobalState : CoroutineScope by CoroutineScope(Dispatchers.Default) { + + const val NOTIFICATION_CHANNEL = "FlClash" + + const val NOTIFICATION_ID = 1 + + val packageName: String + get() = _application.packageName + + val RECEIVE_BROADCASTS_PERMISSIONS: String + get() = "${packageName}.permission.RECEIVE_BROADCASTS" + + + private lateinit var _application: Application + + val application: Application + get() = _application + + + fun log(text: String) { + Log.d("[FlClash]", text) + } + + fun init(application: Application) { + _application = application + } +} \ No newline at end of file diff --git a/android/common/src/main/java/com/follow/clash/common/Service.kt b/android/common/src/main/java/com/follow/clash/common/Service.kt new file mode 100644 index 0000000..949198e --- /dev/null +++ b/android/common/src/main/java/com/follow/clash/common/Service.kt @@ -0,0 +1,72 @@ +package com.follow.clash.common + +import android.content.Intent +import android.os.IBinder +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import kotlinx.coroutines.withTimeout +import java.util.concurrent.atomic.AtomicBoolean + +class ServiceDelegate( + private val intent: Intent, + private val onServiceDisconnected: ((String) -> Unit)? = null, + private val interfaceCreator: (IBinder) -> T, +) : CoroutineScope by CoroutineScope(SupervisorJob() + Dispatchers.Default) { + + private val _bindingState = AtomicBoolean(false) + + private var _serviceState = MutableStateFlow?>(null) + + val serviceState: StateFlow?> = _serviceState + private var job: Job? = null + + private fun handleBind(data: Pair) { + data.first?.let { + _serviceState.value = Pair(interfaceCreator(it), data.second) + } ?: run { + _serviceState.value = Pair(null, data.second) + unbind() + onServiceDisconnected?.invoke(data.second) + _bindingState.set(false) + } + } + + fun bind() { + if (_bindingState.compareAndSet(false, true)) { + job?.cancel() + job = null + _serviceState.value = null + job = launch { + GlobalState.application.bindServiceFlow(intent).collect { handleBind(it) } + } + } + } + + suspend inline fun useService( + timeoutMillis: Long = 5000, crossinline block: (T) -> R + ): Result { + return runCatching { + withTimeout(timeoutMillis) { + val state = serviceState.filterNotNull().first() + state.first?.let { + block(it) + } ?: throw Exception(state.second) + } + } + } + + fun unbind() { + if (_bindingState.compareAndSet(true, false)) { + job?.cancel() + job = null + _serviceState.value = null + } + } +} \ No newline at end of file diff --git a/android/common/src/main/java/com/follow/clash/common/Utils.kt b/android/common/src/main/java/com/follow/clash/common/Utils.kt new file mode 100644 index 0000000..2b22dd5 --- /dev/null +++ b/android/common/src/main/java/com/follow/clash/common/Utils.kt @@ -0,0 +1,14 @@ +package com.follow.clash.common + +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow + + +fun tickerFlow(delayMillis: Long, initialDelayMillis: Long = delayMillis): Flow = flow { + delay(initialDelayMillis) + while (true) { + emit(Unit) + delay(delayMillis) + } +} \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/common/src/main/res/values/strings.xml similarity index 58% rename from android/app/src/main/res/values/strings.xml rename to android/common/src/main/res/values/strings.xml index 2000dd1..c31b207 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/common/src/main/res/values/strings.xml @@ -1,4 +1,4 @@ - FlClash + FlClash diff --git a/android/core/build.gradle.kts b/android/core/build.gradle.kts index 1a18c91..38c9f16 100644 --- a/android/core/build.gradle.kts +++ b/android/core/build.gradle.kts @@ -1,4 +1,4 @@ -import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.argumentsWithVarargAsSingleArray +import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { id("com.android.library") @@ -7,22 +7,13 @@ plugins { android { namespace = "com.follow.clash.core" - compileSdk = 35 - ndkVersion = "28.0.13004108" + compileSdk = libs.versions.compileSdk.get().toInt() + ndkVersion = libs.versions.ndkVersion.get() defaultConfig { - minSdk = 21 + minSdk = libs.versions.minSdk.get().toInt() } - buildTypes { - release { - isJniDebuggable = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } - } sourceSets { getByName("main") { @@ -37,17 +28,30 @@ android { } } - kotlinOptions { - jvmTarget = "17" - } - compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } + + buildTypes { + release { + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } } + +kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_17) + } +} + + dependencies { - implementation("androidx.annotation:annotation-jvm:1.9.1") + implementation(libs.annotation.jvm) } val copyNativeLibs by tasks.register("copyNativeLibs") { @@ -56,6 +60,18 @@ val copyNativeLibs by tasks.register("copyNativeLibs") { } from("../../libclash/android") into("src/main/jniLibs") + + doLast { + val includesDir = file("src/main/jniLibs/includes") + val targetDir = file("src/main/cpp/includes") + if (includesDir.exists()) { + copy { + from(includesDir) + into(targetDir) + } + delete(includesDir) + } + } } afterEvaluate { diff --git a/android/core/proguard-rules.pro b/android/core/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/android/core/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/android/core/src/main/AndroidManifest.xml b/android/core/src/main/AndroidManifest.xml index a5918e6..294dbaa 100644 --- a/android/core/src/main/AndroidManifest.xml +++ b/android/core/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ + \ No newline at end of file diff --git a/android/core/src/main/cpp/CMakeLists.txt b/android/core/src/main/cpp/CMakeLists.txt index cdd7623..72e5c10 100644 --- a/android/core/src/main/cpp/CMakeLists.txt +++ b/android/core/src/main/cpp/CMakeLists.txt @@ -6,7 +6,9 @@ message("CMAKE_SOURCE_DIR ${CMAKE_SOURCE_DIR}") message("CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE}") + if (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + # set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) add_compile_options(-O3) add_compile_options(-flto) @@ -33,7 +35,7 @@ message("LIB_CLASH_PATH ${LIB_CLASH_PATH}") if (EXISTS ${LIB_CLASH_PATH}) message("Found libclash.so for ABI ${ANDROID_ABI}") add_compile_definitions(LIBCLASH) - include_directories(${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}) + include_directories(${CMAKE_SOURCE_DIR}/../cpp/includes/${ANDROID_ABI}) link_directories(${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}) add_library(${CMAKE_PROJECT_NAME} SHARED jni_helper.cpp diff --git a/android/core/src/main/cpp/core.cpp b/android/core/src/main/cpp/core.cpp index 19804e8..640327d 100644 --- a/android/core/src/main/cpp/core.cpp +++ b/android/core/src/main/cpp/core.cpp @@ -1,24 +1,75 @@ -#ifdef LIBCLASH #include + +#ifdef LIBCLASH + #include "jni_helper.h" #include "libclash.h" +#include "bride.h" extern "C" JNIEXPORT void JNICALL -Java_com_follow_clash_core_Core_startTun(JNIEnv *env, jobject, const jint fd, jobject cb) { +Java_com_follow_clash_core_Core_startTun(JNIEnv *env, jobject thiz, jint fd, jobject cb, + jstring stack, jstring address, jstring dns) { const auto interface = new_global(cb); - startTUN(fd, interface); + startTUN(interface, fd, get_string(stack), get_string(address), get_string(dns)); } extern "C" JNIEXPORT void JNICALL -Java_com_follow_clash_core_Core_stopTun(JNIEnv *) { +Java_com_follow_clash_core_Core_stopTun(JNIEnv *env, jobject thiz) { stopTun(); } +extern "C" +JNIEXPORT void JNICALL +Java_com_follow_clash_core_Core_forceGC(JNIEnv *env, jobject thiz) { + forceGC(); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_follow_clash_core_Core_updateDNS(JNIEnv *env, jobject thiz, jstring dns) { + updateDns(get_string(dns)); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_follow_clash_core_Core_invokeAction(JNIEnv *env, jobject thiz, jstring data, jobject cb) { + const auto interface = new_global(cb); + invokeAction(interface, get_string(data)); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_follow_clash_core_Core_setMessageCallback(JNIEnv *env, jobject thiz, jobject cb) { + const auto interface = new_global(cb); + setMessageCallback(interface); +} + +extern "C" +JNIEXPORT jstring JNICALL +Java_com_follow_clash_core_Core_getTraffic(JNIEnv *env, jobject thiz, + const jboolean only_statistics_proxy) { + return new_string(getTraffic(only_statistics_proxy)); +} + +extern "C" +JNIEXPORT jstring JNICALL +Java_com_follow_clash_core_Core_getTotalTraffic(JNIEnv *env, jobject thiz, + const jboolean only_statistics_proxy) { + return new_string(getTotalTraffic(only_statistics_proxy)); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_follow_clash_core_Core_suspended(JNIEnv *env, jobject thiz, jboolean suspended) { + suspend(suspended); +} + static jmethodID m_tun_interface_protect; static jmethodID m_tun_interface_resolve_process; +static jmethodID m_invoke_interface_result; static void release_jni_object_impl(void *obj) { @@ -26,6 +77,10 @@ static void release_jni_object_impl(void *obj) { del_global(static_cast(obj)); } +static void free_string_impl(char *str) { + free(str); +} + static void call_tun_interface_protect_impl(void *tun_interface, const int fd) { ATTACH_JNI(); env->CallVoidMethod(static_cast(tun_interface), @@ -33,21 +88,29 @@ static void call_tun_interface_protect_impl(void *tun_interface, const int fd) { fd); } -static const char * -call_tun_interface_resolve_process_impl(void *tun_interface, int protocol, +static char * +call_tun_interface_resolve_process_impl(void *tun_interface, const int protocol, const char *source, const char *target, const int uid) { ATTACH_JNI(); - const auto packageName = reinterpret_cast(env->CallObjectMethod(static_cast(tun_interface), - m_tun_interface_resolve_process, - protocol, - new_string(source), - new_string(target), - uid)); + const auto packageName = reinterpret_cast(env->CallObjectMethod( + static_cast(tun_interface), + m_tun_interface_resolve_process, + protocol, + new_string(source), + new_string(target), + uid)); return get_string(packageName); } +static void call_invoke_interface_result_impl(void *invoke_interface, const char *data) { + ATTACH_JNI(); + env->CallVoidMethod(static_cast(invoke_interface), + m_invoke_interface_result, + new_string(data)); +} + extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { @@ -60,13 +123,68 @@ JNI_OnLoad(JavaVM *vm, void *) { const auto c_tun_interface = find_class("com/follow/clash/core/TunInterface"); + const auto c_invoke_interface = find_class("com/follow/clash/core/InvokeInterface"); + m_tun_interface_protect = find_method(c_tun_interface, "protect", "(I)V"); m_tun_interface_resolve_process = find_method(c_tun_interface, "resolverProcess", "(ILjava/lang/String;Ljava/lang/String;I)Ljava/lang/String;"); + m_invoke_interface_result = find_method(c_invoke_interface, "onResult", + "(Ljava/lang/String;)V"); + + + protect_func = &call_tun_interface_protect_impl; + resolve_process_func = &call_tun_interface_resolve_process_impl; + result_func = &call_invoke_interface_result_impl; + release_object_func = &release_jni_object_impl; + free_string_func = &free_string_impl; - registerCallbacks(&call_tun_interface_protect_impl, - &call_tun_interface_resolve_process_impl, - &release_jni_object_impl); return JNI_VERSION_1_6; } +#else +extern "C" +JNIEXPORT void JNICALL +Java_com_follow_clash_core_Core_startTun(JNIEnv *env, jobject thiz, jint fd, jobject cb, + jstring stack, jstring address, jstring dns) { +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_follow_clash_core_Core_stopTun(JNIEnv *env, jobject thiz) { +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_follow_clash_core_Core_invokeAction(JNIEnv *env, jobject thiz, jstring data, jobject cb) { +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_follow_clash_core_Core_forceGC(JNIEnv *env, jobject thiz) { +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_follow_clash_core_Core_updateDNS(JNIEnv *env, jobject thiz, jstring dns) { +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_follow_clash_core_Core_setMessageCallback(JNIEnv *env, jobject thiz, jobject cb) { +} + +extern "C" +JNIEXPORT jstring JNICALL +Java_com_follow_clash_core_Core_getTraffic(JNIEnv *env, jobject thiz, + const jboolean only_statistics_proxy) { +} +extern "C" +JNIEXPORT jstring JNICALL +Java_com_follow_clash_core_Core_getTotalTraffic(JNIEnv *env, jobject thiz, + const jboolean only_statistics_proxy) { +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_follow_clash_core_Core_suspended(JNIEnv *env, jobject thiz, jboolean suspended) { +} #endif diff --git a/android/core/src/main/cpp/jni_helper.h b/android/core/src/main/cpp/jni_helper.h index 57fbd55..a408751 100644 --- a/android/core/src/main/cpp/jni_helper.h +++ b/android/core/src/main/cpp/jni_helper.h @@ -19,7 +19,7 @@ extern void jni_attach_thread(scoped_jni *jni); extern void jni_detach_thread(const scoped_jni *env); -extern void release_string(char **str); +extern void release_string( char **str); #define ATTACH_JNI() __attribute__((unused, cleanup(jni_detach_thread))) \ scoped_jni _jni{}; \ diff --git a/android/core/src/main/java/com/follow/clash/core/Core.kt b/android/core/src/main/java/com/follow/clash/core/Core.kt index c863cae..b906c08 100644 --- a/android/core/src/main/java/com/follow/clash/core/Core.kt +++ b/android/core/src/main/java/com/follow/clash/core/Core.kt @@ -7,7 +7,17 @@ import java.net.URL data object Core { private external fun startTun( fd: Int, - cb: TunInterface + cb: TunInterface, + stack: String, + address: String, + dns: String, + ) + + external fun forceGC( + ) + + external fun updateDNS( + dns: String, ) private fun parseInetSocketAddress(address: String): InetSocketAddress { @@ -19,31 +29,81 @@ data object Core { fun startTun( fd: Int, protect: (Int) -> Boolean, - resolverProcess: (protocol: Int, source: InetSocketAddress, target: InetSocketAddress, uid: Int) -> String + resolverProcess: (protocol: Int, source: InetSocketAddress, target: InetSocketAddress, uid: Int) -> String, + stack: String, + address: String, + dns: String, ) { - startTun(fd, object : TunInterface { - override fun protect(fd: Int) { - protect(fd) - } + startTun( + fd, + object : TunInterface { + override fun protect(fd: Int) { + protect(fd) + } - override fun resolverProcess( - protocol: Int, - source: String, - target: String, - uid: Int - ): String { - return resolverProcess( - protocol, - parseInetSocketAddress(source), - parseInetSocketAddress(target), - uid, - ) - } - }); + override fun resolverProcess( + protocol: Int, + source: String, + target: String, + uid: Int + ): String { + return resolverProcess( + protocol, + parseInetSocketAddress(source), + parseInetSocketAddress(target), + uid, + ) + } + }, + stack, + address, + dns + ) + } + + external fun suspended( + suspended: Boolean, + ) + + private external fun invokeAction( + data: String, + cb: InvokeInterface + ) + + fun invokeAction( + data: String, + cb: (result: String?) -> Unit + ) { + invokeAction( + data, + object : InvokeInterface { + override fun onResult(result: String?) { + cb(result) + } + }, + ) + } + + private external fun setMessageCallback(cb: InvokeInterface) + + fun setMessageCallback( + cb: (result: String?) -> Unit + ) { + setMessageCallback( + object : InvokeInterface { + override fun onResult(result: String?) { + cb(result) + } + }, + ) } external fun stopTun() + external fun getTraffic(onlyStatisticsProxy: Boolean): String + + external fun getTotalTraffic(onlyStatisticsProxy: Boolean): String + init { System.loadLibrary("core") } diff --git a/android/core/src/main/java/com/follow/clash/core/InvokeInterface.kt b/android/core/src/main/java/com/follow/clash/core/InvokeInterface.kt new file mode 100644 index 0000000..0bf3a8a --- /dev/null +++ b/android/core/src/main/java/com/follow/clash/core/InvokeInterface.kt @@ -0,0 +1,8 @@ +package com.follow.clash.core + +import androidx.annotation.Keep + +@Keep +interface InvokeInterface { + fun onResult(result: String?) +} \ No newline at end of file diff --git a/android/gradle.properties b/android/gradle.properties index 9b8b8c5..598d13f 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,3 @@ org.gradle.jvmargs=-Xmx4G android.useAndroidX=true android.enableJetifier=true -kotlin_version=1.9.22 -agp_version=8.9.2 diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml new file mode 100644 index 0000000..990edaf --- /dev/null +++ b/android/gradle/libs.versions.toml @@ -0,0 +1,20 @@ +[versions] +#agp = "8.10.1" +minSdk = "23" +targetSdk = "36" +compileSdk = "36" +ndkVersion = "28.0.13004108" +coreKtx = "1.17.0" +annotationJvm = "1.9.1" +coreSplashscreen = "1.0.1" +gson = "2.13.1" +kotlin = "2.2.10" +smaliDexlib2 = "3.0.9" + +[libraries] +build-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +androidx-core = { module = "androidx.core:core-ktx", version.ref = "coreKtx" } +annotation-jvm = { module = "androidx.annotation:annotation-jvm", version.ref = "annotationJvm" } +core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" } +gson = { module = "com.google.code.gson:gson", version.ref = "gson" } +smali-dexlib2 = { module = "com.android.tools.smali:smali-dexlib2", version.ref = "smaliDexlib2" } \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 90df0e8..6064aeb 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,5 +2,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip diff --git a/android/service/.gitignore b/android/service/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/android/service/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/android/service/build.gradle.kts b/android/service/build.gradle.kts new file mode 100644 index 0000000..8d9637b --- /dev/null +++ b/android/service/build.gradle.kts @@ -0,0 +1,48 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + id("com.android.library") + id("org.jetbrains.kotlin.android") + id("kotlin-parcelize") +} + +android { + namespace = "com.follow.clash.service" + compileSdk = 36 + + defaultConfig { + minSdk = libs.versions.minSdk.get().toInt() + } + + buildFeatures { + aidl = true + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + buildTypes { + release { + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } +} + +kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_17) + } +} + + +dependencies { + implementation(project(":core")) + implementation(project(":common")) + implementation(libs.gson) + implementation(libs.androidx.core) +} \ No newline at end of file diff --git a/android/service/src/main/AndroidManifest.xml b/android/service/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9764635 --- /dev/null +++ b/android/service/src/main/AndroidManifest.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/service/src/main/aidl/com/follow/clash/service/ICallbackInterface.aidl b/android/service/src/main/aidl/com/follow/clash/service/ICallbackInterface.aidl new file mode 100644 index 0000000..0588307 --- /dev/null +++ b/android/service/src/main/aidl/com/follow/clash/service/ICallbackInterface.aidl @@ -0,0 +1,6 @@ +// ICallbackInterface.aidl +package com.follow.clash.service; + +interface ICallbackInterface { + void onResult(in byte[] result, boolean isSuccess); +} \ No newline at end of file diff --git a/android/service/src/main/aidl/com/follow/clash/service/IMessageInterface.aidl b/android/service/src/main/aidl/com/follow/clash/service/IMessageInterface.aidl new file mode 100644 index 0000000..1822a7d --- /dev/null +++ b/android/service/src/main/aidl/com/follow/clash/service/IMessageInterface.aidl @@ -0,0 +1,6 @@ +// IMessageInterface.aidl +package com.follow.clash.service; + +interface IMessageInterface { + void onResult(String result); +} \ No newline at end of file diff --git a/android/service/src/main/aidl/com/follow/clash/service/IRemoteInterface.aidl b/android/service/src/main/aidl/com/follow/clash/service/IRemoteInterface.aidl new file mode 100644 index 0000000..c75b3bc --- /dev/null +++ b/android/service/src/main/aidl/com/follow/clash/service/IRemoteInterface.aidl @@ -0,0 +1,15 @@ +// IRemoteInterface.aidl +package com.follow.clash.service; + +import com.follow.clash.service.ICallbackInterface; +import com.follow.clash.service.IMessageInterface; +import com.follow.clash.service.models.VpnOptions; +import com.follow.clash.service.models.NotificationParams; + +interface IRemoteInterface { + void invokeAction(in String data, in ICallbackInterface callback); + void updateNotificationParams(in NotificationParams params); + void startService(in VpnOptions options,in boolean inApp); + void stopService(); + void setMessageCallback(in IMessageInterface messageCallback); +} \ No newline at end of file diff --git a/android/service/src/main/aidl/com/follow/clash/service/models/AccessControl.aidl b/android/service/src/main/aidl/com/follow/clash/service/models/AccessControl.aidl new file mode 100644 index 0000000..ea75b66 --- /dev/null +++ b/android/service/src/main/aidl/com/follow/clash/service/models/AccessControl.aidl @@ -0,0 +1,4 @@ +//AccessControl.aidl +package com.follow.clash.service.models; + +parcelable AccessControl; \ No newline at end of file diff --git a/android/service/src/main/aidl/com/follow/clash/service/models/NotificationParams.aidl b/android/service/src/main/aidl/com/follow/clash/service/models/NotificationParams.aidl new file mode 100644 index 0000000..524221e --- /dev/null +++ b/android/service/src/main/aidl/com/follow/clash/service/models/NotificationParams.aidl @@ -0,0 +1,4 @@ +//NotificationParams.aidl +package com.follow.clash.service.models; + +parcelable NotificationParams; \ No newline at end of file diff --git a/android/service/src/main/aidl/com/follow/clash/service/models/VpnOptions.aidl b/android/service/src/main/aidl/com/follow/clash/service/models/VpnOptions.aidl new file mode 100644 index 0000000..c1d780d --- /dev/null +++ b/android/service/src/main/aidl/com/follow/clash/service/models/VpnOptions.aidl @@ -0,0 +1,6 @@ +//VpnOptions.aidl +package com.follow.clash.service.models; + +import com.follow.clash.service.models.AccessControl; + +parcelable VpnOptions; \ No newline at end of file diff --git a/android/service/src/main/java/com/follow/clash/service/CommonService.kt b/android/service/src/main/java/com/follow/clash/service/CommonService.kt new file mode 100644 index 0000000..0a41376 --- /dev/null +++ b/android/service/src/main/java/com/follow/clash/service/CommonService.kt @@ -0,0 +1,55 @@ +package com.follow.clash.service + +import android.app.Service +import android.content.Intent +import android.os.Binder +import android.os.IBinder +import com.follow.clash.core.Core +import com.follow.clash.service.modules.NetworkObserveModule +import com.follow.clash.service.modules.NotificationModule +import com.follow.clash.service.modules.SuspendModule +import com.follow.clash.service.modules.moduleLoader +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers + +class CommonService : Service(), IBaseService, + CoroutineScope by CoroutineScope(Dispatchers.Default) { + + private val self: CommonService + get() = this + + private val loader = moduleLoader { + install(NetworkObserveModule(self)) + install(NotificationModule(self)) + install(SuspendModule(self)) + } + + override fun onCreate() { + super.onCreate() + handleCreate() + } + + override fun onLowMemory() { + Core.forceGC() + super.onLowMemory() + } + + private val binder = LocalBinder() + + inner class LocalBinder : Binder() { + fun getService(): CommonService = this@CommonService + } + + override fun onBind(intent: Intent): IBinder { + return binder + } + + override fun start() { + loader.load() + } + + override fun stop() { + loader.cancel() + stopSelf() + } +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/follow/clash/FilesProvider.kt b/android/service/src/main/java/com/follow/clash/service/FilesProvider.kt similarity index 58% rename from android/app/src/main/kotlin/com/follow/clash/FilesProvider.kt rename to android/service/src/main/java/com/follow/clash/service/FilesProvider.kt index 5ae2f06..26601fe 100644 --- a/android/app/src/main/kotlin/com/follow/clash/FilesProvider.kt +++ b/android/service/src/main/java/com/follow/clash/service/FilesProvider.kt @@ -1,35 +1,33 @@ -package com.follow.clash +package com.follow.clash.service import android.database.Cursor import android.database.MatrixCursor import android.os.CancellationSignal import android.os.ParcelFileDescriptor -import android.provider.DocumentsContract.Document -import android.provider.DocumentsContract.Root +import android.provider.DocumentsContract import android.provider.DocumentsProvider import java.io.File import java.io.FileNotFoundException - class FilesProvider : DocumentsProvider() { companion object { private const val DEFAULT_ROOT_ID = "0" private val DEFAULT_DOCUMENT_COLUMNS = arrayOf( - Document.COLUMN_DOCUMENT_ID, - Document.COLUMN_DISPLAY_NAME, - Document.COLUMN_MIME_TYPE, - Document.COLUMN_FLAGS, - Document.COLUMN_SIZE, + DocumentsContract.Document.COLUMN_DOCUMENT_ID, + DocumentsContract.Document.COLUMN_DISPLAY_NAME, + DocumentsContract.Document.COLUMN_MIME_TYPE, + DocumentsContract.Document.COLUMN_FLAGS, + DocumentsContract.Document.COLUMN_SIZE, ) private val DEFAULT_ROOT_COLUMNS = arrayOf( - Root.COLUMN_ROOT_ID, - Root.COLUMN_FLAGS, - Root.COLUMN_ICON, - Root.COLUMN_TITLE, - Root.COLUMN_SUMMARY, - Root.COLUMN_DOCUMENT_ID + DocumentsContract.Root.COLUMN_ROOT_ID, + DocumentsContract.Root.COLUMN_FLAGS, + DocumentsContract.Root.COLUMN_ICON, + DocumentsContract.Root.COLUMN_TITLE, + DocumentsContract.Root.COLUMN_SUMMARY, + DocumentsContract.Root.COLUMN_DOCUMENT_ID ) } @@ -40,12 +38,12 @@ class FilesProvider : DocumentsProvider() { override fun queryRoots(projection: Array?): Cursor { return MatrixCursor(projection ?: DEFAULT_ROOT_COLUMNS).apply { newRow().apply { - add(Root.COLUMN_ROOT_ID, DEFAULT_ROOT_ID) - add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY) - add(Root.COLUMN_ICON, R.mipmap.ic_launcher) - add(Root.COLUMN_TITLE, context!!.getString(R.string.fl_clash)) - add(Root.COLUMN_SUMMARY, "Data") - add(Root.COLUMN_DOCUMENT_ID, "/") + add(DocumentsContract.Root.COLUMN_ROOT_ID, DEFAULT_ROOT_ID) + add(DocumentsContract.Root.COLUMN_FLAGS, DocumentsContract.Root.FLAG_LOCAL_ONLY) + add(DocumentsContract.Root.COLUMN_ICON, R.drawable.ic_service) + add(DocumentsContract.Root.COLUMN_TITLE, "FlClash") + add(DocumentsContract.Root.COLUMN_SUMMARY, "Data") + add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, "/") } } } @@ -87,20 +85,20 @@ class FilesProvider : DocumentsProvider() { private fun includeFile(result: MatrixCursor, file: File) { result.newRow().apply { - add(Document.COLUMN_DOCUMENT_ID, file.absolutePath) - add(Document.COLUMN_DISPLAY_NAME, file.name) - add(Document.COLUMN_SIZE, file.length()) + add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, file.absolutePath) + add(DocumentsContract.Document.COLUMN_DISPLAY_NAME, file.name) + add(DocumentsContract.Document.COLUMN_SIZE, file.length()) add( - Document.COLUMN_FLAGS, - Document.FLAG_SUPPORTS_WRITE or Document.FLAG_SUPPORTS_DELETE + DocumentsContract.Document.COLUMN_FLAGS, + DocumentsContract.Document.FLAG_SUPPORTS_WRITE or DocumentsContract.Document.FLAG_SUPPORTS_DELETE ) - add(Document.COLUMN_MIME_TYPE, getDocumentType(file)) + add(DocumentsContract.Document.COLUMN_MIME_TYPE, getDocumentType(file)) } } private fun getDocumentType(file: File): String { return if (file.isDirectory) { - Document.MIME_TYPE_DIR + DocumentsContract.Document.MIME_TYPE_DIR } else { "application/octet-stream" } diff --git a/android/service/src/main/java/com/follow/clash/service/IBaseService.kt b/android/service/src/main/java/com/follow/clash/service/IBaseService.kt new file mode 100644 index 0000000..738d661 --- /dev/null +++ b/android/service/src/main/java/com/follow/clash/service/IBaseService.kt @@ -0,0 +1,18 @@ +package com.follow.clash.service + +import com.follow.clash.common.BroadcastAction +import com.follow.clash.common.sendBroadcast + +interface IBaseService { + fun handleCreate() { + if (!State.inApp) { + BroadcastAction.START.sendBroadcast() + } else { + State.inApp = false + } + } + + fun start() + + fun stop() +} \ No newline at end of file diff --git a/android/service/src/main/java/com/follow/clash/service/RemoteService.kt b/android/service/src/main/java/com/follow/clash/service/RemoteService.kt new file mode 100644 index 0000000..79c0691 --- /dev/null +++ b/android/service/src/main/java/com/follow/clash/service/RemoteService.kt @@ -0,0 +1,99 @@ +package com.follow.clash.service + +import android.app.Service +import android.content.Intent +import android.os.IBinder +import com.follow.clash.common.ServiceDelegate +import com.follow.clash.common.chunkedForAidl +import com.follow.clash.common.intent +import com.follow.clash.core.Core +import com.follow.clash.service.models.NotificationParams +import com.follow.clash.service.models.VpnOptions +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch + +class RemoteService : Service(), + CoroutineScope by CoroutineScope(SupervisorJob() + Dispatchers.Default) { + private var delegate: ServiceDelegate? = null + private var intent: Intent? = null + + private fun handleStopService() { + launch { + delegate?.useService { service -> + service.stop() + delegate?.unbind() + } + } + } + + private fun handleServiceDisconnected(message: String) { + intent = null + delegate = null + } + + private fun handleStartService() { + launch { + val nextIntent = when (State.options?.enable == true) { + true -> VpnService::class.intent + false -> CommonService::class.intent + } + if (intent != nextIntent) { + delegate?.unbind() + delegate = ServiceDelegate(nextIntent, ::handleServiceDisconnected) { binder -> + when (binder) { + is VpnService.LocalBinder -> binder.getService() + is CommonService.LocalBinder -> binder.getService() + else -> throw IllegalArgumentException("Invalid binder type") + } + } + intent = nextIntent + delegate?.bind() + } + delegate?.useService { service -> + service.start() + } + } + } + + private val binder = object : IRemoteInterface.Stub() { + override fun invokeAction(data: String, callback: ICallbackInterface) { + Core.invokeAction(data) { + val chunks = it?.chunkedForAidl() ?: listOf() + val totalSize = chunks.size + chunks.forEachIndexed { index, chunk -> + callback.onResult(chunk, totalSize - 1 == index) + } + } + } + + override fun updateNotificationParams(params: NotificationParams?) { + State.notificationParamsFlow.tryEmit(params) + } + + override fun startService( + options: VpnOptions, inApp: Boolean + ) { + State.options = options + State.inApp = inApp + handleStartService() + } + + override fun stopService() { + handleStopService() + } + + override fun setMessageCallback(messageCallback: IMessageInterface) { + setMessageCallback(messageCallback::onResult) + } + } + + private fun setMessageCallback(cb: (result: String?) -> Unit) { + Core.setMessageCallback(cb) + } + + override fun onBind(intent: Intent?): IBinder { + return binder + } +} \ No newline at end of file diff --git a/android/service/src/main/java/com/follow/clash/service/State.kt b/android/service/src/main/java/com/follow/clash/service/State.kt new file mode 100644 index 0000000..3857508 --- /dev/null +++ b/android/service/src/main/java/com/follow/clash/service/State.kt @@ -0,0 +1,13 @@ +package com.follow.clash.service + +import com.follow.clash.service.models.NotificationParams +import com.follow.clash.service.models.VpnOptions +import kotlinx.coroutines.flow.MutableStateFlow + +object State { + var options: VpnOptions? = null + var inApp: Boolean = false + var notificationParamsFlow: MutableStateFlow = MutableStateFlow( + NotificationParams() + ) +} \ No newline at end of file diff --git a/android/service/src/main/java/com/follow/clash/service/VpnService.kt b/android/service/src/main/java/com/follow/clash/service/VpnService.kt new file mode 100644 index 0000000..fc85347 --- /dev/null +++ b/android/service/src/main/java/com/follow/clash/service/VpnService.kt @@ -0,0 +1,250 @@ +package com.follow.clash.service + +import android.content.Intent +import android.net.ConnectivityManager +import android.net.ProxyInfo +import android.os.Binder +import android.os.Build +import android.os.IBinder +import android.os.Parcel +import android.os.RemoteException +import android.util.Log +import androidx.core.content.getSystemService +import com.follow.clash.common.AccessControlMode +import com.follow.clash.common.BroadcastAction +import com.follow.clash.common.sendBroadcast +import com.follow.clash.core.Core +import com.follow.clash.service.models.VpnOptions +import com.follow.clash.service.models.getIpv4RouteAddress +import com.follow.clash.service.models.getIpv6RouteAddress +import com.follow.clash.service.models.toCIDR +import com.follow.clash.service.modules.NetworkObserveModule +import com.follow.clash.service.modules.NotificationModule +import com.follow.clash.service.modules.SuspendModule +import com.follow.clash.service.modules.moduleLoader +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import java.net.InetSocketAddress +import android.net.VpnService as SystemVpnService + +class VpnService : SystemVpnService(), IBaseService, + CoroutineScope by CoroutineScope(Dispatchers.Default) { + + private val self: VpnService + get() = this + + private val loader = moduleLoader { + install(NetworkObserveModule(self)) + install(NotificationModule(self)) + install(SuspendModule(self)) + } + + override fun onCreate() { + super.onCreate() + handleCreate() + } + + private val connectivity by lazy { + getSystemService() + } + private val uidPageNameMap = mutableMapOf() + + private fun resolverProcess( + protocol: Int, + source: InetSocketAddress, + target: InetSocketAddress, + uid: Int, + ): String { + val nextUid = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + connectivity?.getConnectionOwnerUid(protocol, source, target) ?: -1 + } else { + uid + } + if (nextUid == -1) { + return "" + } + if (!uidPageNameMap.containsKey(nextUid)) { + uidPageNameMap[nextUid] = this.packageManager?.getPackagesForUid(nextUid)?.first() ?: "" + } + return uidPageNameMap[nextUid] ?: "" + } + + val VpnOptions.address + get(): String = buildString { + append(IPV4_ADDRESS) + if (ipv6) { + append(",") + append(IPV6_ADDRESS) + } + } + + val VpnOptions.dns + get(): String { + if (dnsHijacking) { + return NET_ANY + } + return buildString { + append(DNS) + if (ipv6) { + append(",") + append(DNS6) + } + } + } + + + override fun onLowMemory() { + Core.forceGC() + super.onLowMemory() + } + + private val binder = LocalBinder() + + inner class LocalBinder : Binder() { + fun getService(): VpnService = this@VpnService + + override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean { + try { + val isSuccess = super.onTransact(code, data, reply, flags) + if (!isSuccess) { + BroadcastAction.STOP.sendBroadcast() + } + return isSuccess + } catch (e: RemoteException) { + throw e + } + } + } + + override fun onBind(intent: Intent): IBinder { + return binder + } + + private fun handleStart(options: VpnOptions) { + val fd = with(Builder()) { + val cidr = IPV4_ADDRESS.toCIDR() + addAddress(cidr.address, cidr.prefixLength) + Log.d( + "addAddress", "address: ${cidr.address} prefixLength:${cidr.prefixLength}" + ) + val routeAddress = options.getIpv4RouteAddress() + if (routeAddress.isNotEmpty()) { + try { + routeAddress.forEach { i -> + Log.d( + "addRoute4", "address: ${i.address} prefixLength:${i.prefixLength}" + ) + addRoute(i.address, i.prefixLength) + } + } catch (_: Exception) { + addRoute(NET_ANY, 0) + } + } else { + addRoute(NET_ANY, 0) + } + if (options.ipv6) { + try { + val cidr = IPV6_ADDRESS.toCIDR() + Log.d( + "addAddress6", "address: ${cidr.address} prefixLength:${cidr.prefixLength}" + ) + addAddress(cidr.address, cidr.prefixLength) + } catch (_: Exception) { + Log.d( + "addAddress6", "IPv6 is not supported." + ) + } + + try { + val routeAddress = options.getIpv6RouteAddress() + if (routeAddress.isNotEmpty()) { + try { + routeAddress.forEach { i -> + Log.d( + "addRoute6", + "address: ${i.address} prefixLength:${i.prefixLength}" + ) + addRoute(i.address, i.prefixLength) + } + } catch (_: Exception) { + addRoute("::", 0) + } + } else { + addRoute(NET_ANY6, 0) + } + } catch (_: Exception) { + addRoute(NET_ANY6, 0) + } + } + addDnsServer(DNS) + if (options.ipv6) { + addDnsServer(DNS6) + } + setMtu(9000) + options.accessControl.let { accessControl -> + if (accessControl.enable) { + when (accessControl.mode) { + AccessControlMode.ACCEPT_SELECTED -> { + (accessControl.acceptList + packageName).forEach { + addAllowedApplication(it) + } + } + + AccessControlMode.REJECT_SELECTED -> { + (accessControl.rejectList - packageName).forEach { + addDisallowedApplication(it) + } + } + } + } + } + setSession("FlClash") + setBlocking(false) + if (Build.VERSION.SDK_INT >= 29) { + setMetered(false) + } + if (options.allowBypass) { + allowBypass() + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.systemProxy) { + setHttpProxy( + ProxyInfo.buildDirectProxy( + "127.0.0.1", options.port, options.bypassDomain + ) + ) + } + establish()?.detachFd() + ?: throw NullPointerException("Establish VPN rejected by system") + } + Core.startTun( + fd, + protect = this::protect, + resolverProcess = this::resolverProcess, + options.stack, + options.address, + options.dns + ) + } + + override fun start() { + loader.load() + State.options?.let { + handleStart(it) + } + } + + override fun stop() { + loader.cancel() + Core.stopTun() + stopSelf() + } + + companion object { + private const val IPV4_ADDRESS = "172.19.0.1/30" + private const val IPV6_ADDRESS = "fdfe:dcba:9876::1/126" + private const val DNS = "172.19.0.2" + private const val DNS6 = "fdfe:dcba:9876::2" + private const val NET_ANY = "0.0.0.0" + private const val NET_ANY6 = "::" + } +} \ No newline at end of file diff --git a/android/service/src/main/java/com/follow/clash/service/models/NotificationParams.kt b/android/service/src/main/java/com/follow/clash/service/models/NotificationParams.kt new file mode 100644 index 0000000..7196005 --- /dev/null +++ b/android/service/src/main/java/com/follow/clash/service/models/NotificationParams.kt @@ -0,0 +1,11 @@ +package com.follow.clash.service.models + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class NotificationParams( + val title: String = "FlClash", + val stopText: String = "STOP", + val onlyStatisticsProxy: Boolean = false, +) : Parcelable \ No newline at end of file diff --git a/android/service/src/main/java/com/follow/clash/service/models/Traffic.kt b/android/service/src/main/java/com/follow/clash/service/models/Traffic.kt new file mode 100644 index 0000000..0025a2a --- /dev/null +++ b/android/service/src/main/java/com/follow/clash/service/models/Traffic.kt @@ -0,0 +1,19 @@ +package com.follow.clash.service.models + +import com.follow.clash.common.formatBytes +import com.follow.clash.core.Core +import com.google.gson.Gson + +data class Traffic( + val up: Long, + val down: Long, +) + +val Traffic.speedText: String + get() = "${up.formatBytes}/s↑ ${down.formatBytes}/s↓" + +fun Core.getSpeedTrafficText(onlyStatisticsProxy: Boolean): String { + val res = getTraffic(onlyStatisticsProxy) + val traffic = Gson().fromJson(res, Traffic::class.java) + return traffic.speedText +} \ No newline at end of file diff --git a/android/service/src/main/java/com/follow/clash/service/models/VpnOptions.kt b/android/service/src/main/java/com/follow/clash/service/models/VpnOptions.kt new file mode 100644 index 0000000..7d18890 --- /dev/null +++ b/android/service/src/main/java/com/follow/clash/service/models/VpnOptions.kt @@ -0,0 +1,83 @@ +package com.follow.clash.service.models + +import android.os.Parcelable +import com.follow.clash.common.AccessControlMode +import kotlinx.parcelize.Parcelize +import java.net.InetAddress + +@Parcelize +data class AccessControl( + val enable: Boolean, + val mode: AccessControlMode, + val acceptList: List, + val rejectList: List, +) : Parcelable + +@Parcelize +data class VpnOptions( + val enable: Boolean, + val port: Int, + val ipv6: Boolean, + val dnsHijacking: Boolean, + val accessControl: AccessControl, + val allowBypass: Boolean, + val systemProxy: Boolean, + val bypassDomain: List, + val stack: String, + val routeAddress: List, +) : Parcelable + +data class CIDR(val address: InetAddress, val prefixLength: Int) + +fun VpnOptions.getIpv4RouteAddress(): List { + return routeAddress.filter { + it.isIpv4() + }.map { + it.toCIDR() + } +} + +fun VpnOptions.getIpv6RouteAddress(): List { + return routeAddress.filter { + it.isIpv6() + }.map { + it.toCIDR() + } +} + +fun String.isIpv4(): Boolean { + val parts = split("/") + if (parts.size != 2) { + throw IllegalArgumentException("Invalid CIDR format") + } + val address = InetAddress.getByName(parts[0]) + return address.address.size == 4 +} + +fun String.isIpv6(): Boolean { + val parts = split("/") + if (parts.size != 2) { + throw IllegalArgumentException("Invalid CIDR format") + } + val address = InetAddress.getByName(parts[0]) + return address.address.size == 16 +} + +fun String.toCIDR(): CIDR { + val parts = split("/") + if (parts.size != 2) { + throw IllegalArgumentException("Invalid CIDR format") + } + val ipAddress = parts[0] + val prefixLength = + parts[1].toIntOrNull() ?: throw IllegalArgumentException("Invalid prefix length") + + val address = InetAddress.getByName(ipAddress) + + val maxPrefix = if (address.address.size == 4) 32 else 128 + if (prefixLength < 0 || prefixLength > maxPrefix) { + throw IllegalArgumentException("Invalid prefix length for IP version") + } + + return CIDR(address, prefixLength) +} \ No newline at end of file diff --git a/android/service/src/main/java/com/follow/clash/service/modules/Module.kt b/android/service/src/main/java/com/follow/clash/service/modules/Module.kt new file mode 100644 index 0000000..f2fb9cd --- /dev/null +++ b/android/service/src/main/java/com/follow/clash/service/modules/Module.kt @@ -0,0 +1,19 @@ +package com.follow.clash.service.modules + +abstract class Module { + + private var isInstall: Boolean = false + + protected abstract fun onInstall() + protected abstract fun onUninstall() + + fun install() { + isInstall = true + onInstall() + } + + fun uninstall() { + onUninstall() + isInstall = false + } +} \ No newline at end of file diff --git a/android/service/src/main/java/com/follow/clash/service/modules/ModuleLoader.kt b/android/service/src/main/java/com/follow/clash/service/modules/ModuleLoader.kt new file mode 100644 index 0000000..d11215c --- /dev/null +++ b/android/service/src/main/java/com/follow/clash/service/modules/ModuleLoader.kt @@ -0,0 +1,51 @@ +package com.follow.clash.service.modules + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock + +interface ModuleLoaderScope { + fun install(module: T): T +} + +interface ModuleLoader { + fun load() + + fun cancel() +} + +private val mutex = Mutex() +fun CoroutineScope.moduleLoader(block: suspend ModuleLoaderScope.() -> Unit): ModuleLoader { + val modules = mutableListOf() + var job: Job? = null + + return object : ModuleLoader { + override fun load() { + job = launch(Dispatchers.IO) { + mutex.withLock { + val scope = object : ModuleLoaderScope { + override fun install(module: T): T { + modules.add(module) + module.install() + return module + } + } + scope.block() + } + } + } + + override fun cancel() { + launch(Dispatchers.IO) { + job?.cancel() + mutex.withLock { + modules.asReversed().forEach { it.uninstall() } + modules.clear() + } + } + } + } +} \ No newline at end of file diff --git a/android/service/src/main/java/com/follow/clash/service/modules/NetworkObserveModule.kt b/android/service/src/main/java/com/follow/clash/service/modules/NetworkObserveModule.kt new file mode 100644 index 0000000..ddef1da --- /dev/null +++ b/android/service/src/main/java/com/follow/clash/service/modules/NetworkObserveModule.kt @@ -0,0 +1,149 @@ +package com.follow.clash.service.modules + +import android.app.Service +import android.net.ConnectivityManager +import android.net.LinkProperties +import android.net.Network +import android.net.NetworkCapabilities +import android.net.NetworkCapabilities.TRANSPORT_SATELLITE +import android.net.NetworkCapabilities.TRANSPORT_USB +import android.net.NetworkRequest +import android.os.Build +import androidx.core.content.getSystemService +import com.follow.clash.core.Core +import java.net.Inet4Address +import java.net.Inet6Address +import java.net.InetAddress +import java.util.concurrent.ConcurrentHashMap + +private data class NetworkInfo( + @Volatile var losingMs: Long = 0, @Volatile var dnsList: List = emptyList() +) { + fun isAvailable(): Boolean = losingMs < System.currentTimeMillis() +} + +class NetworkObserveModule(private val service: Service) : Module() { + + private val networkInfos = ConcurrentHashMap() + private val connectivity by lazy { + service.getSystemService() + } + private var preDnsList = listOf() + + private val request = NetworkRequest.Builder().apply { + addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + addCapability(NetworkCapabilities.NET_CAPABILITY_FOREGROUND) + } + addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + }.build() + + private val callback = object : ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + networkInfos[network] = NetworkInfo() + onUpdateNetwork() + super.onAvailable(network) + } + + override fun onLosing(network: Network, maxMsToLive: Int) { + networkInfos[network]?.losingMs = System.currentTimeMillis() + maxMsToLive + onUpdateNetwork() + setUnderlyingNetworks(network) + super.onLosing(network, maxMsToLive) + } + + override fun onLost(network: Network) { + networkInfos.remove(network) + onUpdateNetwork() + setUnderlyingNetworks(network) + super.onLost(network) + } + + override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) { + networkInfos[network]?.dnsList = linkProperties.dnsServers + onUpdateNetwork() + setUnderlyingNetworks(network) + super.onLinkPropertiesChanged(network, linkProperties) + } + } + + + override fun onInstall() { + onUpdateNetwork() + connectivity?.registerNetworkCallback(request, callback) + } + + private fun networkToInt(entry: Map.Entry): Int { + val capabilities = connectivity?.getNetworkCapabilities(entry.key) + return when { + capabilities == null -> 100 + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN) -> 90 + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> 0 + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> 1 + Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && capabilities.hasTransport( + TRANSPORT_USB + ) -> 2 + + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> 3 + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> 4 + Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && capabilities.hasTransport( + TRANSPORT_SATELLITE + ) -> 5 + + else -> 20 + } + (if (entry.value.isAvailable()) 0 else 10) + } + + fun onUpdateNetwork() { + val dnsList = (networkInfos.asSequence().minByOrNull { networkToInt(it) }?.value?.dnsList + ?: emptyList()).map { x -> x.asSocketAddressText(53) } + if (dnsList == preDnsList) { + return + } + preDnsList = dnsList + Core.updateDNS(dnsList.joinToString { "," }) + } + + fun setUnderlyingNetworks(network: Network) { +// if (service is VpnService && Build.VERSION.SDK_INT in 22..28) { +// service.setUnderlyingNetworks(arrayOf(network)) +// } + } + + override fun onUninstall() { + connectivity?.unregisterNetworkCallback(callback) + networkInfos.clear() + onUpdateNetwork() + } +} + +fun InetAddress.asSocketAddressText(port: Int): String { + return when (this) { + is Inet6Address -> "[${numericToTextFormat(this)}]:$port" + + is Inet4Address -> "${this.hostAddress}:$port" + + else -> throw IllegalArgumentException("Unsupported Inet type ${this.javaClass}") + } +} + +private fun numericToTextFormat(address: Inet6Address): String { + val src = address.address + val sb = StringBuilder(39) + for (i in 0 until 8) { + sb.append( + Integer.toHexString( + src[i shl 1].toInt() shl 8 and 0xff00 or (src[(i shl 1) + 1].toInt() and 0xff) + ) + ) + if (i < 7) { + sb.append(":") + } + } + if (address.scopeId > 0) { + sb.append("%") + sb.append(address.scopeId) + } + return sb.toString() +} \ No newline at end of file diff --git a/android/service/src/main/java/com/follow/clash/service/modules/NotificationModule.kt b/android/service/src/main/java/com/follow/clash/service/modules/NotificationModule.kt new file mode 100644 index 0000000..b478077 --- /dev/null +++ b/android/service/src/main/java/com/follow/clash/service/modules/NotificationModule.kt @@ -0,0 +1,124 @@ +package com.follow.clash.service.modules + +import android.app.Notification.FOREGROUND_SERVICE_IMMEDIATE +import android.app.Service +import android.app.Service.STOP_FOREGROUND_REMOVE +import android.content.Intent +import android.os.Build +import android.os.PowerManager +import androidx.core.app.NotificationCompat +import androidx.core.content.getSystemService +import com.follow.clash.common.Components +import com.follow.clash.common.GlobalState +import com.follow.clash.common.QuickAction +import com.follow.clash.common.quickIntent +import com.follow.clash.common.receiveBroadcastFlow +import com.follow.clash.common.startForeground +import com.follow.clash.common.tickerFlow +import com.follow.clash.common.toPendingIntent +import com.follow.clash.core.Core +import com.follow.clash.service.R +import com.follow.clash.service.State +import com.follow.clash.service.models.NotificationParams +import com.follow.clash.service.models.getSpeedTrafficText +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.launch + +data class ExtendedNotificationParams( + val title: String, + val stopText: String, + val onlyStatisticsProxy: Boolean, + val contentText: String, +) + +val NotificationParams.extended: ExtendedNotificationParams + get() = ExtendedNotificationParams( + title, stopText, onlyStatisticsProxy, Core.getSpeedTrafficText(onlyStatisticsProxy) + ) + +class NotificationModule(private val service: Service) : Module() { + private val scope = CoroutineScope(Dispatchers.Default) + + override fun onInstall() { + State.notificationParamsFlow.value?.let { + update(it.extended) + } + scope.launch { + val screenFlow = service.receiveBroadcastFlow { + addAction(Intent.ACTION_SCREEN_ON) + addAction(Intent.ACTION_SCREEN_OFF) + }.map { intent -> + intent.action == Intent.ACTION_SCREEN_ON + }.onStart { + emit(isScreenOn()) + } + + combine( + tickerFlow(1000, 0), State.notificationParamsFlow, screenFlow + ) { _, params, screenOn -> + params?.extended to screenOn + }.filter { (params, screenOn) -> params != null && screenOn } + .distinctUntilChanged { old, new -> old.first == new.first && old.second == new.second } + .collect { (params, _) -> + update(params!!) + } + } + } + + private fun isScreenOn(): Boolean { + val pm = service.getSystemService() + return when (pm != null) { + true -> pm.isInteractive + false -> true + } + } + + private val notificationBuilder: NotificationCompat.Builder by lazy { + val intent = Intent().setComponent(Components.MAIN_ACTIVITY) + with( + NotificationCompat.Builder( + service, GlobalState.NOTIFICATION_CHANNEL + ) + ) { + setSmallIcon(R.drawable.ic) + setContentTitle("FlClash") + setContentIntent(intent.toPendingIntent) + setCategory(NotificationCompat.CATEGORY_SERVICE) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE + } + setOngoing(true) + setShowWhen(false) + setOnlyAlertOnce(true) + } + } + + private fun update(params: ExtendedNotificationParams) { + service.startForeground( + with(notificationBuilder) { + setContentTitle(params.title) + setContentText(params.contentText) + setPriority(NotificationCompat.PRIORITY_HIGH) + clearActions() + addAction( + 0, params.stopText, QuickAction.STOP.quickIntent.toPendingIntent + ).build() + }) + } + + override fun onUninstall() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + service.stopForeground(STOP_FOREGROUND_REMOVE) + } else { + service.stopForeground(true) + } + scope.cancel() + } +} \ No newline at end of file diff --git a/android/service/src/main/java/com/follow/clash/service/modules/SuspendModule.kt b/android/service/src/main/java/com/follow/clash/service/modules/SuspendModule.kt new file mode 100644 index 0000000..5fd8d59 --- /dev/null +++ b/android/service/src/main/java/com/follow/clash/service/modules/SuspendModule.kt @@ -0,0 +1,61 @@ +package com.follow.clash.service.modules + +import android.app.Service +import android.content.Intent +import android.os.PowerManager +import androidx.core.content.getSystemService +import com.follow.clash.common.receiveBroadcastFlow +import com.follow.clash.core.Core +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.launch + + +class SuspendModule(private val service: Service) : Module() { + private val scope = CoroutineScope(Dispatchers.Default) + + private fun isScreenOn(): Boolean { + val pm = service.getSystemService() + return when (pm != null) { + true -> pm.isInteractive + false -> true + } + } + + val isDeviceIdleMode: Boolean + get() { + return service.getSystemService()?.isDeviceIdleMode ?: true + } + + private fun onUpdate(isScreenOn: Boolean) { + if (isScreenOn) { + Core.suspended(false) + return + } + Core.suspended(isDeviceIdleMode) + } + + override fun onInstall() { + scope.launch { + val screenFlow = service.receiveBroadcastFlow { + addAction(Intent.ACTION_SCREEN_ON) + addAction(Intent.ACTION_SCREEN_OFF) + }.map { intent -> + intent.action == Intent.ACTION_SCREEN_ON + }.onStart { + emit(isScreenOn()) + } + + screenFlow.collect { + onUpdate(it) + } + } + } + + override fun onUninstall() { + scope.cancel() + } +} \ No newline at end of file diff --git a/android/app/src/main/res/drawable/ic.xml b/android/service/src/main/res/drawable/ic.xml similarity index 100% rename from android/app/src/main/res/drawable/ic.xml rename to android/service/src/main/res/drawable/ic.xml diff --git a/android/service/src/main/res/drawable/ic_service.xml b/android/service/src/main/res/drawable/ic_service.xml new file mode 100644 index 0000000..6864a7f --- /dev/null +++ b/android/service/src/main/res/drawable/ic_service.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts index 8daaa66..f142b85 100644 --- a/android/settings.gradle.kts +++ b/android/settings.gradle.kts @@ -16,14 +16,14 @@ pluginManagement { } } - - plugins { id("dev.flutter.flutter-plugin-loader") version "1.0.0" - id("com.android.application") version "8.9.2" apply false - id("org.jetbrains.kotlin.android") version "1.9.22" apply false + id("com.android.application") version "8.12.1" apply false + id("org.jetbrains.kotlin.android") version "2.2.10" apply false } include(":app") include(":core") +include(":service") +include(":common") diff --git a/arb/intl_en.arb b/arb/intl_en.arb index 74a0596..e7b450d 100644 --- a/arb/intl_en.arb +++ b/arb/intl_en.arb @@ -421,5 +421,12 @@ "proxyChains": "Proxy chains", "log": "Log", "connection": "Connection", - "request": "Request" + "request": "Request", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "restartCoreTip": "Are you sure you want to restart the core?", + "forceRestartCoreTip": "Are you sure you want to force restart the core?", + "dnsHijacking": "DNS hijacking", + "coreStatus": "Core status" } \ No newline at end of file diff --git a/arb/intl_ja.arb b/arb/intl_ja.arb index 3b89759..62b4868 100644 --- a/arb/intl_ja.arb +++ b/arb/intl_ja.arb @@ -422,5 +422,12 @@ "proxyChains": "プロキシチェーン", "log": "ログ", "connection": "接続", - "request": "リクエスト" + "request": "リクエスト", + "connected": "接続済み", + "disconnected": "切断済み", + "connecting": "接続中...", + "restartCoreTip": "コアを再起動してもよろしいですか?", + "forceRestartCoreTip": "コアを強制再起動してもよろしいですか?", + "dnsHijacking": "DNSハイジャッキング", + "coreStatus": "コアステータス" } \ No newline at end of file diff --git a/arb/intl_ru.arb b/arb/intl_ru.arb index d0fff2b..ded51b9 100644 --- a/arb/intl_ru.arb +++ b/arb/intl_ru.arb @@ -422,5 +422,12 @@ "proxyChains": "Цепочки прокси", "log": "Журнал", "connection": "Соединение", - "request": "Запрос" + "request": "Запрос", + "connected": "Подключено", + "disconnected": "Отключено", + "connecting": "Подключение...", + "restartCoreTip": "Вы уверены, что хотите перезапустить ядро?", + "forceRestartCoreTip": "Вы уверены, что хотите принудительно перезапустить ядро?", + "dnsHijacking": "DNS-перехват", + "coreStatus": "Основной статус" } \ No newline at end of file diff --git a/arb/intl_zh_CN.arb b/arb/intl_zh_CN.arb index 4a4a624..2495e6c 100644 --- a/arb/intl_zh_CN.arb +++ b/arb/intl_zh_CN.arb @@ -422,5 +422,12 @@ "proxyChains": "代理链", "log": "日志", "connection": "连接", - "request": "请求" + "request": "请求", + "connected": "已连接", + "disconnected": "已断开", + "connecting": "连接中...", + "restartCoreTip": "您确定要重启核心吗?", + "forceRestartCoreTip": "您确定要强制重启核心吗?", + "dnsHijacking": "DNS劫持", + "coreStatus": "核心状态" } diff --git a/core/Clash.Meta b/core/Clash.Meta index 34ccd12..52dfcca 160000 --- a/core/Clash.Meta +++ b/core/Clash.Meta @@ -1 +1 @@ -Subproject commit 34ccd1202bc18f638787791b9503cd014e7f21e0 +Subproject commit 52dfcca01365b04d4800c72802b86c757f9609e5 diff --git a/core/action.go b/core/action.go index 61ba4b6..c392070 100644 --- a/core/action.go +++ b/core/action.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "unsafe" ) type Action struct { @@ -11,11 +12,11 @@ type Action struct { } type ActionResult struct { - Id string `json:"id"` - Method Method `json:"method"` - Data interface{} `json:"data"` - Code int `json:"code"` - Port int64 + Id string `json:"id"` + Method Method `json:"method"` + Data interface{} `json:"data"` + Code int `json:"code"` + callback unsafe.Pointer } func (result ActionResult) Json() ([]byte, error) { @@ -45,7 +46,7 @@ func handleAction(action *Action, result ActionResult) { result.success(handleGetIsInit()) return case forceGcMethod: - handleForceGc() + handleForceGC() result.success(true) return case shutdownMethod: @@ -73,10 +74,12 @@ func handleAction(action *Action, result ActionResult) { }) return case getTrafficMethod: - result.success(handleGetTraffic()) + data := action.Data.(bool) + result.success(handleGetTraffic(data)) return case getTotalTrafficMethod: - result.success(handleGetTotalTraffic()) + data := action.Data.(bool) + result.success(handleGetTotalTraffic(data)) return case resetTrafficMethod: handleResetTraffic() @@ -175,13 +178,13 @@ func handleAction(action *Action, result ActionResult) { result.success(value) }) return - case setStateMethod: - data := action.Data.(string) - handleSetState(data) - result.success(true) case crashMethod: result.success(true) handleCrash() + case deleteFile: + path := action.Data.(string) + handleDelFile(path, result) + return default: nextHandle(action, result) } diff --git a/core/android_bride.go b/core/android_bride.go deleted file mode 100644 index 25290bc..0000000 --- a/core/android_bride.go +++ /dev/null @@ -1,77 +0,0 @@ -//go:build android && cgo - -package main - -/* -#include - -typedef void (*release_object_func)(void *obj); - -typedef void (*protect_func)(void *tun_interface, int fd); - -typedef const char* (*resolve_process_func)(void *tun_interface, int protocol, const char *source, const char *target, int uid); - -static void protect(protect_func fn, void *tun_interface, int fd) { - if (fn) { - fn(tun_interface, fd); - } -} - -static const char* resolve_process(resolve_process_func fn, void *tun_interface, int protocol, const char *source, const char *target, int uid) { - if (fn) { - return fn(tun_interface, protocol, source, target, uid); - } - return ""; -} - -static void release_object(release_object_func fn, void *obj) { - if (fn) { - return fn(obj); - } -} -*/ -import "C" -import ( - "unsafe" -) - -var ( - globalCallbacks struct { - releaseObjectFunc C.release_object_func - protectFunc C.protect_func - resolveProcessFunc C.resolve_process_func - } -) - -func Protect(callback unsafe.Pointer, fd int) { - if globalCallbacks.protectFunc != nil { - C.protect(globalCallbacks.protectFunc, callback, C.int(fd)) - } -} - -func ResolveProcess(callback unsafe.Pointer, protocol int, source, target string, uid int) string { - if globalCallbacks.resolveProcessFunc == nil { - return "" - } - s := C.CString(source) - defer C.free(unsafe.Pointer(s)) - t := C.CString(target) - defer C.free(unsafe.Pointer(t)) - res := C.resolve_process(globalCallbacks.resolveProcessFunc, callback, C.int(protocol), s, t, C.int(uid)) - defer C.free(unsafe.Pointer(res)) - return C.GoString(res) -} - -func releaseObject(callback unsafe.Pointer) { - if globalCallbacks.releaseObjectFunc == nil { - return - } - C.release_object(globalCallbacks.releaseObjectFunc, callback) -} - -//export registerCallbacks -func registerCallbacks(markSocketFunc C.protect_func, resolveProcessFunc C.resolve_process_func, releaseObjectFunc C.release_object_func) { - globalCallbacks.protectFunc = markSocketFunc - globalCallbacks.resolveProcessFunc = resolveProcessFunc - globalCallbacks.releaseObjectFunc = releaseObjectFunc -} diff --git a/core/bride.c b/core/bride.c new file mode 100644 index 0000000..b432d29 --- /dev/null +++ b/core/bride.c @@ -0,0 +1,31 @@ +#include "bride.h" + +void (*release_object_func)(void *obj); + +void (*free_string_func)(char *data); + +void (*protect_func)(void *tun_interface, int fd); + +char* (*resolve_process_func)(void *tun_interface,int protocol, const char *source, const char *target, int uid); + +void (*result_func)(void *invoke_Interface, const char *data); + +void protect(void *tun_interface, int fd) { + protect_func(tun_interface, fd); +} + +char* resolve_process(void *tun_interface, int protocol, const char *source, const char *target, int uid) { + return resolve_process_func(tun_interface, protocol, source, target, uid); +} + +void release_object(void *obj) { + release_object_func(obj); +} + +void free_string(char *data) { + free_string_func(data); +} + +void result(void *invoke_Interface, const char *data) { + return result_func(invoke_Interface, data); +} \ No newline at end of file diff --git a/core/bride.go b/core/bride.go new file mode 100644 index 0000000..fad830c --- /dev/null +++ b/core/bride.go @@ -0,0 +1,35 @@ +//go:build android && cgo + +package main + +//#include "bride.h" +import "C" +import "unsafe" + +func protect(callback unsafe.Pointer, fd int) { + C.protect(callback, C.int(fd)) +} + +func resolveProcess(callback unsafe.Pointer, protocol int, source, target string, uid int) string { + s := C.CString(source) + defer C.free(unsafe.Pointer(s)) + t := C.CString(target) + defer C.free(unsafe.Pointer(t)) + res := C.resolve_process(callback, C.int(protocol), s, t, C.int(uid)) + return takeCString(res) +} + +func invokeResult(callback unsafe.Pointer, data string) { + s := C.CString(data) + defer C.free(unsafe.Pointer(s)) + C.result(callback, s) +} + +func releaseObject(callback unsafe.Pointer) { + C.release_object(callback) +} + +func takeCString(s *C.char) string { + defer C.free_string(s) + return C.GoString(s) +} diff --git a/core/bride.h b/core/bride.h new file mode 100644 index 0000000..ec18853 --- /dev/null +++ b/core/bride.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +extern void (*release_object_func)(void *obj); + +extern void (*free_string_func)(char *data); + +extern void (*protect_func)(void *tun_interface, int fd); + +extern char* (*resolve_process_func)(void *tun_interface, int protocol, const char *source, const char *target, int uid); + +extern void (*result_func)(void *invoke_Interface, const char *data); + +extern void protect(void *tun_interface, int fd); + +extern char* resolve_process(void *tun_interface, int protocol, const char *source, const char *target, int uid); + +extern void release_object(void *obj); + +extern void free_string(char *data); + +extern void result(void *invoke_Interface, const char *data); \ No newline at end of file diff --git a/core/common.go b/core/common.go index 10e422b..60797eb 100644 --- a/core/common.go +++ b/core/common.go @@ -23,6 +23,7 @@ import ( rp "github.com/metacubex/mihomo/rules/provider" "github.com/metacubex/mihomo/tunnel" "os" + "path/filepath" "sync" ) @@ -159,7 +160,6 @@ func patchSelectGroup(mapping map[string]string) { func defaultSetupParams() *SetupParams { return &SetupParams{ - Config: config.DefaultRawConfig(), TestURL: "https://www.gstatic.com/generate_204", SelectedMap: map[string]string{}, } @@ -235,12 +235,30 @@ func updateConfig(params *UpdateParams) { updateListeners() } +func parseWithPath(path string) (*config.Config, error) { + buf, err := readFile(path) + if err != nil { + return nil, err + } + rawConfig := config.DefaultRawConfig() + err = UnmarshalJson(buf, rawConfig) + if err != nil { + return nil, err + } + parseRawConfig, err := config.ParseRawConfig(rawConfig) + if err != nil { + return nil, err + } + + return parseRawConfig, nil +} + func setupConfig(params *SetupParams) error { runLock.Lock() defer runLock.Unlock() var err error constant.DefaultTestURL = params.TestURL - currentConfig, err = config.ParseRawConfig(params.Config) + currentConfig, err = parseWithPath(filepath.Join(constant.Path.HomeDir(), "config.json")) if err != nil { currentConfig, _ = config.ParseRawConfig(config.DefaultRawConfig()) } diff --git a/core/constant.go b/core/constant.go index 47039cf..1cd68f5 100644 --- a/core/constant.go +++ b/core/constant.go @@ -4,7 +4,6 @@ import ( "encoding/json" "github.com/metacubex/mihomo/adapter/provider" P "github.com/metacubex/mihomo/component/process" - "github.com/metacubex/mihomo/config" "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/tunnel" @@ -18,7 +17,6 @@ type InitParams struct { } type SetupParams struct { - Config *config.RawConfig `json:"config"` SelectedMap map[string]string `json:"selected-map"` TestURL string `json:"test-url"` } @@ -98,13 +96,10 @@ const ( startListenerMethod Method = "startListener" stopListenerMethod Method = "stopListener" updateDnsMethod Method = "updateDns" - setStateMethod Method = "setState" - getAndroidVpnOptionsMethod Method = "getAndroidVpnOptions" - getRunTimeMethod Method = "getRunTime" - getCurrentProfileNameMethod Method = "getCurrentProfileName" crashMethod Method = "crash" setupConfigMethod Method = "setupConfig" getConfigMethod Method = "getConfig" + deleteFile Method = "deleteFile" ) type Method string diff --git a/core/dart-bridge/include/dart_api.h b/core/dart-bridge/include/dart_api.h deleted file mode 100644 index 99dde6f..0000000 --- a/core/dart-bridge/include/dart_api.h +++ /dev/null @@ -1,4185 +0,0 @@ -/* - * Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file - * for details. All rights reserved. Use of this source code is governed by a - * BSD-style license that can be found in the LICENSE file. - */ - -#ifndef RUNTIME_INCLUDE_DART_API_H_ -#define RUNTIME_INCLUDE_DART_API_H_ - -/** \mainpage Dart Embedding API Reference - * - * This reference describes the Dart Embedding API, which is used to embed the - * Dart Virtual Machine within C/C++ applications. - * - * This reference is generated from the header include/dart_api.h. - */ - -/* __STDC_FORMAT_MACROS has to be defined before including to - * enable platform independent printf format specifiers. */ -#ifndef __STDC_FORMAT_MACROS -#define __STDC_FORMAT_MACROS -#endif - -#include -#include -#include - -#if defined(__Fuchsia__) -#include -#endif - -#ifdef __cplusplus -#define DART_EXTERN_C extern "C" -#else -#define DART_EXTERN_C extern -#endif - -#if defined(__CYGWIN__) -#error Tool chain and platform not supported. -#elif defined(_WIN32) -#if defined(DART_SHARED_LIB) -#define DART_EXPORT DART_EXTERN_C __declspec(dllexport) -#else -#define DART_EXPORT DART_EXTERN_C -#endif -#else -#if __GNUC__ >= 4 -#if defined(DART_SHARED_LIB) -#define DART_EXPORT \ - DART_EXTERN_C __attribute__((visibility("default"))) __attribute((used)) -#else -#define DART_EXPORT DART_EXTERN_C -#endif -#else -#error Tool chain not supported. -#endif -#endif - -#if __GNUC__ -#define DART_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) -#elif _MSC_VER -#define DART_WARN_UNUSED_RESULT _Check_return_ -#else -#define DART_WARN_UNUSED_RESULT -#endif - -/* - * ======= - * Handles - * ======= - */ - -/** - * An isolate is the unit of concurrency in Dart. Each isolate has - * its own memory and thread of control. No state is shared between - * isolates. Instead, isolates communicate by message passing. - * - * Each thread keeps track of its current isolate, which is the - * isolate which is ready to execute on the current thread. The - * current isolate may be NULL, in which case no isolate is ready to - * execute. Most of the Dart apis require there to be a current - * isolate in order to function without error. The current isolate is - * set by any call to Dart_CreateIsolateGroup or Dart_EnterIsolate. - */ -typedef struct _Dart_Isolate* Dart_Isolate; -typedef struct _Dart_IsolateGroup* Dart_IsolateGroup; - -/** - * An object reference managed by the Dart VM garbage collector. - * - * Because the garbage collector may move objects, it is unsafe to - * refer to objects directly. Instead, we refer to objects through - * handles, which are known to the garbage collector and updated - * automatically when the object is moved. Handles should be passed - * by value (except in cases like out-parameters) and should never be - * allocated on the heap. - * - * Most functions in the Dart Embedding API return a handle. When a - * function completes normally, this will be a valid handle to an - * object in the Dart VM heap. This handle may represent the result of - * the operation or it may be a special valid handle used merely to - * indicate successful completion. Note that a valid handle may in - * some cases refer to the null object. - * - * --- Error handles --- - * - * When a function encounters a problem that prevents it from - * completing normally, it returns an error handle (See Dart_IsError). - * An error handle has an associated error message that gives more - * details about the problem (See Dart_GetError). - * - * There are four kinds of error handles that can be produced, - * depending on what goes wrong: - * - * - Api error handles are produced when an api function is misused. - * This happens when a Dart embedding api function is called with - * invalid arguments or in an invalid context. - * - * - Unhandled exception error handles are produced when, during the - * execution of Dart code, an exception is thrown but not caught. - * Prototypically this would occur during a call to Dart_Invoke, but - * it can occur in any function which triggers the execution of Dart - * code (for example, Dart_ToString). - * - * An unhandled exception error provides access to an exception and - * stacktrace via the functions Dart_ErrorGetException and - * Dart_ErrorGetStackTrace. - * - * - Compilation error handles are produced when, during the execution - * of Dart code, a compile-time error occurs. As above, this can - * occur in any function which triggers the execution of Dart code. - * - * - Fatal error handles are produced when the system wants to shut - * down the current isolate. - * - * --- Propagating errors --- - * - * When an error handle is returned from the top level invocation of - * Dart code in a program, the embedder must handle the error as they - * see fit. Often, the embedder will print the error message produced - * by Dart_Error and exit the program. - * - * When an error is returned while in the body of a native function, - * it can be propagated up the call stack by calling - * Dart_PropagateError, Dart_SetReturnValue, or Dart_ThrowException. - * Errors should be propagated unless there is a specific reason not - * to. If an error is not propagated then it is ignored. For - * example, if an unhandled exception error is ignored, that - * effectively "catches" the unhandled exception. Fatal errors must - * always be propagated. - * - * When an error is propagated, any current scopes created by - * Dart_EnterScope will be exited. - * - * Using Dart_SetReturnValue to propagate an exception is somewhat - * more convenient than using Dart_PropagateError, and should be - * preferred for reasons discussed below. - * - * Dart_PropagateError and Dart_ThrowException do not return. Instead - * they transfer control non-locally using a setjmp-like mechanism. - * This can be inconvenient if you have resources that you need to - * clean up before propagating the error. - * - * When relying on Dart_PropagateError, we often return error handles - * rather than propagating them from helper functions. Consider the - * following contrived example: - * - * 1 Dart_Handle isLongStringHelper(Dart_Handle arg) { - * 2 intptr_t* length = 0; - * 3 result = Dart_StringLength(arg, &length); - * 4 if (Dart_IsError(result)) { - * 5 return result; - * 6 } - * 7 return Dart_NewBoolean(length > 100); - * 8 } - * 9 - * 10 void NativeFunction_isLongString(Dart_NativeArguments args) { - * 11 Dart_EnterScope(); - * 12 AllocateMyResource(); - * 13 Dart_Handle arg = Dart_GetNativeArgument(args, 0); - * 14 Dart_Handle result = isLongStringHelper(arg); - * 15 if (Dart_IsError(result)) { - * 16 FreeMyResource(); - * 17 Dart_PropagateError(result); - * 18 abort(); // will not reach here - * 19 } - * 20 Dart_SetReturnValue(result); - * 21 FreeMyResource(); - * 22 Dart_ExitScope(); - * 23 } - * - * In this example, we have a native function which calls a helper - * function to do its work. On line 5, the helper function could call - * Dart_PropagateError, but that would not give the native function a - * chance to call FreeMyResource(), causing a leak. Instead, the - * helper function returns the error handle to the caller, giving the - * caller a chance to clean up before propagating the error handle. - * - * When an error is propagated by calling Dart_SetReturnValue, the - * native function will be allowed to complete normally and then the - * exception will be propagated only once the native call - * returns. This can be convenient, as it allows the C code to clean - * up normally. - * - * The example can be written more simply using Dart_SetReturnValue to - * propagate the error. - * - * 1 Dart_Handle isLongStringHelper(Dart_Handle arg) { - * 2 intptr_t* length = 0; - * 3 result = Dart_StringLength(arg, &length); - * 4 if (Dart_IsError(result)) { - * 5 return result - * 6 } - * 7 return Dart_NewBoolean(length > 100); - * 8 } - * 9 - * 10 void NativeFunction_isLongString(Dart_NativeArguments args) { - * 11 Dart_EnterScope(); - * 12 AllocateMyResource(); - * 13 Dart_Handle arg = Dart_GetNativeArgument(args, 0); - * 14 Dart_SetReturnValue(isLongStringHelper(arg)); - * 15 FreeMyResource(); - * 16 Dart_ExitScope(); - * 17 } - * - * In this example, the call to Dart_SetReturnValue on line 14 will - * either return the normal return value or the error (potentially - * generated on line 3). The call to FreeMyResource on line 15 will - * execute in either case. - * - * --- Local and persistent handles --- - * - * Local handles are allocated within the current scope (see - * Dart_EnterScope) and go away when the current scope exits. Unless - * otherwise indicated, callers should assume that all functions in - * the Dart embedding api return local handles. - * - * Persistent handles are allocated within the current isolate. They - * can be used to store objects across scopes. Persistent handles have - * the lifetime of the current isolate unless they are explicitly - * deallocated (see Dart_DeletePersistentHandle). - * The type Dart_Handle represents a handle (both local and persistent). - * The type Dart_PersistentHandle is a Dart_Handle and it is used to - * document that a persistent handle is expected as a parameter to a call - * or the return value from a call is a persistent handle. - * - * FinalizableHandles are persistent handles which are auto deleted when - * the object is garbage collected. It is never safe to use these handles - * unless you know the object is still reachable. - * - * WeakPersistentHandles are persistent handles which are automatically set - * to point Dart_Null when the object is garbage collected. They are not auto - * deleted, so it is safe to use them after the object has become unreachable. - */ -typedef struct _Dart_Handle* Dart_Handle; -typedef Dart_Handle Dart_PersistentHandle; -typedef struct _Dart_WeakPersistentHandle* Dart_WeakPersistentHandle; -typedef struct _Dart_FinalizableHandle* Dart_FinalizableHandle; -// These structs are versioned by DART_API_DL_MAJOR_VERSION, bump the -// version when changing this struct. - -typedef void (*Dart_HandleFinalizer)(void* isolate_callback_data, void* peer); - -/** - * Is this an error handle? - * - * Requires there to be a current isolate. - */ -DART_EXPORT bool Dart_IsError(Dart_Handle handle); - -/** - * Is this an api error handle? - * - * Api error handles are produced when an api function is misused. - * This happens when a Dart embedding api function is called with - * invalid arguments or in an invalid context. - * - * Requires there to be a current isolate. - */ -DART_EXPORT bool Dart_IsApiError(Dart_Handle handle); - -/** - * Is this an unhandled exception error handle? - * - * Unhandled exception error handles are produced when, during the - * execution of Dart code, an exception is thrown but not caught. - * This can occur in any function which triggers the execution of Dart - * code. - * - * See Dart_ErrorGetException and Dart_ErrorGetStackTrace. - * - * Requires there to be a current isolate. - */ -DART_EXPORT bool Dart_IsUnhandledExceptionError(Dart_Handle handle); - -/** - * Is this a compilation error handle? - * - * Compilation error handles are produced when, during the execution - * of Dart code, a compile-time error occurs. This can occur in any - * function which triggers the execution of Dart code. - * - * Requires there to be a current isolate. - */ -DART_EXPORT bool Dart_IsCompilationError(Dart_Handle handle); - -/** - * Is this a fatal error handle? - * - * Fatal error handles are produced when the system wants to shut down - * the current isolate. - * - * Requires there to be a current isolate. - */ -DART_EXPORT bool Dart_IsFatalError(Dart_Handle handle); - -/** - * Gets the error message from an error handle. - * - * Requires there to be a current isolate. - * - * \return A C string containing an error message if the handle is - * error. An empty C string ("") if the handle is valid. This C - * String is scope allocated and is only valid until the next call - * to Dart_ExitScope. -*/ -DART_EXPORT const char* Dart_GetError(Dart_Handle handle); - -/** - * Is this an error handle for an unhandled exception? - */ -DART_EXPORT bool Dart_ErrorHasException(Dart_Handle handle); - -/** - * Gets the exception Object from an unhandled exception error handle. - */ -DART_EXPORT Dart_Handle Dart_ErrorGetException(Dart_Handle handle); - -/** - * Gets the stack trace Object from an unhandled exception error handle. - */ -DART_EXPORT Dart_Handle Dart_ErrorGetStackTrace(Dart_Handle handle); - -/** - * Produces an api error handle with the provided error message. - * - * Requires there to be a current isolate. - * - * \param error the error message. - */ -DART_EXPORT Dart_Handle Dart_NewApiError(const char* error); -DART_EXPORT Dart_Handle Dart_NewCompilationError(const char* error); - -/** - * Produces a new unhandled exception error handle. - * - * Requires there to be a current isolate. - * - * \param exception An instance of a Dart object to be thrown or - * an ApiError or CompilationError handle. - * When an ApiError or CompilationError handle is passed in - * a string object of the error message is created and it becomes - * the Dart object to be thrown. - */ -DART_EXPORT Dart_Handle Dart_NewUnhandledExceptionError(Dart_Handle exception); - -/** - * Propagates an error. - * - * If the provided handle is an unhandled exception error, this - * function will cause the unhandled exception to be rethrown. This - * will proceed in the standard way, walking up Dart frames until an - * appropriate 'catch' block is found, executing 'finally' blocks, - * etc. - * - * If the error is not an unhandled exception error, we will unwind - * the stack to the next C frame. Intervening Dart frames will be - * discarded; specifically, 'finally' blocks will not execute. This - * is the standard way that compilation errors (and the like) are - * handled by the Dart runtime. - * - * In either case, when an error is propagated any current scopes - * created by Dart_EnterScope will be exited. - * - * See the additional discussion under "Propagating Errors" at the - * beginning of this file. - * - * \param handle An error handle (See Dart_IsError) - * - * On success, this function does not return. On failure, the - * process is terminated. - */ -DART_EXPORT void Dart_PropagateError(Dart_Handle handle); - -/** - * Converts an object to a string. - * - * May generate an unhandled exception error. - * - * \return The converted string if no error occurs during - * the conversion. If an error does occur, an error handle is - * returned. - */ -DART_EXPORT Dart_Handle Dart_ToString(Dart_Handle object); - -/** - * Checks to see if two handles refer to identically equal objects. - * - * If both handles refer to instances, this is equivalent to using the top-level - * function identical() from dart:core. Otherwise, returns whether the two - * argument handles refer to the same object. - * - * \param obj1 An object to be compared. - * \param obj2 An object to be compared. - * - * \return True if the objects are identically equal. False otherwise. - */ -DART_EXPORT bool Dart_IdentityEquals(Dart_Handle obj1, Dart_Handle obj2); - -/** - * Allocates a handle in the current scope from a persistent handle. - */ -DART_EXPORT Dart_Handle Dart_HandleFromPersistent(Dart_PersistentHandle object); - -/** - * Allocates a handle in the current scope from a weak persistent handle. - * - * This will be a handle to Dart_Null if the object has been garbage collected. - */ -DART_EXPORT Dart_Handle -Dart_HandleFromWeakPersistent(Dart_WeakPersistentHandle object); - -/** - * Allocates a persistent handle for an object. - * - * This handle has the lifetime of the current isolate unless it is - * explicitly deallocated by calling Dart_DeletePersistentHandle. - * - * Requires there to be a current isolate. - */ -DART_EXPORT Dart_PersistentHandle Dart_NewPersistentHandle(Dart_Handle object); - -/** - * Assign value of local handle to a persistent handle. - * - * Requires there to be a current isolate. - * - * \param obj1 A persistent handle whose value needs to be set. - * \param obj2 An object whose value needs to be set to the persistent handle. - */ -DART_EXPORT void Dart_SetPersistentHandle(Dart_PersistentHandle obj1, - Dart_Handle obj2); - -/** - * Deallocates a persistent handle. - * - * Requires there to be a current isolate group. - */ -DART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object); - -/** - * Allocates a weak persistent handle for an object. - * - * This handle has the lifetime of the current isolate. The handle can also be - * explicitly deallocated by calling Dart_DeleteWeakPersistentHandle. - * - * If the object becomes unreachable the callback is invoked with the peer as - * argument. The callback can be executed on any thread, will have a current - * isolate group, but will not have a current isolate. The callback can only - * call Dart_DeletePersistentHandle or Dart_DeleteWeakPersistentHandle. This - * gives the embedder the ability to cleanup data associated with the object. - * The handle will point to the Dart_Null object after the finalizer has been - * run. It is illegal to call into the VM with any other Dart_* functions from - * the callback. If the handle is deleted before the object becomes - * unreachable, the callback is never invoked. - * - * Requires there to be a current isolate. - * - * \param object An object with identity. - * \param peer A pointer to a native object or NULL. This value is - * provided to callback when it is invoked. - * \param external_allocation_size The number of externally allocated - * bytes for peer. Used to inform the garbage collector. - * \param callback A function pointer that will be invoked sometime - * after the object is garbage collected, unless the handle has been deleted. - * A valid callback needs to be specified it cannot be NULL. - * - * \return The weak persistent handle or NULL. NULL is returned in case of bad - * parameters. - */ -DART_EXPORT Dart_WeakPersistentHandle -Dart_NewWeakPersistentHandle(Dart_Handle object, - void* peer, - intptr_t external_allocation_size, - Dart_HandleFinalizer callback); - -/** - * Deletes the given weak persistent [object] handle. - * - * Requires there to be a current isolate group. - */ -DART_EXPORT void Dart_DeleteWeakPersistentHandle( - Dart_WeakPersistentHandle object); - -/** - * Updates the external memory size for the given weak persistent handle. - * - * May trigger garbage collection. - */ -DART_EXPORT void Dart_UpdateExternalSize(Dart_WeakPersistentHandle object, - intptr_t external_allocation_size); - -/** - * Allocates a finalizable handle for an object. - * - * This handle has the lifetime of the current isolate group unless the object - * pointed to by the handle is garbage collected, in this case the VM - * automatically deletes the handle after invoking the callback associated - * with the handle. The handle can also be explicitly deallocated by - * calling Dart_DeleteFinalizableHandle. - * - * If the object becomes unreachable the callback is invoked with the - * the peer as argument. The callback can be executed on any thread, will have - * an isolate group, but will not have a current isolate. The callback can only - * call Dart_DeletePersistentHandle or Dart_DeleteWeakPersistentHandle. - * This gives the embedder the ability to cleanup data associated with the - * object and clear out any cached references to the handle. All references to - * this handle after the callback will be invalid. It is illegal to call into - * the VM with any other Dart_* functions from the callback. If the handle is - * deleted before the object becomes unreachable, the callback is never - * invoked. - * - * Requires there to be a current isolate. - * - * \param object An object with identity. - * \param peer A pointer to a native object or NULL. This value is - * provided to callback when it is invoked. - * \param external_allocation_size The number of externally allocated - * bytes for peer. Used to inform the garbage collector. - * \param callback A function pointer that will be invoked sometime - * after the object is garbage collected, unless the handle has been deleted. - * A valid callback needs to be specified it cannot be NULL. - * - * \return The finalizable handle or NULL. NULL is returned in case of bad - * parameters. - */ -DART_EXPORT Dart_FinalizableHandle -Dart_NewFinalizableHandle(Dart_Handle object, - void* peer, - intptr_t external_allocation_size, - Dart_HandleFinalizer callback); - -/** - * Deletes the given finalizable [object] handle. - * - * The caller has to provide the actual Dart object the handle was created from - * to prove the object (and therefore the finalizable handle) is still alive. - * - * Requires there to be a current isolate. - */ -DART_EXPORT void Dart_DeleteFinalizableHandle(Dart_FinalizableHandle object, - Dart_Handle strong_ref_to_object); - -/** - * Updates the external memory size for the given finalizable handle. - * - * The caller has to provide the actual Dart object the handle was created from - * to prove the object (and therefore the finalizable handle) is still alive. - * - * May trigger garbage collection. - */ -DART_EXPORT void Dart_UpdateFinalizableExternalSize( - Dart_FinalizableHandle object, - Dart_Handle strong_ref_to_object, - intptr_t external_allocation_size); - -/* - * ========================== - * Initialization and Globals - * ========================== - */ - -/** - * Gets the version string for the Dart VM. - * - * The version of the Dart VM can be accessed without initializing the VM. - * - * \return The version string for the embedded Dart VM. - */ -DART_EXPORT const char* Dart_VersionString(void); - -/** - * Isolate specific flags are set when creating a new isolate using the - * Dart_IsolateFlags structure. - * - * Current version of flags is encoded in a 32-bit integer with 16 bits used - * for each part. - */ - -#define DART_FLAGS_CURRENT_VERSION (0x0000000c) - -typedef struct { - int32_t version; - bool enable_asserts; - bool use_field_guards; - bool use_osr; - bool obfuscate; - bool load_vmservice_library; - bool copy_parent_code; - bool null_safety; - bool is_system_isolate; - bool snapshot_is_dontneed_safe; - bool branch_coverage; -} Dart_IsolateFlags; - -/** - * Initialize Dart_IsolateFlags with correct version and default values. - */ -DART_EXPORT void Dart_IsolateFlagsInitialize(Dart_IsolateFlags* flags); - -/** - * An isolate creation and initialization callback function. - * - * This callback, provided by the embedder, is called when the VM - * needs to create an isolate. The callback should create an isolate - * by calling Dart_CreateIsolateGroup and load any scripts required for - * execution. - * - * This callback may be called on a different thread than the one - * running the parent isolate. - * - * When the function returns NULL, it is the responsibility of this - * function to ensure that Dart_ShutdownIsolate has been called if - * required (for example, if the isolate was created successfully by - * Dart_CreateIsolateGroup() but the root library fails to load - * successfully, then the function should call Dart_ShutdownIsolate - * before returning). - * - * When the function returns NULL, the function should set *error to - * a malloc-allocated buffer containing a useful error message. The - * caller of this function (the VM) will make sure that the buffer is - * freed. - * - * \param script_uri The uri of the main source file or snapshot to load. - * Either the URI of the parent isolate set in Dart_CreateIsolateGroup for - * Isolate.spawn, or the argument to Isolate.spawnUri canonicalized by the - * library tag handler of the parent isolate. - * The callback is responsible for loading the program by a call to - * Dart_LoadScriptFromKernel. - * \param main The name of the main entry point this isolate will - * eventually run. This is provided for advisory purposes only to - * improve debugging messages. The main function is not invoked by - * this function. - * \param package_root Ignored. - * \param package_config Uri of the package configuration file (either in format - * of .packages or .dart_tool/package_config.json) for this isolate - * to resolve package imports against. If this parameter is not passed the - * package resolution of the parent isolate should be used. - * \param flags Default flags for this isolate being spawned. Either inherited - * from the spawning isolate or passed as parameters when spawning the - * isolate from Dart code. - * \param isolate_data The isolate data which was passed to the - * parent isolate when it was created by calling Dart_CreateIsolateGroup(). - * \param error A structure into which the embedder can place a - * C string containing an error message in the case of failures. - * - * \return The embedder returns NULL if the creation and - * initialization was not successful and the isolate if successful. - */ -typedef Dart_Isolate (*Dart_IsolateGroupCreateCallback)( - const char* script_uri, - const char* main, - const char* package_root, - const char* package_config, - Dart_IsolateFlags* flags, - void* isolate_data, - char** error); - -/** - * An isolate initialization callback function. - * - * This callback, provided by the embedder, is called when the VM has created an - * isolate within an existing isolate group (i.e. from the same source as an - * existing isolate). - * - * The callback should setup native resolvers and might want to set a custom - * message handler via [Dart_SetMessageNotifyCallback] and mark the isolate as - * runnable. - * - * This callback may be called on a different thread than the one - * running the parent isolate. - * - * When the function returns `false`, it is the responsibility of this - * function to ensure that `Dart_ShutdownIsolate` has been called. - * - * When the function returns `false`, the function should set *error to - * a malloc-allocated buffer containing a useful error message. The - * caller of this function (the VM) will make sure that the buffer is - * freed. - * - * \param child_isolate_data The callback data to associate with the new - * child isolate. - * \param error A structure into which the embedder can place a - * C string containing an error message in the case the initialization fails. - * - * \return The embedder returns true if the initialization was successful and - * false otherwise (in which case the VM will terminate the isolate). - */ -typedef bool (*Dart_InitializeIsolateCallback)(void** child_isolate_data, - char** error); - -/** - * An isolate shutdown callback function. - * - * This callback, provided by the embedder, is called before the vm - * shuts down an isolate. The isolate being shutdown will be the current - * isolate. It is safe to run Dart code. - * - * This function should be used to dispose of native resources that - * are allocated to an isolate in order to avoid leaks. - * - * \param isolate_group_data The same callback data which was passed to the - * isolate group when it was created. - * \param isolate_data The same callback data which was passed to the isolate - * when it was created. - */ -typedef void (*Dart_IsolateShutdownCallback)(void* isolate_group_data, - void* isolate_data); - -/** - * An isolate cleanup callback function. - * - * This callback, provided by the embedder, is called after the vm - * shuts down an isolate. There will be no current isolate and it is *not* - * safe to run Dart code. - * - * This function should be used to dispose of native resources that - * are allocated to an isolate in order to avoid leaks. - * - * \param isolate_group_data The same callback data which was passed to the - * isolate group when it was created. - * \param isolate_data The same callback data which was passed to the isolate - * when it was created. - */ -typedef void (*Dart_IsolateCleanupCallback)(void* isolate_group_data, - void* isolate_data); - -/** - * An isolate group cleanup callback function. - * - * This callback, provided by the embedder, is called after the vm - * shuts down an isolate group. - * - * This function should be used to dispose of native resources that - * are allocated to an isolate in order to avoid leaks. - * - * \param isolate_group_data The same callback data which was passed to the - * isolate group when it was created. - * - */ -typedef void (*Dart_IsolateGroupCleanupCallback)(void* isolate_group_data); - -/** - * A thread start callback function. - * This callback, provided by the embedder, is called after a thread in the - * vm thread pool starts. - * This function could be used to adjust thread priority or attach native - * resources to the thread. - */ -typedef void (*Dart_ThreadStartCallback)(void); - -/** - * A thread death callback function. - * This callback, provided by the embedder, is called before a thread in the - * vm thread pool exits. - * This function could be used to dispose of native resources that - * are associated and attached to the thread, in order to avoid leaks. - */ -typedef void (*Dart_ThreadExitCallback)(void); - -/** - * Opens a file for reading or writing. - * - * Callback provided by the embedder for file operations. If the - * embedder does not allow file operations this callback can be - * NULL. - * - * \param name The name of the file to open. - * \param write A boolean variable which indicates if the file is to - * opened for writing. If there is an existing file it needs to truncated. - */ -typedef void* (*Dart_FileOpenCallback)(const char* name, bool write); - -/** - * Read contents of file. - * - * Callback provided by the embedder for file operations. If the - * embedder does not allow file operations this callback can be - * NULL. - * - * \param data Buffer allocated in the callback into which the contents - * of the file are read into. It is the responsibility of the caller to - * free this buffer. - * \param file_length A variable into which the length of the file is returned. - * In the case of an error this value would be -1. - * \param stream Handle to the opened file. - */ -typedef void (*Dart_FileReadCallback)(uint8_t** data, - intptr_t* file_length, - void* stream); - -/** - * Write data into file. - * - * Callback provided by the embedder for file operations. If the - * embedder does not allow file operations this callback can be - * NULL. - * - * \param data Buffer which needs to be written into the file. - * \param length Length of the buffer. - * \param stream Handle to the opened file. - */ -typedef void (*Dart_FileWriteCallback)(const void* data, - intptr_t length, - void* stream); - -/** - * Closes the opened file. - * - * Callback provided by the embedder for file operations. If the - * embedder does not allow file operations this callback can be - * NULL. - * - * \param stream Handle to the opened file. - */ -typedef void (*Dart_FileCloseCallback)(void* stream); - -typedef bool (*Dart_EntropySource)(uint8_t* buffer, intptr_t length); - -/** - * Callback provided by the embedder that is used by the vmservice isolate - * to request the asset archive. The asset archive must be an uncompressed tar - * archive that is stored in a Uint8List. - * - * If the embedder has no vmservice isolate assets, the callback can be NULL. - * - * \return The embedder must return a handle to a Uint8List containing an - * uncompressed tar archive or null. - */ -typedef Dart_Handle (*Dart_GetVMServiceAssetsArchive)(void); - -/** - * The current version of the Dart_InitializeFlags. Should be incremented every - * time Dart_InitializeFlags changes in a binary incompatible way. - */ -#define DART_INITIALIZE_PARAMS_CURRENT_VERSION (0x00000008) - -/** Forward declaration */ -struct Dart_CodeObserver; - -/** - * Callback provided by the embedder that is used by the VM to notify on code - * object creation, *before* it is invoked the first time. - * This is useful for embedders wanting to e.g. keep track of PCs beyond - * the lifetime of the garbage collected code objects. - * Note that an address range may be used by more than one code object over the - * lifecycle of a process. Clients of this function should record timestamps for - * these compilation events and when collecting PCs to disambiguate reused - * address ranges. - */ -typedef void (*Dart_OnNewCodeCallback)(struct Dart_CodeObserver* observer, - const char* name, - uintptr_t base, - uintptr_t size); - -typedef struct Dart_CodeObserver { - void* data; - - Dart_OnNewCodeCallback on_new_code; -} Dart_CodeObserver; - -/** - * Optional callback provided by the embedder that is used by the VM to - * implement registration of kernel blobs for the subsequent Isolate.spawnUri - * If no callback is provided, the registration of kernel blobs will throw - * an error. - * - * \param kernel_buffer A buffer which contains a kernel program. Callback - * should copy the contents of `kernel_buffer` as - * it may be freed immediately after registration. - * \param kernel_buffer_size The size of `kernel_buffer`. - * - * \return A C string representing URI which can be later used - * to spawn a new isolate. This C String should be scope allocated - * or owned by the embedder. - * Returns NULL if embedder runs out of memory. - */ -typedef const char* (*Dart_RegisterKernelBlobCallback)( - const uint8_t* kernel_buffer, - intptr_t kernel_buffer_size); - -/** - * Optional callback provided by the embedder that is used by the VM to - * unregister kernel blobs. - * If no callback is provided, the unregistration of kernel blobs will throw - * an error. - * - * \param kernel_blob_uri URI of the kernel blob to unregister. - */ -typedef void (*Dart_UnregisterKernelBlobCallback)(const char* kernel_blob_uri); - -/** - * Describes how to initialize the VM. Used with Dart_Initialize. - */ -typedef struct { - /** - * Identifies the version of the struct used by the client. - * should be initialized to DART_INITIALIZE_PARAMS_CURRENT_VERSION. - */ - int32_t version; - - /** - * A buffer containing snapshot data, or NULL if no snapshot is provided. - * - * If provided, the buffer must remain valid until Dart_Cleanup returns. - */ - const uint8_t* vm_snapshot_data; - - /** - * A buffer containing a snapshot of precompiled instructions, or NULL if - * no snapshot is provided. - * - * If provided, the buffer must remain valid until Dart_Cleanup returns. - */ - const uint8_t* vm_snapshot_instructions; - - /** - * A function to be called during isolate group creation. - * See Dart_IsolateGroupCreateCallback. - */ - Dart_IsolateGroupCreateCallback create_group; - - /** - * A function to be called during isolate - * initialization inside an existing isolate group. - * See Dart_InitializeIsolateCallback. - */ - Dart_InitializeIsolateCallback initialize_isolate; - - /** - * A function to be called right before an isolate is shutdown. - * See Dart_IsolateShutdownCallback. - */ - Dart_IsolateShutdownCallback shutdown_isolate; - - /** - * A function to be called after an isolate was shutdown. - * See Dart_IsolateCleanupCallback. - */ - Dart_IsolateCleanupCallback cleanup_isolate; - - /** - * A function to be called after an isolate group is - * shutdown. See Dart_IsolateGroupCleanupCallback. - */ - Dart_IsolateGroupCleanupCallback cleanup_group; - - Dart_ThreadStartCallback thread_start; - Dart_ThreadExitCallback thread_exit; - Dart_FileOpenCallback file_open; - Dart_FileReadCallback file_read; - Dart_FileWriteCallback file_write; - Dart_FileCloseCallback file_close; - Dart_EntropySource entropy_source; - - /** - * A function to be called by the service isolate when it requires the - * vmservice assets archive. See Dart_GetVMServiceAssetsArchive. - */ - Dart_GetVMServiceAssetsArchive get_service_assets; - - bool start_kernel_isolate; - - /** - * An external code observer callback function. The observer can be invoked - * as early as during the Dart_Initialize() call. - */ - Dart_CodeObserver* code_observer; - - /** - * Kernel blob registration callback function. See Dart_RegisterKernelBlobCallback. - */ - Dart_RegisterKernelBlobCallback register_kernel_blob; - - /** - * Kernel blob unregistration callback function. See Dart_UnregisterKernelBlobCallback. - */ - Dart_UnregisterKernelBlobCallback unregister_kernel_blob; - -#if defined(__Fuchsia__) - /** - * The resource needed to use zx_vmo_replace_as_executable. Can be - * ZX_HANDLE_INVALID if the process has ambient-replace-as-executable or if - * executable memory is not needed (e.g., this is an AOT runtime). - */ - zx_handle_t vmex_resource; -#endif -} Dart_InitializeParams; - -/** - * Initializes the VM. - * - * \param params A struct containing initialization information. The version - * field of the struct must be DART_INITIALIZE_PARAMS_CURRENT_VERSION. - * - * \return NULL if initialization is successful. Returns an error message - * otherwise. The caller is responsible for freeing the error message. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_Initialize( - Dart_InitializeParams* params); - -/** - * Cleanup state in the VM before process termination. - * - * \return NULL if cleanup is successful. Returns an error message otherwise. - * The caller is responsible for freeing the error message. - * - * NOTE: This function must not be called on a thread that was created by the VM - * itself. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_Cleanup(void); - -/** - * Sets command line flags. Should be called before Dart_Initialize. - * - * \param argc The length of the arguments array. - * \param argv An array of arguments. - * - * \return NULL if successful. Returns an error message otherwise. - * The caller is responsible for freeing the error message. - * - * NOTE: This call does not store references to the passed in c-strings. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_SetVMFlags(int argc, - const char** argv); - -/** - * Returns true if the named VM flag is of boolean type, specified, and set to - * true. - * - * \param flag_name The name of the flag without leading punctuation - * (example: "enable_asserts"). - */ -DART_EXPORT bool Dart_IsVMFlagSet(const char* flag_name); - -/* - * ======== - * Isolates - * ======== - */ - -/** - * Creates a new isolate. The new isolate becomes the current isolate. - * - * A snapshot can be used to restore the VM quickly to a saved state - * and is useful for fast startup. If snapshot data is provided, the - * isolate will be started using that snapshot data. Requires a core snapshot or - * an app snapshot created by Dart_CreateSnapshot or - * Dart_CreatePrecompiledSnapshot* from a VM with the same version. - * - * Requires there to be no current isolate. - * - * \param script_uri The main source file or snapshot this isolate will load. - * The VM will provide this URI to the Dart_IsolateGroupCreateCallback when a - * child isolate is created by Isolate.spawn. The embedder should use a URI - * that allows it to load the same program into such a child isolate. - * \param name A short name for the isolate to improve debugging messages. - * Typically of the format 'foo.dart:main()'. - * \param isolate_snapshot_data Buffer containing the snapshot data of the - * isolate or NULL if no snapshot is provided. If provided, the buffer must - * remain valid until the isolate shuts down. - * \param isolate_snapshot_instructions Buffer containing the snapshot - * instructions of the isolate or NULL if no snapshot is provided. If - * provided, the buffer must remain valid until the isolate shuts down. - * \param flags Pointer to VM specific flags or NULL for default flags. - * \param isolate_group_data Embedder group data. This data can be obtained - * by calling Dart_IsolateGroupData and will be passed to the - * Dart_IsolateShutdownCallback, Dart_IsolateCleanupCallback, and - * Dart_IsolateGroupCleanupCallback. - * \param isolate_data Embedder data. This data will be passed to - * the Dart_IsolateGroupCreateCallback when new isolates are spawned from - * this parent isolate. - * \param error Returns NULL if creation is successful, an error message - * otherwise. The caller is responsible for calling free() on the error - * message. - * - * \return The new isolate on success, or NULL if isolate creation failed. - */ -DART_EXPORT Dart_Isolate -Dart_CreateIsolateGroup(const char* script_uri, - const char* name, - const uint8_t* isolate_snapshot_data, - const uint8_t* isolate_snapshot_instructions, - Dart_IsolateFlags* flags, - void* isolate_group_data, - void* isolate_data, - char** error); -/** - * Creates a new isolate inside the isolate group of [group_member]. - * - * Requires there to be no current isolate. - * - * \param group_member An isolate from the same group into which the newly created - * isolate should be born into. Other threads may not have entered / enter this - * member isolate. - * \param name A short name for the isolate for debugging purposes. - * \param shutdown_callback A callback to be called when the isolate is being - * shutdown (may be NULL). - * \param cleanup_callback A callback to be called when the isolate is being - * cleaned up (may be NULL). - * \param child_isolate_data The embedder-specific data associated with this isolate. - * \param error Set to NULL if creation is successful, set to an error - * message otherwise. The caller is responsible for calling free() on the - * error message. - * - * \return The newly created isolate on success, or NULL if isolate creation - * failed. - * - * If successful, the newly created isolate will become the current isolate. - */ -DART_EXPORT Dart_Isolate -Dart_CreateIsolateInGroup(Dart_Isolate group_member, - const char* name, - Dart_IsolateShutdownCallback shutdown_callback, - Dart_IsolateCleanupCallback cleanup_callback, - void* child_isolate_data, - char** error); - -/* TODO(turnidge): Document behavior when there is already a current - * isolate. */ - -/** - * Creates a new isolate from a Dart Kernel file. The new isolate - * becomes the current isolate. - * - * Requires there to be no current isolate. - * - * \param script_uri The main source file or snapshot this isolate will load. - * The VM will provide this URI to the Dart_IsolateGroupCreateCallback when a - * child isolate is created by Isolate.spawn. The embedder should use a URI that - * allows it to load the same program into such a child isolate. - * \param name A short name for the isolate to improve debugging messages. - * Typically of the format 'foo.dart:main()'. - * \param kernel_buffer A buffer which contains a kernel/DIL program. Must - * remain valid until isolate shutdown. - * \param kernel_buffer_size The size of `kernel_buffer`. - * \param flags Pointer to VM specific flags or NULL for default flags. - * \param isolate_group_data Embedder group data. This data can be obtained - * by calling Dart_IsolateGroupData and will be passed to the - * Dart_IsolateShutdownCallback, Dart_IsolateCleanupCallback, and - * Dart_IsolateGroupCleanupCallback. - * \param isolate_data Embedder data. This data will be passed to - * the Dart_IsolateGroupCreateCallback when new isolates are spawned from - * this parent isolate. - * \param error Returns NULL if creation is successful, an error message - * otherwise. The caller is responsible for calling free() on the error - * message. - * - * \return The new isolate on success, or NULL if isolate creation failed. - */ -DART_EXPORT Dart_Isolate -Dart_CreateIsolateGroupFromKernel(const char* script_uri, - const char* name, - const uint8_t* kernel_buffer, - intptr_t kernel_buffer_size, - Dart_IsolateFlags* flags, - void* isolate_group_data, - void* isolate_data, - char** error); -/** - * Shuts down the current isolate. After this call, the current isolate is NULL. - * Any current scopes created by Dart_EnterScope will be exited. Invokes the - * shutdown callback and any callbacks of remaining weak persistent handles. - * - * Requires there to be a current isolate. - */ -DART_EXPORT void Dart_ShutdownIsolate(void); -/* TODO(turnidge): Document behavior when there is no current isolate. */ - -/** - * Returns the current isolate. Will return NULL if there is no - * current isolate. - */ -DART_EXPORT Dart_Isolate Dart_CurrentIsolate(void); - -/** - * Returns the callback data associated with the current isolate. This - * data was set when the isolate got created or initialized. - */ -DART_EXPORT void* Dart_CurrentIsolateData(void); - -/** - * Returns the callback data associated with the given isolate. This - * data was set when the isolate got created or initialized. - */ -DART_EXPORT void* Dart_IsolateData(Dart_Isolate isolate); - -/** - * Returns the current isolate group. Will return NULL if there is no - * current isolate group. - */ -DART_EXPORT Dart_IsolateGroup Dart_CurrentIsolateGroup(void); - -/** - * Returns the callback data associated with the current isolate group. This - * data was passed to the isolate group when it was created. - */ -DART_EXPORT void* Dart_CurrentIsolateGroupData(void); - -/** - * Gets an id that uniquely identifies current isolate group. - * - * It is the responsibility of the caller to free the returned ID. - */ -typedef int64_t Dart_IsolateGroupId; -DART_EXPORT Dart_IsolateGroupId Dart_CurrentIsolateGroupId(void); - -/** - * Returns the callback data associated with the specified isolate group. This - * data was passed to the isolate when it was created. - * The embedder is responsible for ensuring the consistency of this data - * with respect to the lifecycle of an isolate group. - */ -DART_EXPORT void* Dart_IsolateGroupData(Dart_Isolate isolate); - -/** - * Returns the debugging name for the current isolate. - * - * This name is unique to each isolate and should only be used to make - * debugging messages more comprehensible. - */ -DART_EXPORT Dart_Handle Dart_DebugName(void); - -/** - * Returns the debugging name for the current isolate. - * - * This name is unique to each isolate and should only be used to make - * debugging messages more comprehensible. - * - * The returned string is scope allocated and is only valid until the next call - * to Dart_ExitScope. - */ -DART_EXPORT const char* Dart_DebugNameToCString(void); - -/** - * Returns the ID for an isolate which is used to query the service protocol. - * - * It is the responsibility of the caller to free the returned ID. - */ -DART_EXPORT const char* Dart_IsolateServiceId(Dart_Isolate isolate); - -/** - * Enters an isolate. After calling this function, - * the current isolate will be set to the provided isolate. - * - * Requires there to be no current isolate. Multiple threads may not be in - * the same isolate at once. - */ -DART_EXPORT void Dart_EnterIsolate(Dart_Isolate isolate); - -/** - * Kills the given isolate. - * - * This function has the same effect as dart:isolate's - * Isolate.kill(priority:immediate). - * It can interrupt ordinary Dart code but not native code. If the isolate is - * in the middle of a long running native function, the isolate will not be - * killed until control returns to Dart. - * - * Does not require a current isolate. It is safe to kill the current isolate if - * there is one. - */ -DART_EXPORT void Dart_KillIsolate(Dart_Isolate isolate); - -/** - * Notifies the VM that the embedder expects to be idle until |deadline|. The VM - * may use this time to perform garbage collection or other tasks to avoid - * delays during execution of Dart code in the future. - * - * |deadline| is measured in microseconds against the system's monotonic time. - * This clock can be accessed via Dart_TimelineGetMicros(). - * - * Requires there to be a current isolate. - */ -DART_EXPORT void Dart_NotifyIdle(int64_t deadline); - -typedef void (*Dart_HeapSamplingReportCallback)(void* context, - void* data); - -typedef void* (*Dart_HeapSamplingCreateCallback)( - Dart_Isolate isolate, - Dart_IsolateGroup isolate_group, - const char* cls_name, - intptr_t allocation_size); -typedef void (*Dart_HeapSamplingDeleteCallback)(void* data); - -/** - * Starts the heap sampling profiler for each thread in the VM. - */ -DART_EXPORT void Dart_EnableHeapSampling(void); - -/* - * Stops the heap sampling profiler for each thread in the VM. - */ -DART_EXPORT void Dart_DisableHeapSampling(void); - -/* Registers callbacks are invoked once per sampled allocation upon object - * allocation and garbage collection. - * - * |create_callback| can be used to associate additional data with the sampled - * allocation, such as a stack trace. This data pointer will be passed to - * |delete_callback| to allow for proper disposal when the object associated - * with the allocation sample is collected. - * - * The provided callbacks must not call into the VM and should do as little - * work as possible to avoid performance penalities during object allocation and - * garbage collection. - * - * NOTE: It is a fatal error to set either callback to null once they have been - * initialized. - */ -DART_EXPORT void Dart_RegisterHeapSamplingCallback( - Dart_HeapSamplingCreateCallback create_callback, - Dart_HeapSamplingDeleteCallback delete_callback); - -/* - * Reports the surviving allocation samples for all live isolate groups in the - * VM. - * - * When the callback is invoked: - * - |context| will be the context object provided when invoking - * |Dart_ReportSurvivingAllocations|. This can be safely set to null if not - * required. - * - |heap_size| will be equal to the size of the allocated object associated - * with the sample. - * - |cls_name| will be a C String representing - * the class name of the allocated object. This string is valid for the - * duration of the call to Dart_ReportSurvivingAllocations and can be - * freed by the VM at any point after the method returns. - * - |data| will be set to the data associated with the sample by - * |Dart_HeapSamplingCreateCallback|. - * - * If |force_gc| is true, a full GC will be performed before reporting the - * allocations. - */ -DART_EXPORT void Dart_ReportSurvivingAllocations( - Dart_HeapSamplingReportCallback callback, - void* context, - bool force_gc); - -/* - * Sets the average heap sampling rate based on a number of |bytes| for each - * thread. - * - * In other words, approximately every |bytes| allocated will create a sample. - * Defaults to 512 KiB. - */ -DART_EXPORT void Dart_SetHeapSamplingPeriod(intptr_t bytes); - -/** - * Notifies the VM that the embedder expects the application's working set has - * recently shrunk significantly and is not expected to rise in the near future. - * The VM may spend O(heap-size) time performing clean up work. - * - * Requires there to be a current isolate. - */ -DART_EXPORT void Dart_NotifyDestroyed(void); - -/** - * Notifies the VM that the system is running low on memory. - * - * Does not require a current isolate. Only valid after calling Dart_Initialize. - */ -DART_EXPORT void Dart_NotifyLowMemory(void); - -typedef enum { - /** - * Balanced - */ - Dart_PerformanceMode_Default, - /** - * Optimize for low latency, at the expense of throughput and memory overhead - * by performing work in smaller batches (requiring more overhead) or by - * delaying work (requiring more memory). An embedder should not remain in - * this mode indefinitely. - */ - Dart_PerformanceMode_Latency, - /** - * Optimize for high throughput, at the expense of latency and memory overhead - * by performing work in larger batches with more intervening growth. - */ - Dart_PerformanceMode_Throughput, - /** - * Optimize for low memory, at the expensive of throughput and latency by more - * frequently performing work. - */ - Dart_PerformanceMode_Memory, -} Dart_PerformanceMode; - -/** - * Set the desired performance trade-off. - * - * Requires a current isolate. - * - * Returns the previous performance mode. - */ -DART_EXPORT Dart_PerformanceMode -Dart_SetPerformanceMode(Dart_PerformanceMode mode); - -/** - * Starts the CPU sampling profiler. - */ -DART_EXPORT void Dart_StartProfiling(void); - -/** - * Stops the CPU sampling profiler. - * - * Note that some profile samples might still be taken after this function - * returns due to the asynchronous nature of the implementation on some - * platforms. - */ -DART_EXPORT void Dart_StopProfiling(void); - -/** - * Notifies the VM that the current thread should not be profiled until a - * matching call to Dart_ThreadEnableProfiling is made. - * - * NOTE: By default, if a thread has entered an isolate it will be profiled. - * This function should be used when an embedder knows a thread is about - * to make a blocking call and wants to avoid unnecessary interrupts by - * the profiler. - */ -DART_EXPORT void Dart_ThreadDisableProfiling(void); - -/** - * Notifies the VM that the current thread should be profiled. - * - * NOTE: It is only legal to call this function *after* calling - * Dart_ThreadDisableProfiling. - * - * NOTE: By default, if a thread has entered an isolate it will be profiled. - */ -DART_EXPORT void Dart_ThreadEnableProfiling(void); - -/** - * Register symbol information for the Dart VM's profiler and crash dumps. - * - * This consumes the output of //topaz/runtime/dart/profiler_symbols, which - * should be treated as opaque. - */ -DART_EXPORT void Dart_AddSymbols(const char* dso_name, - void* buffer, - intptr_t buffer_size); - -/** - * Exits an isolate. After this call, Dart_CurrentIsolate will - * return NULL. - * - * Requires there to be a current isolate. - */ -DART_EXPORT void Dart_ExitIsolate(void); -/* TODO(turnidge): We don't want users of the api to be able to exit a - * "pure" dart isolate. Implement and document. */ - -/** - * Creates a full snapshot of the current isolate heap. - * - * A full snapshot is a compact representation of the dart vm isolate heap - * and dart isolate heap states. These snapshots are used to initialize - * the vm isolate on startup and fast initialization of an isolate. - * A Snapshot of the heap is created before any dart code has executed. - * - * Requires there to be a current isolate. Not available in the precompiled - * runtime (check Dart_IsPrecompiledRuntime). - * - * \param vm_snapshot_data_buffer Returns a pointer to a buffer containing the - * vm snapshot. This buffer is scope allocated and is only valid - * until the next call to Dart_ExitScope. - * \param vm_snapshot_data_size Returns the size of vm_snapshot_data_buffer. - * \param isolate_snapshot_data_buffer Returns a pointer to a buffer containing - * the isolate snapshot. This buffer is scope allocated and is only valid - * until the next call to Dart_ExitScope. - * \param isolate_snapshot_data_size Returns the size of - * isolate_snapshot_data_buffer. - * \param is_core Create a snapshot containing core libraries. - * Such snapshot should be agnostic to null safety mode. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_CreateSnapshot(uint8_t** vm_snapshot_data_buffer, - intptr_t* vm_snapshot_data_size, - uint8_t** isolate_snapshot_data_buffer, - intptr_t* isolate_snapshot_data_size, - bool is_core); - -/** - * Returns whether the buffer contains a kernel file. - * - * \param buffer Pointer to a buffer that might contain a kernel binary. - * \param buffer_size Size of the buffer. - * - * \return Whether the buffer contains a kernel binary (full or partial). - */ -DART_EXPORT bool Dart_IsKernel(const uint8_t* buffer, intptr_t buffer_size); - -/** - * Make isolate runnable. - * - * When isolates are spawned, this function is used to indicate that - * the creation and initialization (including script loading) of the - * isolate is complete and the isolate can start. - * This function expects there to be no current isolate. - * - * \param isolate The isolate to be made runnable. - * - * \return NULL if successful. Returns an error message otherwise. The caller - * is responsible for freeing the error message. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_IsolateMakeRunnable( - Dart_Isolate isolate); - -/* - * ================== - * Messages and Ports - * ================== - */ - -/** - * A port is used to send or receive inter-isolate messages - */ -typedef int64_t Dart_Port; - -/** - * ILLEGAL_PORT is a port number guaranteed never to be associated with a valid - * port. - */ -#define ILLEGAL_PORT ((Dart_Port)0) - -/** - * A message notification callback. - * - * This callback allows the embedder to provide a custom wakeup mechanism for - * the delivery of inter-isolate messages. This function is called once per - * message on an arbitrary thread. It is the responsibility of the embedder to - * eventually call Dart_HandleMessage once per callback received with the - * destination isolate set as the current isolate to process the message. - */ -typedef void (*Dart_MessageNotifyCallback)(Dart_Isolate destination_isolate); - -/** - * Allows embedders to provide a custom wakeup mechanism for the delivery of - * inter-isolate messages. This setting only applies to the current isolate. - * - * This mechanism is optional: if not provided, the isolate will be scheduled on - * a VM-managed thread pool. An embedder should provide this callback if it - * wants to run an isolate on a specific thread or to interleave handling of - * inter-isolate messages with other event sources. - * - * Most embedders will only call this function once, before isolate - * execution begins. If this function is called after isolate - * execution begins, the embedder is responsible for threading issues. - */ -DART_EXPORT void Dart_SetMessageNotifyCallback( - Dart_MessageNotifyCallback message_notify_callback); -/* TODO(turnidge): Consider moving this to isolate creation so that it - * is impossible to mess up. */ - -/** - * Query the current message notify callback for the isolate. - * - * \return The current message notify callback for the isolate. - */ -DART_EXPORT Dart_MessageNotifyCallback Dart_GetMessageNotifyCallback(void); - -/** - * The VM's default message handler supports pausing an isolate before it - * processes the first message and right after the it processes the isolate's - * final message. This can be controlled for all isolates by two VM flags: - * - * `--pause-isolates-on-start` - * `--pause-isolates-on-exit` - * - * Additionally, Dart_SetShouldPauseOnStart and Dart_SetShouldPauseOnExit can be - * used to control this behaviour on a per-isolate basis. - * - * When an embedder is using a Dart_MessageNotifyCallback the embedder - * needs to cooperate with the VM so that the service protocol can report - * accurate information about isolates and so that tools such as debuggers - * work reliably. - * - * The following functions can be used to implement pausing on start and exit. - */ - -/** - * If the VM flag `--pause-isolates-on-start` was passed this will be true. - * - * \return A boolean value indicating if pause on start was requested. - */ -DART_EXPORT bool Dart_ShouldPauseOnStart(void); - -/** - * Override the VM flag `--pause-isolates-on-start` for the current isolate. - * - * \param should_pause Should the isolate be paused on start? - * - * NOTE: This must be called before Dart_IsolateMakeRunnable. - */ -DART_EXPORT void Dart_SetShouldPauseOnStart(bool should_pause); - -/** - * Is the current isolate paused on start? - * - * \return A boolean value indicating if the isolate is paused on start. - */ -DART_EXPORT bool Dart_IsPausedOnStart(void); - -/** - * Called when the embedder has paused the current isolate on start and when - * the embedder has resumed the isolate. - * - * \param paused Is the isolate paused on start? - */ -DART_EXPORT void Dart_SetPausedOnStart(bool paused); - -/** - * If the VM flag `--pause-isolates-on-exit` was passed this will be true. - * - * \return A boolean value indicating if pause on exit was requested. - */ -DART_EXPORT bool Dart_ShouldPauseOnExit(void); - -/** - * Override the VM flag `--pause-isolates-on-exit` for the current isolate. - * - * \param should_pause Should the isolate be paused on exit? - * - */ -DART_EXPORT void Dart_SetShouldPauseOnExit(bool should_pause); - -/** - * Is the current isolate paused on exit? - * - * \return A boolean value indicating if the isolate is paused on exit. - */ -DART_EXPORT bool Dart_IsPausedOnExit(void); - -/** - * Called when the embedder has paused the current isolate on exit and when - * the embedder has resumed the isolate. - * - * \param paused Is the isolate paused on exit? - */ -DART_EXPORT void Dart_SetPausedOnExit(bool paused); - -/** - * Called when the embedder has caught a top level unhandled exception error - * in the current isolate. - * - * NOTE: It is illegal to call this twice on the same isolate without first - * clearing the sticky error to null. - * - * \param error The unhandled exception error. - */ -DART_EXPORT void Dart_SetStickyError(Dart_Handle error); - -/** - * Does the current isolate have a sticky error? - */ -DART_EXPORT bool Dart_HasStickyError(void); - -/** - * Gets the sticky error for the current isolate. - * - * \return A handle to the sticky error object or null. - */ -DART_EXPORT Dart_Handle Dart_GetStickyError(void); - -/** - * Handles the next pending message for the current isolate. - * - * May generate an unhandled exception error. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_HandleMessage(void); - -/** - * Drains the microtask queue, then blocks the calling thread until the current - * isolate receives a message, then handles all messages. - * - * \param timeout_millis When non-zero, the call returns after the indicated - number of milliseconds even if no message was received. - * \return A valid handle if no error occurs, otherwise an error handle. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_WaitForEvent(int64_t timeout_millis); - -/** - * Handles any pending messages for the vm service for the current - * isolate. - * - * This function may be used by an embedder at a breakpoint to avoid - * pausing the vm service. - * - * This function can indirectly cause the message notify callback to - * be called. - * - * \return true if the vm service requests the program resume - * execution, false otherwise - */ -DART_EXPORT bool Dart_HandleServiceMessages(void); - -/** - * Does the current isolate have pending service messages? - * - * \return true if the isolate has pending service messages, false otherwise. - */ -DART_EXPORT bool Dart_HasServiceMessages(void); - -/** - * Processes any incoming messages for the current isolate. - * - * This function may only be used when the embedder has not provided - * an alternate message delivery mechanism with - * Dart_SetMessageCallbacks. It is provided for convenience. - * - * This function waits for incoming messages for the current - * isolate. As new messages arrive, they are handled using - * Dart_HandleMessage. The routine exits when all ports to the - * current isolate are closed. - * - * \return A valid handle if the run loop exited successfully. If an - * exception or other error occurs while processing messages, an - * error handle is returned. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_RunLoop(void); - -/** - * Lets the VM run message processing for the isolate. - * - * This function expects there to a current isolate and the current isolate - * must not have an active api scope. The VM will take care of making the - * isolate runnable (if not already), handles its message loop and will take - * care of shutting the isolate down once it's done. - * - * \param errors_are_fatal Whether uncaught errors should be fatal. - * \param on_error_port A port to notify on uncaught errors (or ILLEGAL_PORT). - * \param on_exit_port A port to notify on exit (or ILLEGAL_PORT). - * \param error A non-NULL pointer which will hold an error message if the call - * fails. The error has to be free()ed by the caller. - * - * \return If successful the VM takes ownership of the isolate and takes care - * of its message loop. If not successful the caller retains ownership of the - * isolate. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT bool Dart_RunLoopAsync( - bool errors_are_fatal, - Dart_Port on_error_port, - Dart_Port on_exit_port, - char** error); - -/* TODO(turnidge): Should this be removed from the public api? */ - -/** - * Gets the main port id for the current isolate. - */ -DART_EXPORT Dart_Port Dart_GetMainPortId(void); - -/** - * Does the current isolate have live ReceivePorts? - * - * A ReceivePort is live when it has not been closed. - */ -DART_EXPORT bool Dart_HasLivePorts(void); - -/** - * Posts a message for some isolate. The message is a serialized - * object. - * - * Requires there to be a current isolate. - * - * For posting messages outside of an isolate see \ref Dart_PostCObject. - * - * \param port_id The destination port. - * \param object An object from the current isolate. - * - * \return True if the message was posted. - */ -DART_EXPORT bool Dart_Post(Dart_Port port_id, Dart_Handle object); - -/** - * Returns a new SendPort with the provided port id. - * - * \param port_id The destination port. - * - * \return A new SendPort if no errors occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewSendPort(Dart_Port port_id); - -/** - * Gets the SendPort id for the provided SendPort. - * \param port A SendPort object whose id is desired. - * \param port_id Returns the id of the SendPort. - * \return Success if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_SendPortGetId(Dart_Handle port, - Dart_Port* port_id); - -/* - * ====== - * Scopes - * ====== - */ - -/** - * Enters a new scope. - * - * All new local handles will be created in this scope. Additionally, - * some functions may return "scope allocated" memory which is only - * valid within this scope. - * - * Requires there to be a current isolate. - */ -DART_EXPORT void Dart_EnterScope(void); - -/** - * Exits a scope. - * - * The previous scope (if any) becomes the current scope. - * - * Requires there to be a current isolate. - */ -DART_EXPORT void Dart_ExitScope(void); - -/** - * The Dart VM uses "zone allocation" for temporary structures. Zones - * support very fast allocation of small chunks of memory. The chunks - * cannot be deallocated individually, but instead zones support - * deallocating all chunks in one fast operation. - * - * This function makes it possible for the embedder to allocate - * temporary data in the VMs zone allocator. - * - * Zone allocation is possible: - * 1. when inside a scope where local handles can be allocated - * 2. when processing a message from a native port in a native port - * handler - * - * All the memory allocated this way will be reclaimed either on the - * next call to Dart_ExitScope or when the native port handler exits. - * - * \param size Size of the memory to allocate. - * - * \return A pointer to the allocated memory. NULL if allocation - * failed. Failure might due to is no current VM zone. - */ -DART_EXPORT uint8_t* Dart_ScopeAllocate(intptr_t size); - -/* - * ======= - * Objects - * ======= - */ - -/** - * Returns the null object. - * - * \return A handle to the null object. - */ -DART_EXPORT Dart_Handle Dart_Null(void); - -/** - * Is this object null? - */ -DART_EXPORT bool Dart_IsNull(Dart_Handle object); - -/** - * Returns the empty string object. - * - * \return A handle to the empty string object. - */ -DART_EXPORT Dart_Handle Dart_EmptyString(void); - -/** - * Returns types that are not classes, and which therefore cannot be looked up - * as library members by Dart_GetType. - * - * \return A handle to the dynamic, void or Never type. - */ -DART_EXPORT Dart_Handle Dart_TypeDynamic(void); -DART_EXPORT Dart_Handle Dart_TypeVoid(void); -DART_EXPORT Dart_Handle Dart_TypeNever(void); - -/** - * Checks if the two objects are equal. - * - * The result of the comparison is returned through the 'equal' - * parameter. The return value itself is used to indicate success or - * failure, not equality. - * - * May generate an unhandled exception error. - * - * \param obj1 An object to be compared. - * \param obj2 An object to be compared. - * \param equal Returns the result of the equality comparison. - * - * \return A valid handle if no error occurs during the comparison. - */ -DART_EXPORT Dart_Handle Dart_ObjectEquals(Dart_Handle obj1, - Dart_Handle obj2, - bool* equal); - -/** - * Is this object an instance of some type? - * - * The result of the test is returned through the 'instanceof' parameter. - * The return value itself is used to indicate success or failure. - * - * \param object An object. - * \param type A type. - * \param instanceof Return true if 'object' is an instance of type 'type'. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_ObjectIsType(Dart_Handle object, - Dart_Handle type, - bool* instanceof); - -/** - * Query object type. - * - * \param object Some Object. - * - * \return true if Object is of the specified type. - */ -DART_EXPORT bool Dart_IsInstance(Dart_Handle object); -DART_EXPORT bool Dart_IsNumber(Dart_Handle object); -DART_EXPORT bool Dart_IsInteger(Dart_Handle object); -DART_EXPORT bool Dart_IsDouble(Dart_Handle object); -DART_EXPORT bool Dart_IsBoolean(Dart_Handle object); -DART_EXPORT bool Dart_IsString(Dart_Handle object); -DART_EXPORT bool Dart_IsStringLatin1(Dart_Handle object); /* (ISO-8859-1) */ -DART_EXPORT bool Dart_IsExternalString(Dart_Handle object); -DART_EXPORT bool Dart_IsList(Dart_Handle object); -DART_EXPORT bool Dart_IsMap(Dart_Handle object); -DART_EXPORT bool Dart_IsLibrary(Dart_Handle object); -DART_EXPORT bool Dart_IsType(Dart_Handle handle); -DART_EXPORT bool Dart_IsFunction(Dart_Handle handle); -DART_EXPORT bool Dart_IsVariable(Dart_Handle handle); -DART_EXPORT bool Dart_IsTypeVariable(Dart_Handle handle); -DART_EXPORT bool Dart_IsClosure(Dart_Handle object); -DART_EXPORT bool Dart_IsTypedData(Dart_Handle object); -DART_EXPORT bool Dart_IsByteBuffer(Dart_Handle object); -DART_EXPORT bool Dart_IsFuture(Dart_Handle object); - -/* - * ========= - * Instances - * ========= - */ - -/* - * For the purposes of the embedding api, not all objects returned are - * Dart language objects. Within the api, we use the term 'Instance' - * to indicate handles which refer to true Dart language objects. - * - * TODO(turnidge): Reorganize the "Object" section above, pulling down - * any functions that more properly belong here. */ - -/** - * Gets the type of a Dart language object. - * - * \param instance Some Dart object. - * - * \return If no error occurs, the type is returned. Otherwise an - * error handle is returned. - */ -DART_EXPORT Dart_Handle Dart_InstanceGetType(Dart_Handle instance); - -/** - * Returns the name for the provided class type. - * - * \return A valid string handle if no error occurs during the - * operation. - */ -DART_EXPORT Dart_Handle Dart_ClassName(Dart_Handle cls_type); - -/** - * Returns the name for the provided function or method. - * - * \return A valid string handle if no error occurs during the - * operation. - */ -DART_EXPORT Dart_Handle Dart_FunctionName(Dart_Handle function); - -/** - * Returns a handle to the owner of a function. - * - * The owner of an instance method or a static method is its defining - * class. The owner of a top-level function is its defining - * library. The owner of the function of a non-implicit closure is the - * function of the method or closure that defines the non-implicit - * closure. - * - * \return A valid handle to the owner of the function, or an error - * handle if the argument is not a valid handle to a function. - */ -DART_EXPORT Dart_Handle Dart_FunctionOwner(Dart_Handle function); - -/** - * Determines whether a function handle refers to a static function - * of method. - * - * For the purposes of the embedding API, a top-level function is - * implicitly declared static. - * - * \param function A handle to a function or method declaration. - * \param is_static Returns whether the function or method is declared static. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_FunctionIsStatic(Dart_Handle function, - bool* is_static); - -/** - * Is this object a closure resulting from a tear-off (closurized method)? - * - * Returns true for closures produced when an ordinary method is accessed - * through a getter call. Returns false otherwise, in particular for closures - * produced from local function declarations. - * - * \param object Some Object. - * - * \return true if Object is a tear-off. - */ -DART_EXPORT bool Dart_IsTearOff(Dart_Handle object); - -/** - * Retrieves the function of a closure. - * - * \return A handle to the function of the closure, or an error handle if the - * argument is not a closure. - */ -DART_EXPORT Dart_Handle Dart_ClosureFunction(Dart_Handle closure); - -/** - * Returns a handle to the library which contains class. - * - * \return A valid handle to the library with owns class, null if the class - * has no library or an error handle if the argument is not a valid handle - * to a class type. - */ -DART_EXPORT Dart_Handle Dart_ClassLibrary(Dart_Handle cls_type); - -/* - * ============================= - * Numbers, Integers and Doubles - * ============================= - */ - -/** - * Does this Integer fit into a 64-bit signed integer? - * - * \param integer An integer. - * \param fits Returns true if the integer fits into a 64-bit signed integer. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_IntegerFitsIntoInt64(Dart_Handle integer, - bool* fits); - -/** - * Does this Integer fit into a 64-bit unsigned integer? - * - * \param integer An integer. - * \param fits Returns true if the integer fits into a 64-bit unsigned integer. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_IntegerFitsIntoUint64(Dart_Handle integer, - bool* fits); - -/** - * Returns an Integer with the provided value. - * - * \param value The value of the integer. - * - * \return The Integer object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewInteger(int64_t value); - -/** - * Returns an Integer with the provided value. - * - * \param value The unsigned value of the integer. - * - * \return The Integer object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewIntegerFromUint64(uint64_t value); - -/** - * Returns an Integer with the provided value. - * - * \param value The value of the integer represented as a C string - * containing a hexadecimal number. - * - * \return The Integer object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewIntegerFromHexCString(const char* value); - -/** - * Gets the value of an Integer. - * - * The integer must fit into a 64-bit signed integer, otherwise an error occurs. - * - * \param integer An Integer. - * \param value Returns the value of the Integer. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_IntegerToInt64(Dart_Handle integer, - int64_t* value); - -/** - * Gets the value of an Integer. - * - * The integer must fit into a 64-bit unsigned integer, otherwise an - * error occurs. - * - * \param integer An Integer. - * \param value Returns the value of the Integer. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_IntegerToUint64(Dart_Handle integer, - uint64_t* value); - -/** - * Gets the value of an integer as a hexadecimal C string. - * - * \param integer An Integer. - * \param value Returns the value of the Integer as a hexadecimal C - * string. This C string is scope allocated and is only valid until - * the next call to Dart_ExitScope. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_IntegerToHexCString(Dart_Handle integer, - const char** value); - -/** - * Returns a Double with the provided value. - * - * \param value A double. - * - * \return The Double object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewDouble(double value); - -/** - * Gets the value of a Double - * - * \param double_obj A Double - * \param value Returns the value of the Double. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_DoubleValue(Dart_Handle double_obj, double* value); - -/** - * Returns a closure of static function 'function_name' in the class 'class_name' - * in the exported namespace of specified 'library'. - * - * \param library Library object - * \param cls_type Type object representing a Class - * \param function_name Name of the static function in the class - * - * \return A valid Dart instance if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_GetStaticMethodClosure(Dart_Handle library, - Dart_Handle cls_type, - Dart_Handle function_name); - -/* - * ======== - * Booleans - * ======== - */ - -/** - * Returns the True object. - * - * Requires there to be a current isolate. - * - * \return A handle to the True object. - */ -DART_EXPORT Dart_Handle Dart_True(void); - -/** - * Returns the False object. - * - * Requires there to be a current isolate. - * - * \return A handle to the False object. - */ -DART_EXPORT Dart_Handle Dart_False(void); - -/** - * Returns a Boolean with the provided value. - * - * \param value true or false. - * - * \return The Boolean object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewBoolean(bool value); - -/** - * Gets the value of a Boolean - * - * \param boolean_obj A Boolean - * \param value Returns the value of the Boolean. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_BooleanValue(Dart_Handle boolean_obj, bool* value); - -/* - * ======= - * Strings - * ======= - */ - -/** - * Gets the length of a String. - * - * \param str A String. - * \param length Returns the length of the String. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_StringLength(Dart_Handle str, intptr_t* length); - -/** - * Returns a String built from the provided C string - * (There is an implicit assumption that the C string passed in contains - * UTF-8 encoded characters and '\0' is considered as a termination - * character). - * - * \param str A C String - * - * \return The String object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewStringFromCString(const char* str); -/* TODO(turnidge): Document what happens when we run out of memory - * during this call. */ - -/** - * Returns a String built from an array of UTF-8 encoded characters. - * - * \param utf8_array An array of UTF-8 encoded characters. - * \param length The length of the codepoints array. - * - * \return The String object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewStringFromUTF8(const uint8_t* utf8_array, - intptr_t length); - -/** - * Returns a String built from an array of UTF-16 encoded characters. - * - * \param utf16_array An array of UTF-16 encoded characters. - * \param length The length of the codepoints array. - * - * \return The String object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewStringFromUTF16(const uint16_t* utf16_array, - intptr_t length); - -/** - * Returns a String built from an array of UTF-32 encoded characters. - * - * \param utf32_array An array of UTF-32 encoded characters. - * \param length The length of the codepoints array. - * - * \return The String object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewStringFromUTF32(const int32_t* utf32_array, - intptr_t length); - -/** - * Returns a String which references an external array of - * Latin-1 (ISO-8859-1) encoded characters. - * - * \param latin1_array Array of Latin-1 encoded characters. This must not move. - * \param length The length of the characters array. - * \param peer An external pointer to associate with this string. - * \param external_allocation_size The number of externally allocated - * bytes for peer. Used to inform the garbage collector. - * \param callback A callback to be called when this string is finalized. - * - * \return The String object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle -Dart_NewExternalLatin1String(const uint8_t* latin1_array, - intptr_t length, - void* peer, - intptr_t external_allocation_size, - Dart_HandleFinalizer callback); - -/** - * Returns a String which references an external array of UTF-16 encoded - * characters. - * - * \param utf16_array An array of UTF-16 encoded characters. This must not move. - * \param length The length of the characters array. - * \param peer An external pointer to associate with this string. - * \param external_allocation_size The number of externally allocated - * bytes for peer. Used to inform the garbage collector. - * \param callback A callback to be called when this string is finalized. - * - * \return The String object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle -Dart_NewExternalUTF16String(const uint16_t* utf16_array, - intptr_t length, - void* peer, - intptr_t external_allocation_size, - Dart_HandleFinalizer callback); - -/** - * Gets the C string representation of a String. - * (It is a sequence of UTF-8 encoded values with a '\0' termination.) - * - * \param str A string. - * \param cstr Returns the String represented as a C string. - * This C string is scope allocated and is only valid until - * the next call to Dart_ExitScope. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_StringToCString(Dart_Handle str, - const char** cstr); - -/** - * Gets a UTF-8 encoded representation of a String. - * - * Any unpaired surrogate code points in the string will be converted as - * replacement characters (U+FFFD, 0xEF 0xBF 0xBD in UTF-8). If you need - * to preserve unpaired surrogates, use the Dart_StringToUTF16 function. - * - * \param str A string. - * \param utf8_array Returns the String represented as UTF-8 code - * units. This UTF-8 array is scope allocated and is only valid - * until the next call to Dart_ExitScope. - * \param length Used to return the length of the array which was - * actually used. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_StringToUTF8(Dart_Handle str, - uint8_t** utf8_array, - intptr_t* length); - -/** - * Gets the data corresponding to the string object. This function returns - * the data only for Latin-1 (ISO-8859-1) string objects. For all other - * string objects it returns an error. - * - * \param str A string. - * \param latin1_array An array allocated by the caller, used to return - * the string data. - * \param length Used to pass in the length of the provided array. - * Used to return the length of the array which was actually used. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_StringToLatin1(Dart_Handle str, - uint8_t* latin1_array, - intptr_t* length); - -/** - * Gets the UTF-16 encoded representation of a string. - * - * \param str A string. - * \param utf16_array An array allocated by the caller, used to return - * the array of UTF-16 encoded characters. - * \param length Used to pass in the length of the provided array. - * Used to return the length of the array which was actually used. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_StringToUTF16(Dart_Handle str, - uint16_t* utf16_array, - intptr_t* length); - -/** - * Gets the storage size in bytes of a String. - * - * \param str A String. - * \param size Returns the storage size in bytes of the String. - * This is the size in bytes needed to store the String. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_StringStorageSize(Dart_Handle str, intptr_t* size); - -/** - * Retrieves some properties associated with a String. - * Properties retrieved are: - * - character size of the string (one or two byte) - * - length of the string - * - peer pointer of string if it is an external string. - * \param str A String. - * \param char_size Returns the character size of the String. - * \param str_len Returns the length of the String. - * \param peer Returns the peer pointer associated with the String or 0 if - * there is no peer pointer for it. - * \return Success if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_StringGetProperties(Dart_Handle str, - intptr_t* char_size, - intptr_t* str_len, - void** peer); - -/* - * ===== - * Lists - * ===== - */ - -/** - * Returns a List of the desired length. - * - * \param length The length of the list. - * - * \return The List object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewList(intptr_t length); - -typedef enum { - Dart_CoreType_Dynamic, - Dart_CoreType_Int, - Dart_CoreType_String, -} Dart_CoreType_Id; - -// TODO(bkonyi): convert this to use nullable types once NNBD is enabled. -/** - * Returns a List of the desired length with the desired legacy element type. - * - * \param element_type_id The type of elements of the list. - * \param length The length of the list. - * - * \return The List object if no error occurs. Otherwise returns an error - * handle. - */ -DART_EXPORT Dart_Handle Dart_NewListOf(Dart_CoreType_Id element_type_id, - intptr_t length); - -/** - * Returns a List of the desired length with the desired element type. - * - * \param element_type Handle to a nullable type object. E.g., from - * Dart_GetType or Dart_GetNullableType. - * - * \param length The length of the list. - * - * \return The List object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewListOfType(Dart_Handle element_type, - intptr_t length); - -/** - * Returns a List of the desired length with the desired element type, filled - * with the provided object. - * - * \param element_type Handle to a type object. E.g., from Dart_GetType. - * - * \param fill_object Handle to an object of type 'element_type' that will be - * used to populate the list. This parameter can only be Dart_Null() if the - * length of the list is 0 or 'element_type' is a nullable type. - * - * \param length The length of the list. - * - * \return The List object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewListOfTypeFilled(Dart_Handle element_type, - Dart_Handle fill_object, - intptr_t length); - -/** - * Gets the length of a List. - * - * May generate an unhandled exception error. - * - * \param list A List. - * \param length Returns the length of the List. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_ListLength(Dart_Handle list, intptr_t* length); - -/** - * Gets the Object at some index of a List. - * - * If the index is out of bounds, an error occurs. - * - * May generate an unhandled exception error. - * - * \param list A List. - * \param index A valid index into the List. - * - * \return The Object in the List at the specified index if no error - * occurs. Otherwise returns an error handle. - */ -DART_EXPORT Dart_Handle Dart_ListGetAt(Dart_Handle list, intptr_t index); - -/** -* Gets a range of Objects from a List. -* -* If any of the requested index values are out of bounds, an error occurs. -* -* May generate an unhandled exception error. -* -* \param list A List. -* \param offset The offset of the first item to get. -* \param length The number of items to get. -* \param result A pointer to fill with the objects. -* -* \return Success if no error occurs during the operation. -*/ -DART_EXPORT Dart_Handle Dart_ListGetRange(Dart_Handle list, - intptr_t offset, - intptr_t length, - Dart_Handle* result); - -/** - * Sets the Object at some index of a List. - * - * If the index is out of bounds, an error occurs. - * - * May generate an unhandled exception error. - * - * \param list A List. - * \param index A valid index into the List. - * \param value The Object to put in the List. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT Dart_Handle Dart_ListSetAt(Dart_Handle list, - intptr_t index, - Dart_Handle value); - -/** - * May generate an unhandled exception error. - */ -DART_EXPORT Dart_Handle Dart_ListGetAsBytes(Dart_Handle list, - intptr_t offset, - uint8_t* native_array, - intptr_t length); - -/** - * May generate an unhandled exception error. - */ -DART_EXPORT Dart_Handle Dart_ListSetAsBytes(Dart_Handle list, - intptr_t offset, - const uint8_t* native_array, - intptr_t length); - -/* - * ==== - * Maps - * ==== - */ - -/** - * Gets the Object at some key of a Map. - * - * May generate an unhandled exception error. - * - * \param map A Map. - * \param key An Object. - * - * \return The value in the map at the specified key, null if the map does not - * contain the key, or an error handle. - */ -DART_EXPORT Dart_Handle Dart_MapGetAt(Dart_Handle map, Dart_Handle key); - -/** - * Returns whether the Map contains a given key. - * - * May generate an unhandled exception error. - * - * \param map A Map. - * - * \return A handle on a boolean indicating whether map contains the key. - * Otherwise returns an error handle. - */ -DART_EXPORT Dart_Handle Dart_MapContainsKey(Dart_Handle map, Dart_Handle key); - -/** - * Gets the list of keys of a Map. - * - * May generate an unhandled exception error. - * - * \param map A Map. - * - * \return The list of key Objects if no error occurs. Otherwise returns an - * error handle. - */ -DART_EXPORT Dart_Handle Dart_MapKeys(Dart_Handle map); - -/* - * ========== - * Typed Data - * ========== - */ - -typedef enum { - Dart_TypedData_kByteData = 0, - Dart_TypedData_kInt8, - Dart_TypedData_kUint8, - Dart_TypedData_kUint8Clamped, - Dart_TypedData_kInt16, - Dart_TypedData_kUint16, - Dart_TypedData_kInt32, - Dart_TypedData_kUint32, - Dart_TypedData_kInt64, - Dart_TypedData_kUint64, - Dart_TypedData_kFloat32, - Dart_TypedData_kFloat64, - Dart_TypedData_kInt32x4, - Dart_TypedData_kFloat32x4, - Dart_TypedData_kFloat64x2, - Dart_TypedData_kInvalid -} Dart_TypedData_Type; - -/** - * Return type if this object is a TypedData object. - * - * \return kInvalid if the object is not a TypedData object or the appropriate - * Dart_TypedData_Type. - */ -DART_EXPORT Dart_TypedData_Type Dart_GetTypeOfTypedData(Dart_Handle object); - -/** - * Return type if this object is an external TypedData object. - * - * \return kInvalid if the object is not an external TypedData object or - * the appropriate Dart_TypedData_Type. - */ -DART_EXPORT Dart_TypedData_Type -Dart_GetTypeOfExternalTypedData(Dart_Handle object); - -/** - * Returns a TypedData object of the desired length and type. - * - * \param type The type of the TypedData object. - * \param length The length of the TypedData object (length in type units). - * - * \return The TypedData object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewTypedData(Dart_TypedData_Type type, - intptr_t length); - -/** - * Returns a TypedData object which references an external data array. - * - * \param type The type of the data array. - * \param data A data array. This array must not move. - * \param length The length of the data array (length in type units). - * - * \return The TypedData object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewExternalTypedData(Dart_TypedData_Type type, - void* data, - intptr_t length); - -/** - * Returns a TypedData object which references an external data array. - * - * \param type The type of the data array. - * \param data A data array. This array must not move. - * \param length The length of the data array (length in type units). - * \param peer A pointer to a native object or NULL. This value is - * provided to callback when it is invoked. - * \param external_allocation_size The number of externally allocated - * bytes for peer. Used to inform the garbage collector. - * \param callback A function pointer that will be invoked sometime - * after the object is garbage collected, unless the handle has been deleted. - * A valid callback needs to be specified it cannot be NULL. - * - * \return The TypedData object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle -Dart_NewExternalTypedDataWithFinalizer(Dart_TypedData_Type type, - void* data, - intptr_t length, - void* peer, - intptr_t external_allocation_size, - Dart_HandleFinalizer callback); -DART_EXPORT Dart_Handle Dart_NewUnmodifiableExternalTypedDataWithFinalizer( - Dart_TypedData_Type type, - const void* data, - intptr_t length, - void* peer, - intptr_t external_allocation_size, - Dart_HandleFinalizer callback); - -/** - * Returns a ByteBuffer object for the typed data. - * - * \param typed_data The TypedData object. - * - * \return The ByteBuffer object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewByteBuffer(Dart_Handle typed_data); - -/** - * Acquires access to the internal data address of a TypedData object. - * - * \param object The typed data object whose internal data address is to - * be accessed. - * \param type The type of the object is returned here. - * \param data The internal data address is returned here. - * \param len Size of the typed array is returned here. - * - * Notes: - * When the internal address of the object is acquired any calls to a - * Dart API function that could potentially allocate an object or run - * any Dart code will return an error. - * - * Any Dart API functions for accessing the data should not be called - * before the corresponding release. In particular, the object should - * not be acquired again before its release. This leads to undefined - * behavior. - * - * \return Success if the internal data address is acquired successfully. - * Otherwise, returns an error handle. - */ -DART_EXPORT Dart_Handle Dart_TypedDataAcquireData(Dart_Handle object, - Dart_TypedData_Type* type, - void** data, - intptr_t* len); - -/** - * Releases access to the internal data address that was acquired earlier using - * Dart_TypedDataAcquireData. - * - * \param object The typed data object whose internal data address is to be - * released. - * - * \return Success if the internal data address is released successfully. - * Otherwise, returns an error handle. - */ -DART_EXPORT Dart_Handle Dart_TypedDataReleaseData(Dart_Handle object); - -/** - * Returns the TypedData object associated with the ByteBuffer object. - * - * \param byte_buffer The ByteBuffer object. - * - * \return The TypedData object if no error occurs. Otherwise returns - * an error handle. - */ -DART_EXPORT Dart_Handle Dart_GetDataFromByteBuffer(Dart_Handle byte_buffer); - -/* - * ============================================================ - * Invoking Constructors, Methods, Closures and Field accessors - * ============================================================ - */ - -/** - * Invokes a constructor, creating a new object. - * - * This function allows hidden constructors (constructors with leading - * underscores) to be called. - * - * \param type Type of object to be constructed. - * \param constructor_name The name of the constructor to invoke. Use - * Dart_Null() or Dart_EmptyString() to invoke the unnamed constructor. - * This name should not include the name of the class. - * \param number_of_arguments Size of the arguments array. - * \param arguments An array of arguments to the constructor. - * - * \return If the constructor is called and completes successfully, - * then the new object. If an error occurs during execution, then an - * error handle is returned. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_New(Dart_Handle type, - Dart_Handle constructor_name, - int number_of_arguments, - Dart_Handle* arguments); - -/** - * Allocate a new object without invoking a constructor. - * - * \param type The type of an object to be allocated. - * - * \return The new object. If an error occurs during execution, then an - * error handle is returned. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_Allocate(Dart_Handle type); - -/** - * Allocate a new object without invoking a constructor, and sets specified - * native fields. - * - * \param type The type of an object to be allocated. - * \param num_native_fields The number of native fields to set. - * \param native_fields An array containing the value of native fields. - * - * \return The new object. If an error occurs during execution, then an - * error handle is returned. - */ -DART_EXPORT Dart_Handle -Dart_AllocateWithNativeFields(Dart_Handle type, - intptr_t num_native_fields, - const intptr_t* native_fields); - -/** - * Invokes a method or function. - * - * The 'target' parameter may be an object, type, or library. If - * 'target' is an object, then this function will invoke an instance - * method. If 'target' is a type, then this function will invoke a - * static method. If 'target' is a library, then this function will - * invoke a top-level function from that library. - * NOTE: This API call cannot be used to invoke methods of a type object. - * - * This function ignores visibility (leading underscores in names). - * - * May generate an unhandled exception error. - * - * \param target An object, type, or library. - * \param name The name of the function or method to invoke. - * \param number_of_arguments Size of the arguments array. - * \param arguments An array of arguments to the function. - * - * \return If the function or method is called and completes - * successfully, then the return value is returned. If an error - * occurs during execution, then an error handle is returned. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_Invoke(Dart_Handle target, - Dart_Handle name, - int number_of_arguments, - Dart_Handle* arguments); -/* TODO(turnidge): Document how to invoke operators. */ - -/** - * Invokes a Closure with the given arguments. - * - * May generate an unhandled exception error. - * - * \return If no error occurs during execution, then the result of - * invoking the closure is returned. If an error occurs during - * execution, then an error handle is returned. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_InvokeClosure(Dart_Handle closure, - int number_of_arguments, - Dart_Handle* arguments); - -/** - * Invokes a Generative Constructor on an object that was previously - * allocated using Dart_Allocate/Dart_AllocateWithNativeFields. - * - * The 'object' parameter must be an object. - * - * This function ignores visibility (leading underscores in names). - * - * May generate an unhandled exception error. - * - * \param object An object. - * \param name The name of the constructor to invoke. - * Use Dart_Null() or Dart_EmptyString() to invoke the unnamed constructor. - * \param number_of_arguments Size of the arguments array. - * \param arguments An array of arguments to the function. - * - * \return If the constructor is called and completes - * successfully, then the object is returned. If an error - * occurs during execution, then an error handle is returned. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_InvokeConstructor(Dart_Handle object, - Dart_Handle name, - int number_of_arguments, - Dart_Handle* arguments); - -/** - * Gets the value of a field. - * - * The 'container' parameter may be an object, type, or library. If - * 'container' is an object, then this function will access an - * instance field. If 'container' is a type, then this function will - * access a static field. If 'container' is a library, then this - * function will access a top-level variable. - * NOTE: This API call cannot be used to access fields of a type object. - * - * This function ignores field visibility (leading underscores in names). - * - * May generate an unhandled exception error. - * - * \param container An object, type, or library. - * \param name A field name. - * - * \return If no error occurs, then the value of the field is - * returned. Otherwise an error handle is returned. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_GetField(Dart_Handle container, Dart_Handle name); - -/** - * Sets the value of a field. - * - * The 'container' parameter may actually be an object, type, or - * library. If 'container' is an object, then this function will - * access an instance field. If 'container' is a type, then this - * function will access a static field. If 'container' is a library, - * then this function will access a top-level variable. - * NOTE: This API call cannot be used to access fields of a type object. - * - * This function ignores field visibility (leading underscores in names). - * - * May generate an unhandled exception error. - * - * \param container An object, type, or library. - * \param name A field name. - * \param value The new field value. - * - * \return A valid handle if no error occurs. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_SetField(Dart_Handle container, Dart_Handle name, Dart_Handle value); - -/* - * ========== - * Exceptions - * ========== - */ - -/* - * TODO(turnidge): Remove these functions from the api and replace all - * uses with Dart_NewUnhandledExceptionError. */ - -/** - * Throws an exception. - * - * This function causes a Dart language exception to be thrown. This - * will proceed in the standard way, walking up Dart frames until an - * appropriate 'catch' block is found, executing 'finally' blocks, - * etc. - * - * If an error handle is passed into this function, the error is - * propagated immediately. See Dart_PropagateError for a discussion - * of error propagation. - * - * If successful, this function does not return. Note that this means - * that the destructors of any stack-allocated C++ objects will not be - * called. If there are no Dart frames on the stack, an error occurs. - * - * \return An error handle if the exception was not thrown. - * Otherwise the function does not return. - */ -DART_EXPORT Dart_Handle Dart_ThrowException(Dart_Handle exception); - -/** - * Rethrows an exception. - * - * Rethrows an exception, unwinding all dart frames on the stack. If - * successful, this function does not return. Note that this means - * that the destructors of any stack-allocated C++ objects will not be - * called. If there are no Dart frames on the stack, an error occurs. - * - * \return An error handle if the exception was not thrown. - * Otherwise the function does not return. - */ -DART_EXPORT Dart_Handle Dart_ReThrowException(Dart_Handle exception, - Dart_Handle stacktrace); - -/* - * =========================== - * Native fields and functions - * =========================== - */ - -/** - * Gets the number of native instance fields in an object. - */ -DART_EXPORT Dart_Handle Dart_GetNativeInstanceFieldCount(Dart_Handle obj, - int* count); - -/** - * Gets the value of a native field. - * - * TODO(turnidge): Document. - */ -DART_EXPORT Dart_Handle Dart_GetNativeInstanceField(Dart_Handle obj, - int index, - intptr_t* value); - -/** - * Sets the value of a native field. - * - * TODO(turnidge): Document. - */ -DART_EXPORT Dart_Handle Dart_SetNativeInstanceField(Dart_Handle obj, - int index, - intptr_t value); - -/** - * The arguments to a native function. - * - * This object is passed to a native function to represent its - * arguments and return value. It allows access to the arguments to a - * native function by index. It also allows the return value of a - * native function to be set. - */ -typedef struct _Dart_NativeArguments* Dart_NativeArguments; - -/** - * Extracts current isolate group data from the native arguments structure. - */ -DART_EXPORT void* Dart_GetNativeIsolateGroupData(Dart_NativeArguments args); - -typedef enum { - Dart_NativeArgument_kBool = 0, - Dart_NativeArgument_kInt32, - Dart_NativeArgument_kUint32, - Dart_NativeArgument_kInt64, - Dart_NativeArgument_kUint64, - Dart_NativeArgument_kDouble, - Dart_NativeArgument_kString, - Dart_NativeArgument_kInstance, - Dart_NativeArgument_kNativeFields, -} Dart_NativeArgument_Type; - -typedef struct _Dart_NativeArgument_Descriptor { - uint8_t type; - uint8_t index; -} Dart_NativeArgument_Descriptor; - -typedef union _Dart_NativeArgument_Value { - bool as_bool; - int32_t as_int32; - uint32_t as_uint32; - int64_t as_int64; - uint64_t as_uint64; - double as_double; - struct { - Dart_Handle dart_str; - void* peer; - } as_string; - struct { - intptr_t num_fields; - intptr_t* values; - } as_native_fields; - Dart_Handle as_instance; -} Dart_NativeArgument_Value; - -enum { - kNativeArgNumberPos = 0, - kNativeArgNumberSize = 8, - kNativeArgTypePos = kNativeArgNumberPos + kNativeArgNumberSize, - kNativeArgTypeSize = 8, -}; - -#define BITMASK(size) ((1 << size) - 1) -#define DART_NATIVE_ARG_DESCRIPTOR(type, position) \ - (((type & BITMASK(kNativeArgTypeSize)) << kNativeArgTypePos) | \ - (position & BITMASK(kNativeArgNumberSize))) - -/** - * Gets the native arguments based on the types passed in and populates - * the passed arguments buffer with appropriate native values. - * - * \param args the Native arguments block passed into the native call. - * \param num_arguments length of argument descriptor array and argument - * values array passed in. - * \param arg_descriptors an array that describes the arguments that - * need to be retrieved. For each argument to be retrieved the descriptor - * contains the argument number (0, 1 etc.) and the argument type - * described using Dart_NativeArgument_Type, e.g: - * DART_NATIVE_ARG_DESCRIPTOR(Dart_NativeArgument_kBool, 1) indicates - * that the first argument is to be retrieved and it should be a boolean. - * \param arg_values array into which the native arguments need to be - * extracted into, the array is allocated by the caller (it could be - * stack allocated to avoid the malloc/free performance overhead). - * - * \return Success if all the arguments could be extracted correctly, - * returns an error handle if there were any errors while extracting the - * arguments (mismatched number of arguments, incorrect types, etc.). - */ -DART_EXPORT Dart_Handle -Dart_GetNativeArguments(Dart_NativeArguments args, - int num_arguments, - const Dart_NativeArgument_Descriptor* arg_descriptors, - Dart_NativeArgument_Value* arg_values); - -/** - * Gets the native argument at some index. - */ -DART_EXPORT Dart_Handle Dart_GetNativeArgument(Dart_NativeArguments args, - int index); -/* TODO(turnidge): Specify the behavior of an out-of-bounds access. */ - -/** - * Gets the number of native arguments. - */ -DART_EXPORT int Dart_GetNativeArgumentCount(Dart_NativeArguments args); - -/** - * Gets all the native fields of the native argument at some index. - * \param args Native arguments structure. - * \param arg_index Index of the desired argument in the structure above. - * \param num_fields size of the intptr_t array 'field_values' passed in. - * \param field_values intptr_t array in which native field values are returned. - * \return Success if the native fields where copied in successfully. Otherwise - * returns an error handle. On success the native field values are copied - * into the 'field_values' array, if the argument at 'arg_index' is a - * null object then 0 is copied as the native field values into the - * 'field_values' array. - */ -DART_EXPORT Dart_Handle -Dart_GetNativeFieldsOfArgument(Dart_NativeArguments args, - int arg_index, - int num_fields, - intptr_t* field_values); - -/** - * Gets the native field of the receiver. - */ -DART_EXPORT Dart_Handle Dart_GetNativeReceiver(Dart_NativeArguments args, - intptr_t* value); - -/** - * Gets a string native argument at some index. - * \param args Native arguments structure. - * \param arg_index Index of the desired argument in the structure above. - * \param peer Returns the peer pointer if the string argument has one. - * \return Success if the string argument has a peer, if it does not - * have a peer then the String object is returned. Otherwise returns - * an error handle (argument is not a String object). - */ -DART_EXPORT Dart_Handle Dart_GetNativeStringArgument(Dart_NativeArguments args, - int arg_index, - void** peer); - -/** - * Gets an integer native argument at some index. - * \param args Native arguments structure. - * \param index Index of the desired argument in the structure above. - * \param value Returns the integer value if the argument is an Integer. - * \return Success if no error occurs. Otherwise returns an error handle. - */ -DART_EXPORT Dart_Handle Dart_GetNativeIntegerArgument(Dart_NativeArguments args, - int index, - int64_t* value); - -/** - * Gets a boolean native argument at some index. - * \param args Native arguments structure. - * \param index Index of the desired argument in the structure above. - * \param value Returns the boolean value if the argument is a Boolean. - * \return Success if no error occurs. Otherwise returns an error handle. - */ -DART_EXPORT Dart_Handle Dart_GetNativeBooleanArgument(Dart_NativeArguments args, - int index, - bool* value); - -/** - * Gets a double native argument at some index. - * \param args Native arguments structure. - * \param index Index of the desired argument in the structure above. - * \param value Returns the double value if the argument is a double. - * \return Success if no error occurs. Otherwise returns an error handle. - */ -DART_EXPORT Dart_Handle Dart_GetNativeDoubleArgument(Dart_NativeArguments args, - int index, - double* value); - -/** - * Sets the return value for a native function. - * - * If retval is an Error handle, then error will be propagated once - * the native functions exits. See Dart_PropagateError for a - * discussion of how different types of errors are propagated. - */ -DART_EXPORT void Dart_SetReturnValue(Dart_NativeArguments args, - Dart_Handle retval); - -DART_EXPORT void Dart_SetWeakHandleReturnValue(Dart_NativeArguments args, - Dart_WeakPersistentHandle rval); - -DART_EXPORT void Dart_SetBooleanReturnValue(Dart_NativeArguments args, - bool retval); - -DART_EXPORT void Dart_SetIntegerReturnValue(Dart_NativeArguments args, - int64_t retval); - -DART_EXPORT void Dart_SetDoubleReturnValue(Dart_NativeArguments args, - double retval); - -/** - * A native function. - */ -typedef void (*Dart_NativeFunction)(Dart_NativeArguments arguments); - -/** - * Native entry resolution callback. - * - * For libraries and scripts which have native functions, the embedder - * can provide a native entry resolver. This callback is used to map a - * name/arity to a Dart_NativeFunction. If no function is found, the - * callback should return NULL. - * - * The parameters to the native resolver function are: - * \param name a Dart string which is the name of the native function. - * \param num_of_arguments is the number of arguments expected by the - * native function. - * \param auto_setup_scope is a boolean flag that can be set by the resolver - * to indicate if this function needs a Dart API scope (see Dart_EnterScope/ - * Dart_ExitScope) to be setup automatically by the VM before calling into - * the native function. By default most native functions would require this - * to be true but some light weight native functions which do not call back - * into the VM through the Dart API may not require a Dart scope to be - * setup automatically. - * - * \return A valid Dart_NativeFunction which resolves to a native entry point - * for the native function. - * - * See Dart_SetNativeResolver. - */ -typedef Dart_NativeFunction (*Dart_NativeEntryResolver)(Dart_Handle name, - int num_of_arguments, - bool* auto_setup_scope); -/* TODO(turnidge): Consider renaming to NativeFunctionResolver or - * NativeResolver. */ - -/** - * Native entry symbol lookup callback. - * - * For libraries and scripts which have native functions, the embedder - * can provide a callback for mapping a native entry to a symbol. This callback - * maps a native function entry PC to the native function name. If no native - * entry symbol can be found, the callback should return NULL. - * - * The parameters to the native reverse resolver function are: - * \param nf A Dart_NativeFunction. - * - * \return A const UTF-8 string containing the symbol name or NULL. - * - * See Dart_SetNativeResolver. - */ -typedef const uint8_t* (*Dart_NativeEntrySymbol)(Dart_NativeFunction nf); - -/** - * FFI Native C function pointer resolver callback. - * - * See Dart_SetFfiNativeResolver. - */ -typedef void* (*Dart_FfiNativeResolver)(const char* name, uintptr_t args_n); - -/* - * =========== - * Environment - * =========== - */ - -/** - * An environment lookup callback function. - * - * \param name The name of the value to lookup in the environment. - * - * \return A valid handle to a string if the name exists in the - * current environment or Dart_Null() if not. - */ -typedef Dart_Handle (*Dart_EnvironmentCallback)(Dart_Handle name); - -/** - * Sets the environment callback for the current isolate. This - * callback is used to lookup environment values by name in the - * current environment. This enables the embedder to supply values for - * the const constructors bool.fromEnvironment, int.fromEnvironment - * and String.fromEnvironment. - */ -DART_EXPORT Dart_Handle -Dart_SetEnvironmentCallback(Dart_EnvironmentCallback callback); - -/** - * Sets the callback used to resolve native functions for a library. - * - * \param library A library. - * \param resolver A native entry resolver. - * - * \return A valid handle if the native resolver was set successfully. - */ -DART_EXPORT Dart_Handle -Dart_SetNativeResolver(Dart_Handle library, - Dart_NativeEntryResolver resolver, - Dart_NativeEntrySymbol symbol); -/* TODO(turnidge): Rename to Dart_LibrarySetNativeResolver? */ - -/** - * Returns the callback used to resolve native functions for a library. - * - * \param library A library. - * \param resolver a pointer to a Dart_NativeEntryResolver - * - * \return A valid handle if the library was found. - */ -DART_EXPORT Dart_Handle -Dart_GetNativeResolver(Dart_Handle library, Dart_NativeEntryResolver* resolver); - -/** - * Returns the callback used to resolve native function symbols for a library. - * - * \param library A library. - * \param resolver a pointer to a Dart_NativeEntrySymbol. - * - * \return A valid handle if the library was found. - */ -DART_EXPORT Dart_Handle Dart_GetNativeSymbol(Dart_Handle library, - Dart_NativeEntrySymbol* resolver); - -/** - * Sets the callback used to resolve FFI native functions for a library. - * The resolved functions are expected to be a C function pointer of the - * correct signature (as specified in the `@FfiNative()` function - * annotation in Dart code). - * - * NOTE: This is an experimental feature and might change in the future. - * - * \param library A library. - * \param resolver A native function resolver. - * - * \return A valid handle if the native resolver was set successfully. - */ -DART_EXPORT Dart_Handle -Dart_SetFfiNativeResolver(Dart_Handle library, Dart_FfiNativeResolver resolver); - -/* - * ===================== - * Scripts and Libraries - * ===================== - */ - -typedef enum { - Dart_kCanonicalizeUrl = 0, - Dart_kImportTag, - Dart_kKernelTag, -} Dart_LibraryTag; - -/** - * The library tag handler is a multi-purpose callback provided by the - * embedder to the Dart VM. The embedder implements the tag handler to - * provide the ability to load Dart scripts and imports. - * - * -- TAGS -- - * - * Dart_kCanonicalizeUrl - * - * This tag indicates that the embedder should canonicalize 'url' with - * respect to 'library'. For most embedders, the - * Dart_DefaultCanonicalizeUrl function is a sufficient implementation - * of this tag. The return value should be a string holding the - * canonicalized url. - * - * Dart_kImportTag - * - * This tag is used to load a library from IsolateMirror.loadUri. The embedder - * should call Dart_LoadLibraryFromKernel to provide the library to the VM. The - * return value should be an error or library (the result from - * Dart_LoadLibraryFromKernel). - * - * Dart_kKernelTag - * - * This tag is used to load the intermediate file (kernel) generated by - * the Dart front end. This tag is typically used when a 'hot-reload' - * of an application is needed and the VM is 'use dart front end' mode. - * The dart front end typically compiles all the scripts, imports and part - * files into one intermediate file hence we don't use the source/import or - * script tags. The return value should be an error or a TypedData containing - * the kernel bytes. - * - */ -typedef Dart_Handle (*Dart_LibraryTagHandler)( - Dart_LibraryTag tag, - Dart_Handle library_or_package_map_url, - Dart_Handle url); - -/** - * Sets library tag handler for the current isolate. This handler is - * used to handle the various tags encountered while loading libraries - * or scripts in the isolate. - * - * \param handler Handler code to be used for handling the various tags - * encountered while loading libraries or scripts in the isolate. - * - * \return If no error occurs, the handler is set for the isolate. - * Otherwise an error handle is returned. - * - * TODO(turnidge): Document. - */ -DART_EXPORT Dart_Handle -Dart_SetLibraryTagHandler(Dart_LibraryTagHandler handler); - -/** - * Handles deferred loading requests. When this handler is invoked, it should - * eventually load the deferred loading unit with the given id and call - * Dart_DeferredLoadComplete or Dart_DeferredLoadCompleteError. It is - * recommended that the loading occur asynchronously, but it is permitted to - * call Dart_DeferredLoadComplete or Dart_DeferredLoadCompleteError before the - * handler returns. - * - * If an error is returned, it will be propagated through - * `prefix.loadLibrary()`. This is useful for synchronous - * implementations, which must propagate any unwind errors from - * Dart_DeferredLoadComplete or Dart_DeferredLoadComplete. Otherwise the handler - * should return a non-error such as `Dart_Null()`. - */ -typedef Dart_Handle (*Dart_DeferredLoadHandler)(intptr_t loading_unit_id); - -/** - * Sets the deferred load handler for the current isolate. This handler is - * used to handle loading deferred imports in an AppJIT or AppAOT program. - */ -DART_EXPORT Dart_Handle -Dart_SetDeferredLoadHandler(Dart_DeferredLoadHandler handler); - -/** - * Notifies the VM that a deferred load completed successfully. This function - * will eventually cause the corresponding `prefix.loadLibrary()` futures to - * complete. - * - * Requires the current isolate to be the same current isolate during the - * invocation of the Dart_DeferredLoadHandler. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_DeferredLoadComplete(intptr_t loading_unit_id, - const uint8_t* snapshot_data, - const uint8_t* snapshot_instructions); - -/** - * Notifies the VM that a deferred load failed. This function - * will eventually cause the corresponding `prefix.loadLibrary()` futures to - * complete with an error. - * - * If `transient` is true, future invocations of `prefix.loadLibrary()` will - * trigger new load requests. If false, futures invocation will complete with - * the same error. - * - * Requires the current isolate to be the same current isolate during the - * invocation of the Dart_DeferredLoadHandler. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_DeferredLoadCompleteError(intptr_t loading_unit_id, - const char* error_message, - bool transient); - -/** - * Canonicalizes a url with respect to some library. - * - * The url is resolved with respect to the library's url and some url - * normalizations are performed. - * - * This canonicalization function should be sufficient for most - * embedders to implement the Dart_kCanonicalizeUrl tag. - * - * \param base_url The base url relative to which the url is - * being resolved. - * \param url The url being resolved and canonicalized. This - * parameter is a string handle. - * - * \return If no error occurs, a String object is returned. Otherwise - * an error handle is returned. - */ -DART_EXPORT Dart_Handle Dart_DefaultCanonicalizeUrl(Dart_Handle base_url, - Dart_Handle url); - -/** - * Loads the root library for the current isolate. - * - * Requires there to be no current root library. - * - * \param kernel_buffer A buffer which contains a kernel binary (see - * pkg/kernel/binary.md). Must remain valid until isolate group shutdown. - * \param kernel_size Length of the passed in buffer. - * - * \return A handle to the root library, or an error. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_LoadScriptFromKernel(const uint8_t* kernel_buffer, intptr_t kernel_size); - -/** - * Gets the library for the root script for the current isolate. - * - * If the root script has not yet been set for the current isolate, - * this function returns Dart_Null(). This function never returns an - * error handle. - * - * \return Returns the root Library for the current isolate or Dart_Null(). - */ -DART_EXPORT Dart_Handle Dart_RootLibrary(void); - -/** - * Sets the root library for the current isolate. - * - * \return Returns an error handle if `library` is not a library handle. - */ -DART_EXPORT Dart_Handle Dart_SetRootLibrary(Dart_Handle library); - -/** - * Lookup or instantiate a legacy type by name and type arguments from a - * Library. - * - * \param library The library containing the class or interface. - * \param class_name The class name for the type. - * \param number_of_type_arguments Number of type arguments. - * For non parametric types the number of type arguments would be 0. - * \param type_arguments Pointer to an array of type arguments. - * For non parametric types a NULL would be passed in for this argument. - * - * \return If no error occurs, the type is returned. - * Otherwise an error handle is returned. - */ -DART_EXPORT Dart_Handle Dart_GetType(Dart_Handle library, - Dart_Handle class_name, - intptr_t number_of_type_arguments, - Dart_Handle* type_arguments); - -/** - * Lookup or instantiate a nullable type by name and type arguments from - * Library. - * - * \param library The library containing the class or interface. - * \param class_name The class name for the type. - * \param number_of_type_arguments Number of type arguments. - * For non parametric types the number of type arguments would be 0. - * \param type_arguments Pointer to an array of type arguments. - * For non parametric types a NULL would be passed in for this argument. - * - * \return If no error occurs, the type is returned. - * Otherwise an error handle is returned. - */ -DART_EXPORT Dart_Handle Dart_GetNullableType(Dart_Handle library, - Dart_Handle class_name, - intptr_t number_of_type_arguments, - Dart_Handle* type_arguments); - -/** - * Lookup or instantiate a non-nullable type by name and type arguments from - * Library. - * - * \param library The library containing the class or interface. - * \param class_name The class name for the type. - * \param number_of_type_arguments Number of type arguments. - * For non parametric types the number of type arguments would be 0. - * \param type_arguments Pointer to an array of type arguments. - * For non parametric types a NULL would be passed in for this argument. - * - * \return If no error occurs, the type is returned. - * Otherwise an error handle is returned. - */ -DART_EXPORT Dart_Handle -Dart_GetNonNullableType(Dart_Handle library, - Dart_Handle class_name, - intptr_t number_of_type_arguments, - Dart_Handle* type_arguments); - -/** - * Creates a nullable version of the provided type. - * - * \param type The type to be converted to a nullable type. - * - * \return If no error occurs, a nullable type is returned. - * Otherwise an error handle is returned. - */ -DART_EXPORT Dart_Handle Dart_TypeToNullableType(Dart_Handle type); - -/** - * Creates a non-nullable version of the provided type. - * - * \param type The type to be converted to a non-nullable type. - * - * \return If no error occurs, a non-nullable type is returned. - * Otherwise an error handle is returned. - */ -DART_EXPORT Dart_Handle Dart_TypeToNonNullableType(Dart_Handle type); - -/** - * A type's nullability. - * - * \param type A Dart type. - * \param result An out parameter containing the result of the check. True if - * the type is of the specified nullability, false otherwise. - * - * \return Returns an error handle if type is not of type Type. - */ -DART_EXPORT Dart_Handle Dart_IsNullableType(Dart_Handle type, bool* result); -DART_EXPORT Dart_Handle Dart_IsNonNullableType(Dart_Handle type, bool* result); -DART_EXPORT Dart_Handle Dart_IsLegacyType(Dart_Handle type, bool* result); - -/** - * Lookup a class or interface by name from a Library. - * - * \param library The library containing the class or interface. - * \param class_name The name of the class or interface. - * - * \return If no error occurs, the class or interface is - * returned. Otherwise an error handle is returned. - */ -DART_EXPORT Dart_Handle Dart_GetClass(Dart_Handle library, - Dart_Handle class_name); -/* TODO(asiva): The above method needs to be removed once all uses - * of it are removed from the embedder code. */ - -/** - * Returns an import path to a Library, such as "file:///test.dart" or - * "dart:core". - */ -DART_EXPORT Dart_Handle Dart_LibraryUrl(Dart_Handle library); - -/** - * Returns a URL from which a Library was loaded. - */ -DART_EXPORT Dart_Handle Dart_LibraryResolvedUrl(Dart_Handle library); - -/** - * \return An array of libraries. - */ -DART_EXPORT Dart_Handle Dart_GetLoadedLibraries(void); - -DART_EXPORT Dart_Handle Dart_LookupLibrary(Dart_Handle url); -/* TODO(turnidge): Consider returning Dart_Null() when the library is - * not found to distinguish that from a true error case. */ - -/** - * Report an loading error for the library. - * - * \param library The library that failed to load. - * \param error The Dart error instance containing the load error. - * - * \return If the VM handles the error, the return value is - * a null handle. If it doesn't handle the error, the error - * object is returned. - */ -DART_EXPORT Dart_Handle Dart_LibraryHandleError(Dart_Handle library, - Dart_Handle error); - -/** - * Called by the embedder to load a partial program. Does not set the root - * library. - * - * \param kernel_buffer A buffer which contains a kernel binary (see - * pkg/kernel/binary.md). Must remain valid until isolate shutdown. - * \param kernel_buffer_size Length of the passed in buffer. - * - * \return A handle to the main library of the compilation unit, or an error. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_LoadLibraryFromKernel(const uint8_t* kernel_buffer, - intptr_t kernel_buffer_size); -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_LoadLibrary(Dart_Handle kernel_buffer); - -/** - * Indicates that all outstanding load requests have been satisfied. - * This finalizes all the new classes loaded and optionally completes - * deferred library futures. - * - * Requires there to be a current isolate. - * - * \param complete_futures Specify true if all deferred library - * futures should be completed, false otherwise. - * - * \return Success if all classes have been finalized and deferred library - * futures are completed. Otherwise, returns an error. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_FinalizeLoading(bool complete_futures); - -/* - * ===== - * Peers - * ===== - */ - -/** - * The peer field is a lazily allocated field intended for storage of - * an uncommonly used values. Most instances types can have a peer - * field allocated. The exceptions are subtypes of Null, num, and - * bool. - */ - -/** - * Returns the value of peer field of 'object' in 'peer'. - * - * \param object An object. - * \param peer An out parameter that returns the value of the peer - * field. - * - * \return Returns an error if 'object' is a subtype of Null, num, or - * bool. - */ -DART_EXPORT Dart_Handle Dart_GetPeer(Dart_Handle object, void** peer); - -/** - * Sets the value of the peer field of 'object' to the value of - * 'peer'. - * - * \param object An object. - * \param peer A value to store in the peer field. - * - * \return Returns an error if 'object' is a subtype of Null, num, or - * bool. - */ -DART_EXPORT Dart_Handle Dart_SetPeer(Dart_Handle object, void* peer); - -/* - * ====== - * Kernel - * ====== - */ - -/** - * Experimental support for Dart to Kernel parser isolate. - * - * TODO(hausner): Document finalized interface. - * - */ - -// TODO(33433): Remove kernel service from the embedding API. - -typedef enum { - Dart_KernelCompilationStatus_Unknown = -1, - Dart_KernelCompilationStatus_Ok = 0, - Dart_KernelCompilationStatus_Error = 1, - Dart_KernelCompilationStatus_Crash = 2, - Dart_KernelCompilationStatus_MsgFailed = 3, -} Dart_KernelCompilationStatus; - -typedef struct { - Dart_KernelCompilationStatus status; - bool null_safety; - char* error; - uint8_t* kernel; - intptr_t kernel_size; -} Dart_KernelCompilationResult; - -typedef enum { - Dart_KernelCompilationVerbosityLevel_Error = 0, - Dart_KernelCompilationVerbosityLevel_Warning, - Dart_KernelCompilationVerbosityLevel_Info, - Dart_KernelCompilationVerbosityLevel_All, -} Dart_KernelCompilationVerbosityLevel; - -DART_EXPORT bool Dart_IsKernelIsolate(Dart_Isolate isolate); -DART_EXPORT bool Dart_KernelIsolateIsRunning(void); -DART_EXPORT Dart_Port Dart_KernelPort(void); - -/** - * Compiles the given `script_uri` to a kernel file. - * - * \param platform_kernel A buffer containing the kernel of the platform (e.g. - * `vm_platform_strong.dill`). The VM does not take ownership of this memory. - * - * \param platform_kernel_size The length of the platform_kernel buffer. - * - * \param snapshot_compile Set to `true` when the compilation is for a snapshot. - * This is used by the frontend to determine if compilation related information - * should be printed to console (e.g., null safety mode). - * - * \param verbosity Specifies the logging behavior of the kernel compilation - * service. - * - * \return Returns the result of the compilation. - * - * On a successful compilation the returned [Dart_KernelCompilationResult] has - * a status of [Dart_KernelCompilationStatus_Ok] and the `kernel`/`kernel_size` - * fields are set. The caller takes ownership of the malloc()ed buffer. - * - * On a failed compilation the `error` might be set describing the reason for - * the failed compilation. The caller takes ownership of the malloc()ed - * error. - * - * Requires there to be a current isolate. - */ -DART_EXPORT Dart_KernelCompilationResult -Dart_CompileToKernel(const char* script_uri, - const uint8_t* platform_kernel, - const intptr_t platform_kernel_size, - bool incremental_compile, - bool snapshot_compile, - const char* package_config, - Dart_KernelCompilationVerbosityLevel verbosity); - -typedef struct { - const char* uri; - const char* source; -} Dart_SourceFile; - -DART_EXPORT Dart_KernelCompilationResult Dart_KernelListDependencies(void); - -/** - * Sets the kernel buffer which will be used to load Dart SDK sources - * dynamically at runtime. - * - * \param platform_kernel A buffer containing kernel which has sources for the - * Dart SDK populated. Note: The VM does not take ownership of this memory. - * - * \param platform_kernel_size The length of the platform_kernel buffer. - */ -DART_EXPORT void Dart_SetDartLibrarySourcesKernel( - const uint8_t* platform_kernel, - const intptr_t platform_kernel_size); - -/** - * Detect the null safety opt-in status. - * - * When running from source, it is based on the opt-in status of `script_uri`. - * When running from a kernel buffer, it is based on the mode used when - * generating `kernel_buffer`. - * When running from an appJIT or AOT snapshot, it is based on the mode used - * when generating `snapshot_data`. - * - * \param script_uri Uri of the script that contains the source code - * - * \param package_config Uri of the package configuration file (either in format - * of .packages or .dart_tool/package_config.json) for the null safety - * detection to resolve package imports against. If this parameter is not - * passed the package resolution of the parent isolate should be used. - * - * \param original_working_directory current working directory when the VM - * process was launched, this is used to correctly resolve the path specified - * for package_config. - * - * \param snapshot_data Buffer containing the snapshot data of the - * isolate or NULL if no snapshot is provided. If provided, the buffers must - * remain valid until the isolate shuts down. - * - * \param snapshot_instructions Buffer containing the snapshot instructions of - * the isolate or NULL if no snapshot is provided. If provided, the buffers - * must remain valid until the isolate shuts down. - * - * \param kernel_buffer A buffer which contains a kernel/DIL program. Must - * remain valid until isolate shutdown. - * - * \param kernel_buffer_size The size of `kernel_buffer`. - * - * \return Returns true if the null safety is opted in by the input being - * run `script_uri`, `snapshot_data` or `kernel_buffer`. - * - */ -DART_EXPORT bool Dart_DetectNullSafety(const char* script_uri, - const char* package_config, - const char* original_working_directory, - const uint8_t* snapshot_data, - const uint8_t* snapshot_instructions, - const uint8_t* kernel_buffer, - intptr_t kernel_buffer_size); - -#define DART_KERNEL_ISOLATE_NAME "kernel-service" - -/* - * ======= - * Service - * ======= - */ - -#define DART_VM_SERVICE_ISOLATE_NAME "vm-service" - -/** - * Returns true if isolate is the service isolate. - * - * \param isolate An isolate - * - * \return Returns true if 'isolate' is the service isolate. - */ -DART_EXPORT bool Dart_IsServiceIsolate(Dart_Isolate isolate); - -/** - * Writes the CPU profile to the timeline as a series of 'instant' events. - * - * Note that this is an expensive operation. - * - * \param main_port The main port of the Isolate whose profile samples to write. - * \param error An optional error, must be free()ed by caller. - * - * \return Returns true if the profile is successfully written and false - * otherwise. - */ -DART_EXPORT bool Dart_WriteProfileToTimeline(Dart_Port main_port, char** error); - -/* - * ============== - * Precompilation - * ============== - */ - -/** - * Compiles all functions reachable from entry points and marks - * the isolate to disallow future compilation. - * - * Entry points should be specified using `@pragma("vm:entry-point")` - * annotation. - * - * \return An error handle if a compilation error or runtime error running const - * constructors was encountered. - */ -DART_EXPORT Dart_Handle Dart_Precompile(void); - -typedef void (*Dart_CreateLoadingUnitCallback)( - void* callback_data, - intptr_t loading_unit_id, - void** write_callback_data, - void** write_debug_callback_data); -typedef void (*Dart_StreamingWriteCallback)(void* callback_data, - const uint8_t* buffer, - intptr_t size); -typedef void (*Dart_StreamingCloseCallback)(void* callback_data); - -DART_EXPORT Dart_Handle Dart_LoadingUnitLibraryUris(intptr_t loading_unit_id); - -// On Darwin systems, 'dlsym' adds an '_' to the beginning of the symbol name. -// Use the '...CSymbol' definitions for resolving through 'dlsym'. The actual -// symbol names in the objects are given by the '...AsmSymbol' definitions. -#if defined(__APPLE__) -#define kSnapshotBuildIdCSymbol "kDartSnapshotBuildId" -#define kVmSnapshotDataCSymbol "kDartVmSnapshotData" -#define kVmSnapshotInstructionsCSymbol "kDartVmSnapshotInstructions" -#define kVmSnapshotBssCSymbol "kDartVmSnapshotBss" -#define kIsolateSnapshotDataCSymbol "kDartIsolateSnapshotData" -#define kIsolateSnapshotInstructionsCSymbol "kDartIsolateSnapshotInstructions" -#define kIsolateSnapshotBssCSymbol "kDartIsolateSnapshotBss" -#else -#define kSnapshotBuildIdCSymbol "_kDartSnapshotBuildId" -#define kVmSnapshotDataCSymbol "_kDartVmSnapshotData" -#define kVmSnapshotInstructionsCSymbol "_kDartVmSnapshotInstructions" -#define kVmSnapshotBssCSymbol "_kDartVmSnapshotBss" -#define kIsolateSnapshotDataCSymbol "_kDartIsolateSnapshotData" -#define kIsolateSnapshotInstructionsCSymbol "_kDartIsolateSnapshotInstructions" -#define kIsolateSnapshotBssCSymbol "_kDartIsolateSnapshotBss" -#endif - -#define kSnapshotBuildIdAsmSymbol "_kDartSnapshotBuildId" -#define kVmSnapshotDataAsmSymbol "_kDartVmSnapshotData" -#define kVmSnapshotInstructionsAsmSymbol "_kDartVmSnapshotInstructions" -#define kVmSnapshotBssAsmSymbol "_kDartVmSnapshotBss" -#define kIsolateSnapshotDataAsmSymbol "_kDartIsolateSnapshotData" -#define kIsolateSnapshotInstructionsAsmSymbol \ - "_kDartIsolateSnapshotInstructions" -#define kIsolateSnapshotBssAsmSymbol "_kDartIsolateSnapshotBss" - -/** - * Creates a precompiled snapshot. - * - A root library must have been loaded. - * - Dart_Precompile must have been called. - * - * Outputs an assembly file defining the symbols listed in the definitions - * above. - * - * The assembly should be compiled as a static or shared library and linked or - * loaded by the embedder. Running this snapshot requires a VM compiled with - * DART_PRECOMPILED_SNAPSHOT. The kDartVmSnapshotData and - * kDartVmSnapshotInstructions should be passed to Dart_Initialize. The - * kDartIsolateSnapshotData and kDartIsolateSnapshotInstructions should be - * passed to Dart_CreateIsolateGroup. - * - * The callback will be invoked one or more times to provide the assembly code. - * - * If stripped is true, then the assembly code will not include DWARF - * debugging sections. - * - * If debug_callback_data is provided, debug_callback_data will be used with - * the callback to provide separate debugging information. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_CreateAppAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback, - void* callback_data, - bool stripped, - void* debug_callback_data); -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_CreateAppAOTSnapshotAsAssemblies( - Dart_CreateLoadingUnitCallback next_callback, - void* next_callback_data, - bool stripped, - Dart_StreamingWriteCallback write_callback, - Dart_StreamingCloseCallback close_callback); - -/** - * Creates a precompiled snapshot. - * - A root library must have been loaded. - * - Dart_Precompile must have been called. - * - * Outputs an ELF shared library defining the symbols - * - _kDartVmSnapshotData - * - _kDartVmSnapshotInstructions - * - _kDartIsolateSnapshotData - * - _kDartIsolateSnapshotInstructions - * - * The shared library should be dynamically loaded by the embedder. - * Running this snapshot requires a VM compiled with DART_PRECOMPILED_SNAPSHOT. - * The kDartVmSnapshotData and kDartVmSnapshotInstructions should be passed to - * Dart_Initialize. The kDartIsolateSnapshotData and - * kDartIsolateSnapshotInstructions should be passed to Dart_CreateIsolate. - * - * The callback will be invoked one or more times to provide the binary output. - * - * If stripped is true, then the binary output will not include DWARF - * debugging sections. - * - * If debug_callback_data is provided, debug_callback_data will be used with - * the callback to provide separate debugging information. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_CreateAppAOTSnapshotAsElf(Dart_StreamingWriteCallback callback, - void* callback_data, - bool stripped, - void* debug_callback_data); -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_CreateAppAOTSnapshotAsElfs(Dart_CreateLoadingUnitCallback next_callback, - void* next_callback_data, - bool stripped, - Dart_StreamingWriteCallback write_callback, - Dart_StreamingCloseCallback close_callback); - -/** - * Like Dart_CreateAppAOTSnapshotAsAssembly, but only includes - * kDartVmSnapshotData and kDartVmSnapshotInstructions. It also does - * not strip DWARF information from the generated assembly or allow for - * separate debug information. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_CreateVMAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback, - void* callback_data); - -/** - * Sorts the class-ids in depth first traversal order of the inheritance - * tree. This is a costly operation, but it can make method dispatch - * more efficient and is done before writing snapshots. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_SortClasses(void); - -/** - * Creates a snapshot that caches compiled code and type feedback for faster - * startup and quicker warmup in a subsequent process. - * - * Outputs a snapshot in two pieces. The pieces should be passed to - * Dart_CreateIsolateGroup in a VM using the same VM snapshot pieces used in the - * current VM. The instructions piece must be loaded with read and execute - * permissions; the data piece may be loaded as read-only. - * - * - Requires the VM to have not been started with --precompilation. - * - Not supported when targeting IA32. - * - The VM writing the snapshot and the VM reading the snapshot must be the - * same version, must be built in the same DEBUG/RELEASE/PRODUCT mode, must - * be targeting the same architecture, and must both be in checked mode or - * both in unchecked mode. - * - * The buffers are scope allocated and are only valid until the next call to - * Dart_ExitScope. - * - * \return A valid handle if no error occurs during the operation. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_CreateAppJITSnapshotAsBlobs(uint8_t** isolate_snapshot_data_buffer, - intptr_t* isolate_snapshot_data_size, - uint8_t** isolate_snapshot_instructions_buffer, - intptr_t* isolate_snapshot_instructions_size); - -/** - * Like Dart_CreateAppJITSnapshotAsBlobs, but also creates a new VM snapshot. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_CreateCoreJITSnapshotAsBlobs( - uint8_t** vm_snapshot_data_buffer, - intptr_t* vm_snapshot_data_size, - uint8_t** vm_snapshot_instructions_buffer, - intptr_t* vm_snapshot_instructions_size, - uint8_t** isolate_snapshot_data_buffer, - intptr_t* isolate_snapshot_data_size, - uint8_t** isolate_snapshot_instructions_buffer, - intptr_t* isolate_snapshot_instructions_size); - -/** - * Get obfuscation map for precompiled code. - * - * Obfuscation map is encoded as a JSON array of pairs (original name, - * obfuscated name). - * - * \return Returns an error handler if the VM was built in a mode that does not - * support obfuscation. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle -Dart_GetObfuscationMap(uint8_t** buffer, intptr_t* buffer_length); - -/** - * Returns whether the VM only supports running from precompiled snapshots and - * not from any other kind of snapshot or from source (that is, the VM was - * compiled with DART_PRECOMPILED_RUNTIME). - */ -DART_EXPORT bool Dart_IsPrecompiledRuntime(void); - -/** - * Print a native stack trace. Used for crash handling. - * - * If context is NULL, prints the current stack trace. Otherwise, context - * should be a CONTEXT* (Windows) or ucontext_t* (POSIX) from a signal handler - * running on the current thread. - */ -DART_EXPORT void Dart_DumpNativeStackTrace(void* context); - -/** - * Indicate that the process is about to abort, and the Dart VM should not - * attempt to cleanup resources. - */ -DART_EXPORT void Dart_PrepareToAbort(void); - -/** - * Callback provided by the embedder that is used by the VM to - * produce footnotes appended to DWARF stack traces. - * - * Whenever VM formats a stack trace as a string it would call this callback - * passing raw program counters for each frame in the stack trace. - * - * Embedder can then return a string which if not-null will be appended to the - * formatted stack trace. - * - * Returned string is expected to be `malloc()` allocated. VM takes ownership - * of the returned string and will `free()` it. - * - * \param addresses raw program counter addresses for each frame - * \param count number of elements in the addresses array - */ -typedef char* (*Dart_DwarfStackTraceFootnoteCallback)(void* addresses[], - intptr_t count); - -/** - * Configure DWARF stack trace footnote callback. - */ -DART_EXPORT void Dart_SetDwarfStackTraceFootnoteCallback( - Dart_DwarfStackTraceFootnoteCallback callback); - -#endif /* INCLUDE_DART_API_H_ */ /* NOLINT */ diff --git a/core/dart-bridge/include/dart_api_dl.c b/core/dart-bridge/include/dart_api_dl.c deleted file mode 100644 index c4a68f4..0000000 --- a/core/dart-bridge/include/dart_api_dl.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file - * for details. All rights reserved. Use of this source code is governed by a - * BSD-style license that can be found in the LICENSE file. - */ - -#include "dart_api_dl.h" /* NOLINT */ -#include "dart_version.h" /* NOLINT */ -#include "internal/dart_api_dl_impl.h" /* NOLINT */ - -#include - -#define DART_API_DL_DEFINITIONS(name, R, A) name##_Type name##_DL = NULL; - -DART_API_ALL_DL_SYMBOLS(DART_API_DL_DEFINITIONS) - -#undef DART_API_DL_DEFINITIONS - -typedef void* DartApiEntry_function; - -DartApiEntry_function FindFunctionPointer(const DartApiEntry* entries, - const char* name) { - while (entries->name != NULL) { - if (strcmp(entries->name, name) == 0) return entries->function; - entries++; - } - return NULL; -} - -intptr_t Dart_InitializeApiDL(void* data) { - DartApi* dart_api_data = (DartApi*)data; - - if (dart_api_data->major != DART_API_DL_MAJOR_VERSION) { - // If the DartVM we're running on does not have the same version as this - // file was compiled against, refuse to initialize. The symbols are not - // compatible. - return -1; - } - // Minor versions are allowed to be different. - // If the DartVM has a higher minor version, it will provide more symbols - // than we initialize here. - // If the DartVM has a lower minor version, it will not provide all symbols. - // In that case, we leave the missing symbols un-initialized. Those symbols - // should not be used by the Dart and native code. The client is responsible - // for checking the minor version number himself based on which symbols it - // is using. - // (If we would error out on this case, recompiling native code against a - // newer SDK would break all uses on older SDKs, which is too strict.) - - const DartApiEntry* dart_api_function_pointers = dart_api_data->functions; - -#define DART_API_DL_INIT(name, R, A) \ - name##_DL = \ - (name##_Type)(FindFunctionPointer(dart_api_function_pointers, #name)); - DART_API_ALL_DL_SYMBOLS(DART_API_DL_INIT) -#undef DART_API_DL_INIT - - return 0; -} diff --git a/core/dart-bridge/include/dart_api_dl.h b/core/dart-bridge/include/dart_api_dl.h deleted file mode 100644 index cce3450..0000000 --- a/core/dart-bridge/include/dart_api_dl.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file - * for details. All rights reserved. Use of this source code is governed by a - * BSD-style license that can be found in the LICENSE file. - */ - -#ifndef RUNTIME_INCLUDE_DART_API_DL_H_ -#define RUNTIME_INCLUDE_DART_API_DL_H_ - -#include "dart_api.h" /* NOLINT */ -#include "dart_native_api.h" /* NOLINT */ - -/** \mainpage Dynamically Linked Dart API - * - * This exposes a subset of symbols from dart_api.h and dart_native_api.h - * available in every Dart embedder through dynamic linking. - * - * All symbols are postfixed with _DL to indicate that they are dynamically - * linked and to prevent conflicts with the original symbol. - * - * Link `dart_api_dl.c` file into your library and invoke - * `Dart_InitializeApiDL` with `NativeApi.initializeApiDLData`. - */ - -DART_EXPORT intptr_t Dart_InitializeApiDL(void* data); - -// ============================================================================ -// IMPORTANT! Never update these signatures without properly updating -// DART_API_DL_MAJOR_VERSION and DART_API_DL_MINOR_VERSION. -// -// Verbatim copy of `dart_native_api.h` and `dart_api.h` symbol names and types -// to trigger compile-time errors if the symbols in those files are updated -// without updating these. -// -// Function return and argument types, and typedefs are carbon copied. Structs -// are typechecked nominally in C/C++, so they are not copied, instead a -// comment is added to their definition. -typedef int64_t Dart_Port_DL; - -typedef void (*Dart_NativeMessageHandler_DL)(Dart_Port_DL dest_port_id, - Dart_CObject* message); - -// dart_native_api.h symbols can be called on any thread. -#define DART_NATIVE_API_DL_SYMBOLS(F) \ - /***** dart_native_api.h *****/ \ - /* Dart_Port */ \ - F(Dart_PostCObject, bool, (Dart_Port_DL port_id, Dart_CObject * message)) \ - F(Dart_PostInteger, bool, (Dart_Port_DL port_id, int64_t message)) \ - F(Dart_NewNativePort, Dart_Port_DL, \ - (const char* name, Dart_NativeMessageHandler_DL handler, \ - bool handle_concurrently)) \ - F(Dart_CloseNativePort, bool, (Dart_Port_DL native_port_id)) - -// dart_api.h symbols can only be called on Dart threads. -#define DART_API_DL_SYMBOLS(F) \ - /***** dart_api.h *****/ \ - /* Errors */ \ - F(Dart_IsError, bool, (Dart_Handle handle)) \ - F(Dart_IsApiError, bool, (Dart_Handle handle)) \ - F(Dart_IsUnhandledExceptionError, bool, (Dart_Handle handle)) \ - F(Dart_IsCompilationError, bool, (Dart_Handle handle)) \ - F(Dart_IsFatalError, bool, (Dart_Handle handle)) \ - F(Dart_GetError, const char*, (Dart_Handle handle)) \ - F(Dart_ErrorHasException, bool, (Dart_Handle handle)) \ - F(Dart_ErrorGetException, Dart_Handle, (Dart_Handle handle)) \ - F(Dart_ErrorGetStackTrace, Dart_Handle, (Dart_Handle handle)) \ - F(Dart_NewApiError, Dart_Handle, (const char* error)) \ - F(Dart_NewCompilationError, Dart_Handle, (const char* error)) \ - F(Dart_NewUnhandledExceptionError, Dart_Handle, (Dart_Handle exception)) \ - F(Dart_PropagateError, void, (Dart_Handle handle)) \ - /* Dart_Handle, Dart_PersistentHandle, Dart_WeakPersistentHandle */ \ - F(Dart_HandleFromPersistent, Dart_Handle, (Dart_PersistentHandle object)) \ - F(Dart_HandleFromWeakPersistent, Dart_Handle, \ - (Dart_WeakPersistentHandle object)) \ - F(Dart_NewPersistentHandle, Dart_PersistentHandle, (Dart_Handle object)) \ - F(Dart_SetPersistentHandle, void, \ - (Dart_PersistentHandle obj1, Dart_Handle obj2)) \ - F(Dart_DeletePersistentHandle, void, (Dart_PersistentHandle object)) \ - F(Dart_NewWeakPersistentHandle, Dart_WeakPersistentHandle, \ - (Dart_Handle object, void* peer, intptr_t external_allocation_size, \ - Dart_HandleFinalizer callback)) \ - F(Dart_DeleteWeakPersistentHandle, void, (Dart_WeakPersistentHandle object)) \ - F(Dart_UpdateExternalSize, void, \ - (Dart_WeakPersistentHandle object, intptr_t external_allocation_size)) \ - F(Dart_NewFinalizableHandle, Dart_FinalizableHandle, \ - (Dart_Handle object, void* peer, intptr_t external_allocation_size, \ - Dart_HandleFinalizer callback)) \ - F(Dart_DeleteFinalizableHandle, void, \ - (Dart_FinalizableHandle object, Dart_Handle strong_ref_to_object)) \ - F(Dart_UpdateFinalizableExternalSize, void, \ - (Dart_FinalizableHandle object, Dart_Handle strong_ref_to_object, \ - intptr_t external_allocation_size)) \ - /* Isolates */ \ - F(Dart_CurrentIsolate, Dart_Isolate, (void)) \ - F(Dart_ExitIsolate, void, (void)) \ - F(Dart_EnterIsolate, void, (Dart_Isolate)) \ - /* Dart_Port */ \ - F(Dart_Post, bool, (Dart_Port_DL port_id, Dart_Handle object)) \ - F(Dart_NewSendPort, Dart_Handle, (Dart_Port_DL port_id)) \ - F(Dart_SendPortGetId, Dart_Handle, \ - (Dart_Handle port, Dart_Port_DL * port_id)) \ - /* Scopes */ \ - F(Dart_EnterScope, void, (void)) \ - F(Dart_ExitScope, void, (void)) \ - /* Objects */ \ - F(Dart_IsNull, bool, (Dart_Handle)) - -#define DART_API_ALL_DL_SYMBOLS(F) \ - DART_NATIVE_API_DL_SYMBOLS(F) \ - DART_API_DL_SYMBOLS(F) -// IMPORTANT! Never update these signatures without properly updating -// DART_API_DL_MAJOR_VERSION and DART_API_DL_MINOR_VERSION. -// -// End of verbatim copy. -// ============================================================================ - -// Copy of definition of DART_EXPORT without 'used' attribute. -// -// The 'used' attribute cannot be used with DART_API_ALL_DL_SYMBOLS because -// they are not function declarations, but variable declarations with a -// function pointer type. -// -// The function pointer variables are initialized with the addresses of the -// functions in the VM. If we were to use function declarations instead, we -// would need to forward the call to the VM adding indirection. -#if defined(__CYGWIN__) -#error Tool chain and platform not supported. -#elif defined(_WIN32) -#if defined(DART_SHARED_LIB) -#define DART_EXPORT_DL DART_EXTERN_C __declspec(dllexport) -#else -#define DART_EXPORT_DL DART_EXTERN_C -#endif -#else -#if __GNUC__ >= 4 -#if defined(DART_SHARED_LIB) -#define DART_EXPORT_DL DART_EXTERN_C __attribute__((visibility("default"))) -#else -#define DART_EXPORT_DL DART_EXTERN_C -#endif -#else -#error Tool chain not supported. -#endif -#endif - -#define DART_API_DL_DECLARATIONS(name, R, A) \ - typedef R(*name##_Type) A; \ - DART_EXPORT_DL name##_Type name##_DL; - -DART_API_ALL_DL_SYMBOLS(DART_API_DL_DECLARATIONS) - -#undef DART_API_DL_DECLARATIONS - -#undef DART_EXPORT_DL - -#endif /* RUNTIME_INCLUDE_DART_API_DL_H_ */ /* NOLINT */ diff --git a/core/dart-bridge/include/dart_native_api.h b/core/dart-bridge/include/dart_native_api.h deleted file mode 100644 index 79194e0..0000000 --- a/core/dart-bridge/include/dart_native_api.h +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file - * for details. All rights reserved. Use of this source code is governed by a - * BSD-style license that can be found in the LICENSE file. - */ - -#ifndef RUNTIME_INCLUDE_DART_NATIVE_API_H_ -#define RUNTIME_INCLUDE_DART_NATIVE_API_H_ - -#include "dart_api.h" /* NOLINT */ - -/* - * ========================================== - * Message sending/receiving from native code - * ========================================== - */ - -/** - * A Dart_CObject is used for representing Dart objects as native C - * data outside the Dart heap. These objects are totally detached from - * the Dart heap. Only a subset of the Dart objects have a - * representation as a Dart_CObject. - * - * The string encoding in the 'value.as_string' is UTF-8. - * - * All the different types from dart:typed_data are exposed as type - * kTypedData. The specific type from dart:typed_data is in the type - * field of the as_typed_data structure. The length in the - * as_typed_data structure is always in bytes. - * - * The data for kTypedData is copied on message send and ownership remains with - * the caller. The ownership of data for kExternalTyped is passed to the VM on - * message send and returned when the VM invokes the - * Dart_HandleFinalizer callback; a non-NULL callback must be provided. - * - * Note that Dart_CObject_kNativePointer is intended for internal use by - * dart:io implementation and has no connection to dart:ffi Pointer class. - * It represents a pointer to a native resource of a known type. - * The receiving side will only see this pointer as an integer and will not - * see the specified finalizer. - * The specified finalizer will only be invoked if the message is not delivered. - */ -typedef enum { - Dart_CObject_kNull = 0, - Dart_CObject_kBool, - Dart_CObject_kInt32, - Dart_CObject_kInt64, - Dart_CObject_kDouble, - Dart_CObject_kString, - Dart_CObject_kArray, - Dart_CObject_kTypedData, - Dart_CObject_kExternalTypedData, - Dart_CObject_kSendPort, - Dart_CObject_kCapability, - Dart_CObject_kNativePointer, - Dart_CObject_kUnsupported, - Dart_CObject_kUnmodifiableExternalTypedData, - Dart_CObject_kNumberOfTypes -} Dart_CObject_Type; -// This enum is versioned by DART_API_DL_MAJOR_VERSION, only add at the end -// and bump the DART_API_DL_MINOR_VERSION. - -typedef struct _Dart_CObject { - Dart_CObject_Type type; - union { - bool as_bool; - int32_t as_int32; - int64_t as_int64; - double as_double; - const char* as_string; - struct { - Dart_Port id; - Dart_Port origin_id; - } as_send_port; - struct { - int64_t id; - } as_capability; - struct { - intptr_t length; - struct _Dart_CObject** values; - } as_array; - struct { - Dart_TypedData_Type type; - intptr_t length; /* in elements, not bytes */ - const uint8_t* values; - } as_typed_data; - struct { - Dart_TypedData_Type type; - intptr_t length; /* in elements, not bytes */ - uint8_t* data; - void* peer; - Dart_HandleFinalizer callback; - } as_external_typed_data; - struct { - intptr_t ptr; - intptr_t size; - Dart_HandleFinalizer callback; - } as_native_pointer; - } value; -} Dart_CObject; -// This struct is versioned by DART_API_DL_MAJOR_VERSION, bump the version when -// changing this struct. - -/** - * Posts a message on some port. The message will contain the Dart_CObject - * object graph rooted in 'message'. - * - * While the message is being sent the state of the graph of Dart_CObject - * structures rooted in 'message' should not be accessed, as the message - * generation will make temporary modifications to the data. When the message - * has been sent the graph will be fully restored. - * - * If true is returned, the message was enqueued, and finalizers for external - * typed data will eventually run, even if the receiving isolate shuts down - * before processing the message. If false is returned, the message was not - * enqueued and ownership of external typed data in the message remains with the - * caller. - * - * This function may be called on any thread when the VM is running (that is, - * after Dart_Initialize has returned and before Dart_Cleanup has been called). - * - * \param port_id The destination port. - * \param message The message to send. - * - * \return True if the message was posted. - */ -DART_EXPORT bool Dart_PostCObject(Dart_Port port_id, Dart_CObject* message); - -/** - * Posts a message on some port. The message will contain the integer 'message'. - * - * \param port_id The destination port. - * \param message The message to send. - * - * \return True if the message was posted. - */ -DART_EXPORT bool Dart_PostInteger(Dart_Port port_id, int64_t message); - -/** - * A native message handler. - * - * This handler is associated with a native port by calling - * Dart_NewNativePort. - * - * The message received is decoded into the message structure. The - * lifetime of the message data is controlled by the caller. All the - * data references from the message are allocated by the caller and - * will be reclaimed when returning to it. - */ -typedef void (*Dart_NativeMessageHandler)(Dart_Port dest_port_id, - Dart_CObject* message); - -/** - * Creates a new native port. When messages are received on this - * native port, then they will be dispatched to the provided native - * message handler. - * - * \param name The name of this port in debugging messages. - * \param handler The C handler to run when messages arrive on the port. - * \param handle_concurrently Is it okay to process requests on this - * native port concurrently? - * - * \return If successful, returns the port id for the native port. In - * case of error, returns ILLEGAL_PORT. - */ -DART_EXPORT Dart_Port Dart_NewNativePort(const char* name, - Dart_NativeMessageHandler handler, - bool handle_concurrently); -/* TODO(turnidge): Currently handle_concurrently is ignored. */ - -/** - * Closes the native port with the given id. - * - * The port must have been allocated by a call to Dart_NewNativePort. - * - * \param native_port_id The id of the native port to close. - * - * \return Returns true if the port was closed successfully. - */ -DART_EXPORT bool Dart_CloseNativePort(Dart_Port native_port_id); - -/* - * ================== - * Verification Tools - * ================== - */ - -/** - * Forces all loaded classes and functions to be compiled eagerly in - * the current isolate.. - * - * TODO(turnidge): Document. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_CompileAll(void); - -/** - * Finalizes all classes. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_FinalizeAllClasses(void); - -/* This function is intentionally undocumented. - * - * It should not be used outside internal tests. - */ -DART_EXPORT void* Dart_ExecuteInternalCommand(const char* command, void* arg); - -#endif /* INCLUDE_DART_NATIVE_API_H_ */ /* NOLINT */ diff --git a/core/dart-bridge/include/dart_tools_api.h b/core/dart-bridge/include/dart_tools_api.h deleted file mode 100644 index 7b706bc..0000000 --- a/core/dart-bridge/include/dart_tools_api.h +++ /dev/null @@ -1,658 +0,0 @@ -// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -#ifndef RUNTIME_INCLUDE_DART_TOOLS_API_H_ -#define RUNTIME_INCLUDE_DART_TOOLS_API_H_ - -#include "dart_api.h" /* NOLINT */ - -/** \mainpage Dart Tools Embedding API Reference - * - * This reference describes the Dart embedding API for tools. Tools include - * a debugger, service protocol, and timeline. - * - * NOTE: The APIs described in this file are unstable and subject to change. - * - * This reference is generated from the header include/dart_tools_api.h. - */ - -/* - * ======== - * Debugger - * ======== - */ - -/** - * ILLEGAL_ISOLATE_ID is a number guaranteed never to be associated with a - * valid isolate. - */ -#define ILLEGAL_ISOLATE_ID ILLEGAL_PORT - -/** - * ILLEGAL_ISOLATE_GROUP_ID is a number guaranteed never to be associated with a - * valid isolate group. - */ -#define ILLEGAL_ISOLATE_GROUP_ID 0 - -/* - * ======= - * Service - * ======= - */ - -/** - * A service request callback function. - * - * These callbacks, registered by the embedder, are called when the VM receives - * a service request it can't handle and the service request command name - * matches one of the embedder registered handlers. - * - * The return value of the callback indicates whether the response - * should be used as a regular result or an error result. - * Specifically, if the callback returns true, a regular JSON-RPC - * response is built in the following way: - * - * { - * "jsonrpc": "2.0", - * "result": , - * "id": , - * } - * - * If the callback returns false, a JSON-RPC error is built like this: - * - * { - * "jsonrpc": "2.0", - * "error": , - * "id": , - * } - * - * \param method The rpc method name. - * \param param_keys Service requests can have key-value pair parameters. The - * keys and values are flattened and stored in arrays. - * \param param_values The values associated with the keys. - * \param num_params The length of the param_keys and param_values arrays. - * \param user_data The user_data pointer registered with this handler. - * \param result A C string containing a valid JSON object. The returned - * pointer will be freed by the VM by calling free. - * - * \return True if the result is a regular JSON-RPC response, false if the - * result is a JSON-RPC error. - */ -typedef bool (*Dart_ServiceRequestCallback)(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object); - -/** - * Register a Dart_ServiceRequestCallback to be called to handle - * requests for the named rpc on a specific isolate. The callback will - * be invoked with the current isolate set to the request target. - * - * \param method The name of the method that this callback is responsible for. - * \param callback The callback to invoke. - * \param user_data The user data passed to the callback. - * - * NOTE: If multiple callbacks with the same name are registered, only - * the last callback registered will be remembered. - */ -DART_EXPORT void Dart_RegisterIsolateServiceRequestCallback( - const char* method, - Dart_ServiceRequestCallback callback, - void* user_data); - -/** - * Register a Dart_ServiceRequestCallback to be called to handle - * requests for the named rpc. The callback will be invoked without a - * current isolate. - * - * \param method The name of the command that this callback is responsible for. - * \param callback The callback to invoke. - * \param user_data The user data passed to the callback. - * - * NOTE: If multiple callbacks with the same name are registered, only - * the last callback registered will be remembered. - */ -DART_EXPORT void Dart_RegisterRootServiceRequestCallback( - const char* method, - Dart_ServiceRequestCallback callback, - void* user_data); - -/** - * Embedder information which can be requested by the VM for internal or - * reporting purposes. - * - * The pointers in this structure are not going to be cached or freed by the VM. - */ - - #define DART_EMBEDDER_INFORMATION_CURRENT_VERSION (0x00000001) - -typedef struct { - int32_t version; - const char* name; // [optional] The name of the embedder - int64_t current_rss; // [optional] the current RSS of the embedder - int64_t max_rss; // [optional] the maximum RSS of the embedder -} Dart_EmbedderInformation; - -/** - * Callback provided by the embedder that is used by the VM to request - * information. - * - * \return Returns a pointer to a Dart_EmbedderInformation structure. - * The embedder keeps the ownership of the structure and any field in it. - * The embedder must ensure that the structure will remain valid until the - * next invocation of the callback. - */ -typedef void (*Dart_EmbedderInformationCallback)( - Dart_EmbedderInformation* info); - -/** - * Register a Dart_ServiceRequestCallback to be called to handle - * requests for the named rpc. The callback will be invoked without a - * current isolate. - * - * \param method The name of the command that this callback is responsible for. - * \param callback The callback to invoke. - * \param user_data The user data passed to the callback. - * - * NOTE: If multiple callbacks are registered, only the last callback registered - * will be remembered. - */ -DART_EXPORT void Dart_SetEmbedderInformationCallback( - Dart_EmbedderInformationCallback callback); - -/** - * Invoke a vm-service method and wait for its result. - * - * \param request_json The utf8-encoded json-rpc request. - * \param request_json_length The length of the json-rpc request. - * - * \param response_json The returned utf8-encoded json response, must be - * free()ed by caller. - * \param response_json_length The length of the returned json response. - * \param error An optional error, must be free()ed by caller. - * - * \return Whether the call was successfully performed. - * - * NOTE: This method does not need a current isolate and must not have the - * vm-isolate being the current isolate. It must be called after - * Dart_Initialize() and before Dart_Cleanup(). - */ -DART_EXPORT bool Dart_InvokeVMServiceMethod(uint8_t* request_json, - intptr_t request_json_length, - uint8_t** response_json, - intptr_t* response_json_length, - char** error); - -/* - * ======== - * Event Streams - * ======== - */ - -/** - * A callback invoked when the VM service gets a request to listen to - * some stream. - * - * \return Returns true iff the embedder supports the named stream id. - */ -typedef bool (*Dart_ServiceStreamListenCallback)(const char* stream_id); - -/** - * A callback invoked when the VM service gets a request to cancel - * some stream. - */ -typedef void (*Dart_ServiceStreamCancelCallback)(const char* stream_id); - -/** - * Adds VM service stream callbacks. - * - * \param listen_callback A function pointer to a listen callback function. - * A listen callback function should not be already set when this function - * is called. A NULL value removes the existing listen callback function - * if any. - * - * \param cancel_callback A function pointer to a cancel callback function. - * A cancel callback function should not be already set when this function - * is called. A NULL value removes the existing cancel callback function - * if any. - * - * \return Success if the callbacks were added. Otherwise, returns an - * error handle. - */ -DART_EXPORT char* Dart_SetServiceStreamCallbacks( - Dart_ServiceStreamListenCallback listen_callback, - Dart_ServiceStreamCancelCallback cancel_callback); - -/** - * Sends a data event to clients of the VM Service. - * - * A data event is used to pass an array of bytes to subscribed VM - * Service clients. For example, in the standalone embedder, this is - * function used to provide WriteEvents on the Stdout and Stderr - * streams. - * - * If the embedder passes in a stream id for which no client is - * subscribed, then the event is ignored. - * - * \param stream_id The id of the stream on which to post the event. - * - * \param event_kind A string identifying what kind of event this is. - * For example, 'WriteEvent'. - * - * \param bytes A pointer to an array of bytes. - * - * \param bytes_length The length of the byte array. - * - * \return NULL if the arguments are well formed. Otherwise, returns an - * error string. The caller is responsible for freeing the error message. - */ -DART_EXPORT char* Dart_ServiceSendDataEvent(const char* stream_id, - const char* event_kind, - const uint8_t* bytes, - intptr_t bytes_length); - -/* - * ======== - * Reload support - * ======== - * - * These functions are used to implement reloading in the Dart VM. - * This is an experimental feature, so embedders should be prepared - * for these functions to change. - */ - -/** - * A callback which determines whether the file at some url has been - * modified since some time. If the file cannot be found, true should - * be returned. - */ -typedef bool (*Dart_FileModifiedCallback)(const char* url, int64_t since); - -DART_EXPORT char* Dart_SetFileModifiedCallback( - Dart_FileModifiedCallback file_modified_callback); - -/** - * Returns true if isolate is currently reloading. - */ -DART_EXPORT bool Dart_IsReloading(); - -/* - * ======== - * Timeline - * ======== - */ - -/** - * Enable tracking of specified timeline category. This is operational - * only when systrace timeline functionality is turned on. - * - * \param categories A comma separated list of categories that need to - * be enabled, the categories are - * "all" : All categories - * "API" - Execution of Dart C API functions - * "Compiler" - Execution of Dart JIT compiler - * "CompilerVerbose" - More detailed Execution of Dart JIT compiler - * "Dart" - Execution of Dart code - * "Debugger" - Execution of Dart debugger - * "Embedder" - Execution of Dart embedder code - * "GC" - Execution of Dart Garbage Collector - * "Isolate" - Dart Isolate lifecycle execution - * "VM" - Execution in Dart VM runtime code - * "" - None - * - * When "all" is specified all the categories are enabled. - * When a comma separated list of categories is specified, the categories - * that are specified will be enabled and the rest will be disabled. - * When "" is specified all the categories are disabled. - * The category names are case sensitive. - * eg: Dart_EnableTimelineCategory("all"); - * Dart_EnableTimelineCategory("GC,API,Isolate"); - * Dart_EnableTimelineCategory("GC,Debugger,Dart"); - * - * \return True if the categories were successfully enabled, False otherwise. - */ -DART_EXPORT bool Dart_SetEnabledTimelineCategory(const char* categories); - -/** - * Returns a timestamp in microseconds. This timestamp is suitable for - * passing into the timeline system, and uses the same monotonic clock - * as dart:developer's Timeline.now. - * - * \return A timestamp that can be passed to the timeline system. - */ -DART_EXPORT int64_t Dart_TimelineGetMicros(); - -/** - * Returns a raw timestamp in from the monotonic clock. - * - * \return A raw timestamp from the monotonic clock. - */ -DART_EXPORT int64_t Dart_TimelineGetTicks(); - -/** - * Returns the frequency of the monotonic clock. - * - * \return The frequency of the monotonic clock. - */ -DART_EXPORT int64_t Dart_TimelineGetTicksFrequency(); - -typedef enum { - Dart_Timeline_Event_Begin, // Phase = 'B'. - Dart_Timeline_Event_End, // Phase = 'E'. - Dart_Timeline_Event_Instant, // Phase = 'i'. - Dart_Timeline_Event_Duration, // Phase = 'X'. - Dart_Timeline_Event_Async_Begin, // Phase = 'b'. - Dart_Timeline_Event_Async_End, // Phase = 'e'. - Dart_Timeline_Event_Async_Instant, // Phase = 'n'. - Dart_Timeline_Event_Counter, // Phase = 'C'. - Dart_Timeline_Event_Flow_Begin, // Phase = 's'. - Dart_Timeline_Event_Flow_Step, // Phase = 't'. - Dart_Timeline_Event_Flow_End, // Phase = 'f'. -} Dart_Timeline_Event_Type; - -/** - * Add a timeline event to the embedder stream. - * - * DEPRECATED: this function will be removed in Dart SDK v3.2. - * - * \param label The name of the event. Its lifetime must extend at least until - * Dart_Cleanup. - * \param timestamp0 The first timestamp of the event. - * \param timestamp1_or_id When reporting an event of type - * |Dart_Timeline_Event_Duration|, the second (end) timestamp of the event - * should be passed through |timestamp1_or_id|. When reporting an event of - * type |Dart_Timeline_Event_Async_Begin|, |Dart_Timeline_Event_Async_End|, - * or |Dart_Timeline_Event_Async_Instant|, the async ID associated with the - * event should be passed through |timestamp1_or_id|. When reporting an - * event of type |Dart_Timeline_Event_Flow_Begin|, - * |Dart_Timeline_Event_Flow_Step|, or |Dart_Timeline_Event_Flow_End|, the - * flow ID associated with the event should be passed through - * |timestamp1_or_id|. When reporting an event of type - * |Dart_Timeline_Event_Begin| or |Dart_Timeline_Event_End|, the event ID - * associated with the event should be passed through |timestamp1_or_id|. - * Note that this event ID will only be used by the MacOS recorder. The - * argument to |timestamp1_or_id| will not be used when reporting events of - * other types. - * \param argument_count The number of argument names and values. - * \param argument_names An array of names of the arguments. The lifetime of the - * names must extend at least until Dart_Cleanup. The array may be reclaimed - * when this call returns. - * \param argument_values An array of values of the arguments. The values and - * the array may be reclaimed when this call returns. - */ -DART_EXPORT void Dart_TimelineEvent(const char* label, - int64_t timestamp0, - int64_t timestamp1_or_id, - Dart_Timeline_Event_Type type, - intptr_t argument_count, - const char** argument_names, - const char** argument_values); - -/** - * Add a timeline event to the embedder stream. - * - * Note regarding flow events: events must be associated with flow IDs in two - * different ways to allow flow events to be serialized correctly in both - * Chrome's JSON trace event format and Perfetto's proto trace format. Events - * of type |Dart_Timeline_Event_Flow_Begin|, |Dart_Timeline_Event_Flow_Step|, - * and |Dart_Timeline_Event_Flow_End| must be reported to support serialization - * in Chrome's trace format. The |flow_ids| argument must be supplied when - * reporting events of type |Dart_Timeline_Event_Begin|, - * |Dart_Timeline_Event_Duration|, |Dart_Timeline_Event_Instant|, - * |Dart_Timeline_Event_Async_Begin|, and |Dart_Timeline_Event_Async_Instant| to - * support serialization in Perfetto's proto format. - * - * \param label The name of the event. Its lifetime must extend at least until - * Dart_Cleanup. - * \param timestamp0 The first timestamp of the event. - * \param timestamp1_or_id When reporting an event of type - * |Dart_Timeline_Event_Duration|, the second (end) timestamp of the event - * should be passed through |timestamp1_or_id|. When reporting an event of - * type |Dart_Timeline_Event_Async_Begin|, |Dart_Timeline_Event_Async_End|, - * or |Dart_Timeline_Event_Async_Instant|, the async ID associated with the - * event should be passed through |timestamp1_or_id|. When reporting an - * event of type |Dart_Timeline_Event_Flow_Begin|, - * |Dart_Timeline_Event_Flow_Step|, or |Dart_Timeline_Event_Flow_End|, the - * flow ID associated with the event should be passed through - * |timestamp1_or_id|. When reporting an event of type - * |Dart_Timeline_Event_Begin| or |Dart_Timeline_Event_End|, the event ID - * associated with the event should be passed through |timestamp1_or_id|. - * Note that this event ID will only be used by the MacOS recorder. The - * argument to |timestamp1_or_id| will not be used when reporting events of - * other types. - * \param flow_id_count The number of flow IDs associated with this event. - * \param flow_ids An array of flow IDs associated with this event. The array - * may be reclaimed when this call returns. - * \param argument_count The number of argument names and values. - * \param argument_names An array of names of the arguments. The lifetime of the - * names must extend at least until Dart_Cleanup. The array may be reclaimed - * when this call returns. - * \param argument_values An array of values of the arguments. The values and - * the array may be reclaimed when this call returns. - */ -DART_EXPORT void Dart_RecordTimelineEvent(const char* label, - int64_t timestamp0, - int64_t timestamp1_or_id, - intptr_t flow_id_count, - const int64_t* flow_ids, - Dart_Timeline_Event_Type type, - intptr_t argument_count, - const char** argument_names, - const char** argument_values); - -/** - * Associates a name with the current thread. This name will be used to name - * threads in the timeline. Can only be called after a call to Dart_Initialize. - * - * \param name The name of the thread. - */ -DART_EXPORT void Dart_SetThreadName(const char* name); - -typedef struct { - const char* name; - const char* value; -} Dart_TimelineRecorderEvent_Argument; - -#define DART_TIMELINE_RECORDER_CURRENT_VERSION (0x00000002) - -typedef struct { - /* Set to DART_TIMELINE_RECORDER_CURRENT_VERSION */ - int32_t version; - - /* The event's type / phase. */ - Dart_Timeline_Event_Type type; - - /* The event's timestamp according to the same clock as - * Dart_TimelineGetMicros. For a duration event, this is the beginning time. - */ - int64_t timestamp0; - - /** - * For a duration event, this is the end time. For an async event, this is the - * async ID. For a flow event, this is the flow ID. For a begin or end event, - * this is the event ID (which is only referenced by the MacOS recorder). - */ - int64_t timestamp1_or_id; - - /* The current isolate of the event, as if by Dart_GetMainPortId, or - * ILLEGAL_PORT if the event had no current isolate. */ - Dart_Port isolate; - - /* The current isolate group of the event, as if by - * Dart_CurrentIsolateGroupId, or ILLEGAL_PORT if the event had no current - * isolate group. */ - Dart_IsolateGroupId isolate_group; - - /* The callback data associated with the isolate if any. */ - void* isolate_data; - - /* The callback data associated with the isolate group if any. */ - void* isolate_group_data; - - /* The name / label of the event. */ - const char* label; - - /* The stream / category of the event. */ - const char* stream; - - intptr_t argument_count; - Dart_TimelineRecorderEvent_Argument* arguments; -} Dart_TimelineRecorderEvent; - -/** - * Callback provided by the embedder to handle the completion of timeline - * events. - * - * \param event A timeline event that has just been completed. The VM keeps - * ownership of the event and any field in it (i.e., the embedder should copy - * any values it needs after the callback returns). - */ -typedef void (*Dart_TimelineRecorderCallback)( - Dart_TimelineRecorderEvent* event); - -/** - * Register a `Dart_TimelineRecorderCallback` to be called as timeline events - * are completed. - * - * The callback will be invoked without a current isolate. - * - * The callback will be invoked on the thread completing the event. Because - * `Dart_TimelineEvent` may be called by any thread, the callback may be called - * on any thread. - * - * The callback may be invoked at any time after `Dart_Initialize` is called and - * before `Dart_Cleanup` returns. - * - * If multiple callbacks are registered, only the last callback registered - * will be remembered. Providing a NULL callback will clear the registration - * (i.e., a NULL callback produced a no-op instead of a crash). - * - * Setting a callback is insufficient to receive events through the callback. The - * VM flag `timeline_recorder` must also be set to `callback`. - */ -DART_EXPORT void Dart_SetTimelineRecorderCallback( - Dart_TimelineRecorderCallback callback); - -/* - * ======= - * Metrics - * ======= - */ - -/** - * Return metrics gathered for the VM and individual isolates. - */ -DART_EXPORT int64_t -Dart_IsolateGroupHeapOldUsedMetric(Dart_IsolateGroup group); // Byte -DART_EXPORT int64_t -Dart_IsolateGroupHeapOldCapacityMetric(Dart_IsolateGroup group); // Byte -DART_EXPORT int64_t -Dart_IsolateGroupHeapOldExternalMetric(Dart_IsolateGroup group); // Byte -DART_EXPORT int64_t -Dart_IsolateGroupHeapNewUsedMetric(Dart_IsolateGroup group); // Byte -DART_EXPORT int64_t -Dart_IsolateGroupHeapNewCapacityMetric(Dart_IsolateGroup group); // Byte -DART_EXPORT int64_t -Dart_IsolateGroupHeapNewExternalMetric(Dart_IsolateGroup group); // Byte - -/* - * ======== - * UserTags - * ======== - */ - -/* - * Gets the current isolate's currently set UserTag instance. - * - * \return The currently set UserTag instance. - */ -DART_EXPORT Dart_Handle Dart_GetCurrentUserTag(); - -/* - * Gets the current isolate's default UserTag instance. - * - * \return The default UserTag with label 'Default' - */ -DART_EXPORT Dart_Handle Dart_GetDefaultUserTag(); - -/* - * Creates a new UserTag instance. - * - * \param label The name of the new UserTag. - * - * \return The newly created UserTag instance or an error handle. - */ -DART_EXPORT Dart_Handle Dart_NewUserTag(const char* label); - -/* - * Updates the current isolate's UserTag to a new value. - * - * \param user_tag The UserTag to be set as the current UserTag. - * - * \return The previously set UserTag instance or an error handle. - */ -DART_EXPORT Dart_Handle Dart_SetCurrentUserTag(Dart_Handle user_tag); - -/* - * Returns the label of a given UserTag instance. - * - * \param user_tag The UserTag from which the label will be retrieved. - * - * \return The UserTag's label. NULL if the user_tag is invalid. The caller is - * responsible for freeing the returned label. - */ -DART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_GetUserTagLabel( - Dart_Handle user_tag); - -/* - * ======= - * Heap Snapshot - * ======= - */ - -/** - * Callback provided by the caller of `Dart_WriteHeapSnapshot` which is - * used to write out chunks of the requested heap snapshot. - * - * \param context An opaque context which was passed to `Dart_WriteHeapSnapshot` - * together with this callback. - * - * \param buffer Pointer to the buffer containing a chunk of the snapshot. - * The callback owns the buffer and needs to `free` it. - * - * \param size Number of bytes in the `buffer` to be written. - * - * \param is_last Set to `true` for the last chunk. The callback will not - * be invoked again after it was invoked once with `is_last` set to `true`. - */ -typedef void (*Dart_HeapSnapshotWriteChunkCallback)(void* context, - uint8_t* buffer, - intptr_t size, - bool is_last); - -/** - * Generate heap snapshot of the current isolate group and stream it into the - * given `callback`. VM would produce snapshot in chunks and send these chunks - * one by one back to the embedder by invoking the provided `callback`. - * - * This API enables embedder to stream snapshot into a file or socket without - * allocating a buffer to hold the whole snapshot in memory. - * - * The isolate group will be paused for the duration of this operation. - * - * \param write Callback used to write chunks of the heap snapshot. - * - * \param context Opaque context which would be passed on each invocation of - * `write` callback. - * - * \returns `nullptr` if the operation is successful otherwise error message. - * Caller owns error message string and needs to `free` it. - */ -DART_EXPORT char* Dart_WriteHeapSnapshot( - Dart_HeapSnapshotWriteChunkCallback write, - void* context); - -#endif // RUNTIME_INCLUDE_DART_TOOLS_API_H_ diff --git a/core/dart-bridge/include/dart_version.h b/core/dart-bridge/include/dart_version.h deleted file mode 100644 index e2d3651..0000000 --- a/core/dart-bridge/include/dart_version.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file - * for details. All rights reserved. Use of this source code is governed by a - * BSD-style license that can be found in the LICENSE file. - */ - -#ifndef RUNTIME_INCLUDE_DART_VERSION_H_ -#define RUNTIME_INCLUDE_DART_VERSION_H_ - -// On breaking changes the major version is increased. -// On backwards compatible changes the minor version is increased. -// The versioning covers the symbols exposed in dart_api_dl.h -#define DART_API_DL_MAJOR_VERSION 2 -#define DART_API_DL_MINOR_VERSION 3 - -#endif /* RUNTIME_INCLUDE_DART_VERSION_H_ */ /* NOLINT */ diff --git a/core/dart-bridge/include/internal/dart_api_dl_impl.h b/core/dart-bridge/include/internal/dart_api_dl_impl.h deleted file mode 100644 index e4a5689..0000000 --- a/core/dart-bridge/include/internal/dart_api_dl_impl.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file - * for details. All rights reserved. Use of this source code is governed by a - * BSD-style license that can be found in the LICENSE file. - */ - -#ifndef RUNTIME_INCLUDE_INTERNAL_DART_API_DL_IMPL_H_ -#define RUNTIME_INCLUDE_INTERNAL_DART_API_DL_IMPL_H_ - -typedef struct { - const char* name; - void (*function)(void); -} DartApiEntry; - -typedef struct { - const int major; - const int minor; - const DartApiEntry* const functions; -} DartApi; - -#endif /* RUNTIME_INCLUDE_INTERNAL_DART_API_DL_IMPL_H_ */ /* NOLINT */ diff --git a/core/dart-bridge/lib.go b/core/dart-bridge/lib.go deleted file mode 100644 index 36847f1..0000000 --- a/core/dart-bridge/lib.go +++ /dev/null @@ -1,42 +0,0 @@ -//go:build cgo - -package dart_bridge - -/* -#include -#include "stdint.h" -#include "include/dart_api_dl.h" -#include "include/dart_api_dl.c" -#include "include/dart_native_api.h" - -bool GoDart_PostCObject(Dart_Port_DL port, Dart_CObject* obj) { - return Dart_PostCObject_DL(port, obj); -} -*/ -import "C" -import ( - "fmt" - "unsafe" -) - -func InitDartApi(api unsafe.Pointer) { - if C.Dart_InitializeApiDL(api) != 0 { - panic("failed to create dart bridge") - } else { - fmt.Println("Dart Api DL is initialized") - } -} - -func SendToPort(port int64, msg string) bool { - var obj C.Dart_CObject - obj._type = C.Dart_CObject_kString - msgString := C.CString(msg) - defer C.free(unsafe.Pointer(msgString)) - ptr := unsafe.Pointer(&obj.value[0]) - *(**C.char)(ptr) = msgString - isSuccess := C.GoDart_PostCObject(C.Dart_Port_DL(port), &obj) - if !isSuccess { - return false - } - return true -} diff --git a/core/dart-bridge/lib_common.go b/core/dart-bridge/lib_common.go deleted file mode 100644 index f347549..0000000 --- a/core/dart-bridge/lib_common.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build !cgo - -package dart_bridge - -func SendToPort(port int64, msg string) bool { - return false -} diff --git a/core/go.mod b/core/go.mod index 992a2bc..5891061 100644 --- a/core/go.mod +++ b/core/go.mod @@ -10,25 +10,23 @@ require ( ) require ( - github.com/3andne/restls-client-go v0.1.6 // indirect github.com/RyuaNerin/go-krypto v1.3.0 // indirect github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect github.com/ajg/form v1.5.1 // indirect github.com/andybalholm/brotli v1.0.6 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect - github.com/cloudflare/circl v1.3.7 // indirect github.com/coreos/go-iptables v0.8.0 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect - github.com/ebitengine/purego v0.8.3 // indirect - github.com/enfein/mieru/v3 v3.13.0 // indirect + github.com/ebitengine/purego v0.8.4 // indirect + github.com/enfein/mieru/v3 v3.19.1 // indirect github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 // indirect github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gaukas/godicttls v0.0.4 // indirect - github.com/go-chi/chi/v5 v5.2.1 // indirect + github.com/go-chi/chi/v5 v5.2.3 // indirect github.com/go-chi/render v1.0.3 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect @@ -43,35 +41,36 @@ require ( github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 // indirect github.com/josharian/native v1.1.0 // indirect github.com/klauspost/compress v1.17.9 // indirect - github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect - github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mdlayher/netlink v1.7.2 // indirect github.com/mdlayher/socket v0.4.1 // indirect - github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab // indirect + github.com/metacubex/amneziawg-go v0.0.0-20250820070344-732c0c9d418a // indirect + github.com/metacubex/ascon v0.1.0 // indirect github.com/metacubex/bart v0.20.5 // indirect - github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 // indirect + github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b // indirect + github.com/metacubex/blake3 v0.1.0 // indirect github.com/metacubex/chacha v0.1.5 // indirect github.com/metacubex/fswatch v0.1.1 // indirect github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b // indirect github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect - github.com/metacubex/quic-go v0.52.1-0.20250522021943-aef454b9e639 // indirect + github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295 // indirect github.com/metacubex/randv2 v0.2.0 // indirect - github.com/metacubex/sing v0.5.4-0.20250605054047-54dc6097da29 // indirect - github.com/metacubex/sing-mux v0.3.2 // indirect - github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f // indirect - github.com/metacubex/sing-shadowsocks v0.2.11-0.20250621023810-0e9ef9dd0c92 // indirect - github.com/metacubex/sing-shadowsocks2 v0.2.5-0.20250621023950-93d605a2143d // indirect + github.com/metacubex/restls-client-go v0.1.7 // indirect + github.com/metacubex/sing v0.5.5 // indirect + github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac // indirect + github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb // indirect + github.com/metacubex/sing-shadowsocks v0.2.12 // indirect + github.com/metacubex/sing-shadowsocks2 v0.2.6 // indirect github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 // indirect - github.com/metacubex/sing-tun v0.4.7-0.20250611091011-60774779fdd8 // indirect - github.com/metacubex/sing-vmess v0.2.2 // indirect + github.com/metacubex/sing-tun v0.4.7 // indirect + github.com/metacubex/sing-vmess v0.2.4-0.20250822020810-4856053566f0 // indirect github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f // indirect github.com/metacubex/smux v0.0.0-20250503055512-501391591dee // indirect - github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 // indirect - github.com/metacubex/utls v1.7.4-0.20250610022031-808d767c8c73 // indirect - github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 // indirect + github.com/metacubex/tfo-go v0.0.0-20250827083229-aa432b865617 // indirect + github.com/metacubex/utls v1.8.1-0.20250823120917-12f5ba126142 // indirect + github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f // indirect github.com/miekg/dns v1.1.63 // indirect github.com/mroth/weightedrand/v2 v2.1.0 // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect @@ -80,11 +79,10 @@ require ( github.com/oschwald/maxminddb-golang v1.12.0 // indirect github.com/pierrec/lz4/v4 v4.1.14 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/sagernet/cors v1.2.1 // indirect github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect - github.com/samber/lo v1.50.0 // indirect + github.com/samber/lo v1.51.0 // indirect github.com/shirou/gopsutil/v4 v4.25.1 // indirect github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect @@ -112,5 +110,4 @@ require ( golang.org/x/tools v0.24.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - lukechampine.com/blake3 v1.3.0 // indirect ) diff --git a/core/go.sum b/core/go.sum index 5334aac..594f6cd 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,3 @@ -github.com/3andne/restls-client-go v0.1.6 h1:tRx/YilqW7iHpgmEL4E1D8dAsuB0tFF3uvncS+B6I08= -github.com/3andne/restls-client-go v0.1.6/go.mod h1:iEdTZNt9kzPIxjIGSMScUFSBrUH6bFRNg0BWlP4orEY= github.com/RyuaNerin/go-krypto v1.3.0 h1:smavTzSMAx8iuVlGb4pEwl9MD2qicqMzuXR2QWp2/Pg= github.com/RyuaNerin/go-krypto v1.3.0/go.mod h1:9R9TU936laAIqAmjcHo/LsaXYOZlymudOAxjaBf62UM= github.com/RyuaNerin/testingutil v0.1.0 h1:IYT6JL57RV3U2ml3dLHZsVtPOP6yNK7WUVdzzlpNrss= @@ -17,8 +15,6 @@ github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx2 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= -github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc= github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -26,10 +22,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/ebitengine/purego v0.8.3 h1:K+0AjQp63JEZTEMZiwsI9g0+hAMNohwUOtY0RPGexmc= -github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -github.com/enfein/mieru/v3 v3.13.0 h1:eGyxLGkb+lut9ebmx+BGwLJ5UMbEc/wGIYO0AXEKy98= -github.com/enfein/mieru/v3 v3.13.0/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM= +github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= +github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/enfein/mieru/v3 v3.19.1 h1:19b9kgFC7oJXX9RLEO5Pi1gO6yek5cWlpK7IJVUoE8I= +github.com/enfein/mieru/v3 v3.19.1/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM= github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 h1:kXYqH/sL8dS/FdoFjr12ePjnLPorPo2FsnrHNuXSDyo= github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g= @@ -43,8 +39,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= -github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= -github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= +github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= +github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= @@ -82,25 +78,24 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= -github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= -github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= -github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab h1:Chbw+/31UC14YFNr78pESt5Vowlc62zziw05JCUqoL4= -github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab/go.mod h1:xVKK8jC5Sd3hfh7WjmCq+HorehIbrBijaUWmcuKjPcI= +github.com/metacubex/amneziawg-go v0.0.0-20250820070344-732c0c9d418a h1:c1QSGpacSeQdBdWcEKZKGuWLcqIG2wxHEygAcXuDwS4= +github.com/metacubex/amneziawg-go v0.0.0-20250820070344-732c0c9d418a/go.mod h1:MsM/5czONyXMJ3PRr5DbQ4O/BxzAnJWOIcJdLzW6qHY= +github.com/metacubex/ascon v0.1.0 h1:6ZWxmXYszT1XXtwkf6nxfFhc/OTtQ9R3Vyj1jN32lGM= +github.com/metacubex/ascon v0.1.0/go.mod h1:eV5oim4cVPPdEL8/EYaTZ0iIKARH9pnhAK/fcT5Kacc= github.com/metacubex/bart v0.20.5 h1:XkgLZ17QxfxkqKdGsojoM2Zu01mmHyyQSFzt2/calTM= github.com/metacubex/bart v0.20.5/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI= -github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 h1:oBowHVKZycNtAFbZ6avaCSZJYeme2Nrj+4RpV2cNJig= -github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399/go.mod h1:4xcieuIK+M4bGQmQYZVqEaIYqjS1ahO4kXG7EmDgEro= +github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b h1:j7dadXD8I2KTmMt8jg1JcaP1ANL3JEObJPdANKcSYPY= +github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b/go.mod h1:+WmP0VJZDkDszvpa83HzfUp6QzARl/IKkMorH4+nODw= +github.com/metacubex/blake3 v0.1.0 h1:KGnjh/56REO7U+cgZA8dnBhxdP7jByrG7hTP+bu6cqY= +github.com/metacubex/blake3 v0.1.0/go.mod h1:CCkLdzFrqf7xmxCdhQFvJsRRV2mwOLDoSPg6vUTB9Uk= github.com/metacubex/chacha v0.1.5 h1:fKWMb/5c7ZrY8Uoqi79PPFxl+qwR7X/q0OrsAubyX2M= github.com/metacubex/chacha v0.1.5/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8= github.com/metacubex/fswatch v0.1.1 h1:jqU7C/v+g0qc2RUFgmAOPoVvfl2BXXUXEumn6oQuxhU= @@ -111,42 +106,43 @@ github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b h1:RUh4OdVPz/jDrM github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b/go.mod h1:8LpS0IJW1VmWzUm3ylb0e2SK5QDm5lO/2qwWLZgRpBU= github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 h1:1Qpuy+sU3DmyX9HwI+CrBT/oLNJngvBorR2RbajJcqo= github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793/go.mod h1:RjRNb4G52yAgfR+Oe/kp9G4PJJ97Fnj89eY1BFO3YyA= -github.com/metacubex/quic-go v0.52.1-0.20250522021943-aef454b9e639 h1:L+1brQNzBhCCxWlicwfK1TlceemCRmrDE4HmcVHc29w= -github.com/metacubex/quic-go v0.52.1-0.20250522021943-aef454b9e639/go.mod h1:Kc6h++Q/zf3AxcUCevJhJwgrskJumv+pZdR8g/E/10k= +github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295 h1:8JVlYuE8uSJAvmyCd4TjvDxs57xjb0WxEoaWafK5+qs= +github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295/go.mod h1:1lktQFtCD17FZliVypbrDHwbsFSsmz2xz2TRXydvB5c= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= +github.com/metacubex/restls-client-go v0.1.7 h1:eCwiXCTQb5WJu9IlgYvDBA1OgrINv58dEe7hcN5H15k= +github.com/metacubex/restls-client-go v0.1.7/go.mod h1:BN/U52vPw7j8VTSh2vleD/MnmVKCov84mS5VcjVHH4g= github.com/metacubex/sing v0.5.2/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w= -github.com/metacubex/sing v0.5.4-0.20250605054047-54dc6097da29 h1:SD9q025FNTaepuFXFOKDhnGLVu6PQYChBvw2ZYPXeLo= -github.com/metacubex/sing v0.5.4-0.20250605054047-54dc6097da29/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w= -github.com/metacubex/sing-mux v0.3.2 h1:nJv52pyRivHcaZJKk2JgxpaVvj1GAXG81scSa9N7ncw= -github.com/metacubex/sing-mux v0.3.2/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw= -github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f h1:mP3vIm+9hRFI0C0Vl3pE0NESF/L85FDbuB0tGgUii6I= -github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f/go.mod h1:JPTpf7fpnojsSuwRJExhSZSy63pVbp3VM39+zj+sAJM= -github.com/metacubex/sing-shadowsocks v0.2.11-0.20250621023810-0e9ef9dd0c92 h1:Y9ebcKya6ow7VHoESCN5+l4zZvg5eaL2IhI5LLCQxQA= -github.com/metacubex/sing-shadowsocks v0.2.11-0.20250621023810-0e9ef9dd0c92/go.mod h1:/squZ38pXrYjqtg8qn+joVvwbpGNYQNp8yxKsMVbCto= -github.com/metacubex/sing-shadowsocks2 v0.2.5-0.20250621023950-93d605a2143d h1:Ey3A1tA8lVkRbK1FDmwuWj/57Nr8JMdpoVqe45mFzJg= -github.com/metacubex/sing-shadowsocks2 v0.2.5-0.20250621023950-93d605a2143d/go.mod h1:+ukTd0OPFglT3bnKAYTJWYPbuox6HYNXE235r5tHdUk= +github.com/metacubex/sing v0.5.5 h1:m5U8iHvRAUxlme3FZlE/LPIGHjU8oMCUzXWGbQQAC1E= +github.com/metacubex/sing v0.5.5/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w= +github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac h1:wDH/Jh/yqWbzPktqJP+Y1cUG8hchcrzKzUxJiSpnaQs= +github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw= +github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb h1:U/m3h8lp/j7i8zFgfvScLdZa1/Y8dd74oO7iZaQq80s= +github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb/go.mod h1:B60FxaPHjR1SeQB0IiLrgwgvKsaoASfOWdiqhLjmMGA= +github.com/metacubex/sing-shadowsocks v0.2.12 h1:Wqzo8bYXrK5aWqxu/TjlTnYZzAKtKsaFQBdr6IHFaBE= +github.com/metacubex/sing-shadowsocks v0.2.12/go.mod h1:2e5EIaw0rxKrm1YTRmiMnDulwbGxH9hAFlrwQLQMQkU= +github.com/metacubex/sing-shadowsocks2 v0.2.6 h1:ZR1kYT0f0Vi64iQSS09OdhFfppiNkh7kjgRdMm0SB98= +github.com/metacubex/sing-shadowsocks2 v0.2.6/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE= github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI= github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E= -github.com/metacubex/sing-tun v0.4.7-0.20250611091011-60774779fdd8 h1:4zWKqxTx75TbfW2EmlQ3hxM6RTRg2PYOAVMCnU4I61I= -github.com/metacubex/sing-tun v0.4.7-0.20250611091011-60774779fdd8/go.mod h1:2YywXPWW8Z97kTH7RffOeykKzU+l0aiKlglWV1PAS64= -github.com/metacubex/sing-vmess v0.2.2 h1:nG6GIKF1UOGmlzs+BIetdGHkFZ20YqFVIYp5Htqzp+4= -github.com/metacubex/sing-vmess v0.2.2/go.mod h1:CVDNcdSLVYFgTHQlubr88d8CdqupAUDqLjROos+H9xk= +github.com/metacubex/sing-tun v0.4.7 h1:ZDY/W+1c7PeWWKeKRyUo18fySF/TWjB0i5ui81Ar778= +github.com/metacubex/sing-tun v0.4.7/go.mod h1:xHecZRwBnKWe6zG9amAK9cXf91lF6blgjBqm+VvOrmU= +github.com/metacubex/sing-vmess v0.2.4-0.20250822020810-4856053566f0 h1:WZepq4TOZa6WewB8tGAZrrL+bL2R2ivoBzuEgAeolWc= +github.com/metacubex/sing-vmess v0.2.4-0.20250822020810-4856053566f0/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM= github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU= github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f/go.mod h1:jpAkVLPnCpGSfNyVmj6Cq4YbuZsFepm/Dc+9BAOcR80= github.com/metacubex/smux v0.0.0-20250503055512-501391591dee h1:lp6hJ+4wCLZu113awp7P6odM2okB5s60HUyF0FMqKmo= github.com/metacubex/smux v0.0.0-20250503055512-501391591dee/go.mod h1:4bPD8HWx9jPJ9aE4uadgyN7D1/Wz3KmPy+vale8sKLE= -github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 h1:j1VRTiC9JLR4nUbSikx9OGdu/3AgFDqgcLj4GoqyQkc= -github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw= -github.com/metacubex/utls v1.7.4-0.20250610022031-808d767c8c73 h1:HWKsf92BqLYqugATFIJ3hYiEBZ7JF6AoqyvqF39afuI= -github.com/metacubex/utls v1.7.4-0.20250610022031-808d767c8c73/go.mod h1:oknYT0qTOwE4hjPmZOEpzVdefnW7bAdGLvZcqmk4TLU= -github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ= -github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181/go.mod h1:phewKljNYiTVT31Gcif8RiCKnTUOgVWFJjccqYM8s+Y= +github.com/metacubex/tfo-go v0.0.0-20250827083229-aa432b865617 h1:yN3mQ4cT9sPUciw/rO0Isc/8QlO86DB6g9SEMRgQ8Cw= +github.com/metacubex/tfo-go v0.0.0-20250827083229-aa432b865617/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw= +github.com/metacubex/utls v1.8.1-0.20250823120917-12f5ba126142 h1:csEbKOzRAxJXffOeZnnS3/kA/F55JiTbKv5jcYqCXms= +github.com/metacubex/utls v1.8.1-0.20250823120917-12f5ba126142/go.mod h1:67I3skhEY4Sya8f1YxELwWPoeQdXqZCrWNYLvq8gn2U= +github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f h1:FGBPRb1zUabhPhDrlKEjQ9lgIwQ6cHL4x8M9lrERhbk= +github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f/go.mod h1:oPGcV994OGJedmmxrcK9+ni7jUEMGhR+uVQAdaduIP4= github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU= github.com/mroth/weightedrand/v2 v2.1.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4= github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:UqoUn6cHESlliMhOnKLWr+CBH+e3bazUPvFj1XZwAjs= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= @@ -166,16 +162,14 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= -github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ= github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= -github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY= -github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc= +github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI= +github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs= github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI= github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8= @@ -197,7 +191,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= @@ -272,10 +266,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= -lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= diff --git a/core/hub.go b/core/hub.go index f8a0462..08eaa48 100644 --- a/core/hub.go +++ b/core/hub.go @@ -2,7 +2,6 @@ package main import ( "context" - "core/state" "encoding/json" "fmt" "github.com/metacubex/mihomo/adapter" @@ -21,6 +20,7 @@ import ( "github.com/metacubex/mihomo/tunnel" "github.com/metacubex/mihomo/tunnel/statistic" "net" + "os" "runtime" "sort" "strconv" @@ -68,7 +68,7 @@ func handleGetIsInit() bool { return isInit } -func handleForceGc() { +func handleForceGC() { go func() { log.Infoln("[APP] request force GC") runtime.GC() @@ -136,8 +136,8 @@ func handleChangeProxy(data string, fn func(string string)) { }() } -func handleGetTraffic() string { - up, down := statistic.DefaultManager.Current(state.CurrentState.OnlyStatisticsProxy) +func handleGetTraffic(onlyStatisticsProxy bool) string { + up, down := statistic.DefaultManager.Current(onlyStatisticsProxy) traffic := map[string]int64{ "up": up, "down": down, @@ -150,8 +150,8 @@ func handleGetTraffic() string { return string(data) } -func handleGetTotalTraffic() string { - up, down := statistic.DefaultManager.Total(state.CurrentState.OnlyStatisticsProxy) +func handleGetTotalTraffic(onlyStatisticsProxy bool) string { + up, down := statistic.DefaultManager.Total(onlyStatisticsProxy) traffic := map[string]int64{ "up": up, "down": down, @@ -374,6 +374,15 @@ func handleSideLoadExternalProvider(providerName string, data []byte, fn func(va }() } +func handleSuspend(suspended bool) bool { + if suspended { + tunnel.OnSuspend() + } else { + tunnel.OnRunning() + } + return true +} + func handleStartLog() { if logSubscriber != nil { log.UnSubscribe(logSubscriber) @@ -420,10 +429,6 @@ func handleGetMemory(fn func(value string)) { }() } -func handleSetState(params string) { - _ = json.Unmarshal([]byte(params), state.CurrentState) -} - func handleGetConfig(path string) (*config.RawConfig, error) { bytes, err := readFile(path) if err != nil { @@ -450,6 +455,33 @@ func handleUpdateConfig(bytes []byte) string { return "" } +func handleDelFile(path string, result ActionResult) { + go func() { + fileInfo, err := os.Stat(path) + if err != nil { + if !os.IsNotExist(err) { + result.success(err.Error()) + } + result.success("") + return + } + if fileInfo.IsDir() { + err = os.RemoveAll(path) + if err != nil { + result.success(err.Error()) + return + } + } else { + err = os.Remove(path) + if err != nil { + result.success(err.Error()) + return + } + } + result.success("") + }() +} + func handleSetupConfig(bytes []byte) string { var params = defaultSetupParams() err := UnmarshalJson(bytes, params) diff --git a/core/lib.go b/core/lib.go index b5e6229..df1d622 100644 --- a/core/lib.go +++ b/core/lib.go @@ -6,37 +6,155 @@ package main #include */ import "C" + import ( - bridge "core/dart-bridge" + "context" + "core/platform" + t "core/tun" "encoding/json" + "errors" + "github.com/metacubex/mihomo/component/dialer" + "github.com/metacubex/mihomo/component/process" + "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/dns" + "github.com/metacubex/mihomo/listener/sing_tun" + "github.com/metacubex/mihomo/log" + "golang.org/x/sync/semaphore" + "net" + "strings" + "sync" + "syscall" "unsafe" ) -var messagePort int64 = -1 +var messageCallback unsafe.Pointer -//export initNativeApiBridge -func initNativeApiBridge(api unsafe.Pointer) { - bridge.InitDartApi(api) +type TunHandler struct { + listener *sing_tun.Listener + callback unsafe.Pointer + + limit *semaphore.Weighted } -//export attachMessagePort -func attachMessagePort(mPort C.longlong) { - messagePort = int64(mPort) +func (th *TunHandler) start(fd int, stack, address, dns string) { + _ = th.limit.Acquire(context.TODO(), 4) + defer th.limit.Release(4) + th.initHook() + tunListener := t.Start(fd, stack, address, dns) + if tunListener != nil { + log.Infoln("TUN address: %v", tunListener.Address()) + th.listener = tunListener + return + } + th.clear() } -//export getTraffic -func getTraffic() *C.char { - return C.CString(handleGetTraffic()) +func (th *TunHandler) close() { + _ = th.limit.Acquire(context.TODO(), 4) + defer th.limit.Release(4) + th.clear() } -//export getTotalTraffic -func getTotalTraffic() *C.char { - return C.CString(handleGetTotalTraffic()) +func (th *TunHandler) clear() { + th.removeHook() + if th.listener != nil { + _ = th.listener.Close() + } + if th.callback != nil { + releaseObject(th.callback) + } + th.callback = nil + th.listener = nil } -//export freeCString -func freeCString(s *C.char) { - C.free(unsafe.Pointer(s)) +func (th *TunHandler) handleProtect(fd int) { + _ = th.limit.Acquire(context.Background(), 1) + defer th.limit.Release(1) + + if th.listener == nil { + return + } + + protect(th.callback, fd) +} + +func (th *TunHandler) handleResolveProcess(source, target net.Addr) string { + _ = th.limit.Acquire(context.Background(), 1) + defer th.limit.Release(1) + + if th.listener == nil { + return "" + } + var protocol int + uid := -1 + switch source.Network() { + case "udp", "udp4", "udp6": + protocol = syscall.IPPROTO_UDP + case "tcp", "tcp4", "tcp6": + protocol = syscall.IPPROTO_TCP + } + if version < 29 { + uid = platform.QuerySocketUidFromProcFs(source, target) + } + return resolveProcess(th.callback, protocol, source.String(), target.String(), uid) +} + +func (th *TunHandler) initHook() { + dialer.DefaultSocketHook = func(network, address string, conn syscall.RawConn) error { + if platform.ShouldBlockConnection() { + return errBlocked + } + return conn.Control(func(fd uintptr) { + tunHandler.handleProtect(int(fd)) + }) + } + process.DefaultPackageNameResolver = func(metadata *constant.Metadata) (string, error) { + src, dst := metadata.RawSrcAddr, metadata.RawDstAddr + if src == nil || dst == nil { + return "", process.ErrInvalidNetwork + } + return tunHandler.handleResolveProcess(src, dst), nil + } +} + +func (th *TunHandler) removeHook() { + dialer.DefaultSocketHook = nil + process.DefaultPackageNameResolver = nil +} + +var ( + tunLock sync.Mutex + errBlocked = errors.New("blocked") + tunHandler *TunHandler +) + +func handleStopTun() { + tunLock.Lock() + defer tunLock.Unlock() + if tunHandler != nil { + tunHandler.close() + } +} + +func handleStartTun(callback unsafe.Pointer, fd int, stack, address, dns string) { + handleStopTun() + tunLock.Lock() + defer tunLock.Unlock() + if fd != 0 { + tunHandler = &TunHandler{ + callback: callback, + limit: semaphore.NewWeighted(4), + } + tunHandler.start(fd, stack, address, dns) + } +} + +func handleUpdateDns(value string) { + go func() { + log.Infoln("[DNS] updateDns %s", value) + dns.UpdateSystemDNS(strings.Split(value, ",")) + dns.FlushCacheWithDefaultResolver() + }() } func (result ActionResult) send() { @@ -44,59 +162,96 @@ func (result ActionResult) send() { if err != nil { return } - bridge.SendToPort(result.Port, string(data)) + invokeResult(result.callback, string(data)) + if result.Method != messageMethod { + releaseObject(result.callback) + } +} + +func nextHandle(action *Action, result ActionResult) bool { + switch action.Method { + case updateDnsMethod: + data := action.Data.(string) + handleUpdateDns(data) + result.success(true) + return true + } + return false } //export invokeAction -func invokeAction(paramsChar *C.char, port C.longlong) { - params := C.GoString(paramsChar) - i := int64(port) +func invokeAction(callback unsafe.Pointer, paramsChar *C.char) { + params := takeCString(paramsChar) var action = &Action{} err := json.Unmarshal([]byte(params), action) if err != nil { - bridge.SendToPort(i, err.Error()) + invokeResult(callback, err.Error()) return } result := ActionResult{ - Id: action.Id, - Method: action.Method, - Port: i, + Id: action.Id, + Method: action.Method, + callback: callback, } go handleAction(action, result) } +//export startTUN +func startTUN(callback unsafe.Pointer, fd C.int, stackChar, addressChar, dnsChar *C.char) bool { + handleStartTun(callback, int(fd), takeCString(stackChar), takeCString(addressChar), takeCString(dnsChar)) + return true +} + +//export setMessageCallback +func setMessageCallback(callback unsafe.Pointer) { + if messageCallback != nil { + releaseObject(messageCallback) + } + messageCallback = callback +} + +//export getTotalTraffic +func getTotalTraffic(onlyStatisticsProxy bool) *C.char { + data := C.CString(handleGetTotalTraffic(onlyStatisticsProxy)) + defer C.free(unsafe.Pointer(data)) + return data +} + +//export getTraffic +func getTraffic(onlyStatisticsProxy bool) *C.char { + data := C.CString(handleGetTraffic(onlyStatisticsProxy)) + defer C.free(unsafe.Pointer(data)) + return data +} + func sendMessage(message Message) { - if messagePort == -1 { + if messageCallback == nil { return } result := ActionResult{ - Method: messageMethod, - Port: messagePort, - Data: message, + Method: messageMethod, + callback: messageCallback, + Data: message, } result.send() } -//export getConfig -func getConfig(s *C.char) *C.char { - path := C.GoString(s) - config, err := handleGetConfig(path) - if err != nil { - return C.CString("") - } - marshal, err := json.Marshal(config) - if err != nil { - return C.CString("") - } - return C.CString(string(marshal)) +//export stopTun +func stopTun() { + handleStopTun() } -//export startListener -func startListener() { - handleStartListener() +//export suspend +func suspend(suspended bool) { + handleSuspend(suspended) } -//export stopListener -func stopListener() { - handleStopListener() +//export forceGC +func forceGC() { + handleForceGC() +} + +//export updateDns +func updateDns(s *C.char) { + handleUpdateDns(takeCString(s)) } diff --git a/core/lib_android.go b/core/lib_android.go deleted file mode 100644 index 00b3b35..0000000 --- a/core/lib_android.go +++ /dev/null @@ -1,267 +0,0 @@ -//go:build android && cgo - -package main - -import "C" -import ( - "context" - bridge "core/dart-bridge" - "core/platform" - "core/state" - t "core/tun" - "encoding/json" - "errors" - "fmt" - "github.com/metacubex/mihomo/component/dialer" - "github.com/metacubex/mihomo/component/process" - "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/dns" - "github.com/metacubex/mihomo/listener/sing_tun" - "github.com/metacubex/mihomo/log" - "golang.org/x/sync/semaphore" - "net" - "strconv" - "strings" - "sync" - "syscall" - "time" - "unsafe" -) - -type TunHandler struct { - listener *sing_tun.Listener - callback unsafe.Pointer - - limit *semaphore.Weighted -} - -func (t *TunHandler) close() { - _ = t.limit.Acquire(context.TODO(), 4) - defer t.limit.Release(4) - removeTunHook() - if t.listener != nil { - _ = t.listener.Close() - } - - if t.callback != nil { - releaseObject(t.callback) - } - t.callback = nil - t.listener = nil -} - -func (t *TunHandler) handleProtect(fd int) { - _ = t.limit.Acquire(context.Background(), 1) - defer t.limit.Release(1) - - if t.listener == nil { - return - } - - Protect(t.callback, fd) -} - -func (t *TunHandler) handleResolveProcess(source, target net.Addr) string { - _ = t.limit.Acquire(context.Background(), 1) - defer t.limit.Release(1) - - if t.listener == nil { - return "" - } - var protocol int - uid := -1 - switch source.Network() { - case "udp", "udp4", "udp6": - protocol = syscall.IPPROTO_UDP - case "tcp", "tcp4", "tcp6": - protocol = syscall.IPPROTO_TCP - } - if version < 29 { - uid = platform.QuerySocketUidFromProcFs(source, target) - } - return ResolveProcess(t.callback, protocol, source.String(), target.String(), uid) -} - -var ( - tunLock sync.Mutex - runTime *time.Time - errBlocked = errors.New("blocked") - tunHandler *TunHandler -) - -func handleStopTun() { - tunLock.Lock() - defer tunLock.Unlock() - runTime = nil - if tunHandler != nil { - tunHandler.close() - } -} - -func handleStartTun(fd int, callback unsafe.Pointer) { - handleStopTun() - tunLock.Lock() - defer tunLock.Unlock() - now := time.Now() - runTime = &now - if fd != 0 { - tunHandler = &TunHandler{ - callback: callback, - limit: semaphore.NewWeighted(4), - } - initTunHook() - tunListener, _ := t.Start(fd, currentConfig.General.Tun.Device, currentConfig.General.Tun.Stack) - if tunListener != nil { - log.Infoln("TUN address: %v", tunListener.Address()) - tunHandler.listener = tunListener - } else { - removeTunHook() - } - } -} - -func handleGetRunTime() string { - if runTime == nil { - return "" - } - return strconv.FormatInt(runTime.UnixMilli(), 10) -} - -func initTunHook() { - dialer.DefaultSocketHook = func(network, address string, conn syscall.RawConn) error { - if platform.ShouldBlockConnection() { - return errBlocked - } - return conn.Control(func(fd uintptr) { - tunHandler.handleProtect(int(fd)) - }) - } - process.DefaultPackageNameResolver = func(metadata *constant.Metadata) (string, error) { - src, dst := metadata.RawSrcAddr, metadata.RawDstAddr - if src == nil || dst == nil { - return "", process.ErrInvalidNetwork - } - return tunHandler.handleResolveProcess(src, dst), nil - } -} - -func removeTunHook() { - dialer.DefaultSocketHook = nil - process.DefaultPackageNameResolver = nil -} - -func handleGetAndroidVpnOptions() string { - tunLock.Lock() - defer tunLock.Unlock() - options := state.AndroidVpnOptions{ - Enable: state.CurrentState.VpnProps.Enable, - Port: currentConfig.General.MixedPort, - Ipv4Address: state.DefaultIpv4Address, - Ipv6Address: state.GetIpv6Address(), - AccessControl: state.CurrentState.VpnProps.AccessControl, - SystemProxy: state.CurrentState.VpnProps.SystemProxy, - AllowBypass: state.CurrentState.VpnProps.AllowBypass, - RouteAddress: currentConfig.General.Tun.RouteAddress, - BypassDomain: state.CurrentState.BypassDomain, - DnsServerAddress: state.GetDnsServerAddress(), - } - data, err := json.Marshal(options) - if err != nil { - fmt.Println("Error:", err) - return "" - } - return string(data) -} - -func handleUpdateDns(value string) { - go func() { - log.Infoln("[DNS] updateDns %s", value) - dns.UpdateSystemDNS(strings.Split(value, ",")) - dns.FlushCacheWithDefaultResolver() - }() -} - -func handleGetCurrentProfileName() string { - if state.CurrentState == nil { - return "" - } - return state.CurrentState.CurrentProfileName -} - -func nextHandle(action *Action, result ActionResult) bool { - switch action.Method { - case getAndroidVpnOptionsMethod: - result.success(handleGetAndroidVpnOptions()) - return true - case updateDnsMethod: - data := action.Data.(string) - handleUpdateDns(data) - result.success(true) - return true - case getRunTimeMethod: - result.success(handleGetRunTime()) - return true - case getCurrentProfileNameMethod: - result.success(handleGetCurrentProfileName()) - return true - } - return false -} - -//export quickStart -func quickStart(initParamsChar *C.char, paramsChar *C.char, stateParamsChar *C.char, port C.longlong) { - i := int64(port) - paramsString := C.GoString(initParamsChar) - bytes := []byte(C.GoString(paramsChar)) - stateParams := C.GoString(stateParamsChar) - go func() { - res := handleInitClash(paramsString) - if res == false { - bridge.SendToPort(i, "init error") - } - handleSetState(stateParams) - bridge.SendToPort(i, handleSetupConfig(bytes)) - }() -} - -//export startTUN -func startTUN(fd C.int, callback unsafe.Pointer) bool { - go func() { - handleStartTun(int(fd), callback) - }() - return true -} - -//export getRunTime -func getRunTime() *C.char { - return C.CString(handleGetRunTime()) -} - -//export stopTun -func stopTun() { - go func() { - handleStopTun() - }() -} - -//export getCurrentProfileName -func getCurrentProfileName() *C.char { - return C.CString(handleGetCurrentProfileName()) -} - -//export getAndroidVpnOptions -func getAndroidVpnOptions() *C.char { - return C.CString(handleGetAndroidVpnOptions()) -} - -//export setState -func setState(s *C.char) { - paramsString := C.GoString(s) - handleSetState(paramsString) -} - -//export updateDns -func updateDns(s *C.char) { - dnsList := C.GoString(s) - handleUpdateDns(dnsList) -} diff --git a/core/lib_no_android.go b/core/lib_no_android.go deleted file mode 100644 index 84f10ce..0000000 --- a/core/lib_no_android.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build !android && cgo - -package main - -func nextHandle(action *Action, result func(data interface{})) bool { - return false -} diff --git a/core/platform/procfs.go b/core/platform/procfs.go index 94989c0..a6d604b 100644 --- a/core/platform/procfs.go +++ b/core/platform/procfs.go @@ -1,5 +1,4 @@ //go:build linux -// +build linux package platform diff --git a/core/state/state.go b/core/state/state.go deleted file mode 100644 index 62beaf5..0000000 --- a/core/state/state.go +++ /dev/null @@ -1,59 +0,0 @@ -package state - -import "net/netip" - -var DefaultIpv4Address = "172.19.0.1/30" -var DefaultDnsAddress = "172.19.0.2" -var DefaultIpv6Address = "fdfe:dcba:9876::1/126" - -type AndroidVpnOptions struct { - Enable bool `json:"enable"` - Port int `json:"port"` - AccessControl *AccessControl `json:"accessControl"` - AllowBypass bool `json:"allowBypass"` - SystemProxy bool `json:"systemProxy"` - BypassDomain []string `json:"bypassDomain"` - RouteAddress []netip.Prefix `json:"routeAddress"` - Ipv4Address string `json:"ipv4Address"` - Ipv6Address string `json:"ipv6Address"` - DnsServerAddress string `json:"dnsServerAddress"` -} - -type AccessControl struct { - Enable bool `json:"enable"` - Mode string `json:"mode"` - AcceptList []string `json:"acceptList"` - RejectList []string `json:"rejectList"` -} - -type AndroidVpnRawOptions struct { - Enable bool `json:"enable"` - AccessControl *AccessControl `json:"accessControl"` - AllowBypass bool `json:"allowBypass"` - SystemProxy bool `json:"systemProxy"` - Ipv6 bool `json:"ipv6"` -} - -type State struct { - VpnProps AndroidVpnRawOptions `json:"vpn-props"` - CurrentProfileName string `json:"current-profile-name"` - OnlyStatisticsProxy bool `json:"only-statistics-proxy"` - BypassDomain []string `json:"bypass-domain"` -} - -var CurrentState = &State{ - OnlyStatisticsProxy: false, - CurrentProfileName: "", -} - -func GetIpv6Address() string { - if CurrentState.VpnProps.Ipv6 { - return DefaultIpv6Address - } else { - return "" - } -} - -func GetDnsServerAddress() string { - return DefaultDnsAddress -} diff --git a/core/tun/tun.go b/core/tun/tun.go index 8cae371..5d5d1d0 100644 --- a/core/tun/tun.go +++ b/core/tun/tun.go @@ -4,7 +4,6 @@ package tun import "C" import ( - "core/state" "github.com/metacubex/mihomo/constant" LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/sing_tun" @@ -12,43 +11,46 @@ import ( "github.com/metacubex/mihomo/tunnel" "net" "net/netip" + "strings" ) -type Props struct { - Fd int `json:"fd"` - Gateway string `json:"gateway"` - Gateway6 string `json:"gateway6"` - Portal string `json:"portal"` - Portal6 string `json:"portal6"` - Dns string `json:"dns"` - Dns6 string `json:"dns6"` -} - -func Start(fd int, device string, stack constant.TUNStack) (*sing_tun.Listener, error) { +func Start(fd int, stack string, address, dns string) *sing_tun.Listener { var prefix4 []netip.Prefix - tempPrefix4, err := netip.ParsePrefix(state.DefaultIpv4Address) - if err != nil { - log.Errorln("startTUN error:", err) - return nil, err - } - prefix4 = append(prefix4, tempPrefix4) var prefix6 []netip.Prefix - if state.CurrentState.VpnProps.Ipv6 { - tempPrefix6, err := netip.ParsePrefix(state.DefaultIpv6Address) - if err != nil { - log.Errorln("startTUN error:", err) - return nil, err + tunStack, ok := constant.StackTypeMapping[strings.ToLower(stack)] + if !ok { + tunStack = constant.TunSystem + } + for _, a := range strings.Split(address, ",") { + a = strings.TrimSpace(a) + if len(a) == 0 { + continue + } + prefix, err := netip.ParsePrefix(a) + if err != nil { + log.Errorln("TUN:", err) + return nil + } + if prefix.Addr().Is4() { + prefix4 = append(prefix4, prefix) + } else { + prefix6 = append(prefix6, prefix) } - prefix6 = append(prefix6, tempPrefix6) } var dnsHijack []string - dnsHijack = append(dnsHijack, net.JoinHostPort(state.GetDnsServerAddress(), "53")) + for _, d := range strings.Split(dns, ",") { + d = strings.TrimSpace(d) + if len(d) == 0 { + continue + } + dnsHijack = append(dnsHijack, net.JoinHostPort(d, "53")) + } options := LC.Tun{ Enable: true, - Device: device, - Stack: stack, + Device: "FlClash", + Stack: tunStack, DNSHijack: dnsHijack, AutoRoute: false, AutoDetectInterface: false, @@ -61,9 +63,9 @@ func Start(fd int, device string, stack constant.TUNStack) (*sing_tun.Listener, listener, err := sing_tun.New(options, tunnel.Tunnel) if err != nil { - log.Errorln("startTUN error:", err) - return nil, err + log.Errorln("TUN:", err) + return nil } - return listener, nil + return listener } diff --git a/lib/application.dart b/lib/application.dart index 3baa1b0..ecbd769 100644 --- a/lib/application.dart +++ b/lib/application.dart @@ -1,8 +1,8 @@ import 'dart:async'; import 'package:connectivity_plus/connectivity_plus.dart'; -import 'package:fl_clash/clash/clash.dart'; import 'package:fl_clash/common/common.dart'; +import 'package:fl_clash/core/core.dart'; import 'package:fl_clash/l10n/l10n.dart'; import 'package:fl_clash/manager/hotkey_manager.dart'; import 'package:fl_clash/manager/manager.dart'; @@ -17,9 +17,7 @@ import 'controller.dart'; import 'pages/pages.dart'; class Application extends ConsumerStatefulWidget { - const Application({ - super.key, - }); + const Application({super.key}); @override ConsumerState createState() => ApplicationState(); @@ -48,7 +46,6 @@ class ApplicationState extends ConsumerState { @override void initState() { super.initState(); - _autoUpdateGroupTask(); _autoUpdateProfilesTask(); globalState.appController = AppController(context, ref); WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { @@ -62,15 +59,6 @@ class ApplicationState extends ConsumerState { }); } - void _autoUpdateGroupTask() { - _autoUpdateGroupTaskTimer = Timer(const Duration(milliseconds: 20000), () { - WidgetsBinding.instance.addPostFrameCallback((_) { - globalState.appController.updateGroupsDebounce(); - _autoUpdateGroupTask(); - }); - }); - } - void _autoUpdateProfilesTask() { _autoUpdateProfilesTaskTimer = Timer(const Duration(minutes: 20), () async { await globalState.appController.autoUpdateProfiles(); @@ -82,28 +70,20 @@ class ApplicationState extends ConsumerState { if (system.isDesktop) { return WindowManager( child: TrayManager( - child: HotKeyManager( - child: ProxyManager( - child: child, - ), - ), + child: HotKeyManager(child: ProxyManager(child: child)), ), ); } - return AndroidManager( - child: TileManager( - child: child, - ), - ); + return AndroidManager(child: TileManager(child: child)); } Widget _buildState(Widget child) { return AppStateManager( - child: ClashManager( + child: CoreManager( child: ConnectivityManager( onConnectivityChanged: (results) async { if (!results.contains(ConnectivityResult.vpn)) { - clashCore.closeConnections(); + coreController.closeConnections(); } globalState.appController.updateLocalIp(); globalState.appController.addCheckIpNumDebounce(); @@ -116,79 +96,68 @@ class ApplicationState extends ConsumerState { Widget _buildPlatformApp(Widget child) { if (system.isDesktop) { - return WindowHeaderContainer( - child: child, - ); + return WindowHeaderContainer(child: child); } - return VpnManager( - child: child, - ); + return VpnManager(child: child); } Widget _buildApp(Widget child) { - return MessageManager( - child: ThemeManager( - child: child, - ), - ); + return MessageManager(child: ThemeManager(child: child)); } @override Widget build(context) { - return _buildPlatformState( - _buildState( - Consumer( - builder: (_, ref, child) { - final locale = - ref.watch(appSettingProvider.select((state) => state.locale)); - final themeProps = ref.watch(themeSettingProvider); - return MaterialApp( - debugShowCheckedModeBanner: false, - navigatorKey: globalState.navigatorKey, - localizationsDelegates: const [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - GlobalWidgetsLocalizations.delegate - ], - builder: (_, child) { - return AppEnvManager( - child: _buildApp( - AppSidebarContainer( - child: _buildPlatformApp( - child!, - ), - ), + return Consumer( + builder: (_, ref, child) { + final locale = ref.watch( + appSettingProvider.select((state) => state.locale), + ); + final themeProps = ref.watch(themeSettingProvider); + return MaterialApp( + debugShowCheckedModeBanner: false, + navigatorKey: globalState.navigatorKey, + localizationsDelegates: const [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + builder: (_, child) { + return AppEnvManager( + child: _buildApp( + _buildPlatformState( + _buildState( + AppSidebarContainer(child: _buildPlatformApp(child!)), ), - ); - }, - scrollBehavior: BaseScrollBehavior(), - title: appName, - locale: utils.getLocaleForString(locale), - supportedLocales: AppLocalizations.delegate.supportedLocales, - themeMode: themeProps.themeMode, - theme: ThemeData( - useMaterial3: true, - pageTransitionsTheme: _pageTransitionsTheme, - colorScheme: _getAppColorScheme( - brightness: Brightness.light, - primaryColor: themeProps.primaryColor, ), ), - darkTheme: ThemeData( - useMaterial3: true, - pageTransitionsTheme: _pageTransitionsTheme, - colorScheme: _getAppColorScheme( - brightness: Brightness.dark, - primaryColor: themeProps.primaryColor, - ).toPureBlack(themeProps.pureBlack), - ), - home: child!, ); }, - child: const HomePage(), - ), - ), + scrollBehavior: BaseScrollBehavior(), + title: appName, + locale: utils.getLocaleForString(locale), + supportedLocales: AppLocalizations.delegate.supportedLocales, + themeMode: themeProps.themeMode, + theme: ThemeData( + useMaterial3: true, + pageTransitionsTheme: _pageTransitionsTheme, + colorScheme: _getAppColorScheme( + brightness: Brightness.light, + primaryColor: themeProps.primaryColor, + ), + ), + darkTheme: ThemeData( + useMaterial3: true, + pageTransitionsTheme: _pageTransitionsTheme, + colorScheme: _getAppColorScheme( + brightness: Brightness.dark, + primaryColor: themeProps.primaryColor, + ).toPureBlack(themeProps.pureBlack), + ), + home: child!, + ); + }, + child: const HomePage(), ); } @@ -197,7 +166,7 @@ class ApplicationState extends ConsumerState { linkManager.destroy(); _autoUpdateGroupTaskTimer?.cancel(); _autoUpdateProfilesTaskTimer?.cancel(); - await clashCore.destroy(); + await coreController.destroy(); await globalState.appController.savePreferences(); await globalState.appController.handleExit(); super.dispose(); diff --git a/lib/clash/core.dart b/lib/clash/core.dart deleted file mode 100644 index ab916f2..0000000 --- a/lib/clash/core.dart +++ /dev/null @@ -1,284 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; -import 'dart:isolate'; - -import 'package:fl_clash/clash/clash.dart'; -import 'package:fl_clash/clash/interface.dart'; -import 'package:fl_clash/common/common.dart'; -import 'package:fl_clash/enum/enum.dart'; -import 'package:fl_clash/models/models.dart'; -import 'package:fl_clash/state.dart'; -import 'package:flutter/services.dart'; -import 'package:path/path.dart'; - -class ClashCore { - static ClashCore? _instance; - late ClashHandlerInterface clashInterface; - - ClashCore._internal() { - if (system.isAndroid) { - clashInterface = clashLib!; - } else { - clashInterface = clashService!; - } - } - - factory ClashCore() { - _instance ??= ClashCore._internal(); - return _instance!; - } - - Future preload() { - return clashInterface.preload(); - } - - static Future initGeo() async { - final homePath = await appPath.homeDirPath; - final homeDir = Directory(homePath); - final isExists = await homeDir.exists(); - if (!isExists) { - await homeDir.create(recursive: true); - } - const geoFileNameList = [ - mmdbFileName, - geoIpFileName, - geoSiteFileName, - asnFileName, - ]; - try { - for (final geoFileName in geoFileNameList) { - final geoFile = File( - join(homePath, geoFileName), - ); - final isExists = await geoFile.exists(); - if (isExists) { - continue; - } - final data = await rootBundle.load('assets/data/$geoFileName'); - List bytes = data.buffer.asUint8List(); - await geoFile.writeAsBytes(bytes, flush: true); - } - } catch (e) { - exit(0); - } - } - - Future init() async { - await initGeo(); - if (globalState.config.appSetting.openLogs) { - clashCore.startLog(); - } else { - clashCore.stopLog(); - } - final homeDirPath = await appPath.homeDirPath; - return await clashInterface.init( - InitParams( - homeDir: homeDirPath, - version: globalState.appState.version, - ), - ); - } - - Future setState(CoreState state) async { - return await clashInterface.setState(state); - } - - Future shutdown() async { - await clashInterface.shutdown(); - } - - FutureOr get isInit => clashInterface.isInit; - - FutureOr validateConfig(String data) { - return clashInterface.validateConfig(data); - } - - Future updateConfig(UpdateParams updateParams) async { - return await clashInterface.updateConfig(updateParams); - } - - Future setupConfig(SetupParams setupParams) async { - return await clashInterface.setupConfig(setupParams); - } - - Future> getProxiesGroups() async { - final proxies = await clashInterface.getProxies(); - if (proxies.isEmpty) return []; - final groupNames = [ - UsedProxy.GLOBAL.name, - ...(proxies[UsedProxy.GLOBAL.name]['all'] as List).where((e) { - final proxy = proxies[e] ?? {}; - return GroupTypeExtension.valueList.contains(proxy['type']); - }) - ]; - final groupsRaw = groupNames.map((groupName) { - final group = proxies[groupName]; - group['all'] = ((group['all'] ?? []) as List) - .map( - (name) => proxies[name], - ) - .where((proxy) => proxy != null) - .toList(); - return group; - }).toList(); - return groupsRaw - .map( - (e) => Group.fromJson(e), - ) - .toList(); - } - - FutureOr changeProxy(ChangeProxyParams changeProxyParams) async { - return await clashInterface.changeProxy(changeProxyParams); - } - - Future> getConnections() async { - final res = await clashInterface.getConnections(); - final connectionsData = json.decode(res) as Map; - final connectionsRaw = connectionsData['connections'] as List? ?? []; - return connectionsRaw.map((e) => TrackerInfo.fromJson(e)).toList(); - } - - void closeConnection(String id) { - clashInterface.closeConnection(id); - } - - void closeConnections() { - clashInterface.closeConnections(); - } - - void resetConnections() { - clashInterface.resetConnections(); - } - - Future> getExternalProviders() async { - final externalProvidersRawString = - await clashInterface.getExternalProviders(); - if (externalProvidersRawString.isEmpty) { - return []; - } - return Isolate.run>( - () { - final externalProviders = - (json.decode(externalProvidersRawString) as List) - .map( - (item) => ExternalProvider.fromJson(item), - ) - .toList(); - return externalProviders; - }, - ); - } - - Future getExternalProvider( - String externalProviderName) async { - final externalProvidersRawString = - await clashInterface.getExternalProvider(externalProviderName); - if (externalProvidersRawString.isEmpty) { - return null; - } - if (externalProvidersRawString.isEmpty) { - return null; - } - return ExternalProvider.fromJson(json.decode(externalProvidersRawString)); - } - - Future updateGeoData(UpdateGeoDataParams params) { - return clashInterface.updateGeoData(params); - } - - Future sideLoadExternalProvider({ - required String providerName, - required String data, - }) { - return clashInterface.sideLoadExternalProvider( - providerName: providerName, data: data); - } - - Future updateExternalProvider({ - required String providerName, - }) async { - return clashInterface.updateExternalProvider(providerName); - } - - Future startListener() async { - await clashInterface.startListener(); - } - - Future stopListener() async { - await clashInterface.stopListener(); - } - - Future getDelay(String url, String proxyName) async { - final data = await clashInterface.asyncTestDelay(url, proxyName); - return Delay.fromJson(json.decode(data)); - } - - Future> getConfig(String id) async { - final profilePath = await appPath.getProfilePath(id); - final res = await clashInterface.getConfig(profilePath); - if (res.isSuccess) { - return res.data as Map; - } else { - throw res.message; - } - } - - Future getTraffic() async { - final trafficString = await clashInterface.getTraffic(); - if (trafficString.isEmpty) { - return Traffic(); - } - return Traffic.fromMap(json.decode(trafficString)); - } - - Future getCountryCode(String ip) async { - final countryCode = await clashInterface.getCountryCode(ip); - if (countryCode.isEmpty) { - return null; - } - return IpInfo( - ip: ip, - countryCode: countryCode, - ); - } - - Future getTotalTraffic() async { - final totalTrafficString = await clashInterface.getTotalTraffic(); - if (totalTrafficString.isEmpty) { - return Traffic(); - } - return Traffic.fromMap(json.decode(totalTrafficString)); - } - - Future getMemory() async { - final value = await clashInterface.getMemory(); - if (value.isEmpty) { - return 0; - } - return int.parse(value); - } - - void resetTraffic() { - clashInterface.resetTraffic(); - } - - void startLog() { - clashInterface.startLog(); - } - - void stopLog() { - clashInterface.stopLog(); - } - - Future requestGc() async { - await clashInterface.forceGc(); - } - - Future destroy() async { - await clashInterface.destroy(); - } -} - -final clashCore = ClashCore(); diff --git a/lib/clash/generated/clash_ffi.dart b/lib/clash/generated/clash_ffi.dart deleted file mode 100644 index c97847f..0000000 --- a/lib/clash/generated/clash_ffi.dart +++ /dev/null @@ -1,5363 +0,0 @@ -// AUTO GENERATED FILE, DO NOT EDIT. -// -// Generated by `package:ffigen`. -// ignore_for_file: type=lint -import 'dart:ffi' as ffi; - -class ClashFFI { - /// Holds the symbol lookup function. - final ffi.Pointer Function(String symbolName) - _lookup; - - /// The symbols are looked up in [dynamicLibrary]. - ClashFFI(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; - - /// The symbols are looked up with [lookup]. - ClashFFI.fromLookup( - ffi.Pointer Function(String symbolName) - lookup) - : _lookup = lookup; - - ffi.Pointer> signal( - int arg0, - ffi.Pointer> arg1, - ) { - return _signal( - arg0, - arg1, - ); - } - - late final _signalPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer> Function( - ffi.Int, - ffi.Pointer< - ffi.NativeFunction>)>>('signal'); - late final _signal = _signalPtr.asFunction< - ffi.Pointer> Function( - int, ffi.Pointer>)>(); - - int getpriority( - int arg0, - int arg1, - ) { - return _getpriority( - arg0, - arg1, - ); - } - - late final _getpriorityPtr = - _lookup>( - 'getpriority'); - late final _getpriority = - _getpriorityPtr.asFunction(); - - int getiopolicy_np( - int arg0, - int arg1, - ) { - return _getiopolicy_np( - arg0, - arg1, - ); - } - - late final _getiopolicy_npPtr = - _lookup>( - 'getiopolicy_np'); - late final _getiopolicy_np = - _getiopolicy_npPtr.asFunction(); - - int getrlimit( - int arg0, - ffi.Pointer arg1, - ) { - return _getrlimit( - arg0, - arg1, - ); - } - - late final _getrlimitPtr = _lookup< - ffi.NativeFunction)>>( - 'getrlimit'); - late final _getrlimit = - _getrlimitPtr.asFunction)>(); - - int getrusage( - int arg0, - ffi.Pointer arg1, - ) { - return _getrusage( - arg0, - arg1, - ); - } - - late final _getrusagePtr = _lookup< - ffi.NativeFunction)>>( - 'getrusage'); - late final _getrusage = - _getrusagePtr.asFunction)>(); - - int setpriority( - int arg0, - int arg1, - int arg2, - ) { - return _setpriority( - arg0, - arg1, - arg2, - ); - } - - late final _setpriorityPtr = - _lookup>( - 'setpriority'); - late final _setpriority = - _setpriorityPtr.asFunction(); - - int setiopolicy_np( - int arg0, - int arg1, - int arg2, - ) { - return _setiopolicy_np( - arg0, - arg1, - arg2, - ); - } - - late final _setiopolicy_npPtr = - _lookup>( - 'setiopolicy_np'); - late final _setiopolicy_np = - _setiopolicy_npPtr.asFunction(); - - int setrlimit( - int arg0, - ffi.Pointer arg1, - ) { - return _setrlimit( - arg0, - arg1, - ); - } - - late final _setrlimitPtr = _lookup< - ffi.NativeFunction)>>( - 'setrlimit'); - late final _setrlimit = - _setrlimitPtr.asFunction)>(); - - int wait$1( - ffi.Pointer arg0, - ) { - return _wait$1( - arg0, - ); - } - - late final _wait$1Ptr = - _lookup)>>('wait'); - late final _wait$1 = - _wait$1Ptr.asFunction)>(); - - int waitpid( - int arg0, - ffi.Pointer arg1, - int arg2, - ) { - return _waitpid( - arg0, - arg1, - arg2, - ); - } - - late final _waitpidPtr = _lookup< - ffi.NativeFunction< - pid_t Function(pid_t, ffi.Pointer, ffi.Int)>>('waitpid'); - late final _waitpid = - _waitpidPtr.asFunction, int)>(); - - int waitid( - idtype_t arg0, - Dart__uint32_t arg1, - ffi.Pointer arg2, - int arg3, - ) { - return _waitid( - arg0.value, - arg1, - arg2, - arg3, - ); - } - - late final _waitidPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.UnsignedInt, id_t, ffi.Pointer, - ffi.Int)>>('waitid'); - late final _waitid = _waitidPtr - .asFunction, int)>(); - - int wait3( - ffi.Pointer arg0, - int arg1, - ffi.Pointer arg2, - ) { - return _wait3( - arg0, - arg1, - arg2, - ); - } - - late final _wait3Ptr = _lookup< - ffi.NativeFunction< - pid_t Function( - ffi.Pointer, ffi.Int, ffi.Pointer)>>('wait3'); - late final _wait3 = _wait3Ptr.asFunction< - int Function(ffi.Pointer, int, ffi.Pointer)>(); - - int wait4( - int arg0, - ffi.Pointer arg1, - int arg2, - ffi.Pointer arg3, - ) { - return _wait4( - arg0, - arg1, - arg2, - arg3, - ); - } - - late final _wait4Ptr = _lookup< - ffi.NativeFunction< - pid_t Function(pid_t, ffi.Pointer, ffi.Int, - ffi.Pointer)>>('wait4'); - late final _wait4 = _wait4Ptr.asFunction< - int Function(int, ffi.Pointer, int, ffi.Pointer)>(); - - ffi.Pointer alloca( - int arg0, - ) { - return _alloca( - arg0, - ); - } - - late final _allocaPtr = - _lookup Function(ffi.Size)>>( - 'alloca'); - late final _alloca = - _allocaPtr.asFunction Function(int)>(); - - late final ffi.Pointer ___mb_cur_max = - _lookup('__mb_cur_max'); - - int get __mb_cur_max => ___mb_cur_max.value; - - set __mb_cur_max(int value) => ___mb_cur_max.value = value; - - ffi.Pointer malloc_type_malloc( - int size, - int type_id, - ) { - return _malloc_type_malloc( - size, - type_id, - ); - } - - late final _malloc_type_mallocPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Size, malloc_type_id_t)>>('malloc_type_malloc'); - late final _malloc_type_malloc = _malloc_type_mallocPtr - .asFunction Function(int, int)>(); - - ffi.Pointer malloc_type_calloc( - int count, - int size, - int type_id, - ) { - return _malloc_type_calloc( - count, - size, - type_id, - ); - } - - late final _malloc_type_callocPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Size, ffi.Size, malloc_type_id_t)>>('malloc_type_calloc'); - late final _malloc_type_calloc = _malloc_type_callocPtr - .asFunction Function(int, int, int)>(); - - void malloc_type_free( - ffi.Pointer ptr, - int type_id, - ) { - return _malloc_type_free( - ptr, - type_id, - ); - } - - late final _malloc_type_freePtr = _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, malloc_type_id_t)>>('malloc_type_free'); - late final _malloc_type_free = _malloc_type_freePtr - .asFunction, int)>(); - - ffi.Pointer malloc_type_realloc( - ffi.Pointer ptr, - int size, - int type_id, - ) { - return _malloc_type_realloc( - ptr, - size, - type_id, - ); - } - - late final _malloc_type_reallocPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer, ffi.Size, - malloc_type_id_t)>>('malloc_type_realloc'); - late final _malloc_type_realloc = _malloc_type_reallocPtr.asFunction< - ffi.Pointer Function(ffi.Pointer, int, int)>(); - - ffi.Pointer malloc_type_valloc( - int size, - int type_id, - ) { - return _malloc_type_valloc( - size, - type_id, - ); - } - - late final _malloc_type_vallocPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Size, malloc_type_id_t)>>('malloc_type_valloc'); - late final _malloc_type_valloc = _malloc_type_vallocPtr - .asFunction Function(int, int)>(); - - ffi.Pointer malloc_type_aligned_alloc( - int alignment, - int size, - int type_id, - ) { - return _malloc_type_aligned_alloc( - alignment, - size, - type_id, - ); - } - - late final _malloc_type_aligned_allocPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Size, ffi.Size, - malloc_type_id_t)>>('malloc_type_aligned_alloc'); - late final _malloc_type_aligned_alloc = _malloc_type_aligned_allocPtr - .asFunction Function(int, int, int)>(); - - int malloc_type_posix_memalign( - ffi.Pointer> memptr, - int alignment, - int size, - int type_id, - ) { - return _malloc_type_posix_memalign( - memptr, - alignment, - size, - type_id, - ); - } - - late final _malloc_type_posix_memalignPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer>, ffi.Size, - ffi.Size, malloc_type_id_t)>>('malloc_type_posix_memalign'); - late final _malloc_type_posix_memalign = - _malloc_type_posix_memalignPtr.asFunction< - int Function(ffi.Pointer>, int, int, int)>(); - - ffi.Pointer malloc_type_zone_malloc( - ffi.Pointer zone, - int size, - int type_id, - ) { - return _malloc_type_zone_malloc( - zone, - size, - type_id, - ); - } - - late final _malloc_type_zone_mallocPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer, ffi.Size, - malloc_type_id_t)>>('malloc_type_zone_malloc'); - late final _malloc_type_zone_malloc = _malloc_type_zone_mallocPtr.asFunction< - ffi.Pointer Function(ffi.Pointer, int, int)>(); - - ffi.Pointer malloc_type_zone_calloc( - ffi.Pointer zone, - int count, - int size, - int type_id, - ) { - return _malloc_type_zone_calloc( - zone, - count, - size, - type_id, - ); - } - - late final _malloc_type_zone_callocPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer, ffi.Size, - ffi.Size, malloc_type_id_t)>>('malloc_type_zone_calloc'); - late final _malloc_type_zone_calloc = _malloc_type_zone_callocPtr.asFunction< - ffi.Pointer Function( - ffi.Pointer, int, int, int)>(); - - void malloc_type_zone_free( - ffi.Pointer zone, - ffi.Pointer ptr, - int type_id, - ) { - return _malloc_type_zone_free( - zone, - ptr, - type_id, - ); - } - - late final _malloc_type_zone_freePtr = _lookup< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer, ffi.Pointer, - malloc_type_id_t)>>('malloc_type_zone_free'); - late final _malloc_type_zone_free = _malloc_type_zone_freePtr.asFunction< - void Function(ffi.Pointer, ffi.Pointer, int)>(); - - ffi.Pointer malloc_type_zone_realloc( - ffi.Pointer zone, - ffi.Pointer ptr, - int size, - int type_id, - ) { - return _malloc_type_zone_realloc( - zone, - ptr, - size, - type_id, - ); - } - - late final _malloc_type_zone_reallocPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - malloc_type_id_t)>>('malloc_type_zone_realloc'); - late final _malloc_type_zone_realloc = - _malloc_type_zone_reallocPtr.asFunction< - ffi.Pointer Function( - ffi.Pointer, ffi.Pointer, int, int)>(); - - ffi.Pointer malloc_type_zone_valloc( - ffi.Pointer zone, - int size, - int type_id, - ) { - return _malloc_type_zone_valloc( - zone, - size, - type_id, - ); - } - - late final _malloc_type_zone_vallocPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer, ffi.Size, - malloc_type_id_t)>>('malloc_type_zone_valloc'); - late final _malloc_type_zone_valloc = _malloc_type_zone_vallocPtr.asFunction< - ffi.Pointer Function(ffi.Pointer, int, int)>(); - - ffi.Pointer malloc_type_zone_memalign( - ffi.Pointer zone, - int alignment, - int size, - int type_id, - ) { - return _malloc_type_zone_memalign( - zone, - alignment, - size, - type_id, - ); - } - - late final _malloc_type_zone_memalignPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer, ffi.Size, - ffi.Size, malloc_type_id_t)>>('malloc_type_zone_memalign'); - late final _malloc_type_zone_memalign = - _malloc_type_zone_memalignPtr.asFunction< - ffi.Pointer Function( - ffi.Pointer, int, int, int)>(); - - ffi.Pointer malloc( - int __size, - ) { - return _malloc( - __size, - ); - } - - late final _mallocPtr = - _lookup Function(ffi.Size)>>( - 'malloc'); - late final _malloc = - _mallocPtr.asFunction Function(int)>(); - - ffi.Pointer calloc( - int __count, - int __size, - ) { - return _calloc( - __count, - __size, - ); - } - - late final _callocPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Size, ffi.Size)>>('calloc'); - late final _calloc = - _callocPtr.asFunction Function(int, int)>(); - - void free( - ffi.Pointer arg0, - ) { - return _free( - arg0, - ); - } - - late final _freePtr = - _lookup)>>( - 'free'); - late final _free = - _freePtr.asFunction)>(); - - ffi.Pointer realloc( - ffi.Pointer __ptr, - int __size, - ) { - return _realloc( - __ptr, - __size, - ); - } - - late final _reallocPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, ffi.Size)>>('realloc'); - late final _realloc = _reallocPtr - .asFunction Function(ffi.Pointer, int)>(); - - ffi.Pointer reallocf( - ffi.Pointer __ptr, - int __size, - ) { - return _reallocf( - __ptr, - __size, - ); - } - - late final _reallocfPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, ffi.Size)>>('reallocf'); - late final _reallocf = _reallocfPtr - .asFunction Function(ffi.Pointer, int)>(); - - ffi.Pointer valloc( - int __size, - ) { - return _valloc( - __size, - ); - } - - late final _vallocPtr = - _lookup Function(ffi.Size)>>( - 'valloc'); - late final _valloc = - _vallocPtr.asFunction Function(int)>(); - - ffi.Pointer aligned_alloc( - int __alignment, - int __size, - ) { - return _aligned_alloc( - __alignment, - __size, - ); - } - - late final _aligned_allocPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Size, ffi.Size)>>('aligned_alloc'); - late final _aligned_alloc = - _aligned_allocPtr.asFunction Function(int, int)>(); - - int posix_memalign( - ffi.Pointer> __memptr, - int __alignment, - int __size, - ) { - return _posix_memalign( - __memptr, - __alignment, - __size, - ); - } - - late final _posix_memalignPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer>, ffi.Size, - ffi.Size)>>('posix_memalign'); - late final _posix_memalign = _posix_memalignPtr - .asFunction>, int, int)>(); - - void abort() { - return _abort(); - } - - late final _abortPtr = - _lookup>('abort'); - late final _abort = _abortPtr.asFunction(); - - int abs( - int arg0, - ) { - return _abs( - arg0, - ); - } - - late final _absPtr = - _lookup>('abs'); - late final _abs = _absPtr.asFunction(); - - int atexit( - ffi.Pointer> arg0, - ) { - return _atexit( - arg0, - ); - } - - late final _atexitPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer>)>>('atexit'); - late final _atexit = _atexitPtr.asFunction< - int Function(ffi.Pointer>)>(); - - int at_quick_exit( - ffi.Pointer> arg0, - ) { - return _at_quick_exit( - arg0, - ); - } - - late final _at_quick_exitPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer>)>>( - 'at_quick_exit'); - late final _at_quick_exit = _at_quick_exitPtr.asFunction< - int Function(ffi.Pointer>)>(); - - double atof( - ffi.Pointer arg0, - ) { - return _atof( - arg0, - ); - } - - late final _atofPtr = - _lookup)>>( - 'atof'); - late final _atof = - _atofPtr.asFunction)>(); - - int atoi( - ffi.Pointer arg0, - ) { - return _atoi( - arg0, - ); - } - - late final _atoiPtr = - _lookup)>>( - 'atoi'); - late final _atoi = _atoiPtr.asFunction)>(); - - int atol( - ffi.Pointer arg0, - ) { - return _atol( - arg0, - ); - } - - late final _atolPtr = - _lookup)>>( - 'atol'); - late final _atol = _atolPtr.asFunction)>(); - - int atoll( - ffi.Pointer arg0, - ) { - return _atoll( - arg0, - ); - } - - late final _atollPtr = - _lookup)>>( - 'atoll'); - late final _atoll = - _atollPtr.asFunction)>(); - - ffi.Pointer bsearch( - ffi.Pointer __key, - ffi.Pointer __base, - int __nel, - int __width, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Pointer)>> - __compar, - ) { - return _bsearch( - __key, - __base, - __nel, - __width, - __compar, - ); - } - - late final _bsearchPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - ffi.Size, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, - ffi.Pointer)>>)>>('bsearch'); - late final _bsearch = _bsearchPtr.asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - int, - int, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer, ffi.Pointer)>>)>(); - - div_t div( - int arg0, - int arg1, - ) { - return _div( - arg0, - arg1, - ); - } - - late final _divPtr = - _lookup>('div'); - late final _div = _divPtr.asFunction(); - - void exit( - int arg0, - ) { - return _exit( - arg0, - ); - } - - late final _exitPtr = - _lookup>('exit'); - late final _exit = _exitPtr.asFunction(); - - ffi.Pointer getenv( - ffi.Pointer arg0, - ) { - return _getenv( - arg0, - ); - } - - late final _getenvPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer)>>('getenv'); - late final _getenv = _getenvPtr - .asFunction Function(ffi.Pointer)>(); - - int labs( - int arg0, - ) { - return _labs( - arg0, - ); - } - - late final _labsPtr = - _lookup>('labs'); - late final _labs = _labsPtr.asFunction(); - - ldiv_t ldiv( - int arg0, - int arg1, - ) { - return _ldiv( - arg0, - arg1, - ); - } - - late final _ldivPtr = - _lookup>('ldiv'); - late final _ldiv = _ldivPtr.asFunction(); - - int llabs( - int arg0, - ) { - return _llabs( - arg0, - ); - } - - late final _llabsPtr = - _lookup>('llabs'); - late final _llabs = _llabsPtr.asFunction(); - - lldiv_t lldiv( - int arg0, - int arg1, - ) { - return _lldiv( - arg0, - arg1, - ); - } - - late final _lldivPtr = - _lookup>( - 'lldiv'); - late final _lldiv = _lldivPtr.asFunction(); - - int mblen( - ffi.Pointer __s, - int __n, - ) { - return _mblen( - __s, - __n, - ); - } - - late final _mblenPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Size)>>('mblen'); - late final _mblen = - _mblenPtr.asFunction, int)>(); - - int mbstowcs( - ffi.Pointer arg0, - ffi.Pointer arg1, - int arg2, - ) { - return _mbstowcs( - arg0, - arg1, - arg2, - ); - } - - late final _mbstowcsPtr = _lookup< - ffi.NativeFunction< - ffi.Size Function(ffi.Pointer, ffi.Pointer, - ffi.Size)>>('mbstowcs'); - late final _mbstowcs = _mbstowcsPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); - - int mbtowc( - ffi.Pointer arg0, - ffi.Pointer arg1, - int arg2, - ) { - return _mbtowc( - arg0, - arg1, - arg2, - ); - } - - late final _mbtowcPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Pointer, - ffi.Size)>>('mbtowc'); - late final _mbtowc = _mbtowcPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); - - void qsort( - ffi.Pointer __base, - int __nel, - int __width, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Pointer)>> - __compar, - ) { - return _qsort( - __base, - __nel, - __width, - __compar, - ); - } - - late final _qsortPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, - ffi.Size, - ffi.Size, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, - ffi.Pointer)>>)>>('qsort'); - late final _qsort = _qsortPtr.asFunction< - void Function( - ffi.Pointer, - int, - int, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer, ffi.Pointer)>>)>(); - - void quick_exit( - int arg0, - ) { - return _quick_exit( - arg0, - ); - } - - late final _quick_exitPtr = - _lookup>('quick_exit'); - late final _quick_exit = _quick_exitPtr.asFunction(); - - int rand() { - return _rand(); - } - - late final _randPtr = _lookup>('rand'); - late final _rand = _randPtr.asFunction(); - - void srand( - int arg0, - ) { - return _srand( - arg0, - ); - } - - late final _srandPtr = - _lookup>('srand'); - late final _srand = _srandPtr.asFunction(); - - double strtod( - ffi.Pointer arg0, - ffi.Pointer> arg1, - ) { - return _strtod( - arg0, - arg1, - ); - } - - late final _strtodPtr = _lookup< - ffi.NativeFunction< - ffi.Double Function(ffi.Pointer, - ffi.Pointer>)>>('strtod'); - late final _strtod = _strtodPtr.asFunction< - double Function( - ffi.Pointer, ffi.Pointer>)>(); - - double strtof( - ffi.Pointer arg0, - ffi.Pointer> arg1, - ) { - return _strtof( - arg0, - arg1, - ); - } - - late final _strtofPtr = _lookup< - ffi.NativeFunction< - ffi.Float Function(ffi.Pointer, - ffi.Pointer>)>>('strtof'); - late final _strtof = _strtofPtr.asFunction< - double Function( - ffi.Pointer, ffi.Pointer>)>(); - - int strtol( - ffi.Pointer __str, - ffi.Pointer> __endptr, - int __base, - ) { - return _strtol( - __str, - __endptr, - __base, - ); - } - - late final _strtolPtr = _lookup< - ffi.NativeFunction< - ffi.Long Function(ffi.Pointer, - ffi.Pointer>, ffi.Int)>>('strtol'); - late final _strtol = _strtolPtr.asFunction< - int Function( - ffi.Pointer, ffi.Pointer>, int)>(); - - int strtoll( - ffi.Pointer __str, - ffi.Pointer> __endptr, - int __base, - ) { - return _strtoll( - __str, - __endptr, - __base, - ); - } - - late final _strtollPtr = _lookup< - ffi.NativeFunction< - ffi.LongLong Function(ffi.Pointer, - ffi.Pointer>, ffi.Int)>>('strtoll'); - late final _strtoll = _strtollPtr.asFunction< - int Function( - ffi.Pointer, ffi.Pointer>, int)>(); - - int strtoul( - ffi.Pointer __str, - ffi.Pointer> __endptr, - int __base, - ) { - return _strtoul( - __str, - __endptr, - __base, - ); - } - - late final _strtoulPtr = _lookup< - ffi.NativeFunction< - ffi.UnsignedLong Function(ffi.Pointer, - ffi.Pointer>, ffi.Int)>>('strtoul'); - late final _strtoul = _strtoulPtr.asFunction< - int Function( - ffi.Pointer, ffi.Pointer>, int)>(); - - int strtoull( - ffi.Pointer __str, - ffi.Pointer> __endptr, - int __base, - ) { - return _strtoull( - __str, - __endptr, - __base, - ); - } - - late final _strtoullPtr = _lookup< - ffi.NativeFunction< - ffi.UnsignedLongLong Function(ffi.Pointer, - ffi.Pointer>, ffi.Int)>>('strtoull'); - late final _strtoull = _strtoullPtr.asFunction< - int Function( - ffi.Pointer, ffi.Pointer>, int)>(); - - int system( - ffi.Pointer arg0, - ) { - return _system( - arg0, - ); - } - - late final _systemPtr = - _lookup)>>( - 'system'); - late final _system = - _systemPtr.asFunction)>(); - - int wcstombs( - ffi.Pointer arg0, - ffi.Pointer arg1, - int arg2, - ) { - return _wcstombs( - arg0, - arg1, - arg2, - ); - } - - late final _wcstombsPtr = _lookup< - ffi.NativeFunction< - ffi.Size Function(ffi.Pointer, ffi.Pointer, - ffi.Size)>>('wcstombs'); - late final _wcstombs = _wcstombsPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); - - int wctomb( - ffi.Pointer arg0, - int arg1, - ) { - return _wctomb( - arg0, - arg1, - ); - } - - late final _wctombPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.WChar)>>('wctomb'); - late final _wctomb = - _wctombPtr.asFunction, int)>(); - - void _Exit( - int arg0, - ) { - return __Exit( - arg0, - ); - } - - late final __ExitPtr = - _lookup>('_Exit'); - late final __Exit = __ExitPtr.asFunction(); - - int a64l( - ffi.Pointer arg0, - ) { - return _a64l( - arg0, - ); - } - - late final _a64lPtr = - _lookup)>>( - 'a64l'); - late final _a64l = _a64lPtr.asFunction)>(); - - double drand48() { - return _drand48(); - } - - late final _drand48Ptr = - _lookup>('drand48'); - late final _drand48 = _drand48Ptr.asFunction(); - - ffi.Pointer ecvt( - double arg0, - int arg1, - ffi.Pointer arg2, - ffi.Pointer arg3, - ) { - return _ecvt( - arg0, - arg1, - arg2, - arg3, - ); - } - - late final _ecvtPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Double, ffi.Int, - ffi.Pointer, ffi.Pointer)>>('ecvt'); - late final _ecvt = _ecvtPtr.asFunction< - ffi.Pointer Function( - double, int, ffi.Pointer, ffi.Pointer)>(); - - double erand48( - ffi.Pointer arg0, - ) { - return _erand48( - arg0, - ); - } - - late final _erand48Ptr = _lookup< - ffi.NativeFunction< - ffi.Double Function(ffi.Pointer)>>('erand48'); - late final _erand48 = - _erand48Ptr.asFunction)>(); - - ffi.Pointer fcvt( - double arg0, - int arg1, - ffi.Pointer arg2, - ffi.Pointer arg3, - ) { - return _fcvt( - arg0, - arg1, - arg2, - arg3, - ); - } - - late final _fcvtPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Double, ffi.Int, - ffi.Pointer, ffi.Pointer)>>('fcvt'); - late final _fcvt = _fcvtPtr.asFunction< - ffi.Pointer Function( - double, int, ffi.Pointer, ffi.Pointer)>(); - - ffi.Pointer gcvt( - double arg0, - int arg1, - ffi.Pointer arg2, - ) { - return _gcvt( - arg0, - arg1, - arg2, - ); - } - - late final _gcvtPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Double, ffi.Int, ffi.Pointer)>>('gcvt'); - late final _gcvt = _gcvtPtr.asFunction< - ffi.Pointer Function(double, int, ffi.Pointer)>(); - - int getsubopt( - ffi.Pointer> arg0, - ffi.Pointer> arg1, - ffi.Pointer> arg2, - ) { - return _getsubopt( - arg0, - arg1, - arg2, - ); - } - - late final _getsuboptPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer>, - ffi.Pointer>, - ffi.Pointer>)>>('getsubopt'); - late final _getsubopt = _getsuboptPtr.asFunction< - int Function( - ffi.Pointer>, - ffi.Pointer>, - ffi.Pointer>)>(); - - int grantpt( - int arg0, - ) { - return _grantpt( - arg0, - ); - } - - late final _grantptPtr = - _lookup>('grantpt'); - late final _grantpt = _grantptPtr.asFunction(); - - ffi.Pointer initstate( - int arg0, - ffi.Pointer arg1, - int arg2, - ) { - return _initstate( - arg0, - arg1, - arg2, - ); - } - - late final _initstatePtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.UnsignedInt, ffi.Pointer, ffi.Size)>>('initstate'); - late final _initstate = _initstatePtr.asFunction< - ffi.Pointer Function(int, ffi.Pointer, int)>(); - - int jrand48( - ffi.Pointer arg0, - ) { - return _jrand48( - arg0, - ); - } - - late final _jrand48Ptr = _lookup< - ffi.NativeFunction< - ffi.Long Function(ffi.Pointer)>>('jrand48'); - late final _jrand48 = - _jrand48Ptr.asFunction)>(); - - ffi.Pointer l64a( - int arg0, - ) { - return _l64a( - arg0, - ); - } - - late final _l64aPtr = - _lookup Function(ffi.Long)>>( - 'l64a'); - late final _l64a = _l64aPtr.asFunction Function(int)>(); - - void lcong48( - ffi.Pointer arg0, - ) { - return _lcong48( - arg0, - ); - } - - late final _lcong48Ptr = _lookup< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer)>>('lcong48'); - late final _lcong48 = - _lcong48Ptr.asFunction)>(); - - int lrand48() { - return _lrand48(); - } - - late final _lrand48Ptr = - _lookup>('lrand48'); - late final _lrand48 = _lrand48Ptr.asFunction(); - - ffi.Pointer mktemp( - ffi.Pointer arg0, - ) { - return _mktemp( - arg0, - ); - } - - late final _mktempPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer)>>('mktemp'); - late final _mktemp = _mktempPtr - .asFunction Function(ffi.Pointer)>(); - - int mkstemp( - ffi.Pointer arg0, - ) { - return _mkstemp( - arg0, - ); - } - - late final _mkstempPtr = - _lookup)>>( - 'mkstemp'); - late final _mkstemp = - _mkstempPtr.asFunction)>(); - - int mrand48() { - return _mrand48(); - } - - late final _mrand48Ptr = - _lookup>('mrand48'); - late final _mrand48 = _mrand48Ptr.asFunction(); - - int nrand48( - ffi.Pointer arg0, - ) { - return _nrand48( - arg0, - ); - } - - late final _nrand48Ptr = _lookup< - ffi.NativeFunction< - ffi.Long Function(ffi.Pointer)>>('nrand48'); - late final _nrand48 = - _nrand48Ptr.asFunction)>(); - - int posix_openpt( - int arg0, - ) { - return _posix_openpt( - arg0, - ); - } - - late final _posix_openptPtr = - _lookup>('posix_openpt'); - late final _posix_openpt = _posix_openptPtr.asFunction(); - - ffi.Pointer ptsname( - int arg0, - ) { - return _ptsname( - arg0, - ); - } - - late final _ptsnamePtr = - _lookup Function(ffi.Int)>>( - 'ptsname'); - late final _ptsname = - _ptsnamePtr.asFunction Function(int)>(); - - int ptsname_r( - int fildes, - ffi.Pointer buffer, - int buflen, - ) { - return _ptsname_r( - fildes, - buffer, - buflen, - ); - } - - late final _ptsname_rPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function( - ffi.Int, ffi.Pointer, ffi.Size)>>('ptsname_r'); - late final _ptsname_r = - _ptsname_rPtr.asFunction, int)>(); - - int putenv( - ffi.Pointer arg0, - ) { - return _putenv( - arg0, - ); - } - - late final _putenvPtr = - _lookup)>>( - 'putenv'); - late final _putenv = - _putenvPtr.asFunction)>(); - - int random() { - return _random(); - } - - late final _randomPtr = - _lookup>('random'); - late final _random = _randomPtr.asFunction(); - - int rand_r( - ffi.Pointer arg0, - ) { - return _rand_r( - arg0, - ); - } - - late final _rand_rPtr = _lookup< - ffi.NativeFunction)>>( - 'rand_r'); - late final _rand_r = - _rand_rPtr.asFunction)>(); - - ffi.Pointer realpath( - ffi.Pointer arg0, - ffi.Pointer arg1, - ) { - return _realpath( - arg0, - arg1, - ); - } - - late final _realpathPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, ffi.Pointer)>>('realpath'); - late final _realpath = _realpathPtr.asFunction< - ffi.Pointer Function( - ffi.Pointer, ffi.Pointer)>(); - - ffi.Pointer seed48( - ffi.Pointer arg0, - ) { - return _seed48( - arg0, - ); - } - - late final _seed48Ptr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer)>>('seed48'); - late final _seed48 = _seed48Ptr.asFunction< - ffi.Pointer Function( - ffi.Pointer)>(); - - int setenv( - ffi.Pointer __name, - ffi.Pointer __value, - int __overwrite, - ) { - return _setenv( - __name, - __value, - __overwrite, - ); - } - - late final _setenvPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Pointer, - ffi.Int)>>('setenv'); - late final _setenv = _setenvPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); - - void setkey( - ffi.Pointer arg0, - ) { - return _setkey( - arg0, - ); - } - - late final _setkeyPtr = - _lookup)>>( - 'setkey'); - late final _setkey = - _setkeyPtr.asFunction)>(); - - ffi.Pointer setstate( - ffi.Pointer arg0, - ) { - return _setstate( - arg0, - ); - } - - late final _setstatePtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer)>>('setstate'); - late final _setstate = _setstatePtr - .asFunction Function(ffi.Pointer)>(); - - void srand48( - int arg0, - ) { - return _srand48( - arg0, - ); - } - - late final _srand48Ptr = - _lookup>('srand48'); - late final _srand48 = _srand48Ptr.asFunction(); - - void srandom( - int arg0, - ) { - return _srandom( - arg0, - ); - } - - late final _srandomPtr = - _lookup>( - 'srandom'); - late final _srandom = _srandomPtr.asFunction(); - - int unlockpt( - int arg0, - ) { - return _unlockpt( - arg0, - ); - } - - late final _unlockptPtr = - _lookup>('unlockpt'); - late final _unlockpt = _unlockptPtr.asFunction(); - - int unsetenv( - ffi.Pointer arg0, - ) { - return _unsetenv( - arg0, - ); - } - - late final _unsetenvPtr = - _lookup)>>( - 'unsetenv'); - late final _unsetenv = - _unsetenvPtr.asFunction)>(); - - int arc4random() { - return _arc4random(); - } - - late final _arc4randomPtr = - _lookup>('arc4random'); - late final _arc4random = _arc4randomPtr.asFunction(); - - void arc4random_addrandom( - ffi.Pointer arg0, - int arg1, - ) { - return _arc4random_addrandom( - arg0, - arg1, - ); - } - - late final _arc4random_addrandomPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, ffi.Int)>>('arc4random_addrandom'); - late final _arc4random_addrandom = _arc4random_addrandomPtr - .asFunction, int)>(); - - void arc4random_buf( - ffi.Pointer __buf, - int __nbytes, - ) { - return _arc4random_buf( - __buf, - __nbytes, - ); - } - - late final _arc4random_bufPtr = _lookup< - ffi - .NativeFunction, ffi.Size)>>( - 'arc4random_buf'); - late final _arc4random_buf = _arc4random_bufPtr - .asFunction, int)>(); - - void arc4random_stir() { - return _arc4random_stir(); - } - - late final _arc4random_stirPtr = - _lookup>('arc4random_stir'); - late final _arc4random_stir = - _arc4random_stirPtr.asFunction(); - - int arc4random_uniform( - int __upper_bound, - ) { - return _arc4random_uniform( - __upper_bound, - ); - } - - late final _arc4random_uniformPtr = - _lookup>( - 'arc4random_uniform'); - late final _arc4random_uniform = - _arc4random_uniformPtr.asFunction(); - - ffi.Pointer cgetcap( - ffi.Pointer arg0, - ffi.Pointer arg1, - int arg2, - ) { - return _cgetcap( - arg0, - arg1, - arg2, - ); - } - - late final _cgetcapPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer, - ffi.Pointer, ffi.Int)>>('cgetcap'); - late final _cgetcap = _cgetcapPtr.asFunction< - ffi.Pointer Function( - ffi.Pointer, ffi.Pointer, int)>(); - - int cgetclose() { - return _cgetclose(); - } - - late final _cgetclosePtr = - _lookup>('cgetclose'); - late final _cgetclose = _cgetclosePtr.asFunction(); - - int cgetent( - ffi.Pointer> arg0, - ffi.Pointer> arg1, - ffi.Pointer arg2, - ) { - return _cgetent( - arg0, - arg1, - arg2, - ); - } - - late final _cgetentPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer>, - ffi.Pointer>, - ffi.Pointer)>>('cgetent'); - late final _cgetent = _cgetentPtr.asFunction< - int Function(ffi.Pointer>, - ffi.Pointer>, ffi.Pointer)>(); - - int cgetfirst( - ffi.Pointer> arg0, - ffi.Pointer> arg1, - ) { - return _cgetfirst( - arg0, - arg1, - ); - } - - late final _cgetfirstPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer>, - ffi.Pointer>)>>('cgetfirst'); - late final _cgetfirst = _cgetfirstPtr.asFunction< - int Function(ffi.Pointer>, - ffi.Pointer>)>(); - - int cgetmatch( - ffi.Pointer arg0, - ffi.Pointer arg1, - ) { - return _cgetmatch( - arg0, - arg1, - ); - } - - late final _cgetmatchPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer, ffi.Pointer)>>('cgetmatch'); - late final _cgetmatch = _cgetmatchPtr - .asFunction, ffi.Pointer)>(); - - int cgetnext( - ffi.Pointer> arg0, - ffi.Pointer> arg1, - ) { - return _cgetnext( - arg0, - arg1, - ); - } - - late final _cgetnextPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer>, - ffi.Pointer>)>>('cgetnext'); - late final _cgetnext = _cgetnextPtr.asFunction< - int Function(ffi.Pointer>, - ffi.Pointer>)>(); - - int cgetnum( - ffi.Pointer arg0, - ffi.Pointer arg1, - ffi.Pointer arg2, - ) { - return _cgetnum( - arg0, - arg1, - arg2, - ); - } - - late final _cgetnumPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer)>>('cgetnum'); - late final _cgetnum = _cgetnumPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer)>(); - - int cgetset( - ffi.Pointer arg0, - ) { - return _cgetset( - arg0, - ); - } - - late final _cgetsetPtr = - _lookup)>>( - 'cgetset'); - late final _cgetset = - _cgetsetPtr.asFunction)>(); - - int cgetstr( - ffi.Pointer arg0, - ffi.Pointer arg1, - ffi.Pointer> arg2, - ) { - return _cgetstr( - arg0, - arg1, - arg2, - ); - } - - late final _cgetstrPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer>)>>('cgetstr'); - late final _cgetstr = _cgetstrPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer>)>(); - - int cgetustr( - ffi.Pointer arg0, - ffi.Pointer arg1, - ffi.Pointer> arg2, - ) { - return _cgetustr( - arg0, - arg1, - arg2, - ); - } - - late final _cgetustrPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer>)>>('cgetustr'); - late final _cgetustr = _cgetustrPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer>)>(); - - int daemon( - int arg0, - int arg1, - ) { - return _daemon( - arg0, - arg1, - ); - } - - late final _daemonPtr = - _lookup>('daemon'); - late final _daemon = _daemonPtr.asFunction(); - - ffi.Pointer devname( - int arg0, - int arg1, - ) { - return _devname( - arg0, - arg1, - ); - } - - late final _devnamePtr = _lookup< - ffi.NativeFunction Function(dev_t, mode_t)>>( - 'devname'); - late final _devname = - _devnamePtr.asFunction Function(int, int)>(); - - ffi.Pointer devname_r( - int arg0, - int arg1, - ffi.Pointer buf, - int len, - ) { - return _devname_r( - arg0, - arg1, - buf, - len, - ); - } - - late final _devname_rPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - dev_t, mode_t, ffi.Pointer, ffi.Int)>>('devname_r'); - late final _devname_r = _devname_rPtr.asFunction< - ffi.Pointer Function(int, int, ffi.Pointer, int)>(); - - ffi.Pointer getbsize( - ffi.Pointer arg0, - ffi.Pointer arg1, - ) { - return _getbsize( - arg0, - arg1, - ); - } - - late final _getbsizePtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, ffi.Pointer)>>('getbsize'); - late final _getbsize = _getbsizePtr.asFunction< - ffi.Pointer Function( - ffi.Pointer, ffi.Pointer)>(); - - int getloadavg( - ffi.Pointer arg0, - int arg1, - ) { - return _getloadavg( - arg0, - arg1, - ); - } - - late final _getloadavgPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Int)>>('getloadavg'); - late final _getloadavg = - _getloadavgPtr.asFunction, int)>(); - - ffi.Pointer getprogname() { - return _getprogname(); - } - - late final _getprognamePtr = - _lookup Function()>>( - 'getprogname'); - late final _getprogname = - _getprognamePtr.asFunction Function()>(); - - void setprogname( - ffi.Pointer arg0, - ) { - return _setprogname( - arg0, - ); - } - - late final _setprognamePtr = - _lookup)>>( - 'setprogname'); - late final _setprogname = - _setprognamePtr.asFunction)>(); - - int heapsort( - ffi.Pointer __base, - int __nel, - int __width, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Pointer)>> - __compar, - ) { - return _heapsort( - __base, - __nel, - __width, - __compar, - ); - } - - late final _heapsortPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer, - ffi.Size, - ffi.Size, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, - ffi.Pointer)>>)>>('heapsort'); - late final _heapsort = _heapsortPtr.asFunction< - int Function( - ffi.Pointer, - int, - int, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer, ffi.Pointer)>>)>(); - - int mergesort( - ffi.Pointer __base, - int __nel, - int __width, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Pointer)>> - __compar, - ) { - return _mergesort( - __base, - __nel, - __width, - __compar, - ); - } - - late final _mergesortPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer, - ffi.Size, - ffi.Size, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, - ffi.Pointer)>>)>>('mergesort'); - late final _mergesort = _mergesortPtr.asFunction< - int Function( - ffi.Pointer, - int, - int, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer, ffi.Pointer)>>)>(); - - void psort( - ffi.Pointer __base, - int __nel, - int __width, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Pointer)>> - __compar, - ) { - return _psort( - __base, - __nel, - __width, - __compar, - ); - } - - late final _psortPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, - ffi.Size, - ffi.Size, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, - ffi.Pointer)>>)>>('psort'); - late final _psort = _psortPtr.asFunction< - void Function( - ffi.Pointer, - int, - int, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer, ffi.Pointer)>>)>(); - - void psort_r( - ffi.Pointer __base, - int __nel, - int __width, - ffi.Pointer arg3, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer)>> - __compar, - ) { - return _psort_r( - __base, - __nel, - __width, - arg3, - __compar, - ); - } - - late final _psort_rPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, - ffi.Size, - ffi.Size, - ffi.Pointer, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer)>>)>>('psort_r'); - late final _psort_r = _psort_rPtr.asFunction< - void Function( - ffi.Pointer, - int, - int, - ffi.Pointer, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer)>>)>(); - - void qsort_r( - ffi.Pointer __base, - int __nel, - int __width, - ffi.Pointer arg3, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer)>> - __compar, - ) { - return _qsort_r( - __base, - __nel, - __width, - arg3, - __compar, - ); - } - - late final _qsort_rPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, - ffi.Size, - ffi.Size, - ffi.Pointer, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer)>>)>>('qsort_r'); - late final _qsort_r = _qsort_rPtr.asFunction< - void Function( - ffi.Pointer, - int, - int, - ffi.Pointer, - ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer)>>)>(); - - int radixsort( - ffi.Pointer> __base, - int __nel, - ffi.Pointer __table, - int __endbyte, - ) { - return _radixsort( - __base, - __nel, - __table, - __endbyte, - ); - } - - late final _radixsortPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer>, ffi.Int, - ffi.Pointer, ffi.UnsignedInt)>>('radixsort'); - late final _radixsort = _radixsortPtr.asFunction< - int Function(ffi.Pointer>, int, - ffi.Pointer, int)>(); - - int rpmatch( - ffi.Pointer arg0, - ) { - return _rpmatch( - arg0, - ); - } - - late final _rpmatchPtr = - _lookup)>>( - 'rpmatch'); - late final _rpmatch = - _rpmatchPtr.asFunction)>(); - - int sradixsort( - ffi.Pointer> __base, - int __nel, - ffi.Pointer __table, - int __endbyte, - ) { - return _sradixsort( - __base, - __nel, - __table, - __endbyte, - ); - } - - late final _sradixsortPtr = _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer>, ffi.Int, - ffi.Pointer, ffi.UnsignedInt)>>('sradixsort'); - late final _sradixsort = _sradixsortPtr.asFunction< - int Function(ffi.Pointer>, int, - ffi.Pointer, int)>(); - - void sranddev() { - return _sranddev(); - } - - late final _sranddevPtr = - _lookup>('sranddev'); - late final _sranddev = _sranddevPtr.asFunction(); - - void srandomdev() { - return _srandomdev(); - } - - late final _srandomdevPtr = - _lookup>('srandomdev'); - late final _srandomdev = _srandomdevPtr.asFunction(); - - int strtonum( - ffi.Pointer __numstr, - int __minval, - int __maxval, - ffi.Pointer> __errstrp, - ) { - return _strtonum( - __numstr, - __minval, - __maxval, - __errstrp, - ); - } - - late final _strtonumPtr = _lookup< - ffi.NativeFunction< - ffi.LongLong Function(ffi.Pointer, ffi.LongLong, - ffi.LongLong, ffi.Pointer>)>>('strtonum'); - late final _strtonum = _strtonumPtr.asFunction< - int Function(ffi.Pointer, int, int, - ffi.Pointer>)>(); - - int strtoq( - ffi.Pointer __str, - ffi.Pointer> __endptr, - int __base, - ) { - return _strtoq( - __str, - __endptr, - __base, - ); - } - - late final _strtoqPtr = _lookup< - ffi.NativeFunction< - ffi.LongLong Function(ffi.Pointer, - ffi.Pointer>, ffi.Int)>>('strtoq'); - late final _strtoq = _strtoqPtr.asFunction< - int Function( - ffi.Pointer, ffi.Pointer>, int)>(); - - int strtouq( - ffi.Pointer __str, - ffi.Pointer> __endptr, - int __base, - ) { - return _strtouq( - __str, - __endptr, - __base, - ); - } - - late final _strtouqPtr = _lookup< - ffi.NativeFunction< - ffi.UnsignedLongLong Function(ffi.Pointer, - ffi.Pointer>, ffi.Int)>>('strtouq'); - late final _strtouq = _strtouqPtr.asFunction< - int Function( - ffi.Pointer, ffi.Pointer>, int)>(); - - late final ffi.Pointer> _suboptarg = - _lookup>('suboptarg'); - - ffi.Pointer get suboptarg => _suboptarg.value; - - set suboptarg(ffi.Pointer value) => _suboptarg.value = value; - - void protect( - protect_func fn, - ffi.Pointer tun_interface, - int fd, - ) { - return _protect( - fn, - tun_interface, - fd, - ); - } - - late final _protectPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function( - protect_func, ffi.Pointer, ffi.Int)>>('protect'); - late final _protect = _protectPtr - .asFunction, int)>(); - - ffi.Pointer resolve_process( - resolve_process_func fn, - ffi.Pointer tun_interface, - int protocol, - ffi.Pointer source, - ffi.Pointer target, - int uid, - ) { - return _resolve_process( - fn, - tun_interface, - protocol, - source, - target, - uid, - ); - } - - late final _resolve_processPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - resolve_process_func, - ffi.Pointer, - ffi.Int, - ffi.Pointer, - ffi.Pointer, - ffi.Int)>>('resolve_process'); - late final _resolve_process = _resolve_processPtr.asFunction< - ffi.Pointer Function( - resolve_process_func, - ffi.Pointer, - int, - ffi.Pointer, - ffi.Pointer, - int)>(); - - void release_object( - release_object_func fn, - ffi.Pointer obj, - ) { - return _release_object( - fn, - obj, - ); - } - - late final _release_objectPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function( - release_object_func, ffi.Pointer)>>('release_object'); - late final _release_object = _release_objectPtr - .asFunction)>(); - - void registerCallbacks( - protect_func markSocketFunc, - resolve_process_func resolveProcessFunc, - release_object_func releaseObjectFunc, - ) { - return _registerCallbacks( - markSocketFunc, - resolveProcessFunc, - releaseObjectFunc, - ); - } - - late final _registerCallbacksPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function(protect_func, resolve_process_func, - release_object_func)>>('registerCallbacks'); - late final _registerCallbacks = _registerCallbacksPtr.asFunction< - void Function(protect_func, resolve_process_func, release_object_func)>(); - - void initNativeApiBridge( - ffi.Pointer api, - ) { - return _initNativeApiBridge( - api, - ); - } - - late final _initNativeApiBridgePtr = - _lookup)>>( - 'initNativeApiBridge'); - late final _initNativeApiBridge = _initNativeApiBridgePtr - .asFunction)>(); - - void attachMessagePort( - int mPort, - ) { - return _attachMessagePort( - mPort, - ); - } - - late final _attachMessagePortPtr = - _lookup>( - 'attachMessagePort'); - late final _attachMessagePort = - _attachMessagePortPtr.asFunction(); - - ffi.Pointer getTraffic() { - return _getTraffic(); - } - - late final _getTrafficPtr = - _lookup Function()>>( - 'getTraffic'); - late final _getTraffic = - _getTrafficPtr.asFunction Function()>(); - - ffi.Pointer getTotalTraffic() { - return _getTotalTraffic(); - } - - late final _getTotalTrafficPtr = - _lookup Function()>>( - 'getTotalTraffic'); - late final _getTotalTraffic = - _getTotalTrafficPtr.asFunction Function()>(); - - void freeCString( - ffi.Pointer s, - ) { - return _freeCString( - s, - ); - } - - late final _freeCStringPtr = - _lookup)>>( - 'freeCString'); - late final _freeCString = - _freeCStringPtr.asFunction)>(); - - void invokeAction( - ffi.Pointer paramsChar, - int port, - ) { - return _invokeAction( - paramsChar, - port, - ); - } - - late final _invokeActionPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, ffi.LongLong)>>('invokeAction'); - late final _invokeAction = - _invokeActionPtr.asFunction, int)>(); - - ffi.Pointer getConfig( - ffi.Pointer s, - ) { - return _getConfig( - s, - ); - } - - late final _getConfigPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer)>>('getConfig'); - late final _getConfig = _getConfigPtr - .asFunction Function(ffi.Pointer)>(); - - void startListener() { - return _startListener(); - } - - late final _startListenerPtr = - _lookup>('startListener'); - late final _startListener = _startListenerPtr.asFunction(); - - void stopListener() { - return _stopListener(); - } - - late final _stopListenerPtr = - _lookup>('stopListener'); - late final _stopListener = _stopListenerPtr.asFunction(); - - void quickStart( - ffi.Pointer initParamsChar, - ffi.Pointer paramsChar, - ffi.Pointer stateParamsChar, - int port, - ) { - return _quickStart( - initParamsChar, - paramsChar, - stateParamsChar, - port, - ); - } - - late final _quickStartPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer, ffi.LongLong)>>('quickStart'); - late final _quickStart = _quickStartPtr.asFunction< - void Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer, int)>(); - - int startTUN( - int fd, - ffi.Pointer callback, - ) { - return _startTUN( - fd, - callback, - ); - } - - late final _startTUNPtr = _lookup< - ffi.NativeFunction)>>( - 'startTUN'); - late final _startTUN = - _startTUNPtr.asFunction)>(); - - ffi.Pointer getRunTime() { - return _getRunTime(); - } - - late final _getRunTimePtr = - _lookup Function()>>( - 'getRunTime'); - late final _getRunTime = - _getRunTimePtr.asFunction Function()>(); - - void stopTun() { - return _stopTun(); - } - - late final _stopTunPtr = - _lookup>('stopTun'); - late final _stopTun = _stopTunPtr.asFunction(); - - ffi.Pointer getCurrentProfileName() { - return _getCurrentProfileName(); - } - - late final _getCurrentProfileNamePtr = - _lookup Function()>>( - 'getCurrentProfileName'); - late final _getCurrentProfileName = - _getCurrentProfileNamePtr.asFunction Function()>(); - - ffi.Pointer getAndroidVpnOptions() { - return _getAndroidVpnOptions(); - } - - late final _getAndroidVpnOptionsPtr = - _lookup Function()>>( - 'getAndroidVpnOptions'); - late final _getAndroidVpnOptions = - _getAndroidVpnOptionsPtr.asFunction Function()>(); - - void setState( - ffi.Pointer s, - ) { - return _setState( - s, - ); - } - - late final _setStatePtr = - _lookup)>>( - 'setState'); - late final _setState = - _setStatePtr.asFunction)>(); - - void updateDns( - ffi.Pointer s, - ) { - return _updateDns( - s, - ); - } - - late final _updateDnsPtr = - _lookup)>>( - 'updateDns'); - late final _updateDns = - _updateDnsPtr.asFunction)>(); -} - -typedef __int8_t = ffi.SignedChar; -typedef Dart__int8_t = int; -typedef __uint8_t = ffi.UnsignedChar; -typedef Dart__uint8_t = int; -typedef __int16_t = ffi.Short; -typedef Dart__int16_t = int; -typedef __uint16_t = ffi.UnsignedShort; -typedef Dart__uint16_t = int; -typedef __int32_t = ffi.Int; -typedef Dart__int32_t = int; -typedef __uint32_t = ffi.UnsignedInt; -typedef Dart__uint32_t = int; -typedef __int64_t = ffi.LongLong; -typedef Dart__int64_t = int; -typedef __uint64_t = ffi.UnsignedLongLong; -typedef Dart__uint64_t = int; -typedef __darwin_intptr_t = ffi.Long; -typedef Dart__darwin_intptr_t = int; -typedef __darwin_natural_t = ffi.UnsignedInt; -typedef Dart__darwin_natural_t = int; -typedef __darwin_ct_rune_t = ffi.Int; -typedef Dart__darwin_ct_rune_t = int; - -final class __mbstate_t extends ffi.Union { - @ffi.Array.multi([128]) - external ffi.Array __mbstate8; - - @ffi.LongLong() - external int _mbstateL; -} - -typedef __darwin_mbstate_t = __mbstate_t; -typedef __darwin_ptrdiff_t = ffi.Long; -typedef Dart__darwin_ptrdiff_t = int; -typedef __darwin_size_t = ffi.UnsignedLong; -typedef Dart__darwin_size_t = int; -typedef __builtin_va_list = ffi.Pointer; -typedef __darwin_va_list = __builtin_va_list; -typedef __darwin_wchar_t = ffi.Int; -typedef Dart__darwin_wchar_t = int; -typedef __darwin_rune_t = __darwin_wchar_t; -typedef __darwin_wint_t = ffi.Int; -typedef Dart__darwin_wint_t = int; -typedef __darwin_clock_t = ffi.UnsignedLong; -typedef Dart__darwin_clock_t = int; -typedef __darwin_socklen_t = __uint32_t; -typedef __darwin_ssize_t = ffi.Long; -typedef Dart__darwin_ssize_t = int; -typedef __darwin_time_t = ffi.Long; -typedef Dart__darwin_time_t = int; -typedef __darwin_blkcnt_t = __int64_t; -typedef __darwin_blksize_t = __int32_t; -typedef __darwin_dev_t = __int32_t; -typedef __darwin_fsblkcnt_t = ffi.UnsignedInt; -typedef Dart__darwin_fsblkcnt_t = int; -typedef __darwin_fsfilcnt_t = ffi.UnsignedInt; -typedef Dart__darwin_fsfilcnt_t = int; -typedef __darwin_gid_t = __uint32_t; -typedef __darwin_id_t = __uint32_t; -typedef __darwin_ino64_t = __uint64_t; -typedef __darwin_ino_t = __darwin_ino64_t; -typedef __darwin_mach_port_name_t = __darwin_natural_t; -typedef __darwin_mach_port_t = __darwin_mach_port_name_t; -typedef __darwin_mode_t = __uint16_t; -typedef __darwin_off_t = __int64_t; -typedef __darwin_pid_t = __int32_t; -typedef __darwin_sigset_t = __uint32_t; -typedef __darwin_suseconds_t = __int32_t; -typedef __darwin_uid_t = __uint32_t; -typedef __darwin_useconds_t = __uint32_t; - -final class __darwin_pthread_handler_rec extends ffi.Struct { - external ffi - .Pointer)>> - __routine; - - external ffi.Pointer __arg; - - external ffi.Pointer<__darwin_pthread_handler_rec> __next; -} - -final class _opaque_pthread_attr_t extends ffi.Struct { - @ffi.Long() - external int __sig; - - @ffi.Array.multi([56]) - external ffi.Array __opaque; -} - -final class _opaque_pthread_cond_t extends ffi.Struct { - @ffi.Long() - external int __sig; - - @ffi.Array.multi([40]) - external ffi.Array __opaque; -} - -final class _opaque_pthread_condattr_t extends ffi.Struct { - @ffi.Long() - external int __sig; - - @ffi.Array.multi([8]) - external ffi.Array __opaque; -} - -final class _opaque_pthread_mutex_t extends ffi.Struct { - @ffi.Long() - external int __sig; - - @ffi.Array.multi([56]) - external ffi.Array __opaque; -} - -final class _opaque_pthread_mutexattr_t extends ffi.Struct { - @ffi.Long() - external int __sig; - - @ffi.Array.multi([8]) - external ffi.Array __opaque; -} - -final class _opaque_pthread_once_t extends ffi.Struct { - @ffi.Long() - external int __sig; - - @ffi.Array.multi([8]) - external ffi.Array __opaque; -} - -final class _opaque_pthread_rwlock_t extends ffi.Struct { - @ffi.Long() - external int __sig; - - @ffi.Array.multi([192]) - external ffi.Array __opaque; -} - -final class _opaque_pthread_rwlockattr_t extends ffi.Struct { - @ffi.Long() - external int __sig; - - @ffi.Array.multi([16]) - external ffi.Array __opaque; -} - -final class _opaque_pthread_t extends ffi.Struct { - @ffi.Long() - external int __sig; - - external ffi.Pointer<__darwin_pthread_handler_rec> __cleanup_stack; - - @ffi.Array.multi([8176]) - external ffi.Array __opaque; -} - -typedef __darwin_pthread_attr_t = _opaque_pthread_attr_t; -typedef __darwin_pthread_cond_t = _opaque_pthread_cond_t; -typedef __darwin_pthread_condattr_t = _opaque_pthread_condattr_t; -typedef __darwin_pthread_key_t = ffi.UnsignedLong; -typedef Dart__darwin_pthread_key_t = int; -typedef __darwin_pthread_mutex_t = _opaque_pthread_mutex_t; -typedef __darwin_pthread_mutexattr_t = _opaque_pthread_mutexattr_t; -typedef __darwin_pthread_once_t = _opaque_pthread_once_t; -typedef __darwin_pthread_rwlock_t = _opaque_pthread_rwlock_t; -typedef __darwin_pthread_rwlockattr_t = _opaque_pthread_rwlockattr_t; -typedef __darwin_pthread_t = ffi.Pointer<_opaque_pthread_t>; -typedef __darwin_nl_item = ffi.Int; -typedef Dart__darwin_nl_item = int; -typedef __darwin_wctrans_t = ffi.Int; -typedef Dart__darwin_wctrans_t = int; -typedef __darwin_wctype_t = __uint32_t; -typedef u_int8_t = ffi.UnsignedChar; -typedef Dartu_int8_t = int; -typedef u_int16_t = ffi.UnsignedShort; -typedef Dartu_int16_t = int; -typedef u_int32_t = ffi.UnsignedInt; -typedef Dartu_int32_t = int; -typedef u_int64_t = ffi.UnsignedLongLong; -typedef Dartu_int64_t = int; -typedef register_t = ffi.Int64; -typedef Dartregister_t = int; -typedef user_addr_t = u_int64_t; -typedef user_size_t = u_int64_t; -typedef user_ssize_t = ffi.Int64; -typedef Dartuser_ssize_t = int; -typedef user_long_t = ffi.Int64; -typedef Dartuser_long_t = int; -typedef user_ulong_t = u_int64_t; -typedef user_time_t = ffi.Int64; -typedef Dartuser_time_t = int; -typedef user_off_t = ffi.Int64; -typedef Dartuser_off_t = int; -typedef syscall_arg_t = u_int64_t; -typedef ptrdiff_t = __darwin_ptrdiff_t; -typedef rsize_t = __darwin_size_t; -typedef wint_t = __darwin_wint_t; - -final class _GoString_ extends ffi.Struct { - external ffi.Pointer p; - - @ptrdiff_t() - external int n; -} - -enum idtype_t { - P_ALL(0), - P_PID(1), - P_PGID(2); - - final int value; - const idtype_t(this.value); - - static idtype_t fromValue(int value) => switch (value) { - 0 => P_ALL, - 1 => P_PID, - 2 => P_PGID, - _ => throw ArgumentError('Unknown value for idtype_t: $value'), - }; -} - -typedef pid_t = __darwin_pid_t; -typedef id_t = __darwin_id_t; -typedef sig_atomic_t = ffi.Int; -typedef Dartsig_atomic_t = int; - -final class __darwin_arm_exception_state extends ffi.Struct { - @__uint32_t() - external int __exception; - - @__uint32_t() - external int __fsr; - - @__uint32_t() - external int __far; -} - -final class __darwin_arm_exception_state64 extends ffi.Struct { - @__uint64_t() - external int __far; - - @__uint32_t() - external int __esr; - - @__uint32_t() - external int __exception; -} - -final class __darwin_arm_exception_state64_v2 extends ffi.Struct { - @__uint64_t() - external int __far; - - @__uint64_t() - external int __esr; -} - -final class __darwin_arm_thread_state extends ffi.Struct { - @ffi.Array.multi([13]) - external ffi.Array<__uint32_t> __r; - - @__uint32_t() - external int __sp; - - @__uint32_t() - external int __lr; - - @__uint32_t() - external int __pc; - - @__uint32_t() - external int __cpsr; -} - -final class __darwin_arm_thread_state64 extends ffi.Struct { - @ffi.Array.multi([29]) - external ffi.Array<__uint64_t> __x; - - @__uint64_t() - external int __fp; - - @__uint64_t() - external int __lr; - - @__uint64_t() - external int __sp; - - @__uint64_t() - external int __pc; - - @__uint32_t() - external int __cpsr; - - @__uint32_t() - external int __pad; -} - -final class __darwin_arm_vfp_state extends ffi.Struct { - @ffi.Array.multi([64]) - external ffi.Array<__uint32_t> __r; - - @__uint32_t() - external int __fpscr; -} - -final class __darwin_arm_neon_state64 extends ffi.Opaque {} - -final class __darwin_arm_neon_state extends ffi.Opaque {} - -final class __arm_pagein_state extends ffi.Struct { - @ffi.Int() - external int __pagein_error; -} - -final class __arm_legacy_debug_state extends ffi.Struct { - @ffi.Array.multi([16]) - external ffi.Array<__uint32_t> __bvr; - - @ffi.Array.multi([16]) - external ffi.Array<__uint32_t> __bcr; - - @ffi.Array.multi([16]) - external ffi.Array<__uint32_t> __wvr; - - @ffi.Array.multi([16]) - external ffi.Array<__uint32_t> __wcr; -} - -final class __darwin_arm_debug_state32 extends ffi.Struct { - @ffi.Array.multi([16]) - external ffi.Array<__uint32_t> __bvr; - - @ffi.Array.multi([16]) - external ffi.Array<__uint32_t> __bcr; - - @ffi.Array.multi([16]) - external ffi.Array<__uint32_t> __wvr; - - @ffi.Array.multi([16]) - external ffi.Array<__uint32_t> __wcr; - - @__uint64_t() - external int __mdscr_el1; -} - -final class __darwin_arm_debug_state64 extends ffi.Struct { - @ffi.Array.multi([16]) - external ffi.Array<__uint64_t> __bvr; - - @ffi.Array.multi([16]) - external ffi.Array<__uint64_t> __bcr; - - @ffi.Array.multi([16]) - external ffi.Array<__uint64_t> __wvr; - - @ffi.Array.multi([16]) - external ffi.Array<__uint64_t> __wcr; - - @__uint64_t() - external int __mdscr_el1; -} - -final class __darwin_arm_cpmu_state64 extends ffi.Struct { - @ffi.Array.multi([16]) - external ffi.Array<__uint64_t> __ctrs; -} - -final class __darwin_mcontext32 extends ffi.Struct { - external __darwin_arm_exception_state __es; - - external __darwin_arm_thread_state __ss; - - external __darwin_arm_vfp_state __fs; -} - -final class __darwin_mcontext64 extends ffi.Opaque {} - -typedef mcontext_t = ffi.Pointer<__darwin_mcontext64>; -typedef pthread_attr_t = __darwin_pthread_attr_t; - -final class __darwin_sigaltstack extends ffi.Struct { - external ffi.Pointer ss_sp; - - @__darwin_size_t() - external int ss_size; - - @ffi.Int() - external int ss_flags; -} - -typedef stack_t = __darwin_sigaltstack; - -final class __darwin_ucontext extends ffi.Struct { - @ffi.Int() - external int uc_onstack; - - @__darwin_sigset_t() - external int uc_sigmask; - - external __darwin_sigaltstack uc_stack; - - external ffi.Pointer<__darwin_ucontext> uc_link; - - @__darwin_size_t() - external int uc_mcsize; - - external ffi.Pointer<__darwin_mcontext64> uc_mcontext; -} - -typedef ucontext_t = __darwin_ucontext; -typedef sigset_t = __darwin_sigset_t; -typedef uid_t = __darwin_uid_t; - -final class sigval extends ffi.Union { - @ffi.Int() - external int sival_int; - - external ffi.Pointer sival_ptr; -} - -final class sigevent extends ffi.Struct { - @ffi.Int() - external int sigev_notify; - - @ffi.Int() - external int sigev_signo; - - external sigval sigev_value; - - external ffi.Pointer> - sigev_notify_function; - - external ffi.Pointer sigev_notify_attributes; -} - -final class __siginfo extends ffi.Struct { - @ffi.Int() - external int si_signo; - - @ffi.Int() - external int si_errno; - - @ffi.Int() - external int si_code; - - @pid_t() - external int si_pid; - - @uid_t() - external int si_uid; - - @ffi.Int() - external int si_status; - - external ffi.Pointer si_addr; - - external sigval si_value; - - @ffi.Long() - external int si_band; - - @ffi.Array.multi([7]) - external ffi.Array __pad; -} - -typedef siginfo_t = __siginfo; - -final class __sigaction_u extends ffi.Union { - external ffi.Pointer> - __sa_handler; - - external ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function( - ffi.Int, ffi.Pointer<__siginfo>, ffi.Pointer)>> - __sa_sigaction; -} - -final class __sigaction extends ffi.Struct { - external __sigaction_u __sigaction_u$1; - - external ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer, ffi.Int, ffi.Int, - ffi.Pointer, ffi.Pointer)>> sa_tramp; - - @sigset_t() - external int sa_mask; - - @ffi.Int() - external int sa_flags; -} - -final class sigaction extends ffi.Struct { - external __sigaction_u __sigaction_u$1; - - @sigset_t() - external int sa_mask; - - @ffi.Int() - external int sa_flags; -} - -typedef sig_tFunction = ffi.Void Function(ffi.Int); -typedef Dartsig_tFunction = void Function(int); -typedef sig_t = ffi.Pointer>; - -final class sigvec extends ffi.Struct { - external ffi.Pointer> - sv_handler; - - @ffi.Int() - external int sv_mask; - - @ffi.Int() - external int sv_flags; -} - -final class sigstack extends ffi.Struct { - external ffi.Pointer ss_sp; - - @ffi.Int() - external int ss_onstack; -} - -typedef int_least8_t = ffi.Int8; -typedef Dartint_least8_t = int; -typedef int_least16_t = ffi.Int16; -typedef Dartint_least16_t = int; -typedef int_least32_t = ffi.Int32; -typedef Dartint_least32_t = int; -typedef int_least64_t = ffi.Int64; -typedef Dartint_least64_t = int; -typedef uint_least8_t = ffi.Uint8; -typedef Dartuint_least8_t = int; -typedef uint_least16_t = ffi.Uint16; -typedef Dartuint_least16_t = int; -typedef uint_least32_t = ffi.Uint32; -typedef Dartuint_least32_t = int; -typedef uint_least64_t = ffi.Uint64; -typedef Dartuint_least64_t = int; -typedef int_fast8_t = ffi.Int8; -typedef Dartint_fast8_t = int; -typedef int_fast16_t = ffi.Int16; -typedef Dartint_fast16_t = int; -typedef int_fast32_t = ffi.Int32; -typedef Dartint_fast32_t = int; -typedef int_fast64_t = ffi.Int64; -typedef Dartint_fast64_t = int; -typedef uint_fast8_t = ffi.Uint8; -typedef Dartuint_fast8_t = int; -typedef uint_fast16_t = ffi.Uint16; -typedef Dartuint_fast16_t = int; -typedef uint_fast32_t = ffi.Uint32; -typedef Dartuint_fast32_t = int; -typedef uint_fast64_t = ffi.Uint64; -typedef Dartuint_fast64_t = int; -typedef intmax_t = ffi.Long; -typedef Dartintmax_t = int; -typedef uintmax_t = ffi.UnsignedLong; -typedef Dartuintmax_t = int; - -final class timeval extends ffi.Struct { - @__darwin_time_t() - external int tv_sec; - - @__darwin_suseconds_t() - external int tv_usec; -} - -typedef rlim_t = __uint64_t; - -final class rusage extends ffi.Struct { - external timeval ru_utime; - - external timeval ru_stime; - - @ffi.Long() - external int ru_maxrss; - - @ffi.Long() - external int ru_ixrss; - - @ffi.Long() - external int ru_idrss; - - @ffi.Long() - external int ru_isrss; - - @ffi.Long() - external int ru_minflt; - - @ffi.Long() - external int ru_majflt; - - @ffi.Long() - external int ru_nswap; - - @ffi.Long() - external int ru_inblock; - - @ffi.Long() - external int ru_oublock; - - @ffi.Long() - external int ru_msgsnd; - - @ffi.Long() - external int ru_msgrcv; - - @ffi.Long() - external int ru_nsignals; - - @ffi.Long() - external int ru_nvcsw; - - @ffi.Long() - external int ru_nivcsw; -} - -typedef rusage_info_t = ffi.Pointer; - -final class rusage_info_v0 extends ffi.Struct { - @ffi.Array.multi([16]) - external ffi.Array ri_uuid; - - @ffi.Uint64() - external int ri_user_time; - - @ffi.Uint64() - external int ri_system_time; - - @ffi.Uint64() - external int ri_pkg_idle_wkups; - - @ffi.Uint64() - external int ri_interrupt_wkups; - - @ffi.Uint64() - external int ri_pageins; - - @ffi.Uint64() - external int ri_wired_size; - - @ffi.Uint64() - external int ri_resident_size; - - @ffi.Uint64() - external int ri_phys_footprint; - - @ffi.Uint64() - external int ri_proc_start_abstime; - - @ffi.Uint64() - external int ri_proc_exit_abstime; -} - -final class rusage_info_v1 extends ffi.Struct { - @ffi.Array.multi([16]) - external ffi.Array ri_uuid; - - @ffi.Uint64() - external int ri_user_time; - - @ffi.Uint64() - external int ri_system_time; - - @ffi.Uint64() - external int ri_pkg_idle_wkups; - - @ffi.Uint64() - external int ri_interrupt_wkups; - - @ffi.Uint64() - external int ri_pageins; - - @ffi.Uint64() - external int ri_wired_size; - - @ffi.Uint64() - external int ri_resident_size; - - @ffi.Uint64() - external int ri_phys_footprint; - - @ffi.Uint64() - external int ri_proc_start_abstime; - - @ffi.Uint64() - external int ri_proc_exit_abstime; - - @ffi.Uint64() - external int ri_child_user_time; - - @ffi.Uint64() - external int ri_child_system_time; - - @ffi.Uint64() - external int ri_child_pkg_idle_wkups; - - @ffi.Uint64() - external int ri_child_interrupt_wkups; - - @ffi.Uint64() - external int ri_child_pageins; - - @ffi.Uint64() - external int ri_child_elapsed_abstime; -} - -final class rusage_info_v2 extends ffi.Struct { - @ffi.Array.multi([16]) - external ffi.Array ri_uuid; - - @ffi.Uint64() - external int ri_user_time; - - @ffi.Uint64() - external int ri_system_time; - - @ffi.Uint64() - external int ri_pkg_idle_wkups; - - @ffi.Uint64() - external int ri_interrupt_wkups; - - @ffi.Uint64() - external int ri_pageins; - - @ffi.Uint64() - external int ri_wired_size; - - @ffi.Uint64() - external int ri_resident_size; - - @ffi.Uint64() - external int ri_phys_footprint; - - @ffi.Uint64() - external int ri_proc_start_abstime; - - @ffi.Uint64() - external int ri_proc_exit_abstime; - - @ffi.Uint64() - external int ri_child_user_time; - - @ffi.Uint64() - external int ri_child_system_time; - - @ffi.Uint64() - external int ri_child_pkg_idle_wkups; - - @ffi.Uint64() - external int ri_child_interrupt_wkups; - - @ffi.Uint64() - external int ri_child_pageins; - - @ffi.Uint64() - external int ri_child_elapsed_abstime; - - @ffi.Uint64() - external int ri_diskio_bytesread; - - @ffi.Uint64() - external int ri_diskio_byteswritten; -} - -final class rusage_info_v3 extends ffi.Struct { - @ffi.Array.multi([16]) - external ffi.Array ri_uuid; - - @ffi.Uint64() - external int ri_user_time; - - @ffi.Uint64() - external int ri_system_time; - - @ffi.Uint64() - external int ri_pkg_idle_wkups; - - @ffi.Uint64() - external int ri_interrupt_wkups; - - @ffi.Uint64() - external int ri_pageins; - - @ffi.Uint64() - external int ri_wired_size; - - @ffi.Uint64() - external int ri_resident_size; - - @ffi.Uint64() - external int ri_phys_footprint; - - @ffi.Uint64() - external int ri_proc_start_abstime; - - @ffi.Uint64() - external int ri_proc_exit_abstime; - - @ffi.Uint64() - external int ri_child_user_time; - - @ffi.Uint64() - external int ri_child_system_time; - - @ffi.Uint64() - external int ri_child_pkg_idle_wkups; - - @ffi.Uint64() - external int ri_child_interrupt_wkups; - - @ffi.Uint64() - external int ri_child_pageins; - - @ffi.Uint64() - external int ri_child_elapsed_abstime; - - @ffi.Uint64() - external int ri_diskio_bytesread; - - @ffi.Uint64() - external int ri_diskio_byteswritten; - - @ffi.Uint64() - external int ri_cpu_time_qos_default; - - @ffi.Uint64() - external int ri_cpu_time_qos_maintenance; - - @ffi.Uint64() - external int ri_cpu_time_qos_background; - - @ffi.Uint64() - external int ri_cpu_time_qos_utility; - - @ffi.Uint64() - external int ri_cpu_time_qos_legacy; - - @ffi.Uint64() - external int ri_cpu_time_qos_user_initiated; - - @ffi.Uint64() - external int ri_cpu_time_qos_user_interactive; - - @ffi.Uint64() - external int ri_billed_system_time; - - @ffi.Uint64() - external int ri_serviced_system_time; -} - -final class rusage_info_v4 extends ffi.Struct { - @ffi.Array.multi([16]) - external ffi.Array ri_uuid; - - @ffi.Uint64() - external int ri_user_time; - - @ffi.Uint64() - external int ri_system_time; - - @ffi.Uint64() - external int ri_pkg_idle_wkups; - - @ffi.Uint64() - external int ri_interrupt_wkups; - - @ffi.Uint64() - external int ri_pageins; - - @ffi.Uint64() - external int ri_wired_size; - - @ffi.Uint64() - external int ri_resident_size; - - @ffi.Uint64() - external int ri_phys_footprint; - - @ffi.Uint64() - external int ri_proc_start_abstime; - - @ffi.Uint64() - external int ri_proc_exit_abstime; - - @ffi.Uint64() - external int ri_child_user_time; - - @ffi.Uint64() - external int ri_child_system_time; - - @ffi.Uint64() - external int ri_child_pkg_idle_wkups; - - @ffi.Uint64() - external int ri_child_interrupt_wkups; - - @ffi.Uint64() - external int ri_child_pageins; - - @ffi.Uint64() - external int ri_child_elapsed_abstime; - - @ffi.Uint64() - external int ri_diskio_bytesread; - - @ffi.Uint64() - external int ri_diskio_byteswritten; - - @ffi.Uint64() - external int ri_cpu_time_qos_default; - - @ffi.Uint64() - external int ri_cpu_time_qos_maintenance; - - @ffi.Uint64() - external int ri_cpu_time_qos_background; - - @ffi.Uint64() - external int ri_cpu_time_qos_utility; - - @ffi.Uint64() - external int ri_cpu_time_qos_legacy; - - @ffi.Uint64() - external int ri_cpu_time_qos_user_initiated; - - @ffi.Uint64() - external int ri_cpu_time_qos_user_interactive; - - @ffi.Uint64() - external int ri_billed_system_time; - - @ffi.Uint64() - external int ri_serviced_system_time; - - @ffi.Uint64() - external int ri_logical_writes; - - @ffi.Uint64() - external int ri_lifetime_max_phys_footprint; - - @ffi.Uint64() - external int ri_instructions; - - @ffi.Uint64() - external int ri_cycles; - - @ffi.Uint64() - external int ri_billed_energy; - - @ffi.Uint64() - external int ri_serviced_energy; - - @ffi.Uint64() - external int ri_interval_max_phys_footprint; - - @ffi.Uint64() - external int ri_runnable_time; -} - -final class rusage_info_v5 extends ffi.Struct { - @ffi.Array.multi([16]) - external ffi.Array ri_uuid; - - @ffi.Uint64() - external int ri_user_time; - - @ffi.Uint64() - external int ri_system_time; - - @ffi.Uint64() - external int ri_pkg_idle_wkups; - - @ffi.Uint64() - external int ri_interrupt_wkups; - - @ffi.Uint64() - external int ri_pageins; - - @ffi.Uint64() - external int ri_wired_size; - - @ffi.Uint64() - external int ri_resident_size; - - @ffi.Uint64() - external int ri_phys_footprint; - - @ffi.Uint64() - external int ri_proc_start_abstime; - - @ffi.Uint64() - external int ri_proc_exit_abstime; - - @ffi.Uint64() - external int ri_child_user_time; - - @ffi.Uint64() - external int ri_child_system_time; - - @ffi.Uint64() - external int ri_child_pkg_idle_wkups; - - @ffi.Uint64() - external int ri_child_interrupt_wkups; - - @ffi.Uint64() - external int ri_child_pageins; - - @ffi.Uint64() - external int ri_child_elapsed_abstime; - - @ffi.Uint64() - external int ri_diskio_bytesread; - - @ffi.Uint64() - external int ri_diskio_byteswritten; - - @ffi.Uint64() - external int ri_cpu_time_qos_default; - - @ffi.Uint64() - external int ri_cpu_time_qos_maintenance; - - @ffi.Uint64() - external int ri_cpu_time_qos_background; - - @ffi.Uint64() - external int ri_cpu_time_qos_utility; - - @ffi.Uint64() - external int ri_cpu_time_qos_legacy; - - @ffi.Uint64() - external int ri_cpu_time_qos_user_initiated; - - @ffi.Uint64() - external int ri_cpu_time_qos_user_interactive; - - @ffi.Uint64() - external int ri_billed_system_time; - - @ffi.Uint64() - external int ri_serviced_system_time; - - @ffi.Uint64() - external int ri_logical_writes; - - @ffi.Uint64() - external int ri_lifetime_max_phys_footprint; - - @ffi.Uint64() - external int ri_instructions; - - @ffi.Uint64() - external int ri_cycles; - - @ffi.Uint64() - external int ri_billed_energy; - - @ffi.Uint64() - external int ri_serviced_energy; - - @ffi.Uint64() - external int ri_interval_max_phys_footprint; - - @ffi.Uint64() - external int ri_runnable_time; - - @ffi.Uint64() - external int ri_flags; -} - -final class rusage_info_v6 extends ffi.Struct { - @ffi.Array.multi([16]) - external ffi.Array ri_uuid; - - @ffi.Uint64() - external int ri_user_time; - - @ffi.Uint64() - external int ri_system_time; - - @ffi.Uint64() - external int ri_pkg_idle_wkups; - - @ffi.Uint64() - external int ri_interrupt_wkups; - - @ffi.Uint64() - external int ri_pageins; - - @ffi.Uint64() - external int ri_wired_size; - - @ffi.Uint64() - external int ri_resident_size; - - @ffi.Uint64() - external int ri_phys_footprint; - - @ffi.Uint64() - external int ri_proc_start_abstime; - - @ffi.Uint64() - external int ri_proc_exit_abstime; - - @ffi.Uint64() - external int ri_child_user_time; - - @ffi.Uint64() - external int ri_child_system_time; - - @ffi.Uint64() - external int ri_child_pkg_idle_wkups; - - @ffi.Uint64() - external int ri_child_interrupt_wkups; - - @ffi.Uint64() - external int ri_child_pageins; - - @ffi.Uint64() - external int ri_child_elapsed_abstime; - - @ffi.Uint64() - external int ri_diskio_bytesread; - - @ffi.Uint64() - external int ri_diskio_byteswritten; - - @ffi.Uint64() - external int ri_cpu_time_qos_default; - - @ffi.Uint64() - external int ri_cpu_time_qos_maintenance; - - @ffi.Uint64() - external int ri_cpu_time_qos_background; - - @ffi.Uint64() - external int ri_cpu_time_qos_utility; - - @ffi.Uint64() - external int ri_cpu_time_qos_legacy; - - @ffi.Uint64() - external int ri_cpu_time_qos_user_initiated; - - @ffi.Uint64() - external int ri_cpu_time_qos_user_interactive; - - @ffi.Uint64() - external int ri_billed_system_time; - - @ffi.Uint64() - external int ri_serviced_system_time; - - @ffi.Uint64() - external int ri_logical_writes; - - @ffi.Uint64() - external int ri_lifetime_max_phys_footprint; - - @ffi.Uint64() - external int ri_instructions; - - @ffi.Uint64() - external int ri_cycles; - - @ffi.Uint64() - external int ri_billed_energy; - - @ffi.Uint64() - external int ri_serviced_energy; - - @ffi.Uint64() - external int ri_interval_max_phys_footprint; - - @ffi.Uint64() - external int ri_runnable_time; - - @ffi.Uint64() - external int ri_flags; - - @ffi.Uint64() - external int ri_user_ptime; - - @ffi.Uint64() - external int ri_system_ptime; - - @ffi.Uint64() - external int ri_pinstructions; - - @ffi.Uint64() - external int ri_pcycles; - - @ffi.Uint64() - external int ri_energy_nj; - - @ffi.Uint64() - external int ri_penergy_nj; - - @ffi.Uint64() - external int ri_secure_time_in_system; - - @ffi.Uint64() - external int ri_secure_ptime_in_system; - - @ffi.Uint64() - external int ri_neural_footprint; - - @ffi.Uint64() - external int ri_lifetime_max_neural_footprint; - - @ffi.Uint64() - external int ri_interval_max_neural_footprint; - - @ffi.Array.multi([9]) - external ffi.Array ri_reserved; -} - -typedef rusage_info_current = rusage_info_v6; - -final class rlimit extends ffi.Struct { - @rlim_t() - external int rlim_cur; - - @rlim_t() - external int rlim_max; -} - -final class proc_rlimit_control_wakeupmon extends ffi.Struct { - @ffi.Uint32() - external int wm_flags; - - @ffi.Int32() - external int wm_rate; -} - -final class wait extends ffi.Opaque {} - -typedef ct_rune_t = __darwin_ct_rune_t; -typedef rune_t = __darwin_rune_t; - -final class div_t extends ffi.Struct { - @ffi.Int() - external int quot; - - @ffi.Int() - external int rem; -} - -final class ldiv_t extends ffi.Struct { - @ffi.Long() - external int quot; - - @ffi.Long() - external int rem; -} - -final class lldiv_t extends ffi.Struct { - @ffi.LongLong() - external int quot; - - @ffi.LongLong() - external int rem; -} - -typedef malloc_type_id_t = ffi.UnsignedLongLong; -typedef Dartmalloc_type_id_t = int; - -final class _malloc_zone_t extends ffi.Opaque {} - -typedef malloc_zone_t = _malloc_zone_t; -typedef dev_t = __darwin_dev_t; -typedef mode_t = __darwin_mode_t; -typedef release_object_funcFunction = ffi.Void Function( - ffi.Pointer obj); -typedef Dartrelease_object_funcFunction = void Function( - ffi.Pointer obj); -typedef release_object_func - = ffi.Pointer>; -typedef protect_funcFunction = ffi.Void Function( - ffi.Pointer tun_interface, ffi.Int fd); -typedef Dartprotect_funcFunction = void Function( - ffi.Pointer tun_interface, int fd); -typedef protect_func = ffi.Pointer>; -typedef resolve_process_funcFunction = ffi.Pointer Function( - ffi.Pointer tun_interface, - ffi.Int protocol, - ffi.Pointer source, - ffi.Pointer target, - ffi.Int uid); -typedef Dartresolve_process_funcFunction = ffi.Pointer Function( - ffi.Pointer tun_interface, - int protocol, - ffi.Pointer source, - ffi.Pointer target, - int uid); -typedef resolve_process_func - = ffi.Pointer>; -typedef GoInt8 = ffi.SignedChar; -typedef DartGoInt8 = int; -typedef GoUint8 = ffi.UnsignedChar; -typedef DartGoUint8 = int; -typedef GoInt16 = ffi.Short; -typedef DartGoInt16 = int; -typedef GoUint16 = ffi.UnsignedShort; -typedef DartGoUint16 = int; -typedef GoInt32 = ffi.Int; -typedef DartGoInt32 = int; -typedef GoUint32 = ffi.UnsignedInt; -typedef DartGoUint32 = int; -typedef GoInt64 = ffi.LongLong; -typedef DartGoInt64 = int; -typedef GoUint64 = ffi.UnsignedLongLong; -typedef DartGoUint64 = int; -typedef GoInt = GoInt64; -typedef GoUint = GoUint64; -typedef GoUintptr = ffi.Size; -typedef DartGoUintptr = int; -typedef GoFloat32 = ffi.Float; -typedef DartGoFloat32 = double; -typedef GoFloat64 = ffi.Double; -typedef DartGoFloat64 = double; -typedef GoString = _GoString_; -typedef GoMap = ffi.Pointer; -typedef GoChan = ffi.Pointer; - -final class GoInterface extends ffi.Struct { - external ffi.Pointer t; - - external ffi.Pointer v; -} - -final class GoSlice extends ffi.Struct { - external ffi.Pointer data; - - @GoInt() - external int len; - - @GoInt() - external int cap; -} - -const int __has_safe_buffers = 1; - -const int __DARWIN_ONLY_64_BIT_INO_T = 1; - -const int __DARWIN_ONLY_UNIX_CONFORMANCE = 1; - -const int __DARWIN_ONLY_VERS_1050 = 1; - -const int __DARWIN_UNIX03 = 1; - -const int __DARWIN_64_BIT_INO_T = 1; - -const int __DARWIN_VERS_1050 = 1; - -const int __DARWIN_NON_CANCELABLE = 0; - -const String __DARWIN_SUF_EXTSN = '\$DARWIN_EXTSN'; - -const int __DARWIN_C_ANSI = 4096; - -const int __DARWIN_C_FULL = 900000; - -const int __DARWIN_C_LEVEL = 900000; - -const int __STDC_WANT_LIB_EXT1__ = 1; - -const int __DARWIN_NO_LONG_LONG = 0; - -const int _DARWIN_FEATURE_64_BIT_INODE = 1; - -const int _DARWIN_FEATURE_ONLY_64_BIT_INODE = 1; - -const int _DARWIN_FEATURE_ONLY_VERS_1050 = 1; - -const int _DARWIN_FEATURE_ONLY_UNIX_CONFORMANCE = 1; - -const int _DARWIN_FEATURE_UNIX_CONFORMANCE = 3; - -const int __has_ptrcheck = 0; - -const int __DARWIN_NULL = 0; - -const int __PTHREAD_SIZE__ = 8176; - -const int __PTHREAD_ATTR_SIZE__ = 56; - -const int __PTHREAD_MUTEXATTR_SIZE__ = 8; - -const int __PTHREAD_MUTEX_SIZE__ = 56; - -const int __PTHREAD_CONDATTR_SIZE__ = 8; - -const int __PTHREAD_COND_SIZE__ = 40; - -const int __PTHREAD_ONCE_SIZE__ = 8; - -const int __PTHREAD_RWLOCK_SIZE__ = 192; - -const int __PTHREAD_RWLOCKATTR_SIZE__ = 16; - -const int __DARWIN_WCHAR_MAX = 2147483647; - -const int __DARWIN_WCHAR_MIN = -2147483648; - -const int __DARWIN_WEOF = -1; - -const int _FORTIFY_SOURCE = 2; - -const int NULL = 0; - -const int USER_ADDR_NULL = 0; - -const int __API_TO_BE_DEPRECATED = 100000; - -const int __API_TO_BE_DEPRECATED_MACOS = 100000; - -const int __API_TO_BE_DEPRECATED_IOS = 100000; - -const int __API_TO_BE_DEPRECATED_MACCATALYST = 100000; - -const int __API_TO_BE_DEPRECATED_WATCHOS = 100000; - -const int __API_TO_BE_DEPRECATED_TVOS = 100000; - -const int __API_TO_BE_DEPRECATED_DRIVERKIT = 100000; - -const int __API_TO_BE_DEPRECATED_VISIONOS = 100000; - -const int __MAC_10_0 = 1000; - -const int __MAC_10_1 = 1010; - -const int __MAC_10_2 = 1020; - -const int __MAC_10_3 = 1030; - -const int __MAC_10_4 = 1040; - -const int __MAC_10_5 = 1050; - -const int __MAC_10_6 = 1060; - -const int __MAC_10_7 = 1070; - -const int __MAC_10_8 = 1080; - -const int __MAC_10_9 = 1090; - -const int __MAC_10_10 = 101000; - -const int __MAC_10_10_2 = 101002; - -const int __MAC_10_10_3 = 101003; - -const int __MAC_10_11 = 101100; - -const int __MAC_10_11_2 = 101102; - -const int __MAC_10_11_3 = 101103; - -const int __MAC_10_11_4 = 101104; - -const int __MAC_10_12 = 101200; - -const int __MAC_10_12_1 = 101201; - -const int __MAC_10_12_2 = 101202; - -const int __MAC_10_12_4 = 101204; - -const int __MAC_10_13 = 101300; - -const int __MAC_10_13_1 = 101301; - -const int __MAC_10_13_2 = 101302; - -const int __MAC_10_13_4 = 101304; - -const int __MAC_10_14 = 101400; - -const int __MAC_10_14_1 = 101401; - -const int __MAC_10_14_4 = 101404; - -const int __MAC_10_14_5 = 101405; - -const int __MAC_10_14_6 = 101406; - -const int __MAC_10_15 = 101500; - -const int __MAC_10_15_1 = 101501; - -const int __MAC_10_15_4 = 101504; - -const int __MAC_10_16 = 101600; - -const int __MAC_11_0 = 110000; - -const int __MAC_11_1 = 110100; - -const int __MAC_11_3 = 110300; - -const int __MAC_11_4 = 110400; - -const int __MAC_11_5 = 110500; - -const int __MAC_11_6 = 110600; - -const int __MAC_12_0 = 120000; - -const int __MAC_12_1 = 120100; - -const int __MAC_12_2 = 120200; - -const int __MAC_12_3 = 120300; - -const int __MAC_12_4 = 120400; - -const int __MAC_12_5 = 120500; - -const int __MAC_12_6 = 120600; - -const int __MAC_12_7 = 120700; - -const int __MAC_13_0 = 130000; - -const int __MAC_13_1 = 130100; - -const int __MAC_13_2 = 130200; - -const int __MAC_13_3 = 130300; - -const int __MAC_13_4 = 130400; - -const int __MAC_13_5 = 130500; - -const int __MAC_13_6 = 130600; - -const int __MAC_14_0 = 140000; - -const int __MAC_14_1 = 140100; - -const int __MAC_14_2 = 140200; - -const int __MAC_14_3 = 140300; - -const int __MAC_14_4 = 140400; - -const int __MAC_14_5 = 140500; - -const int __MAC_15_0 = 150000; - -const int __MAC_15_1 = 150100; - -const int __MAC_15_2 = 150200; - -const int __IPHONE_2_0 = 20000; - -const int __IPHONE_2_1 = 20100; - -const int __IPHONE_2_2 = 20200; - -const int __IPHONE_3_0 = 30000; - -const int __IPHONE_3_1 = 30100; - -const int __IPHONE_3_2 = 30200; - -const int __IPHONE_4_0 = 40000; - -const int __IPHONE_4_1 = 40100; - -const int __IPHONE_4_2 = 40200; - -const int __IPHONE_4_3 = 40300; - -const int __IPHONE_5_0 = 50000; - -const int __IPHONE_5_1 = 50100; - -const int __IPHONE_6_0 = 60000; - -const int __IPHONE_6_1 = 60100; - -const int __IPHONE_7_0 = 70000; - -const int __IPHONE_7_1 = 70100; - -const int __IPHONE_8_0 = 80000; - -const int __IPHONE_8_1 = 80100; - -const int __IPHONE_8_2 = 80200; - -const int __IPHONE_8_3 = 80300; - -const int __IPHONE_8_4 = 80400; - -const int __IPHONE_9_0 = 90000; - -const int __IPHONE_9_1 = 90100; - -const int __IPHONE_9_2 = 90200; - -const int __IPHONE_9_3 = 90300; - -const int __IPHONE_10_0 = 100000; - -const int __IPHONE_10_1 = 100100; - -const int __IPHONE_10_2 = 100200; - -const int __IPHONE_10_3 = 100300; - -const int __IPHONE_11_0 = 110000; - -const int __IPHONE_11_1 = 110100; - -const int __IPHONE_11_2 = 110200; - -const int __IPHONE_11_3 = 110300; - -const int __IPHONE_11_4 = 110400; - -const int __IPHONE_12_0 = 120000; - -const int __IPHONE_12_1 = 120100; - -const int __IPHONE_12_2 = 120200; - -const int __IPHONE_12_3 = 120300; - -const int __IPHONE_12_4 = 120400; - -const int __IPHONE_13_0 = 130000; - -const int __IPHONE_13_1 = 130100; - -const int __IPHONE_13_2 = 130200; - -const int __IPHONE_13_3 = 130300; - -const int __IPHONE_13_4 = 130400; - -const int __IPHONE_13_5 = 130500; - -const int __IPHONE_13_6 = 130600; - -const int __IPHONE_13_7 = 130700; - -const int __IPHONE_14_0 = 140000; - -const int __IPHONE_14_1 = 140100; - -const int __IPHONE_14_2 = 140200; - -const int __IPHONE_14_3 = 140300; - -const int __IPHONE_14_5 = 140500; - -const int __IPHONE_14_4 = 140400; - -const int __IPHONE_14_6 = 140600; - -const int __IPHONE_14_7 = 140700; - -const int __IPHONE_14_8 = 140800; - -const int __IPHONE_15_0 = 150000; - -const int __IPHONE_15_1 = 150100; - -const int __IPHONE_15_2 = 150200; - -const int __IPHONE_15_3 = 150300; - -const int __IPHONE_15_4 = 150400; - -const int __IPHONE_15_5 = 150500; - -const int __IPHONE_15_6 = 150600; - -const int __IPHONE_15_7 = 150700; - -const int __IPHONE_15_8 = 150800; - -const int __IPHONE_16_0 = 160000; - -const int __IPHONE_16_1 = 160100; - -const int __IPHONE_16_2 = 160200; - -const int __IPHONE_16_3 = 160300; - -const int __IPHONE_16_4 = 160400; - -const int __IPHONE_16_5 = 160500; - -const int __IPHONE_16_6 = 160600; - -const int __IPHONE_16_7 = 160700; - -const int __IPHONE_17_0 = 170000; - -const int __IPHONE_17_1 = 170100; - -const int __IPHONE_17_2 = 170200; - -const int __IPHONE_17_3 = 170300; - -const int __IPHONE_17_4 = 170400; - -const int __IPHONE_17_5 = 170500; - -const int __IPHONE_18_0 = 180000; - -const int __IPHONE_18_1 = 180100; - -const int __IPHONE_18_2 = 180200; - -const int __WATCHOS_1_0 = 10000; - -const int __WATCHOS_2_0 = 20000; - -const int __WATCHOS_2_1 = 20100; - -const int __WATCHOS_2_2 = 20200; - -const int __WATCHOS_3_0 = 30000; - -const int __WATCHOS_3_1 = 30100; - -const int __WATCHOS_3_1_1 = 30101; - -const int __WATCHOS_3_2 = 30200; - -const int __WATCHOS_4_0 = 40000; - -const int __WATCHOS_4_1 = 40100; - -const int __WATCHOS_4_2 = 40200; - -const int __WATCHOS_4_3 = 40300; - -const int __WATCHOS_5_0 = 50000; - -const int __WATCHOS_5_1 = 50100; - -const int __WATCHOS_5_2 = 50200; - -const int __WATCHOS_5_3 = 50300; - -const int __WATCHOS_6_0 = 60000; - -const int __WATCHOS_6_1 = 60100; - -const int __WATCHOS_6_2 = 60200; - -const int __WATCHOS_7_0 = 70000; - -const int __WATCHOS_7_1 = 70100; - -const int __WATCHOS_7_2 = 70200; - -const int __WATCHOS_7_3 = 70300; - -const int __WATCHOS_7_4 = 70400; - -const int __WATCHOS_7_5 = 70500; - -const int __WATCHOS_7_6 = 70600; - -const int __WATCHOS_8_0 = 80000; - -const int __WATCHOS_8_1 = 80100; - -const int __WATCHOS_8_3 = 80300; - -const int __WATCHOS_8_4 = 80400; - -const int __WATCHOS_8_5 = 80500; - -const int __WATCHOS_8_6 = 80600; - -const int __WATCHOS_8_7 = 80700; - -const int __WATCHOS_8_8 = 80800; - -const int __WATCHOS_9_0 = 90000; - -const int __WATCHOS_9_1 = 90100; - -const int __WATCHOS_9_2 = 90200; - -const int __WATCHOS_9_3 = 90300; - -const int __WATCHOS_9_4 = 90400; - -const int __WATCHOS_9_5 = 90500; - -const int __WATCHOS_9_6 = 90600; - -const int __WATCHOS_10_0 = 100000; - -const int __WATCHOS_10_1 = 100100; - -const int __WATCHOS_10_2 = 100200; - -const int __WATCHOS_10_3 = 100300; - -const int __WATCHOS_10_4 = 100400; - -const int __WATCHOS_10_5 = 100500; - -const int __WATCHOS_11_0 = 110000; - -const int __WATCHOS_11_1 = 110100; - -const int __WATCHOS_11_2 = 110200; - -const int __TVOS_9_0 = 90000; - -const int __TVOS_9_1 = 90100; - -const int __TVOS_9_2 = 90200; - -const int __TVOS_10_0 = 100000; - -const int __TVOS_10_0_1 = 100001; - -const int __TVOS_10_1 = 100100; - -const int __TVOS_10_2 = 100200; - -const int __TVOS_11_0 = 110000; - -const int __TVOS_11_1 = 110100; - -const int __TVOS_11_2 = 110200; - -const int __TVOS_11_3 = 110300; - -const int __TVOS_11_4 = 110400; - -const int __TVOS_12_0 = 120000; - -const int __TVOS_12_1 = 120100; - -const int __TVOS_12_2 = 120200; - -const int __TVOS_12_3 = 120300; - -const int __TVOS_12_4 = 120400; - -const int __TVOS_13_0 = 130000; - -const int __TVOS_13_2 = 130200; - -const int __TVOS_13_3 = 130300; - -const int __TVOS_13_4 = 130400; - -const int __TVOS_14_0 = 140000; - -const int __TVOS_14_1 = 140100; - -const int __TVOS_14_2 = 140200; - -const int __TVOS_14_3 = 140300; - -const int __TVOS_14_5 = 140500; - -const int __TVOS_14_6 = 140600; - -const int __TVOS_14_7 = 140700; - -const int __TVOS_15_0 = 150000; - -const int __TVOS_15_1 = 150100; - -const int __TVOS_15_2 = 150200; - -const int __TVOS_15_3 = 150300; - -const int __TVOS_15_4 = 150400; - -const int __TVOS_15_5 = 150500; - -const int __TVOS_15_6 = 150600; - -const int __TVOS_16_0 = 160000; - -const int __TVOS_16_1 = 160100; - -const int __TVOS_16_2 = 160200; - -const int __TVOS_16_3 = 160300; - -const int __TVOS_16_4 = 160400; - -const int __TVOS_16_5 = 160500; - -const int __TVOS_16_6 = 160600; - -const int __TVOS_17_0 = 170000; - -const int __TVOS_17_1 = 170100; - -const int __TVOS_17_2 = 170200; - -const int __TVOS_17_3 = 170300; - -const int __TVOS_17_4 = 170400; - -const int __TVOS_17_5 = 170500; - -const int __TVOS_18_0 = 180000; - -const int __TVOS_18_1 = 180100; - -const int __TVOS_18_2 = 180200; - -const int __BRIDGEOS_2_0 = 20000; - -const int __BRIDGEOS_3_0 = 30000; - -const int __BRIDGEOS_3_1 = 30100; - -const int __BRIDGEOS_3_4 = 30400; - -const int __BRIDGEOS_4_0 = 40000; - -const int __BRIDGEOS_4_1 = 40100; - -const int __BRIDGEOS_5_0 = 50000; - -const int __BRIDGEOS_5_1 = 50100; - -const int __BRIDGEOS_5_3 = 50300; - -const int __BRIDGEOS_6_0 = 60000; - -const int __BRIDGEOS_6_2 = 60200; - -const int __BRIDGEOS_6_4 = 60400; - -const int __BRIDGEOS_6_5 = 60500; - -const int __BRIDGEOS_6_6 = 60600; - -const int __BRIDGEOS_7_0 = 70000; - -const int __BRIDGEOS_7_1 = 70100; - -const int __BRIDGEOS_7_2 = 70200; - -const int __BRIDGEOS_7_3 = 70300; - -const int __BRIDGEOS_7_4 = 70400; - -const int __BRIDGEOS_7_6 = 70600; - -const int __BRIDGEOS_8_0 = 80000; - -const int __BRIDGEOS_8_1 = 80100; - -const int __BRIDGEOS_8_2 = 80200; - -const int __BRIDGEOS_8_3 = 80300; - -const int __BRIDGEOS_8_4 = 80400; - -const int __BRIDGEOS_8_5 = 80500; - -const int __BRIDGEOS_9_0 = 90000; - -const int __BRIDGEOS_9_1 = 90100; - -const int __BRIDGEOS_9_2 = 90200; - -const int __DRIVERKIT_19_0 = 190000; - -const int __DRIVERKIT_20_0 = 200000; - -const int __DRIVERKIT_21_0 = 210000; - -const int __DRIVERKIT_22_0 = 220000; - -const int __DRIVERKIT_22_4 = 220400; - -const int __DRIVERKIT_22_5 = 220500; - -const int __DRIVERKIT_22_6 = 220600; - -const int __DRIVERKIT_23_0 = 230000; - -const int __DRIVERKIT_23_1 = 230100; - -const int __DRIVERKIT_23_2 = 230200; - -const int __DRIVERKIT_23_3 = 230300; - -const int __DRIVERKIT_23_4 = 230400; - -const int __DRIVERKIT_23_5 = 230500; - -const int __DRIVERKIT_24_0 = 240000; - -const int __DRIVERKIT_24_1 = 240100; - -const int __DRIVERKIT_24_2 = 240200; - -const int __VISIONOS_1_0 = 10000; - -const int __VISIONOS_1_1 = 10100; - -const int __VISIONOS_1_2 = 10200; - -const int __VISIONOS_2_0 = 20000; - -const int __VISIONOS_2_1 = 20100; - -const int __VISIONOS_2_2 = 20200; - -const int MAC_OS_X_VERSION_10_0 = 1000; - -const int MAC_OS_X_VERSION_10_1 = 1010; - -const int MAC_OS_X_VERSION_10_2 = 1020; - -const int MAC_OS_X_VERSION_10_3 = 1030; - -const int MAC_OS_X_VERSION_10_4 = 1040; - -const int MAC_OS_X_VERSION_10_5 = 1050; - -const int MAC_OS_X_VERSION_10_6 = 1060; - -const int MAC_OS_X_VERSION_10_7 = 1070; - -const int MAC_OS_X_VERSION_10_8 = 1080; - -const int MAC_OS_X_VERSION_10_9 = 1090; - -const int MAC_OS_X_VERSION_10_10 = 101000; - -const int MAC_OS_X_VERSION_10_10_2 = 101002; - -const int MAC_OS_X_VERSION_10_10_3 = 101003; - -const int MAC_OS_X_VERSION_10_11 = 101100; - -const int MAC_OS_X_VERSION_10_11_2 = 101102; - -const int MAC_OS_X_VERSION_10_11_3 = 101103; - -const int MAC_OS_X_VERSION_10_11_4 = 101104; - -const int MAC_OS_X_VERSION_10_12 = 101200; - -const int MAC_OS_X_VERSION_10_12_1 = 101201; - -const int MAC_OS_X_VERSION_10_12_2 = 101202; - -const int MAC_OS_X_VERSION_10_12_4 = 101204; - -const int MAC_OS_X_VERSION_10_13 = 101300; - -const int MAC_OS_X_VERSION_10_13_1 = 101301; - -const int MAC_OS_X_VERSION_10_13_2 = 101302; - -const int MAC_OS_X_VERSION_10_13_4 = 101304; - -const int MAC_OS_X_VERSION_10_14 = 101400; - -const int MAC_OS_X_VERSION_10_14_1 = 101401; - -const int MAC_OS_X_VERSION_10_14_4 = 101404; - -const int MAC_OS_X_VERSION_10_14_5 = 101405; - -const int MAC_OS_X_VERSION_10_14_6 = 101406; - -const int MAC_OS_X_VERSION_10_15 = 101500; - -const int MAC_OS_X_VERSION_10_15_1 = 101501; - -const int MAC_OS_X_VERSION_10_15_4 = 101504; - -const int MAC_OS_X_VERSION_10_16 = 101600; - -const int MAC_OS_VERSION_11_0 = 110000; - -const int MAC_OS_VERSION_11_1 = 110100; - -const int MAC_OS_VERSION_11_3 = 110300; - -const int MAC_OS_VERSION_11_4 = 110400; - -const int MAC_OS_VERSION_11_5 = 110500; - -const int MAC_OS_VERSION_11_6 = 110600; - -const int MAC_OS_VERSION_12_0 = 120000; - -const int MAC_OS_VERSION_12_1 = 120100; - -const int MAC_OS_VERSION_12_2 = 120200; - -const int MAC_OS_VERSION_12_3 = 120300; - -const int MAC_OS_VERSION_12_4 = 120400; - -const int MAC_OS_VERSION_12_5 = 120500; - -const int MAC_OS_VERSION_12_6 = 120600; - -const int MAC_OS_VERSION_12_7 = 120700; - -const int MAC_OS_VERSION_13_0 = 130000; - -const int MAC_OS_VERSION_13_1 = 130100; - -const int MAC_OS_VERSION_13_2 = 130200; - -const int MAC_OS_VERSION_13_3 = 130300; - -const int MAC_OS_VERSION_13_4 = 130400; - -const int MAC_OS_VERSION_13_5 = 130500; - -const int MAC_OS_VERSION_13_6 = 130600; - -const int MAC_OS_VERSION_14_0 = 140000; - -const int MAC_OS_VERSION_14_1 = 140100; - -const int MAC_OS_VERSION_14_2 = 140200; - -const int MAC_OS_VERSION_14_3 = 140300; - -const int MAC_OS_VERSION_14_4 = 140400; - -const int MAC_OS_VERSION_14_5 = 140500; - -const int MAC_OS_VERSION_15_0 = 150000; - -const int MAC_OS_VERSION_15_1 = 150100; - -const int MAC_OS_VERSION_15_2 = 150200; - -const int __MAC_OS_X_VERSION_MIN_REQUIRED = 150000; - -const int __MAC_OS_X_VERSION_MAX_ALLOWED = 150200; - -const int __ENABLE_LEGACY_MAC_AVAILABILITY = 1; - -const int __DARWIN_NSIG = 32; - -const int NSIG = 32; - -const int _ARM_SIGNAL_ = 1; - -const int SIGHUP = 1; - -const int SIGINT = 2; - -const int SIGQUIT = 3; - -const int SIGILL = 4; - -const int SIGTRAP = 5; - -const int SIGABRT = 6; - -const int SIGIOT = 6; - -const int SIGEMT = 7; - -const int SIGFPE = 8; - -const int SIGKILL = 9; - -const int SIGBUS = 10; - -const int SIGSEGV = 11; - -const int SIGSYS = 12; - -const int SIGPIPE = 13; - -const int SIGALRM = 14; - -const int SIGTERM = 15; - -const int SIGURG = 16; - -const int SIGSTOP = 17; - -const int SIGTSTP = 18; - -const int SIGCONT = 19; - -const int SIGCHLD = 20; - -const int SIGTTIN = 21; - -const int SIGTTOU = 22; - -const int SIGIO = 23; - -const int SIGXCPU = 24; - -const int SIGXFSZ = 25; - -const int SIGVTALRM = 26; - -const int SIGPROF = 27; - -const int SIGWINCH = 28; - -const int SIGINFO = 29; - -const int SIGUSR1 = 30; - -const int SIGUSR2 = 31; - -const int __DARWIN_OPAQUE_ARM_THREAD_STATE64 = 0; - -const int SIGEV_NONE = 0; - -const int SIGEV_SIGNAL = 1; - -const int SIGEV_THREAD = 3; - -const int ILL_NOOP = 0; - -const int ILL_ILLOPC = 1; - -const int ILL_ILLTRP = 2; - -const int ILL_PRVOPC = 3; - -const int ILL_ILLOPN = 4; - -const int ILL_ILLADR = 5; - -const int ILL_PRVREG = 6; - -const int ILL_COPROC = 7; - -const int ILL_BADSTK = 8; - -const int FPE_NOOP = 0; - -const int FPE_FLTDIV = 1; - -const int FPE_FLTOVF = 2; - -const int FPE_FLTUND = 3; - -const int FPE_FLTRES = 4; - -const int FPE_FLTINV = 5; - -const int FPE_FLTSUB = 6; - -const int FPE_INTDIV = 7; - -const int FPE_INTOVF = 8; - -const int SEGV_NOOP = 0; - -const int SEGV_MAPERR = 1; - -const int SEGV_ACCERR = 2; - -const int BUS_NOOP = 0; - -const int BUS_ADRALN = 1; - -const int BUS_ADRERR = 2; - -const int BUS_OBJERR = 3; - -const int TRAP_BRKPT = 1; - -const int TRAP_TRACE = 2; - -const int CLD_NOOP = 0; - -const int CLD_EXITED = 1; - -const int CLD_KILLED = 2; - -const int CLD_DUMPED = 3; - -const int CLD_TRAPPED = 4; - -const int CLD_STOPPED = 5; - -const int CLD_CONTINUED = 6; - -const int POLL_IN = 1; - -const int POLL_OUT = 2; - -const int POLL_MSG = 3; - -const int POLL_ERR = 4; - -const int POLL_PRI = 5; - -const int POLL_HUP = 6; - -const int SA_ONSTACK = 1; - -const int SA_RESTART = 2; - -const int SA_RESETHAND = 4; - -const int SA_NOCLDSTOP = 8; - -const int SA_NODEFER = 16; - -const int SA_NOCLDWAIT = 32; - -const int SA_SIGINFO = 64; - -const int SA_USERTRAMP = 256; - -const int SA_64REGSET = 512; - -const int SA_USERSPACE_MASK = 127; - -const int SIG_BLOCK = 1; - -const int SIG_UNBLOCK = 2; - -const int SIG_SETMASK = 3; - -const int SI_USER = 65537; - -const int SI_QUEUE = 65538; - -const int SI_TIMER = 65539; - -const int SI_ASYNCIO = 65540; - -const int SI_MESGQ = 65541; - -const int SS_ONSTACK = 1; - -const int SS_DISABLE = 4; - -const int MINSIGSTKSZ = 32768; - -const int SIGSTKSZ = 131072; - -const int SV_ONSTACK = 1; - -const int SV_INTERRUPT = 2; - -const int SV_RESETHAND = 4; - -const int SV_NODEFER = 16; - -const int SV_NOCLDSTOP = 8; - -const int SV_SIGINFO = 64; - -const int __WORDSIZE = 64; - -const int INT8_MAX = 127; - -const int INT16_MAX = 32767; - -const int INT32_MAX = 2147483647; - -const int INT64_MAX = 9223372036854775807; - -const int INT8_MIN = -128; - -const int INT16_MIN = -32768; - -const int INT32_MIN = -2147483648; - -const int INT64_MIN = -9223372036854775808; - -const int UINT8_MAX = 255; - -const int UINT16_MAX = 65535; - -const int UINT32_MAX = 4294967295; - -const int UINT64_MAX = -1; - -const int INT_LEAST8_MIN = -128; - -const int INT_LEAST16_MIN = -32768; - -const int INT_LEAST32_MIN = -2147483648; - -const int INT_LEAST64_MIN = -9223372036854775808; - -const int INT_LEAST8_MAX = 127; - -const int INT_LEAST16_MAX = 32767; - -const int INT_LEAST32_MAX = 2147483647; - -const int INT_LEAST64_MAX = 9223372036854775807; - -const int UINT_LEAST8_MAX = 255; - -const int UINT_LEAST16_MAX = 65535; - -const int UINT_LEAST32_MAX = 4294967295; - -const int UINT_LEAST64_MAX = -1; - -const int INT_FAST8_MIN = -128; - -const int INT_FAST16_MIN = -32768; - -const int INT_FAST32_MIN = -2147483648; - -const int INT_FAST64_MIN = -9223372036854775808; - -const int INT_FAST8_MAX = 127; - -const int INT_FAST16_MAX = 32767; - -const int INT_FAST32_MAX = 2147483647; - -const int INT_FAST64_MAX = 9223372036854775807; - -const int UINT_FAST8_MAX = 255; - -const int UINT_FAST16_MAX = 65535; - -const int UINT_FAST32_MAX = 4294967295; - -const int UINT_FAST64_MAX = -1; - -const int INTPTR_MAX = 9223372036854775807; - -const int INTPTR_MIN = -9223372036854775808; - -const int UINTPTR_MAX = -1; - -const int INTMAX_MAX = 9223372036854775807; - -const int UINTMAX_MAX = -1; - -const int INTMAX_MIN = -9223372036854775808; - -const int PTRDIFF_MIN = -9223372036854775808; - -const int PTRDIFF_MAX = 9223372036854775807; - -const int SIZE_MAX = -1; - -const int RSIZE_MAX = 9223372036854775807; - -const int WCHAR_MAX = 2147483647; - -const int WCHAR_MIN = -2147483648; - -const int WINT_MIN = -2147483648; - -const int WINT_MAX = 2147483647; - -const int SIG_ATOMIC_MIN = -2147483648; - -const int SIG_ATOMIC_MAX = 2147483647; - -const int PRIO_PROCESS = 0; - -const int PRIO_PGRP = 1; - -const int PRIO_USER = 2; - -const int PRIO_DARWIN_THREAD = 3; - -const int PRIO_DARWIN_PROCESS = 4; - -const int PRIO_MIN = -20; - -const int PRIO_MAX = 20; - -const int PRIO_DARWIN_BG = 4096; - -const int PRIO_DARWIN_NONUI = 4097; - -const int RUSAGE_SELF = 0; - -const int RUSAGE_CHILDREN = -1; - -const int RUSAGE_INFO_V0 = 0; - -const int RUSAGE_INFO_V1 = 1; - -const int RUSAGE_INFO_V2 = 2; - -const int RUSAGE_INFO_V3 = 3; - -const int RUSAGE_INFO_V4 = 4; - -const int RUSAGE_INFO_V5 = 5; - -const int RUSAGE_INFO_V6 = 6; - -const int RUSAGE_INFO_CURRENT = 6; - -const int RU_PROC_RUNS_RESLIDE = 1; - -const int RLIM_INFINITY = 9223372036854775807; - -const int RLIM_SAVED_MAX = 9223372036854775807; - -const int RLIM_SAVED_CUR = 9223372036854775807; - -const int RLIMIT_CPU = 0; - -const int RLIMIT_FSIZE = 1; - -const int RLIMIT_DATA = 2; - -const int RLIMIT_STACK = 3; - -const int RLIMIT_CORE = 4; - -const int RLIMIT_AS = 5; - -const int RLIMIT_RSS = 5; - -const int RLIMIT_MEMLOCK = 6; - -const int RLIMIT_NPROC = 7; - -const int RLIMIT_NOFILE = 8; - -const int RLIM_NLIMITS = 9; - -const int _RLIMIT_POSIX_FLAG = 4096; - -const int RLIMIT_WAKEUPS_MONITOR = 1; - -const int RLIMIT_CPU_USAGE_MONITOR = 2; - -const int RLIMIT_THREAD_CPULIMITS = 3; - -const int RLIMIT_FOOTPRINT_INTERVAL = 4; - -const int WAKEMON_ENABLE = 1; - -const int WAKEMON_DISABLE = 2; - -const int WAKEMON_GET_PARAMS = 4; - -const int WAKEMON_SET_DEFAULTS = 8; - -const int WAKEMON_MAKE_FATAL = 16; - -const int CPUMON_MAKE_FATAL = 4096; - -const int FOOTPRINT_INTERVAL_RESET = 1; - -const int IOPOL_TYPE_DISK = 0; - -const int IOPOL_TYPE_VFS_ATIME_UPDATES = 2; - -const int IOPOL_TYPE_VFS_MATERIALIZE_DATALESS_FILES = 3; - -const int IOPOL_TYPE_VFS_STATFS_NO_DATA_VOLUME = 4; - -const int IOPOL_TYPE_VFS_TRIGGER_RESOLVE = 5; - -const int IOPOL_TYPE_VFS_IGNORE_CONTENT_PROTECTION = 6; - -const int IOPOL_TYPE_VFS_IGNORE_PERMISSIONS = 7; - -const int IOPOL_TYPE_VFS_SKIP_MTIME_UPDATE = 8; - -const int IOPOL_TYPE_VFS_ALLOW_LOW_SPACE_WRITES = 9; - -const int IOPOL_TYPE_VFS_DISALLOW_RW_FOR_O_EVTONLY = 10; - -const int IOPOL_SCOPE_PROCESS = 0; - -const int IOPOL_SCOPE_THREAD = 1; - -const int IOPOL_SCOPE_DARWIN_BG = 2; - -const int IOPOL_DEFAULT = 0; - -const int IOPOL_IMPORTANT = 1; - -const int IOPOL_PASSIVE = 2; - -const int IOPOL_THROTTLE = 3; - -const int IOPOL_UTILITY = 4; - -const int IOPOL_STANDARD = 5; - -const int IOPOL_APPLICATION = 5; - -const int IOPOL_NORMAL = 1; - -const int IOPOL_ATIME_UPDATES_DEFAULT = 0; - -const int IOPOL_ATIME_UPDATES_OFF = 1; - -const int IOPOL_MATERIALIZE_DATALESS_FILES_DEFAULT = 0; - -const int IOPOL_MATERIALIZE_DATALESS_FILES_OFF = 1; - -const int IOPOL_MATERIALIZE_DATALESS_FILES_ON = 2; - -const int IOPOL_VFS_STATFS_NO_DATA_VOLUME_DEFAULT = 0; - -const int IOPOL_VFS_STATFS_FORCE_NO_DATA_VOLUME = 1; - -const int IOPOL_VFS_TRIGGER_RESOLVE_DEFAULT = 0; - -const int IOPOL_VFS_TRIGGER_RESOLVE_OFF = 1; - -const int IOPOL_VFS_CONTENT_PROTECTION_DEFAULT = 0; - -const int IOPOL_VFS_CONTENT_PROTECTION_IGNORE = 1; - -const int IOPOL_VFS_IGNORE_PERMISSIONS_OFF = 0; - -const int IOPOL_VFS_IGNORE_PERMISSIONS_ON = 1; - -const int IOPOL_VFS_SKIP_MTIME_UPDATE_OFF = 0; - -const int IOPOL_VFS_SKIP_MTIME_UPDATE_ON = 1; - -const int IOPOL_VFS_ALLOW_LOW_SPACE_WRITES_OFF = 0; - -const int IOPOL_VFS_ALLOW_LOW_SPACE_WRITES_ON = 1; - -const int IOPOL_VFS_DISALLOW_RW_FOR_O_EVTONLY_DEFAULT = 0; - -const int IOPOL_VFS_DISALLOW_RW_FOR_O_EVTONLY_ON = 1; - -const int IOPOL_VFS_NOCACHE_WRITE_FS_BLKSIZE_DEFAULT = 0; - -const int IOPOL_VFS_NOCACHE_WRITE_FS_BLKSIZE_ON = 1; - -const int WNOHANG = 1; - -const int WUNTRACED = 2; - -const int WCOREFLAG = 128; - -const int _WSTOPPED = 127; - -const int WEXITED = 4; - -const int WSTOPPED = 8; - -const int WCONTINUED = 16; - -const int WNOWAIT = 32; - -const int WAIT_ANY = -1; - -const int WAIT_MYPGRP = 0; - -const int _QUAD_HIGHWORD = 1; - -const int _QUAD_LOWWORD = 0; - -const int __DARWIN_LITTLE_ENDIAN = 1234; - -const int __DARWIN_BIG_ENDIAN = 4321; - -const int __DARWIN_PDP_ENDIAN = 3412; - -const int LITTLE_ENDIAN = 1234; - -const int BIG_ENDIAN = 4321; - -const int PDP_ENDIAN = 3412; - -const int __DARWIN_BYTE_ORDER = 1234; - -const int BYTE_ORDER = 1234; - -const int EXIT_FAILURE = 1; - -const int EXIT_SUCCESS = 0; - -const int RAND_MAX = 2147483647; diff --git a/lib/clash/interface.dart b/lib/clash/interface.dart deleted file mode 100644 index bf7a30c..0000000 --- a/lib/clash/interface.dart +++ /dev/null @@ -1,421 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:isolate'; - -import 'package:fl_clash/clash/message.dart'; -import 'package:fl_clash/common/common.dart'; -import 'package:fl_clash/enum/enum.dart'; -import 'package:fl_clash/models/models.dart'; - -mixin ClashInterface { - Future init(InitParams params); - - Future preload(); - - Future shutdown(); - - Future get isInit; - - Future forceGc(); - - FutureOr validateConfig(String data); - - FutureOr getConfig(String path); - - Future asyncTestDelay(String url, String proxyName); - - FutureOr updateConfig(UpdateParams updateParams); - - FutureOr setupConfig(SetupParams setupParams); - - FutureOr getProxies(); - - FutureOr changeProxy(ChangeProxyParams changeProxyParams); - - Future startListener(); - - Future stopListener(); - - FutureOr getExternalProviders(); - - FutureOr? getExternalProvider(String externalProviderName); - - Future updateGeoData(UpdateGeoDataParams params); - - Future sideLoadExternalProvider({ - required String providerName, - required String data, - }); - - Future updateExternalProvider(String providerName); - - FutureOr getTraffic(); - - FutureOr getTotalTraffic(); - - FutureOr getCountryCode(String ip); - - FutureOr getMemory(); - - FutureOr resetTraffic(); - - FutureOr startLog(); - - FutureOr stopLog(); - - Future crash(); - - FutureOr getConnections(); - - FutureOr closeConnection(String id); - - FutureOr closeConnections(); - - FutureOr resetConnections(); - - Future setState(CoreState state); -} - -mixin AndroidClashInterface { - Future updateDns(String value); - - Future getAndroidVpnOptions(); - - Future getCurrentProfileName(); - - Future getRunTime(); -} - -abstract class ClashHandlerInterface with ClashInterface { - Map callbackCompleterMap = {}; - - Future handleResult(ActionResult result) async { - final completer = callbackCompleterMap[result.id]; - try { - switch (result.method) { - case ActionMethod.message: - clashMessage.controller.add(result.data); - completer?.complete(true); - return; - case ActionMethod.getConfig: - completer?.complete(result.toResult); - return; - default: - completer?.complete(result.data); - return; - } - } catch (e) { - commonPrint.log('${result.id} error $e'); - } - } - - void sendMessage(String message); - - FutureOr reStart(); - - FutureOr destroy(); - - Future invoke({ - required ActionMethod method, - dynamic data, - Duration? timeout, - FutureOr Function()? onTimeout, - T? defaultValue, - }) async { - final id = '${method.name}#${utils.id}'; - - callbackCompleterMap[id] = Completer(); - - dynamic mDefaultValue = defaultValue; - if (mDefaultValue == null) { - if (T == String) { - mDefaultValue = ''; - } else if (T == bool) { - mDefaultValue = false; - } else if (T == Map) { - mDefaultValue = {}; - } - } - - sendMessage( - json.encode( - Action( - id: id, - method: method, - data: data, - ), - ), - ); - - return (callbackCompleterMap[id] as Completer).safeFuture( - timeout: timeout, - onLast: () { - callbackCompleterMap.remove(id); - }, - onTimeout: onTimeout ?? - () { - return mDefaultValue; - }, - functionName: id, - ); - } - - @override - Future init(InitParams params) { - return invoke( - method: ActionMethod.initClash, - data: json.encode(params), - ); - } - - @override - Future setState(CoreState state) { - return invoke( - method: ActionMethod.setState, - data: json.encode(state), - ); - } - - @override - shutdown() async { - return await invoke( - method: ActionMethod.shutdown, - timeout: Duration(seconds: 1), - ); - } - - @override - Future get isInit { - return invoke( - method: ActionMethod.getIsInit, - ); - } - - @override - Future forceGc() { - return invoke( - method: ActionMethod.forceGc, - ); - } - - @override - FutureOr validateConfig(String data) { - return invoke( - method: ActionMethod.validateConfig, - data: data, - ); - } - - @override - Future updateConfig(UpdateParams updateParams) async { - return await invoke( - method: ActionMethod.updateConfig, - data: json.encode(updateParams), - timeout: Duration(minutes: 2), - ); - } - - @override - Future getConfig(String path) async { - final res = await invoke( - method: ActionMethod.getConfig, - data: path, - timeout: Duration(minutes: 2), - defaultValue: Result.success({}), - ); - return res; - } - - @override - Future setupConfig(SetupParams setupParams) async { - final data = await Isolate.run(() => json.encode(setupParams)); - return await invoke( - method: ActionMethod.setupConfig, - data: data, - timeout: Duration(minutes: 2), - ); - } - - @override - Future crash() { - return invoke( - method: ActionMethod.crash, - ); - } - - @override - Future getProxies() { - return invoke( - method: ActionMethod.getProxies, - timeout: Duration(seconds: 5), - ); - } - - @override - FutureOr changeProxy(ChangeProxyParams changeProxyParams) { - return invoke( - method: ActionMethod.changeProxy, - data: json.encode(changeProxyParams), - ); - } - - @override - FutureOr getExternalProviders() { - return invoke( - method: ActionMethod.getExternalProviders, - ); - } - - @override - FutureOr getExternalProvider(String externalProviderName) { - return invoke( - method: ActionMethod.getExternalProvider, - data: externalProviderName, - ); - } - - @override - Future updateGeoData(UpdateGeoDataParams params) { - return invoke( - method: ActionMethod.updateGeoData, - data: json.encode(params), - timeout: Duration(minutes: 1)); - } - - @override - Future sideLoadExternalProvider({ - required String providerName, - required String data, - }) { - return invoke( - method: ActionMethod.sideLoadExternalProvider, - data: json.encode({ - 'providerName': providerName, - 'data': data, - }), - ); - } - - @override - Future updateExternalProvider(String providerName) { - return invoke( - method: ActionMethod.updateExternalProvider, - data: providerName, - timeout: Duration(minutes: 1), - ); - } - - @override - FutureOr getConnections() { - return invoke( - method: ActionMethod.getConnections, - ); - } - - @override - Future closeConnections() { - return invoke( - method: ActionMethod.closeConnections, - ); - } - - @override - Future resetConnections() { - return invoke( - method: ActionMethod.resetConnections, - ); - } - - @override - Future closeConnection(String id) { - return invoke( - method: ActionMethod.closeConnection, - data: id, - ); - } - - @override - FutureOr getTotalTraffic() { - return invoke( - method: ActionMethod.getTotalTraffic, - ); - } - - @override - FutureOr getTraffic() { - return invoke( - method: ActionMethod.getTraffic, - ); - } - - @override - resetTraffic() { - invoke(method: ActionMethod.resetTraffic); - } - - @override - startLog() { - invoke(method: ActionMethod.startLog); - } - - @override - stopLog() { - invoke( - method: ActionMethod.stopLog, - ); - } - - @override - Future startListener() { - return invoke( - method: ActionMethod.startListener, - ); - } - - @override - stopListener() { - return invoke( - method: ActionMethod.stopListener, - ); - } - - @override - Future asyncTestDelay(String url, String proxyName) { - final delayParams = { - 'proxy-name': proxyName, - 'timeout': httpTimeoutDuration.inMilliseconds, - 'test-url': url, - }; - return invoke( - method: ActionMethod.asyncTestDelay, - data: json.encode(delayParams), - timeout: Duration( - milliseconds: 6000, - ), - onTimeout: () { - return json.encode( - Delay( - name: proxyName, - value: -1, - url: url, - ), - ); - }, - ); - } - - @override - FutureOr getCountryCode(String ip) { - return invoke( - method: ActionMethod.getCountryCode, - data: ip, - ); - } - - @override - FutureOr getMemory() { - return invoke( - method: ActionMethod.getMemory, - ); - } -} diff --git a/lib/clash/lib.dart b/lib/clash/lib.dart deleted file mode 100644 index b15a50a..0000000 --- a/lib/clash/lib.dart +++ /dev/null @@ -1,292 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:ffi'; -import 'dart:isolate'; -import 'dart:ui'; - -import 'package:ffi/ffi.dart'; -import 'package:fl_clash/common/common.dart'; -import 'package:fl_clash/enum/enum.dart'; -import 'package:fl_clash/models/models.dart'; -import 'package:fl_clash/plugins/service.dart'; -import 'package:fl_clash/state.dart'; - -import 'generated/clash_ffi.dart'; -import 'interface.dart'; - -class ClashLib extends ClashHandlerInterface with AndroidClashInterface { - static ClashLib? _instance; - Completer _canSendCompleter = Completer(); - SendPort? sendPort; - final receiverPort = ReceivePort(); - - ClashLib._internal() { - _initService(); - } - - @override - preload() { - return _canSendCompleter.future; - } - - Future _initService() async { - await service?.destroy(); - _registerMainPort(receiverPort.sendPort); - receiverPort.listen((message) { - if (message is SendPort) { - if (_canSendCompleter.isCompleted) { - sendPort = null; - _canSendCompleter = Completer(); - } - sendPort = message; - _canSendCompleter.complete(true); - } else { - handleResult( - ActionResult.fromJson(json.decode( - message, - )), - ); - } - }); - await service?.init(); - } - - void _registerMainPort(SendPort sendPort) { - IsolateNameServer.removePortNameMapping(mainIsolate); - IsolateNameServer.registerPortWithName(sendPort, mainIsolate); - } - - factory ClashLib() { - _instance ??= ClashLib._internal(); - return _instance!; - } - - @override - destroy() async { - await service?.destroy(); - return true; - } - - @override - reStart() { - _initService(); - } - - @override - Future shutdown() async { - await super.shutdown(); - destroy(); - return true; - } - - @override - sendMessage(String message) async { - await _canSendCompleter.future; - sendPort?.send(message); - } - - // @override - // Future stopTun() { - // return invoke( - // method: ActionMethod.stopTun, - // ); - // } - - @override - Future getAndroidVpnOptions() async { - final res = await invoke( - method: ActionMethod.getAndroidVpnOptions, - ); - if (res.isEmpty) { - return null; - } - return AndroidVpnOptions.fromJson(json.decode(res)); - } - - @override - Future updateDns(String value) { - return invoke( - method: ActionMethod.updateDns, - data: value, - ); - } - - @override - Future getRunTime() async { - final runTimeString = await invoke( - method: ActionMethod.getRunTime, - ); - if (runTimeString.isEmpty) { - return null; - } - return DateTime.fromMillisecondsSinceEpoch(int.parse(runTimeString)); - } - - @override - Future getCurrentProfileName() { - return invoke( - method: ActionMethod.getCurrentProfileName, - ); - } -} - -class ClashLibHandler { - static ClashLibHandler? _instance; - - late final ClashFFI clashFFI; - - late final DynamicLibrary lib; - - ClashLibHandler._internal() { - lib = DynamicLibrary.open('libclash.so'); - clashFFI = ClashFFI(lib); - clashFFI.initNativeApiBridge( - NativeApi.initializeApiDLData, - ); - } - - factory ClashLibHandler() { - _instance ??= ClashLibHandler._internal(); - return _instance!; - } - - Future invokeAction(String actionParams) { - final completer = Completer(); - final receiver = ReceivePort(); - receiver.listen((message) { - if (!completer.isCompleted) { - completer.complete(message); - receiver.close(); - } - }); - final actionParamsChar = actionParams.toNativeUtf8().cast(); - clashFFI.invokeAction( - actionParamsChar, - receiver.sendPort.nativePort, - ); - malloc.free(actionParamsChar); - return completer.future; - } - - void attachMessagePort(int messagePort) { - clashFFI.attachMessagePort( - messagePort, - ); - } - - void updateDns(String dns) { - final dnsChar = dns.toNativeUtf8().cast(); - clashFFI.updateDns(dnsChar); - malloc.free(dnsChar); - } - - void setState(CoreState state) { - final stateChar = json.encode(state).toNativeUtf8().cast(); - clashFFI.setState(stateChar); - malloc.free(stateChar); - } - - String getCurrentProfileName() { - final currentProfileRaw = clashFFI.getCurrentProfileName(); - final currentProfile = currentProfileRaw.cast().toDartString(); - clashFFI.freeCString(currentProfileRaw); - return currentProfile; - } - - AndroidVpnOptions getAndroidVpnOptions() { - final vpnOptionsRaw = clashFFI.getAndroidVpnOptions(); - final vpnOptions = json.decode(vpnOptionsRaw.cast().toDartString()); - clashFFI.freeCString(vpnOptionsRaw); - return AndroidVpnOptions.fromJson(vpnOptions); - } - - Traffic getTraffic() { - final trafficRaw = clashFFI.getTraffic(); - final trafficString = trafficRaw.cast().toDartString(); - clashFFI.freeCString(trafficRaw); - if (trafficString.isEmpty) { - return Traffic(); - } - return Traffic.fromMap(json.decode(trafficString)); - } - - Traffic getTotalTraffic(bool value) { - final trafficRaw = clashFFI.getTotalTraffic(); - final trafficString = trafficRaw.cast().toDartString(); - clashFFI.freeCString(trafficRaw); - if (trafficString.isEmpty) { - return Traffic(); - } - return Traffic.fromMap(json.decode(trafficString)); - } - - Future startListener() async { - clashFFI.startListener(); - return true; - } - - Future stopListener() async { - clashFFI.stopListener(); - return true; - } - - DateTime? getRunTime() { - final runTimeRaw = clashFFI.getRunTime(); - final runTimeString = runTimeRaw.cast().toDartString(); - if (runTimeString.isEmpty) { - return null; - } - return DateTime.fromMillisecondsSinceEpoch(int.parse(runTimeString)); - } - - Future> getConfig(String id) async { - final path = await appPath.getProfilePath(id); - final pathChar = path.toNativeUtf8().cast(); - final configRaw = clashFFI.getConfig(pathChar); - final configString = configRaw.cast().toDartString(); - if (configString.isEmpty) { - return {}; - } - final config = json.decode(configString); - malloc.free(pathChar); - clashFFI.freeCString(configRaw); - return config; - } - - Future quickStart( - InitParams initParams, - SetupParams setupParams, - CoreState state, - ) { - final completer = Completer(); - final receiver = ReceivePort(); - receiver.listen((message) { - if (!completer.isCompleted) { - completer.complete(message); - receiver.close(); - } - }); - final params = json.encode(setupParams); - final initValue = json.encode(initParams); - final stateParams = json.encode(state); - final initParamsChar = initValue.toNativeUtf8().cast(); - final paramsChar = params.toNativeUtf8().cast(); - final stateParamsChar = stateParams.toNativeUtf8().cast(); - clashFFI.quickStart( - initParamsChar, - paramsChar, - stateParamsChar, - receiver.sendPort.nativePort, - ); - malloc.free(initParamsChar); - malloc.free(paramsChar); - malloc.free(stateParamsChar); - return completer.future; - } -} - -ClashLib? get clashLib => - system.isAndroid && !globalState.isService ? ClashLib() : null; - -ClashLibHandler? get clashLibHandler => - system.isAndroid && globalState.isService ? ClashLibHandler() : null; diff --git a/lib/clash/message.dart b/lib/clash/message.dart deleted file mode 100644 index 06794ab..0000000 --- a/lib/clash/message.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'dart:async'; - -import 'package:fl_clash/enum/enum.dart'; -import 'package:fl_clash/models/models.dart'; -import 'package:flutter/foundation.dart'; - -class ClashMessage { - final controller = StreamController>(); - - ClashMessage._() { - controller.stream.listen( - (message) { - if (message.isEmpty) { - return; - } - final m = AppMessage.fromJson(message); - for (final AppMessageListener listener in _listeners) { - switch (m.type) { - case AppMessageType.log: - listener.onLog(Log.fromJson(m.data)); - break; - case AppMessageType.delay: - listener.onDelay(Delay.fromJson(m.data)); - break; - case AppMessageType.request: - listener.onRequest(TrackerInfo.fromJson(m.data)); - break; - case AppMessageType.loaded: - listener.onLoaded(m.data); - break; - } - } - }, - ); - } - - static final ClashMessage instance = ClashMessage._(); - - final ObserverList _listeners = - ObserverList(); - - bool get hasListeners { - return _listeners.isNotEmpty; - } - - void addListener(AppMessageListener listener) { - _listeners.add(listener); - } - - void removeListener(AppMessageListener listener) { - _listeners.remove(listener); - } -} - -final clashMessage = ClashMessage.instance; diff --git a/lib/clash/service.dart b/lib/clash/service.dart deleted file mode 100644 index 04de6fe..0000000 --- a/lib/clash/service.dart +++ /dev/null @@ -1,160 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:fl_clash/clash/interface.dart'; -import 'package:fl_clash/common/common.dart'; -import 'package:fl_clash/models/core.dart'; -import 'package:fl_clash/state.dart'; - -class ClashService extends ClashHandlerInterface { - static ClashService? _instance; - - Completer serverCompleter = Completer(); - - Completer socketCompleter = Completer(); - - bool isStarting = false; - - Process? process; - - factory ClashService() { - _instance ??= ClashService._internal(); - return _instance!; - } - - ClashService._internal() { - _initServer(); - reStart(); - } - - Future _initServer() async { - runZonedGuarded(() async { - final address = !system.isWindows - ? InternetAddress( - unixSocketPath, - type: InternetAddressType.unix, - ) - : InternetAddress( - localhost, - type: InternetAddressType.IPv4, - ); - await _deleteSocketFile(); - final server = await ServerSocket.bind( - address, - 0, - shared: true, - ); - serverCompleter.complete(server); - await for (final socket in server) { - await _destroySocket(); - socketCompleter.complete(socket); - socket - .transform(uint8ListToListIntConverter) - .transform(utf8.decoder) - .transform(LineSplitter()) - .listen( - (data) { - handleResult( - ActionResult.fromJson( - json.decode(data.trim()), - ), - ); - }, - ); - } - }, (error, stack) { - commonPrint.log(error.toString()); - if (error is SocketException) { - globalState.showNotifier(error.toString()); - // globalState.appController.restartCore(); - } - }); - } - - @override - reStart() async { - if (isStarting == true) { - return; - } - isStarting = true; - socketCompleter = Completer(); - if (process != null) { - await shutdown(); - } - final serverSocket = await serverCompleter.future; - final arg = system.isWindows - ? '${serverSocket.port}' - : serverSocket.address.address; - if (system.isWindows && await system.checkIsAdmin()) { - final isSuccess = await request.startCoreByHelper(arg); - if (isSuccess) { - return; - } - } - process = await Process.start( - appPath.corePath, - [ - arg, - ], - ); - process?.stdout.listen((_) {}); - process?.stderr.listen((e) { - final error = utf8.decode(e); - if (error.isNotEmpty) { - commonPrint.log(error); - } - }); - isStarting = false; - } - - @override - destroy() async { - final server = await serverCompleter.future; - await server.close(); - await _deleteSocketFile(); - return true; - } - - @override - sendMessage(String message) async { - final socket = await socketCompleter.future; - socket.writeln(message); - } - - Future _deleteSocketFile() async { - if (!system.isWindows) { - final file = File(unixSocketPath); - if (await file.exists()) { - await file.delete(); - } - } - } - - Future _destroySocket() async { - if (socketCompleter.isCompleted) { - final lastSocket = await socketCompleter.future; - await lastSocket.close(); - socketCompleter = Completer(); - } - } - - @override - shutdown() async { - if (system.isWindows) { - await request.stopCoreByHelper(); - } - await _destroySocket(); - process?.kill(); - process = null; - return true; - } - - @override - Future preload() async { - await serverCompleter.future; - return true; - } -} - -final clashService = system.isDesktop ? ClashService() : null; diff --git a/lib/common/android.dart b/lib/common/android.dart index 09aa7bf..2fe42c2 100644 --- a/lib/common/android.dart +++ b/lib/common/android.dart @@ -1,13 +1,12 @@ -import 'package:fl_clash/plugins/app.dart'; +import 'package:fl_clash/plugins/service.dart'; import 'package:fl_clash/state.dart'; import 'system.dart'; class Android { Future init() async { - app?.onExit = () async { - await globalState.appController.savePreferences(); - }; + await service?.init(); + await service?.syncAndroidState(globalState.getAndroidState()); } } diff --git a/lib/common/archive.dart b/lib/common/archive.dart index ab0512a..ea94206 100644 --- a/lib/common/archive.dart +++ b/lib/common/archive.dart @@ -14,16 +14,15 @@ extension ArchiveExt on Archive { final data = entity.readAsBytesSync(); final archiveFile = ArchiveFile(relativePath, data.length, data); addFile(archiveFile); - } else if (entity is Directory) { - addDirectoryToArchive(entity.path, parentPath); } + // else if (entity is Directory) { + // addDirectoryToArchive(entity.path, parentPath); + // } } } - void add(String name, T raw) { + void addTextFile(String name, T raw) { final data = json.encode(raw); - addFile( - ArchiveFile(name, data.length, data), - ); + addFile(ArchiveFile.string(name, data)); } } diff --git a/lib/common/cache.dart b/lib/common/cache.dart new file mode 100644 index 0000000..177cf02 --- /dev/null +++ b/lib/common/cache.dart @@ -0,0 +1,190 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:fl_clash/common/common.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; + +class LocalImageCacheManager extends CacheManager { + static const key = 'LocalImageCacheData'; + + static final LocalImageCacheManager _instance = LocalImageCacheManager._(); + + factory LocalImageCacheManager() { + return _instance; + } + + LocalImageCacheManager._() + : super( + Config( + key, + stalePeriod: Duration(days: 30), + maxNrOfCacheObjects: 1000, + fileService: _LocalImageCacheFileService(), + ), + ); +} + +class _LocalImageCacheFileService extends FileService { + _LocalImageCacheFileService(); + + @override + Future get( + String url, { + Map? headers, + }) async { + final response = await request.dio.get( + url, + options: Options(headers: headers, responseType: ResponseType.stream), + ); + return _LocalImageResponse(response); + } +} + +class _LocalImageResponse implements FileServiceResponse { + _LocalImageResponse(this._response); + + final DateTime _receivedTime = DateTime.now(); + + final Response _response; + + @override + int get statusCode => _response.statusCode ?? 0; + + @override + Stream> get content => + _response.data?.stream.transform(uint8ListToListIntConverter) ?? + Stream.empty(); + + @override + int? get contentLength => _response.data?.contentLength; + + @override + DateTime get validTill { + // Without a cache-control header we keep the file for a week + var ageDuration = const Duration(days: 7); + final controlHeader = _response.headers.value( + HttpHeaders.cacheControlHeader, + ); + if (controlHeader != null) { + final controlSettings = controlHeader.split(','); + for (final setting in controlSettings) { + final sanitizedSetting = setting.trim().toLowerCase(); + if (sanitizedSetting.startsWith('max-age=')) { + final validSeconds = + int.tryParse(sanitizedSetting.split('=')[1]) ?? 0; + if (validSeconds > 0) { + ageDuration = Duration(seconds: validSeconds); + } + } + } + } + if (ageDuration > const Duration(days: 7)) { + return _receivedTime.add(ageDuration); + } + return _receivedTime.add(const Duration(days: 7)); + } + + @override + String? get eTag => _response.headers.value(HttpHeaders.etagHeader); + + @override + String get fileExtension { + var fileExtension = ''; + final contentTypeHeader = _response.headers.value( + HttpHeaders.contentTypeHeader, + ); + if (contentTypeHeader != null) { + final contentType = ContentType.parse(contentTypeHeader); + fileExtension = contentType.fileExtension; + } + return fileExtension; + } +} + +extension ContentTypeConverter on ContentType { + String get fileExtension => mimeTypes[mimeType] ?? '.$subType'; +} + +const mimeTypes = { + 'application/vnd.android.package-archive': '.apk', + 'application/epub+zip': '.epub', + 'application/gzip': '.gz', + 'application/java-archive': '.jar', + 'application/json': '.json', + 'application/ld+json': '.jsonld', + 'application/msword': '.doc', + 'application/octet-stream': '.bin', + 'application/ogg': '.ogx', + 'application/pdf': '.pdf', + 'application/php': '.php', + 'application/rtf': '.rtf', + 'application/vnd.amazon.ebook': '.azw', + 'application/vnd.apple.installer+xml': '.mpkg', + 'application/vnd.mozilla.xul+xml': '.xul', + 'application/vnd.ms-excel': '.xls', + 'application/vnd.ms-fontobject': '.eot', + 'application/vnd.ms-powerpoint': '.ppt', + 'application/vnd.oasis.opendocument.presentation': '.odp', + 'application/vnd.oasis.opendocument.spreadsheet': '.ods', + 'application/vnd.oasis.opendocument.text': '.odt', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation': + '.pptx', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': '.xlsx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': + '.docx', + 'application/vnd.rar': '.rar', + 'application/vnd.visio': '.vsd', + 'application/x-7z-compressed': '.7z', + 'application/x-abiword': '.abw', + 'application/x-bzip': '.bz', + 'application/x-bzip2': '.bz2', + 'application/x-csh': '.csh', + 'application/x-freearc': '.arc', + 'application/x-sh': '.sh', + 'application/x-shockwave-flash': '.swf', + 'application/x-tar': '.tar', + 'application/xhtml+xml': '.xhtml', + 'application/xml': '.xml', + 'application/zip': '.zip', + 'audio/3gpp': '.3gp', + 'audio/3gpp2': '.3g2', + 'audio/aac': '.aac', + 'audio/x-aac': '.aac', + 'audio/midi': '.midi', + 'audio/x-midi': '.midi', + 'audio/x-m4a': '.m4a', + 'audio/m4a': '.m4a', + 'audio/mpeg': '.mp3', + 'audio/ogg': '.oga', + 'audio/opus': '.opus', + 'audio/wav': '.wav', + 'audio/x-wav': '.wav', + 'audio/webm': '.weba', + 'font/otf': '.otf', + 'font/ttf': '.ttf', + 'font/woff': '.woff', + 'font/woff2': '.woff2', + 'image/bmp': '.bmp', + 'image/gif': '.gif', + 'image/jpeg': '.jpg', + 'image/png': '.png', + 'image/svg+xml': '.svg', + 'image/tiff': '.tiff', + 'image/vnd.microsoft.icon': '.ico', + 'image/webp': '.webp', + 'text/calendar': '.ics', + 'text/css': '.css', + 'text/csv': '.csv', + 'text/html': '.html', + 'text/javascript': '.js', + 'text/plain': '.txt', + 'text/xml': '.xml', + 'video/3gpp': '.3gp', + 'video/3gpp2': '.3g2', + 'video/mp2t': '.ts', + 'video/mpeg': '.mpeg', + 'video/ogg': '.ogv', + 'video/webm': '.webm', + 'video/x-msvideo': '.avi', + 'video/quicktime': '.mov', +}; diff --git a/lib/common/common.dart b/lib/common/common.dart index 35eb626..3624ca9 100644 --- a/lib/common/common.dart +++ b/lib/common/common.dart @@ -1,6 +1,8 @@ export 'android.dart'; export 'app_localizations.dart'; +export 'cache.dart'; export 'color.dart'; +export 'compute.dart'; export 'constant.dart'; export 'context.dart'; export 'converter.dart'; diff --git a/lib/common/compute.dart b/lib/common/compute.dart new file mode 100644 index 0000000..4f23b96 --- /dev/null +++ b/lib/common/compute.dart @@ -0,0 +1,114 @@ +import 'package:fl_clash/enum/enum.dart'; +import 'package:fl_clash/models/models.dart'; + +import 'string.dart'; +import 'utils.dart'; + +List computeSort({ + required List groups, + required ProxiesSortType sortType, + required DelayMap delayMap, + required SelectedMap selectedMap, + required String defaultTestUrl, +}) { + return groups.map((group) { + final proxies = group.all; + final newProxies = switch (sortType) { + ProxiesSortType.none => proxies, + ProxiesSortType.delay => _sortOfDelay( + groups: groups, + proxies: proxies, + delayMap: delayMap, + selectedMap: selectedMap, + testUrl: group.testUrl.getSafeValue(defaultTestUrl), + ), + ProxiesSortType.name => _sortOfName(proxies), + }; + return group.copyWith(all: newProxies); + }).toList(); +} + +DelayState computeProxyDelayState({ + required String proxyName, + required String testUrl, + required List groups, + required SelectedMap selectedMap, + required DelayMap delayMap, +}) { + final state = computeRealSelectedProxyState( + proxyName, + groups: groups, + selectedMap: selectedMap, + ); + final currentDelayMap = delayMap[state.testUrl.getSafeValue(testUrl)] ?? {}; + final delay = currentDelayMap[state.proxyName]; + return DelayState(delay: delay ?? 0, group: state.group); +} + +SelectedProxyState computeRealSelectedProxyState( + String proxyName, { + required List groups, + required SelectedMap selectedMap, +}) { + return _getRealSelectedProxyState( + SelectedProxyState(proxyName: proxyName), + groups: groups, + selectedMap: selectedMap, + ); +} + +SelectedProxyState _getRealSelectedProxyState( + SelectedProxyState state, { + required List groups, + required SelectedMap selectedMap, +}) { + if (state.proxyName.isEmpty) return state; + final index = groups.indexWhere((element) => element.name == state.proxyName); + final newState = state.copyWith(group: true); + if (index == -1) return newState; + final group = groups[index]; + final currentSelectedName = group.getCurrentSelectedName( + selectedMap[newState.proxyName] ?? '', + ); + if (currentSelectedName.isEmpty) { + return newState; + } + return _getRealSelectedProxyState( + newState.copyWith(proxyName: currentSelectedName, testUrl: group.testUrl), + groups: groups, + selectedMap: selectedMap, + ); +} + +List _sortOfDelay({ + required List groups, + required List proxies, + required DelayMap delayMap, + required SelectedMap selectedMap, + required String testUrl, +}) { + return List.from(proxies)..sort((a, b) { + final aDelayState = computeProxyDelayState( + proxyName: a.name, + testUrl: testUrl, + groups: groups, + selectedMap: selectedMap, + delayMap: delayMap, + ); + final bDelayState = computeProxyDelayState( + proxyName: b.name, + testUrl: testUrl, + groups: groups, + selectedMap: selectedMap, + delayMap: delayMap, + ); + return aDelayState.compareTo(bDelayState); + }); +} + +List _sortOfName(List proxies) { + return List.of(proxies)..sort( + (a, b) => + utils.sortByChar(utils.getPinyin(a.name), utils.getPinyin(b.name)), + ); +} diff --git a/lib/common/constant.dart b/lib/common/constant.dart index 99a2bf0..53b7ca9 100644 --- a/lib/common/constant.dart +++ b/lib/common/constant.dart @@ -36,8 +36,8 @@ const geoIpFileName = 'GeoIP.dat'; const geoSiteFileName = 'GeoSite.dat'; final double kHeaderHeight = system.isDesktop ? !system.isMacOS - ? 40 - : 28 + ? 40 + : 28 : 0; const profilesDirectoryName = 'profiles'; const localhost = '127.0.0.1'; @@ -84,7 +84,7 @@ const profilesStoreKey = PageStorageKey('profiles'); const defaultPrimaryColor = 0XFFD8C0C3; double getWidgetHeight(num lines) { - return max(lines * 84 + (lines - 1) * 16, 0).ap; + return max(lines * 80 + (lines - 1) * 16, 0).ap; } const maxLength = 1000; diff --git a/lib/common/function.dart b/lib/common/function.dart index 800a72f..a444f3a 100644 --- a/lib/common/function.dart +++ b/lib/common/function.dart @@ -9,23 +9,17 @@ class Debouncer { FunctionTag tag, Function func, { List? args, - Duration duration = const Duration(milliseconds: 600), + Duration? duration, }) { final timer = _operations[tag]; if (timer != null) { timer.cancel(); } - _operations[tag] = Timer( - duration, - () { - _operations[tag]?.cancel(); - _operations.remove(tag); - Function.apply( - func, - args, - ); - }, - ); + _operations[tag] = Timer(duration ?? const Duration(milliseconds: 600), () { + _operations[tag]?.cancel(); + _operations.remove(tag); + Function.apply(func, args); + }); } void cancel(dynamic tag) { @@ -47,17 +41,11 @@ class Throttler { if (timer != null) { return true; } - _operations[tag] = Timer( - duration, - () { - _operations[tag]?.cancel(); - _operations.remove(tag); - Function.apply( - func, - args, - ); - }, - ); + _operations[tag] = Timer(duration, () { + _operations[tag]?.cancel(); + _operations.remove(tag); + Function.apply(func, args); + }); return false; } diff --git a/lib/common/future.dart b/lib/common/future.dart index f101476..c39217b 100644 --- a/lib/common/future.dart +++ b/lib/common/future.dart @@ -3,42 +3,37 @@ import 'dart:ui'; import 'package:fl_clash/common/common.dart'; -extension CompleterExt on Completer { - Future safeFuture({ +extension FutureExt on Future { + Future withTimeout({ Duration? timeout, + String? tag, VoidCallback? onLast, FutureOr Function()? onTimeout, - required String functionName, }) { - final realTimeout = timeout ?? const Duration(seconds: 30); - Timer(realTimeout + commonDuration, () { + final realTimout = timeout ?? const Duration(minutes: 3); + Timer(realTimout + commonDuration, () { if (onLast != null) { onLast(); } }); - return future.withTimeout( - timeout: realTimeout, - functionName: functionName, - onTimeout: onTimeout, - ); - } -} - -extension FutureExt on Future { - Future withTimeout({ - required Duration timeout, - required String functionName, - FutureOr Function()? onTimeout, - }) { return this.timeout( - timeout, + realTimout, onTimeout: () async { if (onTimeout != null) { return onTimeout(); } else { - throw TimeoutException('$functionName timeout'); + throw TimeoutException('${tag ?? runtimeType} timeout'); } }, ); } } + +extension CompleterExt on Completer { + void safeCompleter(T value) { + if (isCompleted) { + return; + } + complete(value); + } +} diff --git a/lib/common/http.dart b/lib/common/http.dart index 3e57160..a37dc55 100644 --- a/lib/common/http.dart +++ b/lib/common/http.dart @@ -18,7 +18,7 @@ class FlClashHttpOverrides extends HttpOverrides { @override HttpClient createHttpClient(SecurityContext? context) { final client = super.createHttpClient(context); - client.badCertificateCallback = (_, __, ___) => true; + client.badCertificateCallback = (_, _, _) => true; client.findProxy = handleFindProxy; return client; } diff --git a/lib/common/mixin.dart b/lib/common/mixin.dart index 17ed5e7..7d96782 100644 --- a/lib/common/mixin.dart +++ b/lib/common/mixin.dart @@ -1,8 +1,13 @@ import 'package:riverpod/riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; -mixin AutoDisposeNotifierMixin on AutoDisposeNotifier { +mixin AutoDisposeNotifierMixin on AnyNotifier { set value(T value) { - state = value; + if (ref.mounted) { + state = value; + } else { + onUpdate(value); + } } @override @@ -17,28 +22,25 @@ mixin AutoDisposeNotifierMixin on AutoDisposeNotifier { void onUpdate(T value) {} } -// mixin PageMixin on State { -// initPageState() { -// WidgetsBinding.instance.addPostFrameCallback((_) { -// final commonScaffoldState = context.commonScaffoldState; -// commonScaffoldState?.actions = actions; -// commonScaffoldState?.floatingActionButton = floatingActionButton; -// commonScaffoldState?.onKeywordsUpdate = onKeywordsUpdate; -// commonScaffoldState?.updateSearchState( -// (_) => onSearch != null -// ? AppBarSearchState( -// onSearch: onSearch!, -// ) -// : null, -// ); -// }); -// } -// -// List get actions => []; -// -// Widget? get floatingActionButton => null; -// -// Function(String)? get onSearch => null; -// -// Function(List)? get onKeywordsUpdate => null; -// } +mixin AnyNotifierMixin on AnyNotifier { + T get value; + + set value(T value) { + if (ref.mounted) { + state = value; + } else { + onUpdate(value); + } + } + + @override + bool updateShouldNotify(previous, next) { + final res = super.updateShouldNotify(previous, next); + if (res) { + onUpdate(next); + } + return res; + } + + void onUpdate(T value) {} +} diff --git a/lib/common/navigation.dart b/lib/common/navigation.dart index 0519e26..b648654 100644 --- a/lib/common/navigation.dart +++ b/lib/common/navigation.dart @@ -1,9 +1,7 @@ import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/models/models.dart'; -import 'package:fl_clash/providers/providers.dart'; import 'package:fl_clash/views/views.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; class Navigation { static Navigation? _instance; @@ -17,25 +15,12 @@ class Navigation { keep: false, icon: Icon(Icons.space_dashboard), label: PageLabel.dashboard, - builder: (_) => const DashboardView( - key: GlobalObjectKey(PageLabel.dashboard), - ), + builder: (_) => const DashboardView(), ), NavigationItem( icon: const Icon(Icons.article), label: PageLabel.proxies, - builder: (_) => ProviderScope( - overrides: [ - queryProvider.overrideWith( - () => Query(), - ), - ], - child: const ProxiesView( - key: GlobalObjectKey( - PageLabel.proxies, - ), - ), - ), + builder: (_) => const ProxiesView(), modes: hasProxies ? [NavigationItemMode.mobile, NavigationItemMode.desktop] : [], @@ -43,31 +28,19 @@ class Navigation { NavigationItem( icon: Icon(Icons.folder), label: PageLabel.profiles, - builder: (_) => const ProfilesView( - key: GlobalObjectKey( - PageLabel.profiles, - ), - ), + builder: (_) => const ProfilesView(), ), NavigationItem( icon: Icon(Icons.view_timeline), label: PageLabel.requests, - builder: (_) => const RequestsView( - key: GlobalObjectKey( - PageLabel.requests, - ), - ), + builder: (_) => const RequestsView(), description: 'requestsDesc', modes: [NavigationItemMode.desktop, NavigationItemMode.more], ), NavigationItem( icon: Icon(Icons.ballot), label: PageLabel.connections, - builder: (_) => const ConnectionsView( - key: GlobalObjectKey( - PageLabel.connections, - ), - ), + builder: (_) => const ConnectionsView(), description: 'connectionsDesc', modes: [NavigationItemMode.desktop, NavigationItemMode.more], ), @@ -75,21 +48,13 @@ class Navigation { icon: Icon(Icons.storage), label: PageLabel.resources, description: 'resourcesDesc', - builder: (_) => const ResourcesView( - key: GlobalObjectKey( - PageLabel.resources, - ), - ), + builder: (_) => const ResourcesView(), modes: [NavigationItemMode.more], ), NavigationItem( icon: const Icon(Icons.adb), label: PageLabel.logs, - builder: (_) => const LogsView( - key: GlobalObjectKey( - PageLabel.logs, - ), - ), + builder: (_) => const LogsView(), description: 'logsDesc', modes: openLogs ? [NavigationItemMode.desktop, NavigationItemMode.more] @@ -98,11 +63,7 @@ class Navigation { NavigationItem( icon: Icon(Icons.construction), label: PageLabel.tools, - builder: (_) => const ToolsView( - key: GlobalObjectKey( - PageLabel.tools, - ), - ), + builder: (_) => const ToolsView(), modes: [NavigationItemMode.desktop, NavigationItemMode.mobile], ), ]; diff --git a/lib/common/navigator.dart b/lib/common/navigator.dart index a7d05c1..2ccfbcd 100644 --- a/lib/common/navigator.dart +++ b/lib/common/navigator.dart @@ -7,17 +7,13 @@ import 'package:flutter/material.dart'; class BaseNavigator { static Future push(BuildContext context, Widget child) async { if (globalState.appState.viewMode != ViewMode.mobile) { - return await Navigator.of(context).push( - CommonDesktopRoute( - builder: (context) => child, - ), - ); + return await Navigator.of( + context, + ).push(CommonDesktopRoute(builder: (context) => child)); } - return await Navigator.of(context).push( - CommonRoute( - builder: (context) => child, - ), - ); + return await Navigator.of( + context, + ).push(CommonRoute(builder: (context) => child)); } // static Future modal(BuildContext context, Widget child) async { @@ -39,9 +35,7 @@ class BaseNavigator { class CommonDesktopRoute extends PageRoute { final Widget Function(BuildContext context) builder; - CommonDesktopRoute({ - required this.builder, - }); + CommonDesktopRoute({required this.builder}); @override Color? get barrierColor => null; @@ -59,10 +53,7 @@ class CommonDesktopRoute extends PageRoute { return Semantics( scopesRoute: true, explicitChildNodes: true, - child: FadeTransition( - opacity: animation, - child: result, - ), + child: FadeTransition(opacity: animation, child: result), ); } @@ -77,9 +68,7 @@ class CommonDesktopRoute extends PageRoute { } class CommonRoute extends MaterialPageRoute { - CommonRoute({ - required super.builder, - }); + CommonRoute({required super.builder}); @override Duration get transitionDuration => const Duration(milliseconds: 500); @@ -139,11 +128,12 @@ class CommonPageTransition extends StatefulWidget { final bool linearTransition; static Widget? delegatedTransition( - BuildContext context, - Animation animation, - Animation secondaryAnimation, - bool allowSnapshotting, - Widget? child) { + BuildContext context, + Animation animation, + Animation secondaryAnimation, + bool allowSnapshotting, + Widget? child, + ) { final Animation delegatedPositionAnimation = CurvedAnimation( parent: secondaryAnimation, curve: Curves.linearToEaseOut, @@ -222,23 +212,23 @@ class _CommonPageTransitionState extends State { ); } _primaryPositionAnimation = - (_primaryPositionCurve ?? widget.primaryRouteAnimation) - .drive(_kRightMiddleTween); + (_primaryPositionCurve ?? widget.primaryRouteAnimation).drive( + _kRightMiddleTween, + ); _secondaryPositionAnimation = - (_secondaryPositionCurve ?? widget.secondaryRouteAnimation) - .drive(_kMiddleLeftTween); + (_secondaryPositionCurve ?? widget.secondaryRouteAnimation).drive( + _kMiddleLeftTween, + ); _primaryShadowAnimation = (_primaryShadowCurve ?? widget.primaryRouteAnimation).drive( - DecorationTween( - begin: const _CommonEdgeShadowDecoration(), - end: _CommonEdgeShadowDecoration( - [ - widget.context.colorScheme.inverseSurface.withValues(alpha: 0.02), - Colors.transparent, - ], - ), - ), - ); + DecorationTween( + begin: const _CommonEdgeShadowDecoration(), + end: _CommonEdgeShadowDecoration([ + widget.context.colorScheme.inverseSurface.withValues(alpha: 0.02), + Colors.transparent, + ]), + ), + ); } @override @@ -273,10 +263,8 @@ class _CommonEdgeShadowDecoration extends Decoration { } class _CommonEdgeShadowPainter extends BoxPainter { - _CommonEdgeShadowPainter( - this._decoration, - super.onChanged, - ) : assert(_decoration._colors == null || _decoration._colors!.length > 1); + _CommonEdgeShadowPainter(this._decoration, super.onChanged) + : assert(_decoration._colors == null || _decoration._colors.length > 1); final _CommonEdgeShadowDecoration _decoration; @@ -304,11 +292,16 @@ class _CommonEdgeShadowPainter extends BoxPainter { bandColorIndex += 1; } final Paint paint = Paint() - ..color = Color.lerp(colors[bandColorIndex], colors[bandColorIndex + 1], - (dx % bandWidth) / bandWidth)!; + ..color = Color.lerp( + colors[bandColorIndex], + colors[bandColorIndex + 1], + (dx % bandWidth) / bandWidth, + )!; final double x = start + shadowDirection * dx; canvas.drawRect( - Rect.fromLTWH(x - 1.0, offset.dy, 1.0, shadowHeight), paint); + Rect.fromLTWH(x - 1.0, offset.dy, 1.0, shadowHeight), + paint, + ); } } } diff --git a/lib/common/num.dart b/lib/common/num.dart index 1d26018..dc97748 100644 --- a/lib/common/num.dart +++ b/lib/common/num.dart @@ -1,3 +1,5 @@ +import 'package:fl_clash/enum/enum.dart'; +import 'package:fl_clash/models/common.dart'; import 'package:fl_clash/state.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -17,6 +19,20 @@ extension NumExt on num { double get ap { return this * (1 + (globalState.theme.textScaleFactor - 1) * 0.5); } + + TrafficShow get traffic { + final units = TrafficUnit.values; + var size = toDouble(); + var unitIndex = 0; + while (size >= 1024 && unitIndex < units.length - 1) { + size /= 1024; + unitIndex++; + } + return TrafficShow( + value: size.fixed(decimals: 1), + unit: units[unitIndex].name, + ); + } } extension DoubleExt on double { diff --git a/lib/common/path.dart b/lib/common/path.dart index da019d7..018e7c8 100644 --- a/lib/common/path.dart +++ b/lib/common/path.dart @@ -10,6 +10,7 @@ class AppPath { Completer dataDir = Completer(); Completer downloadDir = Completer(); Completer tempDir = Completer(); + Completer cacheDir = Completer(); late String appDirPath; AppPath._internal() { @@ -23,6 +24,9 @@ class AppPath { getDownloadsDirectory().then((value) { downloadDir.complete(value); }); + getApplicationCacheDirectory().then((value) { + cacheDir.complete(value); + }); } factory AppPath() { @@ -58,8 +62,13 @@ class AppPath { } Future get lockFilePath async { - final directory = await dataDir.future; - return join(directory.path, 'FlClash.lock'); + final homeDirPath = await appPath.homeDirPath; + return join(homeDirPath, 'FlClash.lock'); + } + + Future get configFilePath async { + final homeDirPath = await appPath.homeDirPath; + return join(homeDirPath, 'config.json'); } Future get sharedPreferencesPath async { @@ -77,13 +86,19 @@ class AppPath { return join(directory, '$id.yaml'); } + Future getIconsCacheDir() async { + final directory = await cacheDir.future; + return join(directory.path, 'icons'); + } + + Future getProvidersRootPath() async { + final directory = await profilesPath; + return join(directory, 'providers'); + } + Future getProvidersDirPath(String id) async { final directory = await profilesPath; - return join( - directory, - 'providers', - id, - ); + return join(directory, 'providers', id); } Future getProvidersFilePath( @@ -92,13 +107,7 @@ class AppPath { String url, ) async { final directory = await profilesPath; - return join( - directory, - 'providers', - id, - type, - url.toMd5(), - ); + return join(directory, 'providers', id, type, url.toMd5()); } Future get tempPath async { diff --git a/lib/common/picker.dart b/lib/common/picker.dart index 2c3d1aa..37d89be 100644 --- a/lib/common/picker.dart +++ b/lib/common/picker.dart @@ -35,9 +35,10 @@ class Picker { return null; } final controller = MobileScannerController(); - final capture = await controller.analyzeImage(xFile.path, formats: [ - BarcodeFormat.qrCode, - ]); + final capture = await controller.analyzeImage( + xFile.path, + formats: [BarcodeFormat.qrCode], + ); final result = capture?.barcodes.first.rawValue; if (result == null || !result.isUrl) { throw appLocalizations.pleaseUploadValidQrcode; diff --git a/lib/common/preferences.dart b/lib/common/preferences.dart index dd40bde..681d4c8 100644 --- a/lib/common/preferences.dart +++ b/lib/common/preferences.dart @@ -16,7 +16,7 @@ class Preferences { Preferences._internal() { SharedPreferences.getInstance() .then((value) => sharedPreferencesCompleter.complete(value)) - .onError((_, __) => sharedPreferencesCompleter.complete(null)); + .onError((_, _) => sharedPreferencesCompleter.complete(null)); } factory Preferences() { @@ -42,10 +42,7 @@ class Preferences { Future saveConfig(Config config) async { final preferences = await sharedPreferencesCompleter.future; - return await preferences?.setString( - configKey, - json.encode(config), - ) ?? + return await preferences?.setString(configKey, json.encode(config)) ?? false; } diff --git a/lib/common/request.dart b/lib/common/request.dart index 2fb2489..54d5c05 100644 --- a/lib/common/request.dart +++ b/lib/common/request.dart @@ -11,35 +11,29 @@ import 'package:fl_clash/state.dart'; import 'package:flutter/cupertino.dart'; class Request { - late final Dio _dio; + late final Dio dio; late final Dio _clashDio; String? userAgent; Request() { - _dio = Dio( - BaseOptions( - headers: { - 'User-Agent': browserUa, - }, - ), - ); + dio = Dio(BaseOptions(headers: {'User-Agent': browserUa})); _clashDio = Dio(); - _clashDio.httpClientAdapter = IOHttpClientAdapter(createHttpClient: () { - final client = HttpClient(); - client.findProxy = (Uri uri) { - client.userAgent = globalState.ua; - return FlClashHttpOverrides.handleFindProxy(uri); - }; - return client; - }); + _clashDio.httpClientAdapter = IOHttpClientAdapter( + createHttpClient: () { + final client = HttpClient(); + client.findProxy = (Uri uri) { + client.userAgent = globalState.ua; + return FlClashHttpOverrides.handleFindProxy(uri); + }; + return client; + }, + ); } Future getFileResponseForUrl(String url) async { final response = await _clashDio.get( url, - options: Options( - responseType: ResponseType.bytes, - ), + options: Options(responseType: ResponseType.bytes), ); return response; } @@ -47,20 +41,16 @@ class Request { Future getTextResponseForUrl(String url) async { final response = await _clashDio.get( url, - options: Options( - responseType: ResponseType.plain, - ), + options: Options(responseType: ResponseType.plain), ); return response; } Future getImage(String url) async { if (url.isEmpty) return null; - final response = await _dio.get( + final response = await dio.get( url, - options: Options( - responseType: ResponseType.bytes, - ), + options: Options(responseType: ResponseType.bytes), ); final data = response.data; if (data == null) return null; @@ -68,11 +58,9 @@ class Request { } Future?> checkForUpdate() async { - final response = await _dio.get( + final response = await dio.get( 'https://api.github.com/repos/$repository/releases/latest', - options: Options( - responseType: ResponseType.json, - ), + options: Options(responseType: ResponseType.json), ); if (response.statusCode != 200) return null; final data = response.data as Map; @@ -85,9 +73,12 @@ class Request { } final Map)> _ipInfoSources = { - 'https://ipwho.is/': IpInfo.fromIpwhoIsJson, - 'https://api.ip.sb/geoip/': IpInfo.fromIpSbJson, + 'https://ipwho.is/': IpInfo.fromIpWhoIsJson, + 'https://api.myip.com/': IpInfo.fromMyIpJson, 'https://ipapi.co/json/': IpInfo.fromIpApiCoJson, + 'https://ident.me/json/': IpInfo.fromIdentMeJson, + 'http://ip-api.com/json/': IpInfo.fromIpAPIJson, + 'https://api.ip.sb/geoip/': IpInfo.fromIpSbJson, 'https://ipinfo.io/json/': IpInfo.fromIpInfoIoJson, }; @@ -101,27 +92,27 @@ class Request { } } - final future = Dio().get>( + final future = dio.get>( source.key, cancelToken: cancelToken, - options: Options( - responseType: ResponseType.json, - ), + options: Options(responseType: ResponseType.json), ); - future.then((res) { - if (res.statusCode == HttpStatus.ok && res.data != null) { - completer.complete(Result.success(source.value(res.data!))); - } else { - failureCount++; - handleFailRes(); - } - }).catchError((e) { - failureCount++; - if (e is DioException && e.type == DioExceptionType.cancel) { - completer.complete(Result.error('cancelled')); - } - handleFailRes(); - }); + future + .then((res) { + if (res.statusCode == HttpStatus.ok && res.data != null) { + completer.complete(Result.success(source.value(res.data!))); + return; + } + failureCount++; + handleFailRes(); + }) + .catchError((e) { + failureCount++; + if (e is DioException && e.type == DioExceptionType.cancel) { + completer.complete(Result.error('cancelled')); + } + handleFailRes(); + }); return completer.future; }); final res = await Future.any(futures); @@ -131,18 +122,12 @@ class Request { Future pingHelper() async { try { - final response = await _dio + final response = await dio .get( 'http://$localhost:$helperPort/ping', - options: Options( - responseType: ResponseType.plain, - ), + options: Options(responseType: ResponseType.plain), ) - .timeout( - const Duration( - milliseconds: 2000, - ), - ); + .timeout(const Duration(milliseconds: 2000)); if (response.statusCode != HttpStatus.ok) { return false; } @@ -154,22 +139,13 @@ class Request { Future startCoreByHelper(String arg) async { try { - final response = await _dio + final response = await dio .post( 'http://$localhost:$helperPort/start', - data: json.encode({ - 'path': appPath.corePath, - 'arg': arg, - }), - options: Options( - responseType: ResponseType.plain, - ), + data: json.encode({'path': appPath.corePath, 'arg': arg}), + options: Options(responseType: ResponseType.plain), ) - .timeout( - const Duration( - milliseconds: 2000, - ), - ); + .timeout(const Duration(milliseconds: 2000)); if (response.statusCode != HttpStatus.ok) { return false; } @@ -182,18 +158,12 @@ class Request { Future stopCoreByHelper() async { try { - final response = await _dio + final response = await dio .post( 'http://$localhost:$helperPort/stop', - options: Options( - responseType: ResponseType.plain, - ), + options: Options(responseType: ResponseType.plain), ) - .timeout( - const Duration( - milliseconds: 2000, - ), - ); + .timeout(const Duration(milliseconds: 2000)); if (response.statusCode != HttpStatus.ok) { return false; } diff --git a/lib/common/tray.dart b/lib/common/tray.dart index 9f96216..ff0d959 100644 --- a/lib/common/tray.dart +++ b/lib/common/tray.dart @@ -19,9 +19,6 @@ class Tray { required Brightness? brightness, bool force = false, }) async { - if (system.isAndroid) { - return; - } if (Platform.isLinux || force) { await trayManager.destroy(); } @@ -197,4 +194,4 @@ class Tray { } } -final tray = Tray(); +final tray = system.isDesktop ? Tray() : null; diff --git a/lib/common/utils.dart b/lib/common/utils.dart index 44677b0..68dfcc3 100644 --- a/lib/common/utils.dart +++ b/lib/common/utils.dart @@ -21,16 +21,15 @@ class Utils { String get id { final timestamp = DateTime.now().microsecondsSinceEpoch; final random = Random(); - final randomStr = - String.fromCharCodes(List.generate(8, (_) => random.nextInt(26) + 97)); + final randomStr = String.fromCharCodes( + List.generate(8, (_) => random.nextInt(26) + 97), + ); return '$timestamp$randomStr'; } String getDateStringLast2(int value) { var valueRaw = '0$value'; - return valueRaw.substring( - valueRaw.length - 2, - ); + return valueRaw.substring(valueRaw.length - 2); } String generateRandomString({int minLength = 10, int maxLength = 100}) { @@ -43,8 +42,9 @@ class Utils { String result = ''; for (int i = 0; i < length; i++) { if (random.nextBool()) { - result += - String.fromCharCode(0x4E00 + random.nextInt(0x9FA5 - 0x4E00 + 1)); + result += String.fromCharCode( + 0x4E00 + random.nextInt(0x9FA5 - 0x4E00 + 1), + ); } else { result += latinChars[random.nextInt(latinChars.length)]; } @@ -60,8 +60,9 @@ class Utils { bytes[6] = (bytes[6] & 0x0F) | 0x40; bytes[8] = (bytes[8] & 0x3F) | 0x80; - final hex = - bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(); + final hex = bytes + .map((byte) => byte.toRadixString(16).padLeft(2, '0')) + .join(); return '${hex.substring(0, 8)}-${hex.substring(8, 12)}-${hex.substring(12, 16)}-${hex.substring(16, 20)}-${hex.substring(20, 32)}'; } @@ -102,9 +103,10 @@ class Utils { } if (localSplit.length == 3) { return Locale.fromSubtags( - languageCode: localSplit[0], - scriptCode: localSplit[1], - countryCode: localSplit[2]); + languageCode: localSplit[0], + scriptCode: localSplit[1], + countryCode: localSplit[2], + ); } return null; } @@ -141,9 +143,7 @@ class Utils { } } - String getTrayIconPath({ - required Brightness brightness, - }) { + String getTrayIconPath({required Brightness brightness}) { if (system.isMacOS) { return 'assets/images/icon_white.png'; } @@ -188,16 +188,20 @@ class Utils { if (disposition == null) return null; final parseValue = HeaderValue.parse(disposition); final parameters = parseValue.parameters; - final fileNamePointKey = parameters.keys - .firstWhere((key) => key == 'filename*', orElse: () => ''); + final fileNamePointKey = parameters.keys.firstWhere( + (key) => key == 'filename*', + orElse: () => '', + ); if (fileNamePointKey.isNotEmpty) { final res = parameters[fileNamePointKey]?.split("''") ?? []; if (res.length >= 2) { return Uri.decodeComponent(res[1]); } } - final fileNameKey = parameters.keys - .firstWhere((key) => key == 'filename', orElse: () => ''); + final fileNameKey = parameters.keys.firstWhere( + (key) => key == 'filename', + orElse: () => '', + ); if (fileNameKey.isEmpty) return null; return parameters[fileNameKey]; } @@ -236,19 +240,7 @@ class Utils { return max((viewWidth / 320).floor(), 1); } - final _indexPrimary = [ - 50, - 100, - 200, - 300, - 400, - 500, - 600, - 700, - 800, - 850, - 900, - ]; + final _indexPrimary = [50, 100, 200, 300, 400, 500, 600, 700, 800, 850, 900]; MaterialColor _createPrimarySwatch(Color color) { final Map swatch = {}; @@ -302,16 +294,15 @@ class Utils { } Future getLocalIpAddress() async { - List interfaces = await NetworkInterface.list( - includeLoopback: false, - ) - ..sort((a, b) { - if (a.isWifi && !b.isWifi) return -1; - if (!a.isWifi && b.isWifi) return 1; - if (a.includesIPv4 && !b.includesIPv4) return -1; - if (!a.includesIPv4 && b.includesIPv4) return 1; - return 0; - }); + List interfaces = + await NetworkInterface.list(includeLoopback: false) + ..sort((a, b) { + if (a.isWifi && !b.isWifi) return -1; + if (!a.isWifi && b.isWifi) return 1; + if (a.includesIPv4 && !b.includesIPv4) return -1; + if (!a.includesIPv4 && b.includesIPv4) return 1; + return 0; + }); for (final interface in interfaces) { final addresses = interface.addresses; if (addresses.isEmpty) { @@ -329,59 +320,9 @@ class Utils { SingleActivator controlSingleActivator(LogicalKeyboardKey trigger) { final control = system.isMacOS ? false : true; - return SingleActivator( - trigger, - control: control, - meta: !control, - ); + return SingleActivator(trigger, control: control, meta: !control); } - // dynamic convertYamlNode(dynamic node) { - // if (node is YamlMap) { - // final map = {}; - // YamlNode? mergeKeyNode; - // for (final entry in node.nodes.entries) { - // if (entry.key is YamlScalar && - // (entry.key as YamlScalar).value == '<<') { - // mergeKeyNode = entry.value; - // break; - // } - // } - // if (mergeKeyNode != null) { - // final mergeValue = mergeKeyNode.value; - // if (mergeValue is YamlMap) { - // map.addAll(convertYamlNode(mergeValue) as Map); - // } else if (mergeValue is YamlList) { - // for (final node in mergeValue.nodes) { - // if (node.value is YamlMap) { - // map.addAll(convertYamlNode(node.value) as Map); - // } - // } - // } - // } - // - // node.nodes.forEach((key, value) { - // String stringKey; - // if (key is YamlScalar) { - // stringKey = key.value.toString(); - // } else { - // stringKey = key.toString(); - // } - // map[stringKey] = convertYamlNode(value.value); - // }); - // return map; - // } else if (node is YamlList) { - // final list = []; - // for (final item in node.nodes) { - // list.add(convertYamlNode(item.value)); - // } - // return list; - // } else if (node is YamlScalar) { - // return node.value; - // } - // return node; - // } - FutureOr handleWatch(Function function) async { if (kDebugMode) { final stopwatch = Stopwatch()..start(); diff --git a/lib/controller.dart b/lib/controller.dart index 43efb7a..97b4e31 100644 --- a/lib/controller.dart +++ b/lib/controller.dart @@ -4,10 +4,11 @@ import 'dart:io'; import 'dart:isolate'; import 'package:archive/archive.dart'; -import 'package:fl_clash/clash/clash.dart'; import 'package:fl_clash/common/archive.dart'; +import 'package:fl_clash/core/core.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/plugins/app.dart'; +import 'package:fl_clash/plugins/service.dart'; import 'package:fl_clash/providers/providers.dart'; import 'package:fl_clash/state.dart'; import 'package:fl_clash/widgets/dialog.dart'; @@ -20,7 +21,6 @@ import 'package:url_launcher/url_launcher.dart'; import 'common/common.dart'; import 'models/models.dart'; -import 'views/profiles/override_profile.dart'; class AppController { int? lastProfileModified; @@ -42,8 +42,8 @@ class AppController { }); } - void updateGroupsDebounce() { - debouncer.call(FunctionTag.updateGroups, updateGroups); + void updateGroupsDebounce([Duration? duration]) { + debouncer.call(FunctionTag.updateGroups, updateGroups, duration: duration); } void addCheckIpNumDebounce() { @@ -52,9 +52,7 @@ class AppController { }); } - void applyProfileDebounce({ - bool silence = false, - }) { + void applyProfileDebounce({bool silence = false}) { debouncer.call(FunctionTag.applyProfile, (silence) { applyProfile(silence: silence); }, args: [silence]); @@ -65,33 +63,31 @@ class AppController { } void changeProxyDebounce(String groupName, String proxyName) { - debouncer.call(FunctionTag.changeProxy, - (String groupName, String proxyName) async { - await changeProxy( - groupName: groupName, - proxyName: proxyName, - ); + debouncer.call(FunctionTag.changeProxy, ( + String groupName, + String proxyName, + ) async { + await changeProxy(groupName: groupName, proxyName: proxyName); await updateGroups(); }, args: [groupName, proxyName]); } Future restartCore() async { - commonPrint.log('restart core'); - await clashService?.reStart(); + await coreController.shutdown(); + await _connectCore(); await _initCore(); - if (_ref.read(runTimeProvider.notifier).isStart) { + _ref.read(initProvider.notifier).value = true; + if (_ref.read(isStartProvider)) { await globalState.handleStart(); } } Future updateStatus(bool isStart) async { if (isStart) { - await globalState.handleStart([ - updateRunTime, - updateTraffic, - ]); - final currentLastModified = - await _ref.read(currentProfileProvider)?.profileLastModified; + await globalState.handleStart([updateRunTime, updateTraffic]); + final currentLastModified = await _ref + .read(currentProfileProvider) + ?.profileLastModified; if (currentLastModified == null || lastProfileModified == null) { addCheckIpNumDebounce(); return; @@ -103,7 +99,7 @@ class AppController { applyProfileDebounce(); } else { await globalState.handleStop(); - clashCore.resetTraffic(); + coreController.resetTraffic(); _ref.read(trafficsProvider.notifier).clear(); _ref.read(totalTrafficProvider.notifier).value = Traffic(); _ref.read(runTimeProvider.notifier).value = null; @@ -123,10 +119,13 @@ class AppController { } Future updateTraffic() async { - final traffic = await clashCore.getTraffic(); + final onlyStatisticsProxy = _ref.read( + appSettingProvider.select((state) => state.onlyStatisticsProxy), + ); + final traffic = await coreController.getTraffic(onlyStatisticsProxy); _ref.read(trafficsProvider.notifier).addTraffic(traffic); - _ref.read(totalTrafficProvider.notifier).value = - await clashCore.getTotalTraffic(); + _ref.read(totalTrafficProvider.notifier).value = await coreController + .getTotalTraffic(onlyStatisticsProxy); } Future addProfile(Profile profile) async { @@ -152,8 +151,8 @@ class AppController { } Future updateProviders() async { - _ref.read(providersProvider.notifier).value = - await clashCore.getExternalProviders(); + _ref.read(providersProvider.notifier).value = await coreController + .getExternalProviders(); } Future updateLocalIp() async { @@ -193,8 +192,9 @@ class AppController { void updateOrAddHotKeyAction(HotKeyAction hotKeyAction) { final hotKeyActions = _ref.read(hotKeyActionsProvider); - final index = - hotKeyActions.indexWhere((item) => item.action == hotKeyAction.action); + final index = hotKeyActions.indexWhere( + (item) => item.action == hotKeyAction.action, + ); if (index == -1) { _ref.read(hotKeyActionsProvider.notifier).value = List.from(hotKeyActions) ..add(hotKeyAction); @@ -225,16 +225,12 @@ class AppController { } String? getCurrentGroupName() { - final currentGroupName = _ref.read(currentProfileProvider.select( - (state) => state?.currentGroupName, - )); + final currentGroupName = _ref.read( + currentProfileProvider.select((state) => state?.currentGroupName), + ); return currentGroupName; } - ProxyCardState getProxyCardState(String proxyName) { - return _ref.read(getProxyCardStateProvider(proxyName)); - } - String? getSelectedProxyName(String groupName) { return _ref.read(getSelectedProxyNameProvider(groupName)); } @@ -244,18 +240,13 @@ class AppController { if (profile == null || profile.currentGroupName == groupName) { return; } - setProfile( - profile.copyWith(currentGroupName: groupName), - ); + setProfile(profile.copyWith(currentGroupName: groupName)); } Future updateClashConfig() async { - await safeRun( - () async { - await _updateClashConfig(); - }, - needLoading: true, - ); + await safeRun(() async { + await _updateClashConfig(); + }, needLoading: true); } Future _updateClashConfig() async { @@ -265,16 +256,14 @@ class AppController { return; } final realTunEnable = _ref.read(realTunEnableProvider); - final message = await clashCore.updateConfig( - updateParams.copyWith.tun( - enable: realTunEnable, - ), + final message = await coreController.updateConfig( + updateParams.copyWith.tun(enable: realTunEnable), ); if (message.isNotEmpty) throw message; } Future> _requestAdmin(bool enableTun) async { - if(system.isWindows && kDebugMode){ + if (system.isWindows && kDebugMode) { return Result.success(false); } final realTunEnable = _ref.read(realTunEnableProvider); @@ -296,12 +285,9 @@ class AppController { } Future setupClashConfig() async { - await safeRun( - () async { - await _setupClashConfig(); - }, - needLoading: true, - ); + await safeRun(() async { + await _setupClashConfig(); + }, needLoading: true); } Future _setupClashConfig() async { @@ -313,14 +299,9 @@ class AppController { } final realTunEnable = _ref.read(realTunEnableProvider); final realPatchConfig = patchConfig.copyWith.tun(enable: realTunEnable); - final params = await globalState.getSetupParams( - pathConfig: realPatchConfig, - ); - final message = await clashCore.setupConfig(params); + final message = await coreController.setupConfig(realPatchConfig); lastProfileModified = await _ref.read( - currentProfileProvider.select( - (state) => state?.profileLastModified, - ), + currentProfileProvider.select((state) => state?.profileLastModified), ); if (message.isNotEmpty) { throw message; @@ -328,7 +309,7 @@ class AppController { } Future _applyProfile() async { - await clashCore.requestGc(); + await coreController.requestGc(); await setupClashConfig(); await updateGroups(); await updateProviders(); @@ -338,12 +319,9 @@ class AppController { if (silence) { await _applyProfile(); } else { - await safeRun( - () async { - await _applyProfile(); - }, - needLoading: true, - ); + await safeRun(() async { + await _applyProfile(); + }, needLoading: true); } addCheckIpNumDebounce(); } @@ -367,9 +345,7 @@ class AppController { for (final profile in _ref.read(profilesProvider)) { if (!profile.autoUpdate) continue; final isNotNeedUpdate = profile.lastUpdateDate - ?.add( - profile.autoUpdateDuration, - ) + ?.add(profile.autoUpdateDuration) .isBeforeNow; if (isNotNeedUpdate == false || profile.type == ProfileType.file) { continue; @@ -386,7 +362,22 @@ class AppController { try { _ref.read(groupsProvider.notifier).value = await retry( task: () async { - return await clashCore.getProxiesGroups(); + final sortType = _ref.read( + proxiesStyleSettingProvider.select((state) => state.sortType), + ); + final delayMap = _ref.read(delayDataSourceProvider); + final testUrl = _ref.read( + appSettingProvider.select((state) => state.testUrl), + ); + final selectedMap = _ref.read( + currentProfileProvider.select((state) => state?.selectedMap ?? {}), + ); + return await coreController.getProxiesGroups( + selectedMap: selectedMap, + sortType: sortType, + delayMap: delayMap, + defaultTestUrl: testUrl, + ); }, retryIf: (res) => res.isEmpty, ); @@ -413,14 +404,11 @@ class AppController { required String groupName, required String proxyName, }) async { - await clashCore.changeProxy( - ChangeProxyParams( - groupName: groupName, - proxyName: proxyName, - ), + await coreController.changeProxy( + ChangeProxyParams(groupName: groupName, proxyName: proxyName), ); if (_ref.read(appSettingProvider).closeConnections) { - clashCore.closeConnections(); + coreController.closeConnections(); } addCheckIpNumDebounce(); } @@ -455,8 +443,8 @@ class AppController { await savePreferences(); await macOS?.updateDns(true); await proxy?.stopProxy(); - await clashCore.shutdown(); - await clashService?.destroy(); + await coreController.shutdown(); + await coreController.destroy(); } finally { system.exit(); } @@ -465,9 +453,7 @@ class AppController { Future handleClear() async { await preferences.clearPreferences(); commonPrint.log('clear preferences'); - globalState.config = Config( - themeProps: defaultThemeProps, - ); + globalState.config = Config(themeProps: defaultThemeProps); } Future autoCheckUpdate() async { @@ -494,15 +480,9 @@ class AppController { text: '$tagName \n', style: textTheme.headlineSmall, children: [ - TextSpan( - text: '\n', - style: textTheme.bodyMedium, - ), + TextSpan(text: '\n', style: textTheme.bodyMedium), for (final submit in submits) - TextSpan( - text: '- $submit \n', - style: textTheme.bodyMedium, - ), + TextSpan(text: '- $submit \n', style: textTheme.bodyMedium), ], ), confirmText: appLocalizations.goDownload, @@ -510,15 +490,11 @@ class AppController { if (res != true) { return; } - launchUrl( - Uri.parse('https://github.com/$repository/releases/latest'), - ); + launchUrl(Uri.parse('https://github.com/$repository/releases/latest')); } else if (handleError) { globalState.showMessage( title: appLocalizations.checkUpdate, - message: TextSpan( - text: appLocalizations.checkUpdateError, - ), + message: TextSpan(text: appLocalizations.checkUpdateError), ); } } @@ -542,12 +518,9 @@ class AppController { } Future _initCore() async { - final isInit = await clashCore.isInit; + final isInit = await coreController.isInit; if (!isInit) { - await clashCore.init(); - await clashCore.setState( - globalState.getCoreState(), - ); + await coreController.init(globalState.appState.version); } await applyProfile(); } @@ -555,17 +528,15 @@ class AppController { Future init() async { FlutterError.onError = (details) { if (kDebugMode) { - commonPrint.log(details.stack.toString()); + commonPrint.log( + 'exception: ${details.exception} stack: ${details.stack}', + ); } }; updateTray(true); - await _initCore(); - await _initStatus(); - autoLaunch?.updateStatus( - _ref.read(appSettingProvider).autoLaunch, - ); autoUpdateProfiles(); autoCheckUpdate(); + autoLaunch?.updateStatus(_ref.read(appSettingProvider).autoLaunch); if (!_ref.read(appSettingProvider).silentLaunch) { window?.show(); } else { @@ -573,9 +544,28 @@ class AppController { } await _handlePreference(); await _handlerDisclaimer(); + await _connectCore(); + await service?.syncAndroidState(globalState.getAndroidState()); + await _initCore(); + await _initStatus(); _ref.read(initProvider.notifier).value = true; } + Future _connectCore() async { + _ref.read(coreStatusProvider.notifier).value = CoreStatus.connecting; + final message = await coreController.preload(); + if (message.isNotEmpty) { + _ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected; + if (context.mounted) { + context.showNotifier(message); + } + return; + } + Future.delayed(const Duration(milliseconds: 600), () { + _ref.read(coreStatusProvider.notifier).value = CoreStatus.connected; + }); + } + Future _initStatus() async { if (system.isAndroid) { await globalState.updateStartTime(); @@ -603,34 +593,32 @@ class AppController { } void initLink() { - linkManager.initAppLinksListen( - (url) async { - final res = await globalState.showMessage( - title: '${appLocalizations.add}${appLocalizations.profile}', - message: TextSpan( - children: [ - TextSpan(text: appLocalizations.doYouWantToPass), - TextSpan( - text: ' $url ', - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - decoration: TextDecoration.underline, - decorationColor: Theme.of(context).colorScheme.primary, - ), + linkManager.initAppLinksListen((url) async { + final res = await globalState.showMessage( + title: '${appLocalizations.add}${appLocalizations.profile}', + message: TextSpan( + children: [ + TextSpan(text: appLocalizations.doYouWantToPass), + TextSpan( + text: ' $url ', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + decoration: TextDecoration.underline, + decorationColor: Theme.of(context).colorScheme.primary, ), - TextSpan( - text: - '${appLocalizations.create}${appLocalizations.profile}'), - ], - ), - ); + ), + TextSpan( + text: '${appLocalizations.create}${appLocalizations.profile}', + ), + ], + ), + ); - if (res != true) { - return; - } - addProfileFormURL(url); - }, - ); + if (res != true) { + return; + } + addProfileFormURL(url); + }); } Future showDisclaimer() async { @@ -647,17 +635,17 @@ class AppController { ), TextButton( onPressed: () { - _ref.read(appSettingProvider.notifier).updateState( + _ref + .read(appSettingProvider.notifier) + .updateState( (state) => state.copyWith(disclaimerAccepted: true), ); Navigator.of(context).pop(true); }, child: Text(appLocalizations.agree), - ) + ), ], - child: SelectableText( - appLocalizations.disclaimerDesc, - ), + child: SelectableText(appLocalizations.disclaimerDesc), ), ) ?? false; @@ -682,9 +670,7 @@ class AppController { final profile = await safeRun( () async { - return await Profile.normal( - url: url, - ).update(); + return await Profile.normal(url: url).update(); }, needLoading: true, title: '${appLocalizations.add}${appLocalizations.profile}', @@ -718,9 +704,7 @@ class AppController { } Future addProfileFormQrCode() async { - final url = await safeRun( - picker.pickerConfigQRCode, - ); + final url = await safeRun(picker.pickerConfigQRCode); if (url == null) return; addProfileFormURL(url); } @@ -735,63 +719,7 @@ class AppController { _ref.read(providersProvider.notifier).setProvider(provider); } - List _sortOfName(List proxies) { - return List.of(proxies) - ..sort( - (a, b) => utils.sortByChar( - utils.getPinyin(a.name), - utils.getPinyin(b.name), - ), - ); - } - - List _sortOfDelay({ - required List proxies, - String? testUrl, - }) { - return List.of(proxies) - ..sort( - (a, b) { - final aDelay = _ref.read(getDelayProvider( - proxyName: a.name, - testUrl: testUrl, - )); - final bDelay = _ref.read( - getDelayProvider( - proxyName: b.name, - testUrl: testUrl, - ), - ); - if (aDelay == null && bDelay == null) { - return 0; - } - if (aDelay == null || aDelay == -1) { - return 1; - } - if (bDelay == null || bDelay == -1) { - return -1; - } - return aDelay.compareTo(bDelay); - }, - ); - } - - List getSortProxies({ - required List proxies, - required ProxiesSortType sortType, - String? testUrl, - }) { - return switch (sortType) { - ProxiesSortType.none => proxies, - ProxiesSortType.delay => _sortOfDelay( - proxies: proxies, - testUrl: testUrl, - ), - ProxiesSortType.name => _sortOfName(proxies), - }; - } - - Future clearEffect(String profileId) async { + Future clearEffect(String profileId) async { final profilePath = await appPath.getProfilePath(profileId); final providersDirPath = await appPath.getProvidersDirPath(profileId); return await Isolate.run(() async { @@ -800,28 +728,28 @@ class AppController { if (isExists) { profileFile.delete(recursive: true); } - final providersFileDir = File(providersDirPath); - final providersFileIsExists = await providersFileDir.exists(); - if (providersFileIsExists) { - providersFileDir.delete(recursive: true); - } + await coreController.deleteFile(providersDirPath); }); } void updateTun() { - _ref.read(patchClashConfigProvider.notifier).updateState( - (state) => state.copyWith.tun(enable: !state.tun.enable), - ); + _ref + .read(patchClashConfigProvider.notifier) + .updateState((state) => state.copyWith.tun(enable: !state.tun.enable)); } void updateSystemProxy() { - _ref.read(networkSettingProvider.notifier).updateState( - (state) => state.copyWith( - systemProxy: !state.systemProxy, - ), + _ref + .read(networkSettingProvider.notifier) + .updateState( + (state) => state.copyWith(systemProxy: !state.systemProxy), ); } + void handleCoreDisconnected() { + _ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected; + } + Future> getPackages() async { if (_ref.read(isMobileViewProvider)) { await Future.delayed(commonDuration); @@ -834,21 +762,18 @@ class AppController { } void updateStart() { - updateStatus(!_ref.read(runTimeProvider.notifier).isStart); + updateStatus(!_ref.read(isStartProvider)); } void updateCurrentSelectedMap(String groupName, String proxyName) { final currentProfile = _ref.read(currentProfileProvider); if (currentProfile != null && currentProfile.selectedMap[groupName] != proxyName) { - final SelectedMap selectedMap = Map.from( - currentProfile.selectedMap, - )..[groupName] = proxyName; - _ref.read(profilesProvider.notifier).setProfile( - currentProfile.copyWith( - selectedMap: selectedMap, - ), - ); + final SelectedMap selectedMap = Map.from(currentProfile.selectedMap) + ..[groupName] = proxyName; + _ref + .read(profilesProvider.notifier) + .setProfile(currentProfile.copyWith(selectedMap: selectedMap)); } } @@ -857,17 +782,15 @@ class AppController { if (currentProfile == null) { return; } - _ref.read(profilesProvider.notifier).setProfile( - currentProfile.copyWith( - unfoldSet: value, - ), - ); + _ref + .read(profilesProvider.notifier) + .setProfile(currentProfile.copyWith(unfoldSet: value)); } void changeMode(Mode mode) { - _ref.read(patchClashConfigProvider.notifier).updateState( - (state) => state.copyWith(mode: mode), - ); + _ref + .read(patchClashConfigProvider.notifier) + .updateState((state) => state.copyWith(mode: mode)); if (mode == Mode.global) { updateCurrentGroupName(GroupName.GLOBAL.name); } @@ -875,11 +798,9 @@ class AppController { } void updateAutoLaunch() { - _ref.read(appSettingProvider.notifier).updateState( - (state) => state.copyWith( - autoLaunch: !state.autoLaunch, - ), - ); + _ref + .read(appSettingProvider.notifier) + .updateState((state) => state.copyWith(autoLaunch: !state.autoLaunch)); } Future updateVisible() async { @@ -892,64 +813,23 @@ class AppController { } void updateMode() { - _ref.read(patchClashConfigProvider.notifier).updateState( - (state) { - final index = Mode.values.indexWhere((item) => item == state.mode); - if (index == -1) { - return null; - } - final nextIndex = index + 1 > Mode.values.length - 1 ? 0 : index + 1; - return state.copyWith( - mode: Mode.values[nextIndex], - ); - }, - ); - } - - Future handleAddOrUpdate(WidgetRef ref, [Rule? rule]) async { - final res = await globalState.showCommonDialog( - child: AddRuleDialog( - rule: rule, - snippet: ref.read( - profileOverrideStateProvider.select( - (state) => state.snippet!, - ), - ), - ), - ); - if (res == null) { - return; - } - ref.read(profileOverrideStateProvider.notifier).updateState( - (state) { - final model = state.copyWith.overrideData!( - rule: state.overrideData!.rule.updateRules( - (rules) { - final index = rules.indexWhere((item) => item.id == res.id); - if (index == -1) { - return List.from([res, ...rules]); - } - return List.from(rules)..[index] = res; - }, - ), - ); - return model; - }, - ); + _ref.read(patchClashConfigProvider.notifier).updateState((state) { + final index = Mode.values.indexWhere((item) => item == state.mode); + if (index == -1) { + return null; + } + final nextIndex = index + 1 > Mode.values.length - 1 ? 0 : index + 1; + return state.copyWith(mode: Mode.values[nextIndex]); + }); } Future exportLogs() async { - final logsRaw = _ref.read(logsProvider).list.map( - (item) => item.toString(), - ); + final logsRaw = _ref.read(logsProvider).list.map((item) => item.toString()); final data = await Isolate.run>(() async { final logsRawString = logsRaw.join('\n'); return utf8.encode(logsRawString); }); - return await picker.saveFile( - utils.logFile, - Uint8List.fromList(data), - ) != + return await picker.saveFile(utils.logFile, Uint8List.fromList(data)) != null; } @@ -959,17 +839,15 @@ class AppController { final configJson = globalState.config.toJson(); return Isolate.run>(() async { final archive = Archive(); - archive.add('config.json', configJson); + archive.addTextFile('config.json', configJson); archive.addDirectoryToArchive(profilesPath, homeDirPath); final zipEncoder = ZipEncoder(); - return zipEncoder.encode(archive) ?? []; + return zipEncoder.encode(archive); }); } Future updateTray([bool focus = false]) async { - tray.update( - trayState: _ref.read(trayStateProvider), - ); + tray?.update(trayState: _ref.read(trayStateProvider)); } Future recoveryData( @@ -981,18 +859,19 @@ class AppController { return zipDecoder.decodeBytes(data); }); final homeDirPath = await appPath.homeDirPath; - final configs = - archive.files.where((item) => item.name.endsWith('.json')).toList(); - final profiles = - archive.files.where((item) => !item.name.endsWith('.json')); - final configIndex = - configs.indexWhere((config) => config.name == 'config.json'); + final configs = archive.files + .where((item) => item.name.endsWith('.json')) + .toList(); + final profiles = archive.files.where( + (item) => !item.name.endsWith('.json'), + ); + final configIndex = configs.indexWhere( + (config) => config.name == 'config.json', + ); if (configIndex == -1) throw 'invalid backup file'; final configFile = configs[configIndex]; var tempConfig = Config.compatibleFromJson( - json.decode( - utf8.decode(configFile.content), - ), + json.decode(utf8.decode(configFile.content)), ); for (final profile in profiles) { final filePath = join(homeDirPath, profile.name); @@ -1000,38 +879,30 @@ class AppController { await file.create(recursive: true); await file.writeAsBytes(profile.content); } - final clashConfigIndex = - configs.indexWhere((config) => config.name == 'clashConfig.json'); + final clashConfigIndex = configs.indexWhere( + (config) => config.name == 'clashConfig.json', + ); if (clashConfigIndex != -1) { final clashConfigFile = configs[clashConfigIndex]; tempConfig = tempConfig.copyWith( patchClashConfig: ClashConfig.fromJson( - json.decode( - utf8.decode( - clashConfigFile.content, - ), - ), + json.decode(utf8.decode(clashConfigFile.content)), ), ); } - _recovery( - tempConfig, - recoveryOption, - ); + _recovery(tempConfig, recoveryOption); } void _recovery(Config config, RecoveryOption recoveryOption) { - final recoveryStrategy = _ref.read(appSettingProvider.select( - (state) => state.recoveryStrategy, - )); + final recoveryStrategy = _ref.read( + appSettingProvider.select((state) => state.recoveryStrategy), + ); final profiles = config.profiles; if (recoveryStrategy == RecoveryStrategy.override) { _ref.read(profilesProvider.notifier).value = profiles; } else { for (final profile in profiles) { - _ref.read(profilesProvider.notifier).setProfile( - profile, - ); + _ref.read(profilesProvider.notifier).setProfile(profile); } } final onlyProfiles = recoveryOption == RecoveryOption.onlyProfiles; @@ -1072,15 +943,13 @@ class AppController { final res = await futureFunction(); return res; } catch (e) { - commonPrint.log('$e'); + commonPrint.log('$futureFunction ===> $e'); if (realSilence) { globalState.showNotifier(e.toString()); } else { globalState.showMessage( title: title ?? appLocalizations.tip, - message: TextSpan( - text: e.toString(), - ), + message: TextSpan(text: e.toString()), ); } return null; diff --git a/lib/core/controller.dart b/lib/core/controller.dart new file mode 100644 index 0000000..ba51afe --- /dev/null +++ b/lib/core/controller.dart @@ -0,0 +1,281 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:isolate'; + +import 'package:fl_clash/common/common.dart'; +import 'package:fl_clash/core/core.dart'; +import 'package:fl_clash/core/interface.dart'; +import 'package:fl_clash/enum/enum.dart'; +import 'package:fl_clash/models/models.dart'; +import 'package:fl_clash/state.dart'; +import 'package:flutter/services.dart'; +import 'package:path/path.dart'; + +class CoreController { + static CoreController? _instance; + late CoreHandlerInterface _interface; + + CoreController._internal() { + if (system.isAndroid) { + _interface = coreLib!; + } else { + _interface = coreService!; + } + } + + factory CoreController() { + _instance ??= CoreController._internal(); + return _instance!; + } + + Future preload() { + return _interface.preload(); + } + + static Future initGeo() async { + final homePath = await appPath.homeDirPath; + final homeDir = Directory(homePath); + final isExists = await homeDir.exists(); + if (!isExists) { + await homeDir.create(recursive: true); + } + const geoFileNameList = [ + mmdbFileName, + geoIpFileName, + geoSiteFileName, + asnFileName, + ]; + try { + for (final geoFileName in geoFileNameList) { + final geoFile = File(join(homePath, geoFileName)); + final isExists = await geoFile.exists(); + if (isExists) { + continue; + } + final data = await rootBundle.load('assets/data/$geoFileName'); + List bytes = data.buffer.asUint8List(); + await geoFile.writeAsBytes(bytes, flush: true); + } + } catch (e) { + exit(0); + } + } + + Future init(int version) async { + await initGeo(); + final homeDirPath = await appPath.homeDirPath; + return await _interface.init( + InitParams(homeDir: homeDirPath, version: version), + ); + } + + Future shutdown() async { + await _interface.shutdown(); + } + + FutureOr get isInit => _interface.isInit; + + FutureOr validateConfig(String data) { + return _interface.validateConfig(data); + } + + Future updateConfig(UpdateParams updateParams) async { + return await _interface.updateConfig(updateParams); + } + + Future setupConfig(ClashConfig clashConfig) async { + await globalState.genConfigFile(clashConfig); + final params = await globalState.getSetupParams(); + return await _interface.setupConfig(params); + } + + Future> getProxiesGroups({ + required ProxiesSortType sortType, + required DelayMap delayMap, + required SelectedMap selectedMap, + required String defaultTestUrl, + }) async { + final proxies = await _interface.getProxies(); + return Isolate.run>(() { + if (proxies.isEmpty) return []; + final groupNames = [ + UsedProxy.GLOBAL.name, + ...(proxies[UsedProxy.GLOBAL.name]['all'] as List).where((e) { + final proxy = proxies[e] ?? {}; + return GroupTypeExtension.valueList.contains(proxy['type']); + }), + ]; + final groupsRaw = groupNames.map((groupName) { + final group = proxies[groupName]; + group['all'] = ((group['all'] ?? []) as List) + .map((name) => proxies[name]) + .where((proxy) => proxy != null) + .toList(); + return group; + }).toList(); + final groups = groupsRaw.map((e) => Group.fromJson(e)).toList(); + return computeSort( + groups: groups, + sortType: sortType, + delayMap: delayMap, + selectedMap: selectedMap, + defaultTestUrl: defaultTestUrl, + ); + }); + } + + FutureOr changeProxy(ChangeProxyParams changeProxyParams) async { + return await _interface.changeProxy(changeProxyParams); + } + + Future> getConnections() async { + final res = await _interface.getConnections(); + final connectionsData = json.decode(res) as Map; + final connectionsRaw = connectionsData['connections'] as List? ?? []; + return connectionsRaw.map((e) => TrackerInfo.fromJson(e)).toList(); + } + + void closeConnection(String id) { + _interface.closeConnection(id); + } + + void closeConnections() { + _interface.closeConnections(); + } + + void resetConnections() { + _interface.resetConnections(); + } + + Future> getExternalProviders() async { + final externalProvidersRawString = await _interface.getExternalProviders(); + if (externalProvidersRawString.isEmpty) { + return []; + } + return Isolate.run>(() { + final externalProviders = + (json.decode(externalProvidersRawString) as List) + .map((item) => ExternalProvider.fromJson(item)) + .toList(); + return externalProviders; + }); + } + + Future getExternalProvider( + String externalProviderName, + ) async { + final externalProvidersRawString = await _interface.getExternalProvider( + externalProviderName, + ); + if (externalProvidersRawString.isEmpty) { + return null; + } + return ExternalProvider.fromJson(json.decode(externalProvidersRawString)); + } + + Future updateGeoData(UpdateGeoDataParams params) { + return _interface.updateGeoData(params); + } + + Future sideLoadExternalProvider({ + required String providerName, + required String data, + }) { + return _interface.sideLoadExternalProvider( + providerName: providerName, + data: data, + ); + } + + Future updateExternalProvider({required String providerName}) async { + return _interface.updateExternalProvider(providerName); + } + + Future startListener() async { + return await _interface.startListener(); + } + + Future stopListener() async { + return await _interface.stopListener(); + } + + Future getDelay(String url, String proxyName) async { + final data = await _interface.asyncTestDelay(url, proxyName); + return Delay.fromJson(json.decode(data)); + } + + Future> getConfig(String id) async { + final profilePath = await appPath.getProfilePath(id); + final res = await _interface.getConfig(profilePath); + if (res.isSuccess) { + return res.data; + } else { + throw res.message; + } + } + + Future getTraffic(bool onlyStatisticsProxy) async { + final trafficString = await _interface.getTraffic(onlyStatisticsProxy); + if (trafficString.isEmpty) { + return Traffic(); + } + return Traffic.fromJson(json.decode(trafficString)); + } + + Future getCountryCode(String ip) async { + final countryCode = await _interface.getCountryCode(ip); + if (countryCode.isEmpty) { + return null; + } + return IpInfo(ip: ip, countryCode: countryCode); + } + + Future getTotalTraffic(bool onlyStatisticsProxy) async { + final totalTrafficString = await _interface.getTotalTraffic( + onlyStatisticsProxy, + ); + if (totalTrafficString.isEmpty) { + return Traffic(); + } + return Traffic.fromJson(json.decode(totalTrafficString)); + } + + Future getMemory() async { + final value = await _interface.getMemory(); + if (value.isEmpty) { + return 0; + } + return int.parse(value); + } + + void resetTraffic() { + _interface.resetTraffic(); + } + + void startLog() { + _interface.startLog(); + } + + void stopLog() { + _interface.stopLog(); + } + + Future requestGc() async { + await _interface.forceGc(); + } + + Future destroy() async { + await _interface.destroy(); + } + + Future crash() async { + await _interface.crash(); + } + + Future deleteFile(String path) async { + return await _interface.deleteFile(path); + } +} + +final coreController = CoreController(); diff --git a/lib/clash/clash.dart b/lib/core/core.dart similarity index 56% rename from lib/clash/clash.dart rename to lib/core/core.dart index 295b423..92b5283 100644 --- a/lib/clash/clash.dart +++ b/lib/core/core.dart @@ -1,4 +1,5 @@ +export 'controller.dart'; export 'core.dart'; +export 'event.dart'; export 'lib.dart'; -export 'message.dart'; export 'service.dart'; diff --git a/lib/core/event.dart b/lib/core/event.dart new file mode 100644 index 0000000..51f4d99 --- /dev/null +++ b/lib/core/event.dart @@ -0,0 +1,68 @@ +import 'dart:async'; + +import 'package:fl_clash/enum/enum.dart'; +import 'package:fl_clash/models/models.dart'; +import 'package:flutter/foundation.dart'; + +abstract mixin class CoreEventListener { + void onLog(Log log) {} + + void onDelay(Delay delay) {} + + void onRequest(TrackerInfo connection) {} + + void onLoaded(String providerName) {} + + void onCrash(String message) {} +} + +class CoreEventManager { + final _controller = StreamController(); + + CoreEventManager._() { + _controller.stream.listen((event) { + for (final CoreEventListener listener in _listeners) { + switch (event.type) { + case CoreEventType.log: + listener.onLog(Log.fromJson(event.data)); + break; + case CoreEventType.delay: + listener.onDelay(Delay.fromJson(event.data)); + break; + case CoreEventType.request: + listener.onRequest(TrackerInfo.fromJson(event.data)); + break; + case CoreEventType.loaded: + listener.onLoaded(event.data); + break; + case CoreEventType.crash: + listener.onCrash(event.data); + break; + } + } + }); + } + + static final CoreEventManager instance = CoreEventManager._(); + + final ObserverList _listeners = + ObserverList(); + + bool get hasListeners { + return _listeners.isNotEmpty; + } + + void sendEvent(CoreEvent event) { + _controller.add(event); + } + + void addListener(CoreEventListener listener) { + _listeners.add(listener); + } + + void removeListener(CoreEventListener listener) { + _listeners.remove(listener); + } +} + +final coreEventManager = CoreEventManager.instance; diff --git a/lib/core/interface.dart b/lib/core/interface.dart new file mode 100644 index 0000000..a619e5f --- /dev/null +++ b/lib/core/interface.dart @@ -0,0 +1,327 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:isolate'; + +import 'package:fl_clash/common/common.dart'; +import 'package:fl_clash/enum/enum.dart'; +import 'package:fl_clash/models/models.dart'; + +mixin CoreInterface { + Future init(InitParams params); + + Future preload(); + + Future shutdown(); + + Future get isInit; + + Future forceGc(); + + Future validateConfig(String data); + + Future getConfig(String path); + + Future asyncTestDelay(String url, String proxyName); + + Future updateConfig(UpdateParams updateParams); + + Future setupConfig(SetupParams setupParams); + + Future getProxies(); + + Future changeProxy(ChangeProxyParams changeProxyParams); + + Future startListener(); + + Future stopListener(); + + Future getExternalProviders(); + + Future? getExternalProvider(String externalProviderName); + + Future updateGeoData(UpdateGeoDataParams params); + + Future sideLoadExternalProvider({ + required String providerName, + required String data, + }); + + Future updateExternalProvider(String providerName); + + FutureOr getTraffic(bool onlyStatisticsProxy); + + FutureOr getTotalTraffic(bool onlyStatisticsProxy); + + FutureOr getCountryCode(String ip); + + FutureOr getMemory(); + + FutureOr resetTraffic(); + + FutureOr startLog(); + + FutureOr stopLog(); + + Future crash(); + + FutureOr getConnections(); + + FutureOr closeConnection(String id); + + FutureOr deleteFile(String path); + + FutureOr closeConnections(); + + FutureOr resetConnections(); +} + +abstract class CoreHandlerInterface with CoreInterface { + Future get connected; + + FutureOr destroy(); + + Future _invoke({ + required ActionMethod method, + dynamic data, + Duration? timeout, + }) async { + await connected; + return invoke(method: method, data: data, timeout: timeout); + } + + Future invoke({ + required ActionMethod method, + dynamic data, + Duration? timeout, + }); + + Future parasResult(ActionResult result) async { + return switch (result.method) { + ActionMethod.getConfig => result.toResult as T, + _ => result.data as T, + }; + } + + @override + Future init(InitParams params) async { + return await _invoke( + method: ActionMethod.initClash, + data: json.encode(params), + ) ?? + false; + } + + @override + Future shutdown(); + + @override + Future get isInit async { + return await _invoke(method: ActionMethod.getIsInit) ?? false; + } + + @override + Future forceGc() async { + return await _invoke(method: ActionMethod.forceGc) ?? false; + } + + @override + Future validateConfig(String data) async { + return await _invoke( + method: ActionMethod.validateConfig, + data: data, + ) ?? + ''; + } + + @override + Future updateConfig(UpdateParams updateParams) async { + return await _invoke( + method: ActionMethod.updateConfig, + data: json.encode(updateParams), + ) ?? + ''; + } + + @override + Future getConfig(String path) async { + return await _invoke(method: ActionMethod.getConfig, data: path) ?? + Result>.success({}); + } + + @override + Future setupConfig(SetupParams setupParams) async { + final data = await Isolate.run(() => json.encode(setupParams)); + return await _invoke( + method: ActionMethod.setupConfig, + data: data, + ) ?? + ''; + } + + @override + Future crash() async { + return await _invoke(method: ActionMethod.crash) ?? false; + } + + @override + Future getProxies() async { + final map = await _invoke(method: ActionMethod.getProxies); + return map ?? {}; + } + + @override + Future changeProxy(ChangeProxyParams changeProxyParams) async { + return await _invoke( + method: ActionMethod.changeProxy, + data: json.encode(changeProxyParams), + ) ?? + ''; + } + + @override + Future getExternalProviders() async { + return await _invoke(method: ActionMethod.getExternalProviders) ?? + ''; + } + + @override + Future getExternalProvider(String externalProviderName) async { + return await _invoke( + method: ActionMethod.getExternalProvider, + data: externalProviderName, + ) ?? + ''; + } + + @override + Future updateGeoData(UpdateGeoDataParams params) async { + return await _invoke( + method: ActionMethod.updateGeoData, + data: json.encode(params), + ) ?? + ''; + } + + @override + Future sideLoadExternalProvider({ + required String providerName, + required String data, + }) async { + return await _invoke( + method: ActionMethod.sideLoadExternalProvider, + data: json.encode({'providerName': providerName, 'data': data}), + ) ?? + ''; + } + + @override + Future updateExternalProvider(String providerName) async { + return await _invoke( + method: ActionMethod.updateExternalProvider, + data: providerName, + ) ?? + ''; + } + + @override + Future getConnections() async { + return await _invoke(method: ActionMethod.getConnections) ?? ''; + } + + @override + Future closeConnections() async { + return await _invoke(method: ActionMethod.closeConnections) ?? false; + } + + @override + Future resetConnections() async { + return await _invoke(method: ActionMethod.resetConnections) ?? false; + } + + @override + Future closeConnection(String id) async { + return await _invoke( + method: ActionMethod.closeConnection, + data: id, + ) ?? + false; + } + + @override + Future getTotalTraffic(bool onlyStatisticsProxy) async { + return await _invoke( + method: ActionMethod.getTotalTraffic, + data: onlyStatisticsProxy, + ) ?? + ''; + } + + @override + Future getTraffic(bool onlyStatisticsProxy) async { + return await _invoke( + method: ActionMethod.getTraffic, + data: onlyStatisticsProxy, + ) ?? + ''; + } + + @override + Future deleteFile(String path) async { + return await _invoke(method: ActionMethod.deleteFile, data: path) ?? + ''; + } + + @override + resetTraffic() { + _invoke(method: ActionMethod.resetTraffic); + } + + @override + startLog() { + _invoke(method: ActionMethod.startLog); + } + + @override + stopLog() { + _invoke(method: ActionMethod.stopLog); + } + + @override + Future startListener() async { + return await _invoke(method: ActionMethod.startListener) ?? false; + } + + @override + Future stopListener() async { + return await _invoke(method: ActionMethod.stopListener) ?? false; + } + + @override + Future asyncTestDelay(String url, String proxyName) async { + final delayParams = { + 'proxy-name': proxyName, + 'timeout': httpTimeoutDuration.inMilliseconds, + 'test-url': url, + }; + return await _invoke( + method: ActionMethod.asyncTestDelay, + data: json.encode(delayParams), + timeout: Duration(seconds: 6), + ) ?? + json.encode(Delay(name: proxyName, value: -1, url: url)); + } + + @override + Future getCountryCode(String ip) async { + return await _invoke( + method: ActionMethod.getCountryCode, + data: ip, + ) ?? + ''; + } + + @override + Future getMemory() async { + return await _invoke(method: ActionMethod.getMemory) ?? ''; + } +} diff --git a/lib/core/lib.dart b/lib/core/lib.dart new file mode 100644 index 0000000..f2a146a --- /dev/null +++ b/lib/core/lib.dart @@ -0,0 +1,62 @@ +import 'dart:async'; + +import 'package:fl_clash/common/common.dart'; +import 'package:fl_clash/enum/enum.dart'; +import 'package:fl_clash/models/core.dart'; +import 'package:fl_clash/plugins/service.dart'; + +import 'interface.dart'; + +class CoreLib extends CoreHandlerInterface { + static CoreLib? _instance; + + Completer _connectedCompleter = Completer(); + + CoreLib._internal(); + + @override + Future preload() async { + final res = await service?.init(); + if (res?.isEmpty == true) { + _connectedCompleter.complete(true); + } + return res ?? ''; + } + + factory CoreLib() { + _instance ??= CoreLib._internal(); + return _instance!; + } + + @override + destroy() async { + return true; + } + + @override + Future shutdown() async { + _connectedCompleter = Completer(); + return true; + } + + @override + Future invoke({ + required ActionMethod method, + dynamic data, + Duration? timeout, + }) async { + final id = '${method.name}#${utils.id}'; + final result = await service + ?.invokeAction(Action(id: id, method: method, data: data)) + .withTimeout(onTimeout: () => null); + if (result == null) { + return null; + } + return parasResult(result); + } + + @override + Future get connected => _connectedCompleter.future; +} + +CoreLib? get coreLib => system.isAndroid ? CoreLib() : null; diff --git a/lib/core/service.dart b/lib/core/service.dart new file mode 100644 index 0000000..87046f7 --- /dev/null +++ b/lib/core/service.dart @@ -0,0 +1,185 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:fl_clash/common/common.dart'; +import 'package:fl_clash/core/core.dart'; +import 'package:fl_clash/enum/enum.dart'; +import 'package:fl_clash/models/core.dart'; + +import 'interface.dart'; + +class CoreService extends CoreHandlerInterface { + static CoreService? _instance; + + final Completer _serverCompleter = Completer(); + + Completer _socketCompleter = Completer(); + + final Map _callbackCompleterMap = {}; + + Process? _process; + + factory CoreService() { + _instance ??= CoreService._internal(); + return _instance!; + } + + CoreService._internal() { + _initServer(); + } + + Future handleResult(ActionResult result) async { + final completer = _callbackCompleterMap[result.id]; + final data = await parasResult(result); + if (result.id?.isEmpty == true) { + coreEventManager.sendEvent(CoreEvent.fromJson(result.data)); + } + completer?.complete(data); + } + + void _initServer() { + runZonedGuarded( + () async { + final address = !system.isWindows + ? InternetAddress(unixSocketPath, type: InternetAddressType.unix) + : InternetAddress(localhost, type: InternetAddressType.IPv4); + await _deleteSocketFile(); + final server = await ServerSocket.bind(address, 0, shared: true); + _serverCompleter.complete(server); + await for (final socket in server) { + await _attachSocket(socket); + } + }, + (error, stack) async { + commonPrint.log('Service error: $error'); + }, + ); + } + + Future _attachSocket(Socket socket) async { + await _destroySocket(); + _socketCompleter.complete(socket); + socket + .transform(uint8ListToListIntConverter) + .transform(utf8.decoder) + .transform(LineSplitter()) + .listen((data) { + handleResult(ActionResult.fromJson(json.decode(data.trim()))); + }) + .onDone(() { + _handleInvokeCrashEvent(); + }); + } + + void _handleInvokeCrashEvent() { + coreEventManager.sendEvent( + CoreEvent(type: CoreEventType.crash, data: 'socket done'), + ); + } + + Future start() async { + if (_process != null) { + await shutdown(); + } + final serverSocket = await _serverCompleter.future; + final arg = system.isWindows + ? '${serverSocket.port}' + : serverSocket.address.address; + if (system.isWindows && await system.checkIsAdmin()) { + final isSuccess = await request.startCoreByHelper(arg); + if (isSuccess) { + return; + } + } + _process = await Process.start(appPath.corePath, [arg]); + _process?.stdout.listen((_) {}); + _process?.stderr.listen((e) { + final error = utf8.decode(e); + if (error.isNotEmpty) { + commonPrint.log(error); + } + }); + } + + @override + destroy() async { + final server = await _serverCompleter.future; + await server.close(); + await _deleteSocketFile(); + return true; + } + + Future sendMessage(String message) async { + final socket = await _socketCompleter.future; + socket.writeln(message); + } + + Future _deleteSocketFile() async { + if (!system.isWindows) { + final file = File(unixSocketPath); + if (await file.exists()) { + await file.delete(); + } + } + } + + Future _destroySocket() async { + if (_socketCompleter.isCompleted) { + final lastSocket = await _socketCompleter.future; + _socketCompleter = Completer(); + lastSocket.close(); + } + } + + @override + shutdown() async { + await _destroySocket(); + _clearCompleter(); + if (system.isWindows) { + await request.stopCoreByHelper(); + } + _process?.kill(); + _process = null; + return true; + } + + void _clearCompleter() { + for (final completer in _callbackCompleterMap.values) { + completer.safeCompleter(null); + } + } + + @override + Future preload() async { + await _serverCompleter.future; + await start(); + return ''; + } + + @override + Future invoke({ + required ActionMethod method, + dynamic data, + Duration? timeout, + }) async { + final id = '${method.name}#${utils.id}'; + _callbackCompleterMap[id] = Completer(); + sendMessage(json.encode(Action(id: id, method: method, data: data))); + return (_callbackCompleterMap[id] as Completer).future.withTimeout( + timeout: timeout, + onLast: () { + final completer = _callbackCompleterMap[id]; + completer?.safeCompleter(null); + _callbackCompleterMap.remove(id); + }, + tag: id, + onTimeout: () => null, + ); + } + + @override + Future get connected => _socketCompleter.future; +} + +final coreService = system.isDesktop ? CoreService() : null; diff --git a/lib/enum/enum.dart b/lib/enum/enum.dart index 9168260..d159d11 100644 --- a/lib/enum/enum.dart +++ b/lib/enum/enum.dart @@ -2,6 +2,7 @@ import 'dart:io'; +import 'package:fl_clash/common/color.dart'; import 'package:fl_clash/common/system.dart'; import 'package:fl_clash/views/dashboard/widgets/widgets.dart'; import 'package:fl_clash/widgets/widgets.dart'; @@ -58,11 +59,8 @@ enum GroupType { enum GroupName { GLOBAL, Proxy, Auto, Fallback } extension GroupTypeExtension on GroupType { - static List get valueList => GroupType.values - .map( - (e) => e.toString().split('.').last, - ) - .toList(); + static List get valueList => + GroupType.values.map((e) => e.toString().split('.').last).toList(); bool get isComputedSelected { return [GroupType.URLTest, GroupType.Fallback].contains(this); @@ -80,11 +78,8 @@ extension GroupTypeExtension on GroupType { enum UsedProxy { GLOBAL, DIRECT, REJECT } extension UsedProxyExtension on UsedProxy { - static List get valueList => UsedProxy.values - .map( - (e) => e.toString().split('.').last, - ) - .toList(); + static List get valueList => + UsedProxy.values.map((e) => e.toString().split('.').last).toList(); String get value => UsedProxyExtension.valueList[index]; } @@ -93,13 +88,7 @@ enum Mode { rule, global, direct } enum ViewMode { mobile, laptop, desktop } -enum LogLevel { - debug, - info, - warning, - error, - silent, -} +enum LogLevel { debug, info, warning, error, silent } extension LogLevelExt on LogLevel { Color? get color { @@ -107,7 +96,7 @@ extension LogLevelExt on LogLevel { LogLevel.silent => Colors.grey.shade700, LogLevel.debug => Colors.grey.shade400, LogLevel.info => null, - LogLevel.warning => Colors.yellowAccent, + LogLevel.warning => Colors.orangeAccent.darken(), LogLevel.error => Colors.redAccent, }; } @@ -138,24 +127,13 @@ enum ResultType { error, } -enum AppMessageType { - log, - delay, - request, - loaded, -} +enum CoreEventType { log, delay, request, loaded, crash } -enum InvokeMessageType { - protect, - process, -} +enum InvokeMessageType { protect, process } enum FindProcessMode { always, off } -enum RecoveryOption { - all, - onlyProfiles, -} +enum RecoveryOption { all, onlyProfiles } enum ChipType { action, delete } @@ -177,7 +155,7 @@ enum DnsMode { fakeIp, @JsonValue('redir-host') redirHost, - hosts + hosts, } enum ExternalControllerStatus { @@ -192,28 +170,12 @@ enum ExternalControllerStatus { } enum KeyboardModifier { - alt([ - PhysicalKeyboardKey.altLeft, - PhysicalKeyboardKey.altRight, - ]), - capsLock([ - PhysicalKeyboardKey.capsLock, - ]), - control([ - PhysicalKeyboardKey.controlLeft, - PhysicalKeyboardKey.controlRight, - ]), - fn([ - PhysicalKeyboardKey.fn, - ]), - meta([ - PhysicalKeyboardKey.metaLeft, - PhysicalKeyboardKey.metaRight, - ]), - shift([ - PhysicalKeyboardKey.shiftLeft, - PhysicalKeyboardKey.shiftRight, - ]); + alt([PhysicalKeyboardKey.altLeft, PhysicalKeyboardKey.altRight]), + capsLock([PhysicalKeyboardKey.capsLock]), + control([PhysicalKeyboardKey.controlLeft, PhysicalKeyboardKey.controlRight]), + fn([PhysicalKeyboardKey.fn]), + meta([PhysicalKeyboardKey.metaLeft, PhysicalKeyboardKey.metaRight]), + shift([PhysicalKeyboardKey.shiftLeft, PhysicalKeyboardKey.shiftRight]); final List physicalKeys; @@ -233,19 +195,9 @@ extension KeyboardModifierExt on KeyboardModifier { } } -enum HotAction { - start, - view, - mode, - proxy, - tun, -} +enum HotAction { start, view, mode, proxy, tun } -enum ProxiesIconStyle { - standard, - none, - icon, -} +enum ProxiesIconStyle { standard, none, icon } enum FontFamily { twEmoji('Twemoji'), @@ -257,10 +209,7 @@ enum FontFamily { const FontFamily(this.value); } -enum RouteMode { - bypassPrivate, - config, -} +enum RouteMode { bypassPrivate, config } enum ActionMethod { message, @@ -294,6 +243,7 @@ enum ActionMethod { getMemory, crash, setupConfig, + deleteFile, ///Android, setState, @@ -307,11 +257,7 @@ enum ActionMethod { enum AuthorizeCode { none, success, error } -enum WindowsHelperServiceStatus { - none, - presence, - running, -} +enum WindowsHelperServiceStatus { none, presence, running } enum FunctionTag { updateClashConfig, @@ -337,79 +283,30 @@ enum FunctionTag { } enum DashboardWidget { - networkSpeed( - GridItem( - crossAxisCellCount: 8, - child: NetworkSpeed(), - ), - ), - outboundModeV2( - GridItem( - crossAxisCellCount: 8, - child: OutboundModeV2(), - ), - ), - outboundMode( - GridItem( - crossAxisCellCount: 4, - child: OutboundMode(), - ), - ), - trafficUsage( - GridItem( - crossAxisCellCount: 4, - child: TrafficUsage(), - ), - ), - networkDetection( - GridItem( - crossAxisCellCount: 4, - child: NetworkDetection(), - ), - ), + networkSpeed(GridItem(crossAxisCellCount: 8, child: NetworkSpeed())), + outboundModeV2(GridItem(crossAxisCellCount: 8, child: OutboundModeV2())), + outboundMode(GridItem(crossAxisCellCount: 4, child: OutboundMode())), + trafficUsage(GridItem(crossAxisCellCount: 4, child: TrafficUsage())), + networkDetection(GridItem(crossAxisCellCount: 4, child: NetworkDetection())), tunButton( - GridItem( - crossAxisCellCount: 4, - child: TUNButton(), - ), + GridItem(crossAxisCellCount: 4, child: TUNButton()), platforms: desktopPlatforms, ), vpnButton( - GridItem( - crossAxisCellCount: 4, - child: VpnButton(), - ), - platforms: [ - SupportPlatform.Android, - ], + GridItem(crossAxisCellCount: 4, child: VpnButton()), + platforms: [SupportPlatform.Android], ), systemProxyButton( - GridItem( - crossAxisCellCount: 4, - child: SystemProxyButton(), - ), + GridItem(crossAxisCellCount: 4, child: SystemProxyButton()), platforms: desktopPlatforms, ), - intranetIp( - GridItem( - crossAxisCellCount: 4, - child: IntranetIP(), - ), - ), - memoryInfo( - GridItem( - crossAxisCellCount: 4, - child: MemoryInfo(), - ), - ); + intranetIp(GridItem(crossAxisCellCount: 4, child: IntranetIP())), + memoryInfo(GridItem(crossAxisCellCount: 4, child: MemoryInfo())); final GridItem widget; final List platforms; - const DashboardWidget( - this.widget, { - this.platforms = SupportPlatform.values, - }); + const DashboardWidget(this.widget, {this.platforms = SupportPlatform.values}); static DashboardWidget getDashboardWidget(GridItem gridItem) { final dashboardWidgets = DashboardWidget.values; @@ -420,10 +317,7 @@ enum DashboardWidget { } } -enum GeodataLoader { - standard, - memconservative, -} +enum GeodataLoader { standard, memconservative } enum PageLabel { dashboard, @@ -478,51 +372,30 @@ enum RuleAction { extension RuleActionExt on RuleAction { bool get hasParams => [ - RuleAction.GEOIP, - RuleAction.IP_ASN, - RuleAction.SRC_IP_ASN, - RuleAction.IP_CIDR, - RuleAction.IP_CIDR6, - RuleAction.IP_SUFFIX, - RuleAction.RULE_SET, - ].contains(this); + RuleAction.GEOIP, + RuleAction.IP_ASN, + RuleAction.SRC_IP_ASN, + RuleAction.IP_CIDR, + RuleAction.IP_CIDR6, + RuleAction.IP_SUFFIX, + RuleAction.RULE_SET, + ].contains(this); } -enum OverrideRuleType { - override, - added, -} +enum OverrideRuleType { override, added } -enum RuleTarget { - DIRECT, - REJECT, -} +enum RuleTarget { DIRECT, REJECT } -enum RecoveryStrategy { - compatible, - override, -} +enum RecoveryStrategy { compatible, override } -enum CacheTag { - logs, - rules, - requests, - proxiesList, -} +enum CacheTag { logs, rules, requests, proxiesList } -enum Language { - yaml, - javaScript, -} +enum Language { yaml, javaScript } -enum ImportOption { - file, - url, -} +enum ImportOption { file, url } -enum ScrollPositionCacheKeys { - tools, - profiles, - proxiesList, - proxiesTabList, -} +enum ScrollPositionCacheKey { tools, profiles, proxiesList, proxiesTabList } + +enum QueryTag { proxies } + +enum CoreStatus { connecting, connected, disconnected } diff --git a/lib/l10n/intl/messages_en.dart b/lib/l10n/intl/messages_en.dart index 0e1dbe0..122afd0 100644 --- a/lib/l10n/intl/messages_en.dart +++ b/lib/l10n/intl/messages_en.dart @@ -177,6 +177,8 @@ class MessageLookup extends MessageLookupByLibrary { "Opening it will lose part of its application ability and gain the support of full amount of Clash.", ), "confirm": MessageLookupByLibrary.simpleMessage("Confirm"), + "connected": MessageLookupByLibrary.simpleMessage("Connected"), + "connecting": MessageLookupByLibrary.simpleMessage("Connecting..."), "connection": MessageLookupByLibrary.simpleMessage("Connection"), "connections": MessageLookupByLibrary.simpleMessage("Connections"), "connectionsDesc": MessageLookupByLibrary.simpleMessage( @@ -194,6 +196,7 @@ class MessageLookup extends MessageLookupByLibrary { "copySuccess": MessageLookupByLibrary.simpleMessage("Copy success"), "core": MessageLookupByLibrary.simpleMessage("Core"), "coreInfo": MessageLookupByLibrary.simpleMessage("Core info"), + "coreStatus": MessageLookupByLibrary.simpleMessage("Core status"), "country": MessageLookupByLibrary.simpleMessage("Country"), "crashTest": MessageLookupByLibrary.simpleMessage("Crash test"), "create": MessageLookupByLibrary.simpleMessage("Create"), @@ -238,6 +241,7 @@ class MessageLookup extends MessageLookupByLibrary { "disclaimerDesc": MessageLookupByLibrary.simpleMessage( "This software is only used for non-commercial purposes such as learning exchanges and scientific research. It is strictly prohibited to use this software for commercial purposes. Any commercial activity, if any, has nothing to do with this software.", ), + "disconnected": MessageLookupByLibrary.simpleMessage("Disconnected"), "discoverNewVersion": MessageLookupByLibrary.simpleMessage( "Discover the new version", ), @@ -247,6 +251,7 @@ class MessageLookup extends MessageLookupByLibrary { "dnsDesc": MessageLookupByLibrary.simpleMessage( "Update DNS related settings", ), + "dnsHijacking": MessageLookupByLibrary.simpleMessage("DNS hijacking"), "dnsMode": MessageLookupByLibrary.simpleMessage("DNS mode"), "doYouWantToPass": MessageLookupByLibrary.simpleMessage( "Do you want to pass", @@ -301,6 +306,9 @@ class MessageLookup extends MessageLookupByLibrary { "There is a certain performance loss after opening", ), "fontFamily": MessageLookupByLibrary.simpleMessage("FontFamily"), + "forceRestartCoreTip": MessageLookupByLibrary.simpleMessage( + "Are you sure you want to force restart the core?", + ), "fourColumns": MessageLookupByLibrary.simpleMessage("Four columns"), "fruitSaladScheme": MessageLookupByLibrary.simpleMessage("FruitSalad"), "general": MessageLookupByLibrary.simpleMessage("General"), @@ -606,6 +614,9 @@ class MessageLookup extends MessageLookupByLibrary { "respectRulesDesc": MessageLookupByLibrary.simpleMessage( "DNS connection following rules, need to configure proxy-server-nameserver", ), + "restartCoreTip": MessageLookupByLibrary.simpleMessage( + "Are you sure you want to restart the core?", + ), "routeAddress": MessageLookupByLibrary.simpleMessage("Route address"), "routeAddressDesc": MessageLookupByLibrary.simpleMessage( "Config listen route address", diff --git a/lib/l10n/intl/messages_ja.dart b/lib/l10n/intl/messages_ja.dart index 553e3d8..b447dda 100644 --- a/lib/l10n/intl/messages_ja.dart +++ b/lib/l10n/intl/messages_ja.dart @@ -133,6 +133,8 @@ class MessageLookup extends MessageLookupByLibrary { "有効化すると一部機能を失いますが、Clashの完全サポートを獲得", ), "confirm": MessageLookupByLibrary.simpleMessage("確認"), + "connected": MessageLookupByLibrary.simpleMessage("接続済み"), + "connecting": MessageLookupByLibrary.simpleMessage("接続中..."), "connection": MessageLookupByLibrary.simpleMessage("接続"), "connections": MessageLookupByLibrary.simpleMessage("接続"), "connectionsDesc": MessageLookupByLibrary.simpleMessage("現在の接続データを表示"), @@ -146,6 +148,7 @@ class MessageLookup extends MessageLookupByLibrary { "copySuccess": MessageLookupByLibrary.simpleMessage("コピー成功"), "core": MessageLookupByLibrary.simpleMessage("コア"), "coreInfo": MessageLookupByLibrary.simpleMessage("コア情報"), + "coreStatus": MessageLookupByLibrary.simpleMessage("コアステータス"), "country": MessageLookupByLibrary.simpleMessage("国"), "crashTest": MessageLookupByLibrary.simpleMessage("クラッシュテスト"), "create": MessageLookupByLibrary.simpleMessage("作成"), @@ -182,9 +185,11 @@ class MessageLookup extends MessageLookupByLibrary { "disclaimerDesc": MessageLookupByLibrary.simpleMessage( "本ソフトウェアは学習交流や科学研究などの非営利目的でのみ使用されます。商用利用は厳禁です。いかなる商用活動も本ソフトウェアとは無関係です。", ), + "disconnected": MessageLookupByLibrary.simpleMessage("切断済み"), "discoverNewVersion": MessageLookupByLibrary.simpleMessage("新バージョンを発見"), "discovery": MessageLookupByLibrary.simpleMessage("新しいバージョンを発見"), "dnsDesc": MessageLookupByLibrary.simpleMessage("DNS関連設定の更新"), + "dnsHijacking": MessageLookupByLibrary.simpleMessage("DNSハイジャッキング"), "dnsMode": MessageLookupByLibrary.simpleMessage("DNSモード"), "doYouWantToPass": MessageLookupByLibrary.simpleMessage("通過させますか?"), "domain": MessageLookupByLibrary.simpleMessage("ドメイン"), @@ -229,6 +234,9 @@ class MessageLookup extends MessageLookupByLibrary { "有効化するとパフォーマンスが若干低下します", ), "fontFamily": MessageLookupByLibrary.simpleMessage("フォントファミリー"), + "forceRestartCoreTip": MessageLookupByLibrary.simpleMessage( + "コアを強制再起動してもよろしいですか?", + ), "fourColumns": MessageLookupByLibrary.simpleMessage("4列"), "fruitSaladScheme": MessageLookupByLibrary.simpleMessage("フルーツサラダ"), "general": MessageLookupByLibrary.simpleMessage("一般"), @@ -460,6 +468,7 @@ class MessageLookup extends MessageLookupByLibrary { "respectRulesDesc": MessageLookupByLibrary.simpleMessage( "DNS接続がルールに従う(proxy-server-nameserverの設定が必要)", ), + "restartCoreTip": MessageLookupByLibrary.simpleMessage("コアを再起動してもよろしいですか?"), "routeAddress": MessageLookupByLibrary.simpleMessage("ルートアドレス"), "routeAddressDesc": MessageLookupByLibrary.simpleMessage("ルートアドレスを設定"), "routeMode": MessageLookupByLibrary.simpleMessage("ルートモード"), diff --git a/lib/l10n/intl/messages_ru.dart b/lib/l10n/intl/messages_ru.dart index e80f88f..f1f273c 100644 --- a/lib/l10n/intl/messages_ru.dart +++ b/lib/l10n/intl/messages_ru.dart @@ -182,6 +182,8 @@ class MessageLookup extends MessageLookupByLibrary { "Включение приведет к потере части функциональности приложения, но обеспечит полную поддержку Clash.", ), "confirm": MessageLookupByLibrary.simpleMessage("Подтвердить"), + "connected": MessageLookupByLibrary.simpleMessage("Подключено"), + "connecting": MessageLookupByLibrary.simpleMessage("Подключение..."), "connection": MessageLookupByLibrary.simpleMessage("Соединение"), "connections": MessageLookupByLibrary.simpleMessage("Соединения"), "connectionsDesc": MessageLookupByLibrary.simpleMessage( @@ -199,6 +201,7 @@ class MessageLookup extends MessageLookupByLibrary { "copySuccess": MessageLookupByLibrary.simpleMessage("Копирование успешно"), "core": MessageLookupByLibrary.simpleMessage("Ядро"), "coreInfo": MessageLookupByLibrary.simpleMessage("Информация о ядре"), + "coreStatus": MessageLookupByLibrary.simpleMessage("Основной статус"), "country": MessageLookupByLibrary.simpleMessage("Страна"), "crashTest": MessageLookupByLibrary.simpleMessage("Тест на сбои"), "create": MessageLookupByLibrary.simpleMessage("Создать"), @@ -245,6 +248,7 @@ class MessageLookup extends MessageLookupByLibrary { "disclaimerDesc": MessageLookupByLibrary.simpleMessage( "Это программное обеспечение используется только в некоммерческих целях, таких как учебные обмены и научные исследования. Запрещено использовать это программное обеспечение в коммерческих целях. Любая коммерческая деятельность, если таковая имеется, не имеет отношения к этому программному обеспечению.", ), + "disconnected": MessageLookupByLibrary.simpleMessage("Отключено"), "discoverNewVersion": MessageLookupByLibrary.simpleMessage( "Обнаружена новая версия", ), @@ -254,6 +258,7 @@ class MessageLookup extends MessageLookupByLibrary { "dnsDesc": MessageLookupByLibrary.simpleMessage( "Обновление настроек, связанных с DNS", ), + "dnsHijacking": MessageLookupByLibrary.simpleMessage("DNS-перехват"), "dnsMode": MessageLookupByLibrary.simpleMessage("Режим DNS"), "doYouWantToPass": MessageLookupByLibrary.simpleMessage( "Вы хотите пропустить", @@ -316,6 +321,9 @@ class MessageLookup extends MessageLookupByLibrary { "При включении возможны небольшие потери производительности", ), "fontFamily": MessageLookupByLibrary.simpleMessage("Семейство шрифтов"), + "forceRestartCoreTip": MessageLookupByLibrary.simpleMessage( + "Вы уверены, что хотите принудительно перезапустить ядро?", + ), "fourColumns": MessageLookupByLibrary.simpleMessage("Четыре столбца"), "fruitSaladScheme": MessageLookupByLibrary.simpleMessage("Фруктовый микс"), "general": MessageLookupByLibrary.simpleMessage("Общие"), @@ -641,6 +649,9 @@ class MessageLookup extends MessageLookupByLibrary { "respectRulesDesc": MessageLookupByLibrary.simpleMessage( "DNS-соединение следует правилам, необходимо настроить proxy-server-nameserver", ), + "restartCoreTip": MessageLookupByLibrary.simpleMessage( + "Вы уверены, что хотите перезапустить ядро?", + ), "routeAddress": MessageLookupByLibrary.simpleMessage("Адрес маршрутизации"), "routeAddressDesc": MessageLookupByLibrary.simpleMessage( "Настройка адреса прослушивания маршрутизации", diff --git a/lib/l10n/intl/messages_zh_CN.dart b/lib/l10n/intl/messages_zh_CN.dart index a66935a..1641fd2 100644 --- a/lib/l10n/intl/messages_zh_CN.dart +++ b/lib/l10n/intl/messages_zh_CN.dart @@ -123,6 +123,8 @@ class MessageLookup extends MessageLookupByLibrary { "开启将失去部分应用能力,获得全量的Clash的支持", ), "confirm": MessageLookupByLibrary.simpleMessage("确定"), + "connected": MessageLookupByLibrary.simpleMessage("已连接"), + "connecting": MessageLookupByLibrary.simpleMessage("连接中..."), "connection": MessageLookupByLibrary.simpleMessage("连接"), "connections": MessageLookupByLibrary.simpleMessage("连接"), "connectionsDesc": MessageLookupByLibrary.simpleMessage("查看当前连接数据"), @@ -136,6 +138,7 @@ class MessageLookup extends MessageLookupByLibrary { "copySuccess": MessageLookupByLibrary.simpleMessage("复制成功"), "core": MessageLookupByLibrary.simpleMessage("内核"), "coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"), + "coreStatus": MessageLookupByLibrary.simpleMessage("核心状态"), "country": MessageLookupByLibrary.simpleMessage("区域"), "crashTest": MessageLookupByLibrary.simpleMessage("崩溃测试"), "create": MessageLookupByLibrary.simpleMessage("创建"), @@ -168,9 +171,11 @@ class MessageLookup extends MessageLookupByLibrary { "disclaimerDesc": MessageLookupByLibrary.simpleMessage( "本软件仅供学习交流、科研等非商业性质的用途,严禁将本软件用于商业目的。如有任何商业行为,均与本软件无关。", ), + "disconnected": MessageLookupByLibrary.simpleMessage("已断开"), "discoverNewVersion": MessageLookupByLibrary.simpleMessage("发现新版本"), "discovery": MessageLookupByLibrary.simpleMessage("发现新版本"), "dnsDesc": MessageLookupByLibrary.simpleMessage("更新DNS相关设置"), + "dnsHijacking": MessageLookupByLibrary.simpleMessage("DNS劫持"), "dnsMode": MessageLookupByLibrary.simpleMessage("DNS模式"), "doYouWantToPass": MessageLookupByLibrary.simpleMessage("是否要通过"), "domain": MessageLookupByLibrary.simpleMessage("域名"), @@ -209,6 +214,7 @@ class MessageLookup extends MessageLookupByLibrary { "findProcessMode": MessageLookupByLibrary.simpleMessage("查找进程"), "findProcessModeDesc": MessageLookupByLibrary.simpleMessage("开启后会有一定性能损耗"), "fontFamily": MessageLookupByLibrary.simpleMessage("字体"), + "forceRestartCoreTip": MessageLookupByLibrary.simpleMessage("您确定要强制重启核心吗?"), "fourColumns": MessageLookupByLibrary.simpleMessage("四列"), "fruitSaladScheme": MessageLookupByLibrary.simpleMessage("果缤纷"), "general": MessageLookupByLibrary.simpleMessage("常规"), @@ -404,6 +410,7 @@ class MessageLookup extends MessageLookupByLibrary { "respectRulesDesc": MessageLookupByLibrary.simpleMessage( "DNS连接跟随rules,需配置proxy-server-nameserver", ), + "restartCoreTip": MessageLookupByLibrary.simpleMessage("您确定要重启核心吗?"), "routeAddress": MessageLookupByLibrary.simpleMessage("路由地址"), "routeAddressDesc": MessageLookupByLibrary.simpleMessage("配置监听路由地址"), "routeMode": MessageLookupByLibrary.simpleMessage("路由模式"), diff --git a/lib/l10n/l10n.dart b/lib/l10n/l10n.dart index daa4b74..0082e02 100644 --- a/lib/l10n/l10n.dart +++ b/lib/l10n/l10n.dart @@ -28,10 +28,9 @@ class AppLocalizations { static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); static Future load(Locale locale) { - final name = - (locale.countryCode?.isEmpty ?? false) - ? locale.languageCode - : locale.toString(); + final name = (locale.countryCode?.isEmpty ?? false) + ? locale.languageCode + : locale.toString(); final localeName = Intl.canonicalizedLocale(name); return initializeMessages(localeName).then((_) { Intl.defaultLocale = localeName; @@ -3259,6 +3258,66 @@ class AppLocalizations { String get request { return Intl.message('Request', name: 'request', desc: '', args: []); } + + /// `Connected` + String get connected { + return Intl.message('Connected', name: 'connected', desc: '', args: []); + } + + /// `Disconnected` + String get disconnected { + return Intl.message( + 'Disconnected', + name: 'disconnected', + desc: '', + args: [], + ); + } + + /// `Connecting...` + String get connecting { + return Intl.message( + 'Connecting...', + name: 'connecting', + desc: '', + args: [], + ); + } + + /// `Are you sure you want to restart the core?` + String get restartCoreTip { + return Intl.message( + 'Are you sure you want to restart the core?', + name: 'restartCoreTip', + desc: '', + args: [], + ); + } + + /// `Are you sure you want to force restart the core?` + String get forceRestartCoreTip { + return Intl.message( + 'Are you sure you want to force restart the core?', + name: 'forceRestartCoreTip', + desc: '', + args: [], + ); + } + + /// `DNS hijacking` + String get dnsHijacking { + return Intl.message( + 'DNS hijacking', + name: 'dnsHijacking', + desc: '', + args: [], + ); + } + + /// `Core status` + String get coreStatus { + return Intl.message('Core status', name: 'coreStatus', desc: '', args: []); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/main.dart b/lib/main.dart index daf6078..148a81a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,128 +1,48 @@ import 'dart:async'; -import 'dart:convert'; -import 'dart:ffi'; import 'dart:io'; -import 'dart:isolate'; -import 'dart:ui'; import 'package:fl_clash/plugins/app.dart'; +import 'package:fl_clash/plugins/service.dart'; import 'package:fl_clash/plugins/tile.dart'; -import 'package:fl_clash/plugins/vpn.dart'; import 'package:fl_clash/state.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'application.dart'; -import 'clash/core.dart'; -import 'clash/lib.dart'; import 'common/common.dart'; -import 'models/models.dart'; +import 'core/controller.dart'; Future main() async { - globalState.isService = false; WidgetsFlutterBinding.ensureInitialized(); final version = await system.version; - await clashCore.preload(); await globalState.initApp(version); - await android?.init(); - await window?.init(version); HttpOverrides.global = FlClashHttpOverrides(); - runApp(ProviderScope( - child: const Application(), - )); + runApp(ProviderScope(child: const Application())); } @pragma('vm:entry-point') Future _service(List flags) async { - globalState.isService = true; WidgetsFlutterBinding.ensureInitialized(); - final quickStart = flags.contains('quick'); - final clashLibHandler = ClashLibHandler(); await globalState.init(); - + await coreController.preload(); + await service?.syncAndroidState(globalState.getAndroidState()); tile?.addListener( _TileListenerWithService( onStop: () async { await app?.tip(appLocalizations.stopVpn); - clashLibHandler.stopListener(); - await vpn?.stop(); - exit(0); + await globalState.handleStop(); }, ), ); - - vpn?.handleGetStartForegroundParams = () { - final traffic = clashLibHandler.getTraffic(); - return json.encode({ - 'title': clashLibHandler.getCurrentProfileName(), - 'content': '$traffic' - }); - }; - - vpn?.addListener( - _VpnListenerWithService( - onDnsChanged: (String dns) { - clashLibHandler.updateDns(dns); - }, - ), - ); - if (!quickStart) { - _handleMainIpc(clashLibHandler); - } else { - commonPrint.log('quick start'); - await ClashCore.initGeo(); + Future(() async { app?.tip(appLocalizations.startVpn); - final homeDirPath = await appPath.homeDirPath; final version = await system.version; + await coreController.init(version); final clashConfig = globalState.config.patchClashConfig.copyWith.tun( enable: false, ); - Future(() async { - final profileId = globalState.config.currentProfileId; - if (profileId == null) { - return; - } - final params = await globalState.getSetupParams( - pathConfig: clashConfig, - ); - final res = await clashLibHandler.quickStart( - InitParams( - homeDir: homeDirPath, - version: version, - ), - params, - globalState.getCoreState(), - ); - debugPrint(res); - if (res.isNotEmpty) { - await vpn?.stop(); - exit(0); - } - await vpn?.start( - clashLibHandler.getAndroidVpnOptions(), - ); - clashLibHandler.startListener(); - }); - } -} - -void _handleMainIpc(ClashLibHandler clashLibHandler) { - final sendPort = IsolateNameServer.lookupPortByName(mainIsolate); - if (sendPort == null) { - return; - } - final serviceReceiverPort = ReceivePort(); - serviceReceiverPort.listen((message) async { - final res = await clashLibHandler.invokeAction(message); - sendPort.send(res); - }); - sendPort.send(serviceReceiverPort.sendPort); - final messageReceiverPort = ReceivePort(); - clashLibHandler.attachMessagePort( - messageReceiverPort.sendPort.nativePort, - ); - messageReceiverPort.listen((message) { - sendPort.send(message); + await globalState.handleStart(); + await coreController.setupConfig(clashConfig); }); } @@ -130,27 +50,11 @@ void _handleMainIpc(ClashLibHandler clashLibHandler) { class _TileListenerWithService with TileListener { final Function() _onStop; - const _TileListenerWithService({ - required Function() onStop, - }) : _onStop = onStop; + const _TileListenerWithService({required Function() onStop}) + : _onStop = onStop; @override void onStop() { _onStop(); } } - -@immutable -class _VpnListenerWithService with VpnListener { - final Function(String dns) _onDnsChanged; - - const _VpnListenerWithService({ - required Function(String dns) onDnsChanged, - }) : _onDnsChanged = onDnsChanged; - - @override - void onDnsChanged(String dns) { - super.onDnsChanged(dns); - _onDnsChanged(dns); - } -} diff --git a/lib/manager/android_manager.dart b/lib/manager/android_manager.dart index e735971..b271b40 100644 --- a/lib/manager/android_manager.dart +++ b/lib/manager/android_manager.dart @@ -1,4 +1,8 @@ +import 'package:fl_clash/core/core.dart'; +import 'package:fl_clash/enum/enum.dart'; +import 'package:fl_clash/models/core.dart'; import 'package:fl_clash/plugins/app.dart'; +import 'package:fl_clash/plugins/service.dart'; import 'package:fl_clash/providers/providers.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -6,23 +10,49 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; class AndroidManager extends ConsumerStatefulWidget { final Widget child; - const AndroidManager({ - super.key, - required this.child, - }); + const AndroidManager({super.key, required this.child}); @override ConsumerState createState() => _AndroidContainerState(); } -class _AndroidContainerState extends ConsumerState { +class _AndroidContainerState extends ConsumerState + with ServiceListener { @override void initState() { super.initState(); - ref.listenManual(appSettingProvider.select((state) => state.hidden), - (prev, next) { + ref.listenManual(appSettingProvider.select((state) => state.hidden), ( + prev, + next, + ) { app?.updateExcludeFromRecents(next); }, fireImmediately: true); + ref.listenManual(androidStateProvider, (prev, next) { + if (prev != next) { + service?.syncAndroidState(next); + } + }); + service?.addListener(this); + } + + @override + Future dispose() async { + service?.removeListener(this); + super.dispose(); + } + + @override + void onServiceEvent(CoreEvent event) { + coreEventManager.sendEvent(event); + super.onServiceEvent(event); + } + + @override + void onServiceCrash(String message) { + coreEventManager.sendEvent( + CoreEvent(type: CoreEventType.crash, data: message), + ); + super.onServiceCrash(message); } @override diff --git a/lib/manager/app_manager.dart b/lib/manager/app_manager.dart index ff206bc..e899317 100644 --- a/lib/manager/app_manager.dart +++ b/lib/manager/app_manager.dart @@ -14,10 +14,7 @@ import 'package:intl/intl.dart'; class AppStateManager extends ConsumerStatefulWidget { final Widget child; - const AppStateManager({ - super.key, - required this.child, - }); + const AppStateManager({super.key, required this.child}); @override ConsumerState createState() => _AppStateManagerState(); @@ -36,46 +33,40 @@ class _AppStateManagerState extends ConsumerState } }); }); - ref.listenManual( - checkIpProvider, - (prev, next) { - if (prev != next && next.b) { - detectionState.startCheck(); - } - }, - fireImmediately: true, - ); + ref.listenManual(checkIpProvider, (prev, next) { + if (prev != next && next.b) { + detectionState.startCheck(); + } + }, fireImmediately: true); ref.listenManual(configStateProvider, (prev, next) { if (prev != next) { globalState.appController.savePreferencesDebounce(); } }); + ref.listenManual(needUpdateGroupsProvider, (prev, next) { + if (prev != next) { + globalState.appController.updateGroupsDebounce(); + } + }); if (window == null) { return; } - ref.listenManual( - autoSetSystemDnsStateProvider, - (prev, next) async { - if (prev == next) { - return; - } - if (next.a == true && next.b == true) { - macOS?.updateDns(false); - } else { - macOS?.updateDns(true); - } - }, - ); - ref.listenManual( - currentBrightnessProvider, - (prev, next) { - if (prev == next) { - return; - } - window?.updateMacOSBrightness(next); - }, - fireImmediately: true, - ); + ref.listenManual(autoSetSystemDnsStateProvider, (prev, next) async { + if (prev == next) { + return; + } + if (next.a == true && next.b == true) { + macOS?.updateDns(false); + } else { + macOS?.updateDns(true); + } + }); + ref.listenManual(currentBrightnessProvider, (prev, next) { + if (prev == next) { + return; + } + window?.updateMacOSBrightness(next); + }, fireImmediately: true); } @override @@ -119,10 +110,7 @@ class _AppStateManagerState extends ConsumerState class AppEnvManager extends StatelessWidget { final Widget child; - const AppEnvManager({ - super.key, - required this.child, - }); + const AppEnvManager({super.key, required this.child}); @override Widget build(BuildContext context) { @@ -149,14 +137,11 @@ class AppEnvManager extends StatelessWidget { class AppSidebarContainer extends ConsumerWidget { final Widget child; - const AppSidebarContainer({ - super.key, - required this.child, - }); + const AppSidebarContainer({super.key, required this.child}); Widget _buildLoading() { return Consumer( - builder: (_, ref, __) { + builder: (_, ref, _) { final loading = ref.watch(loadingProvider); final isMobileView = ref.watch(isMobileViewProvider); return loading && !isMobileView @@ -180,10 +165,7 @@ class AppSidebarContainer extends ConsumerWidget { ); } return TransparentMacOSSidebar( - child: Material( - color: Colors.transparent, - child: child, - ), + child: Material(color: Colors.transparent, child: child), ); } @@ -206,15 +188,8 @@ class AppSidebarContainer extends ConsumerWidget { context: context, child: Column( children: [ - SizedBox( - height: 32, - ), - if (!system.isMacOS) ...[ - AppIcon(), - SizedBox( - height: 12, - ), - ], + SizedBox(height: 32), + if (!system.isMacOS) ...[AppIcon(), SizedBox(height: 12)], Expanded( child: ScrollConfiguration( behavior: HiddenBarScrollBehavior(), @@ -222,27 +197,26 @@ class AppSidebarContainer extends ConsumerWidget { child: IntrinsicHeight( child: NavigationRail( backgroundColor: Colors.transparent, - selectedLabelTextStyle: - context.textTheme.labelLarge!.copyWith( - color: context.colorScheme.onSurface, - ), - unselectedLabelTextStyle: - context.textTheme.labelLarge!.copyWith( - color: context.colorScheme.onSurface, - ), + selectedLabelTextStyle: context + .textTheme + .labelLarge! + .copyWith(color: context.colorScheme.onSurface), + unselectedLabelTextStyle: context + .textTheme + .labelLarge! + .copyWith(color: context.colorScheme.onSurface), destinations: navigationItems .map( (e) => NavigationRailDestination( icon: e.icon, - label: Text( - Intl.message(e.label.name), - ), + label: Text(Intl.message(e.label.name)), ), ) .toList(), onDestinationSelected: (index) { - globalState.appController - .toPage(navigationItems[index].label); + globalState.appController.toPage( + navigationItems[index].label, + ); }, extended: false, selectedIndex: currentIndex, @@ -254,15 +228,14 @@ class AppSidebarContainer extends ConsumerWidget { ), ), ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), IconButton( onPressed: () { - ref.read(appSettingProvider.notifier).updateState( - (state) => state.copyWith( - showLabel: !state.showLabel, - ), + ref + .read(appSettingProvider.notifier) + .updateState( + (state) => + state.copyWith(showLabel: !state.showLabel), ); }, icon: Icon( @@ -270,21 +243,14 @@ class AppSidebarContainer extends ConsumerWidget { color: context.colorScheme.onSurfaceVariant, ), ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), ], ), ), _buildLoading(), ], ), - Expanded( - flex: 1, - child: ClipRect( - child: child, - ), - ) + Expanded(flex: 1, child: ClipRect(child: child)), ], ); } diff --git a/lib/manager/clash_manager.dart b/lib/manager/core_manager.dart similarity index 57% rename from lib/manager/clash_manager.dart rename to lib/manager/core_manager.dart index f21810f..4a53412 100644 --- a/lib/manager/clash_manager.dart +++ b/lib/manager/core_manager.dart @@ -1,5 +1,5 @@ -import 'package:fl_clash/clash/clash.dart'; import 'package:fl_clash/common/common.dart'; +import 'package:fl_clash/core/core.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/providers/app.dart'; @@ -9,20 +9,17 @@ import 'package:fl_clash/state.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -class ClashManager extends ConsumerStatefulWidget { +class CoreManager extends ConsumerStatefulWidget { final Widget child; - const ClashManager({ - super.key, - required this.child, - }); + const CoreManager({super.key, required this.child}); @override - ConsumerState createState() => _ClashContainerState(); + ConsumerState createState() => _CoreContainerState(); } -class _ClashContainerState extends ConsumerState - with AppMessageListener { +class _CoreContainerState extends ConsumerState + with CoreEventListener { @override Widget build(BuildContext context) { return widget.child; @@ -31,38 +28,32 @@ class _ClashContainerState extends ConsumerState @override void initState() { super.initState(); - clashMessage.addListener(this); + coreEventManager.addListener(this); ref.listenManual(needSetupProvider, (prev, next) { if (prev != next) { globalState.appController.handleChangeProfile(); } }); - ref.listenManual(coreStateProvider, (prev, next) async { - if (prev != next) { - await clashCore.setState(next); - } - }); ref.listenManual(updateParamsProvider, (prev, next) { if (prev != next) { globalState.appController.updateClashConfigDebounce(); } }); - - ref.listenManual( - appSettingProvider.select((state) => state.openLogs), - (prev, next) { - if (next) { - clashCore.startLog(); - } else { - clashCore.stopLog(); - } - }, - ); + ref.listenManual(appSettingProvider.select((state) => state.openLogs), ( + prev, + next, + ) { + if (next) { + coreController.startLog(); + } else { + coreController.stopLog(); + } + }, fireImmediately: true); } @override Future dispose() async { - clashMessage.removeListener(this); + coreEventManager.removeListener(this); super.dispose(); } @@ -71,13 +62,9 @@ class _ClashContainerState extends ConsumerState super.onDelay(delay); final appController = globalState.appController; appController.setDelay(delay); - debouncer.call( - FunctionTag.updateDelay, - () async { - appController.updateGroupsDebounce(); - }, - duration: const Duration(milliseconds: 5000), - ); + debouncer.call(FunctionTag.updateDelay, () async { + appController.updateGroupsDebounce(); + }, duration: const Duration(milliseconds: 5000)); } @override @@ -97,12 +84,21 @@ class _ClashContainerState extends ConsumerState @override Future onLoaded(String providerName) async { - ref.read(providersProvider.notifier).setProvider( - await clashCore.getExternalProvider( - providerName, - ), - ); + ref + .read(providersProvider.notifier) + .setProvider(await coreController.getExternalProvider(providerName)); globalState.appController.updateGroupsDebounce(); super.onLoaded(providerName); } + + @override + Future onCrash(String message) async { + context.showNotifier(message); + if (ref.read(coreStatusProvider) != CoreStatus.connected) { + return; + } + ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected; + await coreController.shutdown(); + super.onCrash(message); + } } diff --git a/lib/manager/manager.dart b/lib/manager/manager.dart index 0dbf611..6179b9a 100644 --- a/lib/manager/manager.dart +++ b/lib/manager/manager.dart @@ -1,7 +1,7 @@ export 'android_manager.dart'; export 'app_manager.dart'; -export 'clash_manager.dart'; export 'connectivity_manager.dart'; +export 'core_manager.dart'; export 'message_manager.dart'; export 'proxy_manager.dart'; export 'theme_manager.dart'; diff --git a/lib/manager/message_manager.dart b/lib/manager/message_manager.dart index 1a911e8..24da85b 100644 --- a/lib/manager/message_manager.dart +++ b/lib/manager/message_manager.dart @@ -9,10 +9,7 @@ import 'package:flutter/material.dart'; class MessageManager extends StatefulWidget { final Widget child; - const MessageManager({ - super.key, - required this.child, - }); + const MessageManager({super.key, required this.child}); @override State createState() => MessageManagerState(); @@ -35,10 +32,7 @@ class MessageManagerState extends State { } Future message(String text) async { - final commonMessage = CommonMessage( - id: utils.uuidV4, - text: text, - ); + final commonMessage = CommonMessage(id: utils.uuidV4, text: text); commonPrint.log(text); _bufferMessages.add(commonMessage); await _showMessage(); @@ -52,22 +46,20 @@ class MessageManagerState extends State { while (_bufferMessages.isNotEmpty) { final commonMessage = _bufferMessages.removeAt(0); _messagesNotifier.value = List.from(_messagesNotifier.value) - ..add( - commonMessage, - ); + ..add(commonMessage); await Future.delayed(Duration(seconds: 1)); Future.delayed(commonMessage.duration, () { _handleRemove(commonMessage); }); - if (_bufferMessages.isEmpty) { - _pushing = false; - } } } Future _handleRemove(CommonMessage commonMessage) async { _messagesNotifier.value = List.from(_messagesNotifier.value) ..remove(commonMessage); + if (_bufferMessages.isEmpty) { + _pushing = false; + } } @override @@ -77,43 +69,55 @@ class MessageManagerState extends State { widget.child, ValueListenableBuilder( valueListenable: _messagesNotifier, - builder: (_, messages, __) { - return FadeThroughBox( + builder: (_, messages, _) { + return Container( margin: EdgeInsets.only( - top: kToolbarHeight + 8, + top: kToolbarHeight + 12, left: 12, right: 12, ), - alignment: Alignment.topRight, - child: messages.isEmpty - ? SizedBox() - : LayoutBuilder( - key: Key(messages.last.id), - builder: (_, constraints) { - return Card( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(12.0), + child: FadeThroughBox( + alignment: Alignment.topRight, + child: messages.isEmpty + ? SizedBox() + : LayoutBuilder( + key: Key(messages.last.id), + builder: (_, constraints) { + return Card( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(12.0), + ), ), - ), - elevation: 10, - color: context.colorScheme.surfaceContainerHigh, - child: Container( - width: min( - constraints.maxWidth, - 500, + elevation: 10, + color: context.colorScheme.surfaceContainerHigh, + child: Container( + width: min(constraints.maxWidth, 500), + padding: EdgeInsets.symmetric( + horizontal: 12, + vertical: 10, + ), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Flexible(child: Text(messages.last.text)), + IconButton( + visualDensity: VisualDensity.compact, + iconSize: 18, + padding: EdgeInsets.zero, + onPressed: () { + _handleRemove(messages.last); + }, + icon: Icon(Icons.close), + ), + ], + ), ), - padding: EdgeInsets.symmetric( - horizontal: 12, - vertical: 16, - ), - child: Text( - messages.last.text, - ), - ), - ); - }, - ), + ); + }, + ), + ), ); }, ), diff --git a/lib/manager/tray_manager.dart b/lib/manager/tray_manager.dart index 61ab743..6448885 100755 --- a/lib/manager/tray_manager.dart +++ b/lib/manager/tray_manager.dart @@ -39,6 +39,7 @@ class _TrayContainerState extends ConsumerState with TrayListener { @override void onTrayIconRightMouseDown() { + // ignore: deprecated_member_use trayManager.popUpContextMenu(bringAppToFront: true); } diff --git a/lib/manager/vpn_manager.dart b/lib/manager/vpn_manager.dart index 869f19a..c3e37d6 100644 --- a/lib/manager/vpn_manager.dart +++ b/lib/manager/vpn_manager.dart @@ -1,6 +1,5 @@ import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; -import 'package:fl_clash/providers/app.dart'; import 'package:fl_clash/providers/state.dart'; import 'package:fl_clash/state.dart'; import 'package:flutter/material.dart'; @@ -9,10 +8,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; class VpnManager extends ConsumerStatefulWidget { final Widget child; - const VpnManager({ - super.key, - required this.child, - }); + const VpnManager({super.key, required this.child}); @override ConsumerState createState() => _VpnContainerState(); @@ -28,16 +24,11 @@ class _VpnContainerState extends ConsumerState { } void showTip() { - debouncer.call( - FunctionTag.vpnTip, - () { - if (ref.read(runTimeProvider.notifier).isStart) { - globalState.showNotifier( - appLocalizations.vpnTip, - ); - } - }, - ); + debouncer.call(FunctionTag.vpnTip, () { + if (ref.read(isStartProvider)) { + globalState.showNotifier(appLocalizations.vpnTip); + } + }); } @override diff --git a/lib/manager/window_manager.dart b/lib/manager/window_manager.dart index ff45574..015a005 100644 --- a/lib/manager/window_manager.dart +++ b/lib/manager/window_manager.dart @@ -12,10 +12,7 @@ import 'package:window_manager/window_manager.dart'; class WindowManager extends ConsumerStatefulWidget { final Widget child; - const WindowManager({ - super.key, - required this.child, - }); + const WindowManager({super.key, required this.child}); @override ConsumerState createState() => _WindowContainerState(); @@ -31,19 +28,16 @@ class _WindowContainerState extends ConsumerState @override void initState() { super.initState(); - ref.listenManual( - appSettingProvider.select((state) => state.autoLaunch), - (prev, next) { - if (prev != next) { - debouncer.call( - FunctionTag.autoLaunch, - () { - autoLaunch?.updateStatus(next); - }, - ); - } - }, - ); + ref.listenManual(appSettingProvider.select((state) => state.autoLaunch), ( + prev, + next, + ) { + if (prev != next) { + debouncer.call(FunctionTag.autoLaunch, () { + autoLaunch?.updateStatus(next); + }); + } + }); windowExtManager.addListener(this); windowManager.addListener(this); } @@ -71,11 +65,10 @@ class _WindowContainerState extends ConsumerState Future onWindowMoved() async { super.onWindowMoved(); final offset = await windowManager.getPosition(); - ref.read(windowSettingProvider.notifier).updateState( - (state) => state.copyWith( - top: offset.dy, - left: offset.dx, - ), + ref + .read(windowSettingProvider.notifier) + .updateState( + (state) => state.copyWith(top: offset.dy, left: offset.dx), ); } @@ -83,11 +76,10 @@ class _WindowContainerState extends ConsumerState Future onWindowResized() async { super.onWindowResized(); final size = await windowManager.getSize(); - ref.read(windowSettingProvider.notifier).updateState( - (state) => state.copyWith( - width: size.width, - height: size.height, - ), + ref + .read(windowSettingProvider.notifier) + .updateState( + (state) => state.copyWith(width: size.width, height: size.height), ); } @@ -117,10 +109,7 @@ class _WindowContainerState extends ConsumerState class WindowHeaderContainer extends StatelessWidget { final Widget child; - const WindowHeaderContainer({ - super.key, - required this.child, - }); + const WindowHeaderContainer({super.key, required this.child}); @override Widget build(BuildContext context) { @@ -135,13 +124,8 @@ class WindowHeaderContainer extends StatelessWidget { children: [ Column( children: [ - SizedBox( - height: kHeaderHeight, - ), - Expanded( - flex: 1, - child: child!, - ), + SizedBox(height: kHeaderHeight), + Expanded(flex: 1, child: child!), ], ), const WindowHeader(), @@ -210,14 +194,10 @@ class _WindowHeaderState extends State { }, icon: ValueListenableBuilder( valueListenable: isPinNotifier, - builder: (_, value, ___) { + builder: (_, value, _) { return value - ? const Icon( - Icons.push_pin, - ) - : const Icon( - Icons.push_pin_outlined, - ); + ? const Icon(Icons.push_pin) + : const Icon(Icons.push_pin_outlined); }, ), ), @@ -233,15 +213,10 @@ class _WindowHeaderState extends State { }, icon: ValueListenableBuilder( valueListenable: isMaximizedNotifier, - builder: (_, value, ___) { + builder: (_, value, _) { return value - ? const Icon( - Icons.filter_none, - size: 20, - ) - : const Icon( - Icons.crop_square, - ); + ? const Icon(Icons.filter_none, size: 20) + : const Icon(Icons.crop_square); }, ), ), @@ -280,15 +255,10 @@ class _WindowHeaderState extends State { ), ), if (system.isMacOS) - const Text( - appName, - ) + const Text(appName) else ...[ - Positioned( - right: 0, - child: _buildActions(), - ), - ] + Positioned(right: 0, child: _buildActions()), + ], ], ), ); diff --git a/lib/models/app.dart b/lib/models/app.dart index b007908..240ea00 100644 --- a/lib/models/app.dart +++ b/lib/models/app.dart @@ -1,5 +1,6 @@ import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; +import 'package:fl_clash/models/selector.dart'; import 'package:flutter/services.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -11,7 +12,7 @@ part 'generated/app.freezed.dart'; typedef DelayMap = Map>; @freezed -class AppState with _$AppState { +abstract class AppState with _$AppState { const factory AppState({ @Default(false) bool isInit, @Default(false) bool backBlock, @@ -34,6 +35,9 @@ class AppState with _$AppState { @Default(false) bool realTunEnable, @Default(false) bool loading, required SystemUiOverlayStyle systemUiOverlayStyle, + ProfileOverrideModel? profileOverrideModel, + @Default({}) Map queryMap, + @Default(CoreStatus.connecting) CoreStatus coreStatus, }) = _AppState; } diff --git a/lib/models/clash_config.dart b/lib/models/clash_config.dart index 0d6ef8a..8f7486b 100644 --- a/lib/models/clash_config.dart +++ b/lib/models/clash_config.dart @@ -1,5 +1,3 @@ -// ignore_for_file: invalid_annotation_target - import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -100,17 +98,14 @@ const defaultBypassPrivateRouteAddress = [ 'f000::/5', 'f800::/6', 'fe00::/9', - 'fec0::/10' + 'fec0::/10', ]; @freezed -class ProxyGroup with _$ProxyGroup { +abstract class ProxyGroup with _$ProxyGroup { const factory ProxyGroup({ required String name, - @JsonKey( - fromJson: GroupType.parseProfileType, - ) - required GroupType type, + @JsonKey(fromJson: GroupType.parseProfileType) required GroupType type, List? proxies, List? use, int? interval, @@ -131,17 +126,15 @@ class ProxyGroup with _$ProxyGroup { } @freezed -class RuleProvider with _$RuleProvider { - const factory RuleProvider({ - required String name, - }) = _RuleProvider; +abstract class RuleProvider with _$RuleProvider { + const factory RuleProvider({required String name}) = _RuleProvider; factory RuleProvider.fromJson(Map json) => _$RuleProviderFromJson(json); } @freezed -class Sniffer with _$Sniffer { +abstract class Sniffer with _$Sniffer { const factory Sniffer({ @Default(false) bool enable, @Default(true) @JsonKey(name: 'override-destination') bool overrideDest, @@ -165,7 +158,7 @@ List _formJsonPorts(List? ports) { } @freezed -class SnifferConfig with _$SnifferConfig { +abstract class SnifferConfig with _$SnifferConfig { const factory SnifferConfig({ @Default([]) @JsonKey(fromJson: _formJsonPorts) List ports, @JsonKey(name: 'override-destination') bool? overrideDest, @@ -176,7 +169,7 @@ class SnifferConfig with _$SnifferConfig { } @freezed -class Tun with _$Tun { +abstract class Tun with _$Tun { const factory Tun({ @Default(false) bool enable, @Default(appName) String device, @@ -206,30 +199,23 @@ extension TunExt on Tun { ? defaultBypassPrivateRouteAddress : routeAddress; return switch (system.isDesktop) { - true => copyWith( - autoRoute: true, - routeAddress: [], - ), + true => copyWith(autoRoute: true, routeAddress: []), false => copyWith( - autoRoute: mRouteAddress.isEmpty ? true : false, - routeAddress: mRouteAddress, - ), + autoRoute: mRouteAddress.isEmpty ? true : false, + routeAddress: mRouteAddress, + ), }; } } @freezed -class FallbackFilter with _$FallbackFilter { +abstract class FallbackFilter with _$FallbackFilter { const factory FallbackFilter({ @Default(true) bool geoip, @Default('CN') @JsonKey(name: 'geoip-code') String geoipCode, @Default(['gfw']) List geosite, @Default(['240.0.0.0/4']) List ipcidr, - @Default([ - '+.google.com', - '+.facebook.com', - '+.youtube.com', - ]) + @Default(['+.google.com', '+.facebook.com', '+.youtube.com']) List domain, }) = _FallbackFilter; @@ -238,7 +224,7 @@ class FallbackFilter with _$FallbackFilter { } @freezed -class Dns with _$Dns { +abstract class Dns with _$Dns { const factory Dns({ @Default(true) bool enable, @Default('0.0.0.0:1053') String listen, @@ -256,32 +242,20 @@ class Dns with _$Dns { @Default('198.18.0.1/16') @JsonKey(name: 'fake-ip-range') String fakeIpRange, - @Default([ - '*.lan', - 'localhost.ptlogin2.qq.com', - ]) + @Default(['*.lan', 'localhost.ptlogin2.qq.com']) @JsonKey(name: 'fake-ip-filter') List fakeIpFilter, @Default({ 'www.baidu.com': '114.114.114.114', '+.internal.crop.com': '10.0.0.1', - 'geosite:cn': 'https://doh.pub/dns-query' + 'geosite:cn': 'https://doh.pub/dns-query', }) @JsonKey(name: 'nameserver-policy') Map nameserverPolicy, - @Default([ - 'https://doh.pub/dns-query', - 'https://dns.alidns.com/dns-query', - ]) + @Default(['https://doh.pub/dns-query', 'https://dns.alidns.com/dns-query']) List nameserver, - @Default([ - 'tls://8.8.4.4', - 'tls://1.1.1.1', - ]) - List fallback, - @Default([ - 'https://doh.pub/dns-query', - ]) + @Default(['tls://8.8.4.4', 'tls://1.1.1.1']) List fallback, + @Default(['https://doh.pub/dns-query']) @JsonKey(name: 'proxy-server-nameserver') List proxyServerNameserver, @Default(FallbackFilter()) @@ -301,7 +275,7 @@ class Dns with _$Dns { } @freezed -class GeoXUrl with _$GeoXUrl { +abstract class GeoXUrl with _$GeoXUrl { const factory GeoXUrl({ @Default( 'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb', @@ -337,7 +311,7 @@ class GeoXUrl with _$GeoXUrl { } @freezed -class ParsedRule with _$ParsedRule { +abstract class ParsedRule with _$ParsedRule { const factory ParsedRule({ required RuleAction ruleAction, String? content, @@ -351,9 +325,7 @@ class ParsedRule with _$ParsedRule { factory ParsedRule.parseString(String value) { final splits = value.split(','); final shortSplits = splits - .where( - (item) => !item.contains('src') && !item.contains('no-resolve'), - ) + .where((item) => !item.contains('src') && !item.contains('no-resolve')) .toList(); final ruleAction = RuleAction.values.firstWhere( (item) => item.value == shortSplits.first, @@ -398,33 +370,25 @@ extension ParsedRuleExt on ParsedRule { if (ruleAction.hasParams) ...[ if (src) 'src', if (noResolve) 'no-resolve', - ] + ], ].join(','); } } @freezed -class Rule with _$Rule { - const factory Rule({ - required String id, - required String value, - }) = _Rule; +abstract class Rule with _$Rule { + const factory Rule({required String id, required String value}) = _Rule; factory Rule.value(String value) { - return Rule( - value: value, - id: utils.uuidV4, - ); + return Rule(value: value, id: utils.uuidV4); } factory Rule.fromJson(Map json) => _$RuleFromJson(json); } @freezed -class SubRule with _$SubRule { - const factory SubRule({ - required String name, - }) = _SubRule; +abstract class SubRule with _$SubRule { + const factory SubRule({required String name}) = _SubRule; factory SubRule.fromJson(Map json) => _$SubRuleFromJson(json); @@ -434,11 +398,7 @@ List _genRule(List? rules) { if (rules == null) { return []; } - return rules - .map( - (item) => Rule.value(item), - ) - .toList(); + return rules.map((item) => Rule.value(item)).toList(); } List _genRuleProviders(Map json) { @@ -446,17 +406,11 @@ List _genRuleProviders(Map json) { } List _genSubRules(Map json) { - return json.entries - .map( - (entry) => SubRule( - name: entry.key, - ), - ) - .toList(); + return json.entries.map((entry) => SubRule(name: entry.key)).toList(); } @freezed -class ClashConfigSnippet with _$ClashConfigSnippet { +abstract class ClashConfigSnippet with _$ClashConfigSnippet { const factory ClashConfigSnippet({ @Default([]) @JsonKey(name: 'proxy-groups') List proxyGroups, @JsonKey(fromJson: _genRule, name: 'rules') @Default([]) List rule, @@ -473,7 +427,7 @@ class ClashConfigSnippet with _$ClashConfigSnippet { } @freezed -class ClashConfig with _$ClashConfig { +abstract class ClashConfig with _$ClashConfig { const factory ClashConfig({ @Default(defaultMixedPort) @JsonKey(name: 'mixed-port') int mixedPort, @Default(0) @JsonKey(name: 'socks-port') int socksPort, @@ -484,7 +438,7 @@ class ClashConfig with _$ClashConfig { @Default(false) @JsonKey(name: 'allow-lan') bool allowLan, @Default(LogLevel.error) @JsonKey(name: 'log-level') LogLevel logLevel, @Default(false) bool ipv6, - @Default(FindProcessMode.off) + @Default(FindProcessMode.always) @JsonKey( name: 'find-process-mode', unknownEnumValue: FindProcessMode.always, diff --git a/lib/models/common.dart b/lib/models/common.dart index fdccd66..051b5e5 100644 --- a/lib/models/common.dart +++ b/lib/models/common.dart @@ -1,7 +1,3 @@ -// ignore_for_file: invalid_annotation_target - -import 'dart:math'; - import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:flutter/material.dart'; @@ -11,7 +7,7 @@ part 'generated/common.freezed.dart'; part 'generated/common.g.dart'; @freezed -class NavigationItem with _$NavigationItem { +abstract class NavigationItem with _$NavigationItem { const factory NavigationItem({ required Icon icon, required PageLabel label, @@ -25,7 +21,7 @@ class NavigationItem with _$NavigationItem { } @freezed -class Package with _$Package { +abstract class Package with _$Package { const factory Package({ required String packageName, required String label, @@ -39,7 +35,7 @@ class Package with _$Package { } @freezed -class Metadata with _$Metadata { +abstract class Metadata with _$Metadata { const factory Metadata({ @Default(0) int uid, @Default('') String network, @@ -65,7 +61,7 @@ class Metadata with _$Metadata { } @freezed -class TrackerInfo with _$TrackerInfo { +abstract class TrackerInfo with _$TrackerInfo { const factory TrackerInfo({ required String id, @Default(0) int upload, @@ -99,9 +95,9 @@ extension TrackerInfoExt on TrackerInfo { final process = metadata.process; final uid = metadata.uid; if (uid != 0) { - return '$process($uid)'; + return '$process($uid)'.trim(); } - return process; + return process.trim(); } } @@ -114,7 +110,7 @@ String _logDateTime(dynamic _) { // } @freezed -class Log with _$Log { +abstract class Log with _$Log { const factory Log({ // @JsonKey(fromJson: _logId) required String id, @JsonKey(name: 'LogLevel') @Default(LogLevel.info) LogLevel logLevel, @@ -122,9 +118,7 @@ class Log with _$Log { @JsonKey(fromJson: _logDateTime) required String dateTime, }) = _Log; - factory Log.app( - String payload, - ) { + factory Log.app(String payload) { return Log( payload: payload, dateTime: _logDateTime(null), @@ -136,7 +130,7 @@ class Log with _$Log { } @freezed -class LogsState with _$LogsState { +abstract class LogsState with _$LogsState { const factory LogsState({ @Default([]) List logs, @Default([]) List keywords, @@ -148,19 +142,17 @@ class LogsState with _$LogsState { extension LogsStateExt on LogsState { List get list { final lowQuery = query.toLowerCase(); - return logs.where( - (log) { - final logLevelName = log.logLevel.name; - return {logLevelName}.containsAll(keywords) && - ((log.payload.toLowerCase().contains(lowQuery)) || - logLevelName.contains(lowQuery)); - }, - ).toList(); + return logs.where((log) { + final logLevelName = log.logLevel.name; + return {logLevelName}.containsAll(keywords) && + ((log.payload.toLowerCase().contains(lowQuery)) || + logLevelName.contains(lowQuery)); + }).toList(); } } @freezed -class TrackerInfosState with _$TrackerInfosState { +abstract class TrackerInfosState with _$TrackerInfosState { const factory TrackerInfosState({ @Default([]) List trackerInfos, @Default([]) List keywords, @@ -178,8 +170,8 @@ extension TrackerInfosStateExt on TrackerInfosState { final process = trackerInfo.metadata.process; final networkText = trackerInfo.metadata.network.toLowerCase(); final hostText = trackerInfo.metadata.host.toLowerCase(); - final destinationIPText = - trackerInfo.metadata.destinationIP.toLowerCase(); + final destinationIPText = trackerInfo.metadata.destinationIP + .toLowerCase(); final processText = trackerInfo.metadata.process.toLowerCase(); final chainsText = chains.join('').toLowerCase(); return {...chains, process}.containsAll(keywords) && @@ -195,7 +187,7 @@ extension TrackerInfosStateExt on TrackerInfosState { const defaultDavFileName = 'backup.zip'; @freezed -class DAV with _$DAV { +abstract class DAV with _$DAV { const factory DAV({ required String uri, required String user, @@ -207,20 +199,18 @@ class DAV with _$DAV { } @freezed -class FileInfo with _$FileInfo { - const factory FileInfo({ - required int size, - required DateTime lastModified, - }) = _FileInfo; +abstract class FileInfo with _$FileInfo { + const factory FileInfo({required int size, required DateTime lastModified}) = + _FileInfo; } extension FileInfoExt on FileInfo { String get desc => - '${TrafficValue(value: size).show} · ${lastModified.lastUpdateTimeDesc}'; + '${size.traffic.show} · ${lastModified.lastUpdateTimeDesc}'; } @freezed -class VersionInfo with _$VersionInfo { +abstract class VersionInfo with _$VersionInfo { const factory VersionInfo({ @Default('') String clashName, @Default('') String version, @@ -230,60 +220,38 @@ class VersionInfo with _$VersionInfo { _$VersionInfoFromJson(json); } -class Traffic { - int id; - TrafficValue up; - TrafficValue down; +@freezed +abstract class Traffic with _$Traffic { + const factory Traffic({@Default(0) num up, @Default(0) num down}) = _Traffic; - Traffic({int? up, int? down}) - : id = DateTime.now().millisecondsSinceEpoch, - up = TrafficValue(value: up), - down = TrafficValue(value: down); - - num get speed => up.value + down.value; - - factory Traffic.fromMap(Map map) { - return Traffic( - up: map['up'], - down: map['down'], - ); - } - - String toSpeedText() { - return '↑ $up/s ↓ $down/s'; - } - - @override - String toString() { - return '$up↑ $down↓'; - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is Traffic && - runtimeType == other.runtimeType && - id == other.id && - up == other.up && - down == other.down; - - @override - int get hashCode => id.hashCode ^ up.hashCode ^ down.hashCode; + factory Traffic.fromJson(Map json) => + _$TrafficFromJson(json); } -@immutable -class TrafficValueShow { - final double value; - final TrafficUnit unit; +extension TrafficExt on Traffic { + String get speedText { + return '↑ ${up.traffic.show}/s ↓ ${down.traffic.show}/s'; + } - const TrafficValueShow({ - required this.value, - required this.unit, - }); + String get desc { + return '${up.traffic.show} ↑ ${down.traffic.show} ↓'; + } + + num get speed => up + down; } @freezed -class Proxy with _$Proxy { +abstract class TrafficShow with _$TrafficShow { + const factory TrafficShow({required String value, required String unit}) = + _TrafficShow; +} + +extension TrafficShowExt on TrafficShow { + String get show => '$value$unit'; +} + +@freezed +abstract class Proxy with _$Proxy { const factory Proxy({ required String name, required String type, @@ -294,7 +262,7 @@ class Proxy with _$Proxy { } @freezed -class Group with _$Group { +abstract class Group with _$Group { const factory Group({ required GroupType type, @Default([]) List all, @@ -326,70 +294,8 @@ extension GroupExt on Group { } } -@immutable -class TrafficValue { - final int _value; - - const TrafficValue({int? value}) : _value = value ?? 0; - - int get value => _value; - - String get show => '$showValue $showUnit'; - - String get shortShow => - '${trafficValueShow.value.fixed(decimals: 1)} $showUnit'; - - String get showValue => trafficValueShow.value.fixed(); - - String get showUnit => trafficValueShow.unit.name; - - TrafficValueShow get trafficValueShow { - if (_value > pow(1024, 4)) { - return TrafficValueShow( - value: _value / pow(1024, 4), - unit: TrafficUnit.TB, - ); - } - if (_value > pow(1024, 3)) { - return TrafficValueShow( - value: _value / pow(1024, 3), - unit: TrafficUnit.GB, - ); - } - if (_value > pow(1024, 2)) { - return TrafficValueShow( - value: _value / pow(1024, 2), unit: TrafficUnit.MB); - } - if (_value > pow(1024, 1)) { - return TrafficValueShow( - value: _value / pow(1024, 1), - unit: TrafficUnit.KB, - ); - } - return TrafficValueShow( - value: _value.toDouble(), - unit: TrafficUnit.B, - ); - } - - @override - String toString() { - return '$showValue$showUnit'; - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is TrafficValue && - runtimeType == other.runtimeType && - _value == other._value; - - @override - int get hashCode => _value.hashCode; -} - @freezed -class ColorSchemes with _$ColorSchemes { +abstract class ColorSchemes with _$ColorSchemes { const factory ColorSchemes({ ColorScheme? lightColorScheme, ColorScheme? darkColorScheme, @@ -426,79 +332,76 @@ extension ColorSchemesExt on ColorSchemes { } } -class IpInfo { - final String ip; - final String countryCode; - - const IpInfo({ - required this.ip, - required this.countryCode, - }); +@freezed +abstract class IpInfo with _$IpInfo { + const factory IpInfo({required String ip, required String countryCode}) = + _IpInfo; static IpInfo fromIpInfoIoJson(Map json) { return switch (json) { - { - 'ip': final String ip, - 'country': final String country, - } => - IpInfo( - ip: ip, - countryCode: country, - ), + {'ip': final String ip, 'country': final String country} => IpInfo( + ip: ip, + countryCode: country, + ), _ => throw const FormatException('invalid json'), }; } static IpInfo fromIpApiCoJson(Map json) { return switch (json) { - { - 'ip': final String ip, - 'country_code': final String countryCode, - } => - IpInfo( - ip: ip, - countryCode: countryCode, - ), + {'ip': final String ip, 'country_code': final String countryCode} => + IpInfo(ip: ip, countryCode: countryCode), _ => throw const FormatException('invalid json'), }; } static IpInfo fromIpSbJson(Map json) { return switch (json) { - { - 'ip': final String ip, - 'country_code': final String countryCode, - } => - IpInfo( - ip: ip, - countryCode: countryCode, - ), + {'ip': final String ip, 'country_code': final String countryCode} => + IpInfo(ip: ip, countryCode: countryCode), _ => throw const FormatException('invalid json'), }; } - static IpInfo fromIpwhoIsJson(Map json) { + static IpInfo fromIpWhoIsJson(Map json) { return switch (json) { - { - 'ip': final String ip, - 'country_code': final String countryCode, - } => - IpInfo( - ip: ip, - countryCode: countryCode, - ), + {'ip': final String ip, 'country_code': final String countryCode} => + IpInfo(ip: ip, countryCode: countryCode), _ => throw const FormatException('invalid json'), }; } - @override - String toString() { - return 'IpInfo{ip: $ip, countryCode: $countryCode}'; + static IpInfo fromMyIpJson(Map json) { + return switch (json) { + {'ip': final String ip, 'cc': final String countryCode} => IpInfo( + ip: ip, + countryCode: countryCode, + ), + _ => throw const FormatException('invalid json'), + }; + } + + static IpInfo fromIpAPIJson(Map json) { + return switch (json) { + {'query': final String ip, 'countryCode': final String countryCode} => + IpInfo(ip: ip, countryCode: countryCode), + _ => throw const FormatException('invalid json'), + }; + } + + static IpInfo fromIdentMeJson(Map json) { + return switch (json) { + {'ip': final String ip, 'cc': final String countryCode} => IpInfo( + ip: ip, + countryCode: countryCode, + ), + _ => throw const FormatException('invalid json'), + }; } } @freezed -class HotKeyAction with _$HotKeyAction { +abstract class HotKeyAction with _$HotKeyAction { const factory HotKeyAction({ required HotAction action, int? key, @@ -512,7 +415,7 @@ class HotKeyAction with _$HotKeyAction { typedef Validator = String? Function(String? value); @freezed -class Field with _$Field { +abstract class Field with _$Field { const factory Field({ required String label, required String value, @@ -520,35 +423,32 @@ class Field with _$Field { }) = _Field; } -enum PopupMenuItemType { - primary, - danger, -} +enum PopupMenuItemType { primary, danger } class PopupMenuItemData { const PopupMenuItemData({ this.icon, required this.label, required this.onPressed, + this.danger = false, }); final String label; final VoidCallback? onPressed; final IconData? icon; + final bool danger; } @freezed -class TextPainterParams with _$TextPainterParams { - const factory TextPainterParams({ - required String? text, - required double? fontSize, - required double textScaleFactor, - @Default(double.infinity) double maxWidth, - int? maxLines, - }) = _TextPainterParams; +abstract class AndroidState with _$AndroidState { + const factory AndroidState({ + required String currentProfileName, + required String stopText, + required bool onlyStatisticsProxy, + }) = _AndroidState; - factory TextPainterParams.fromJson(Map json) => - _$TextPainterParamsFromJson(json); + factory AndroidState.fromJson(Map json) => + _$AndroidStateFromJson(json); } class CloseWindowIntent extends Intent { @@ -556,24 +456,18 @@ class CloseWindowIntent extends Intent { } @freezed -class Result with _$Result { +abstract class Result with _$Result { const factory Result({ required T? data, required ResultType type, required String message, }) = _Result; - factory Result.success(T data) => Result( - data: data, - type: ResultType.success, - message: '', - ); + factory Result.success(T data) => + Result(data: data, type: ResultType.success, message: ''); - factory Result.error(String message) => Result( - data: null, - type: ResultType.error, - message: message, - ); + factory Result.error(String message) => + Result(data: null, type: ResultType.error, message: message); } extension ResultExt on Result { @@ -583,23 +477,42 @@ extension ResultExt on Result { } @freezed -class Script with _$Script { +abstract class Script with _$Script { const factory Script({ required String id, required String label, required String content, }) = _Script; - factory Script.create({ - required String label, - required String content, - }) { - return Script( - id: utils.uuidV4, - label: label, - content: content, - ); + factory Script.create({required String label, required String content}) { + return Script(id: utils.uuidV4, label: label, content: content); } factory Script.fromJson(Map json) => _$ScriptFromJson(json); } + +@freezed +abstract class DelayState with _$DelayState { + const factory DelayState({required int delay, required bool group}) = + _DelayState; +} + +extension DelayStateExt on DelayState { + int get priority { + if (delay > 0) return 0; + if (delay == 0) return 1; + return 2; + } + + int compareTo(DelayState other) { + if (priority != other.priority) { + return priority.compareTo(other.priority); + } + if (delay != other.delay) { + return delay.compareTo(other.delay); + } + if (group && !group) return -1; + if (!group && group) return 1; + return 0; + } +} diff --git a/lib/models/config.dart b/lib/models/config.dart index 51b377d..730d2a6 100644 --- a/lib/models/config.dart +++ b/lib/models/config.dart @@ -1,5 +1,3 @@ -// ignore_for_file: invalid_annotation_target - import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:flutter/material.dart'; @@ -27,7 +25,7 @@ const defaultBypassDomain = [ '172.2*', '172.30.*', '172.31.*', - '192.168.*' + '192.168.*', ]; const defaultAppSettingProps = AppSettingProps(); @@ -36,9 +34,7 @@ const defaultNetworkProps = NetworkProps(); const defaultProxiesStyle = ProxiesStyle(); const defaultWindowProps = WindowProps(); const defaultAccessControl = AccessControl(); -final defaultThemeProps = ThemeProps( - primaryColor: defaultPrimaryColor, -); +final defaultThemeProps = ThemeProps(primaryColor: defaultPrimaryColor); const List defaultDashboardWidgets = [ DashboardWidget.networkSpeed, @@ -64,7 +60,7 @@ List dashboardWidgetsSafeFormJson( } @freezed -class AppSettingProps with _$AppSettingProps { +abstract class AppSettingProps with _$AppSettingProps { const factory AppSettingProps({ String? locale, @Default(defaultDashboardWidgets) @@ -98,7 +94,7 @@ class AppSettingProps with _$AppSettingProps { } @freezed -class AccessControl with _$AccessControl { +abstract class AccessControl with _$AccessControl { const factory AccessControl({ @Default(false) bool enable, @Default(AccessControlMode.rejectSelected) AccessControlMode mode, @@ -115,13 +111,13 @@ class AccessControl with _$AccessControl { extension AccessControlExt on AccessControl { List get currentList => switch (mode) { - AccessControlMode.acceptSelected => acceptList, - AccessControlMode.rejectSelected => rejectList, - }; + AccessControlMode.acceptSelected => acceptList, + AccessControlMode.rejectSelected => rejectList, + }; } @freezed -class WindowProps with _$WindowProps { +abstract class WindowProps with _$WindowProps { const factory WindowProps({ @Default(750) double width, @Default(600) double height, @@ -134,12 +130,13 @@ class WindowProps with _$WindowProps { } @freezed -class VpnProps with _$VpnProps { +abstract class VpnProps with _$VpnProps { const factory VpnProps({ @Default(true) bool enable, @Default(true) bool systemProxy, @Default(false) bool ipv6, @Default(true) bool allowBypass, + @Default(false) bool dnsHijacking, @Default(defaultAccessControl) AccessControl accessControl, }) = _VpnProps; @@ -148,7 +145,7 @@ class VpnProps with _$VpnProps { } @freezed -class NetworkProps with _$NetworkProps { +abstract class NetworkProps with _$NetworkProps { const factory NetworkProps({ @Default(true) bool systemProxy, @Default(defaultBypassDomain) List bypassDomain, @@ -161,7 +158,7 @@ class NetworkProps with _$NetworkProps { } @freezed -class ProxiesStyle with _$ProxiesStyle { +abstract class ProxiesStyle with _$ProxiesStyle { const factory ProxiesStyle({ @Default(ProxiesType.tab) ProxiesType type, @Default(ProxiesSortType.none) ProxiesSortType sortType, @@ -176,7 +173,7 @@ class ProxiesStyle with _$ProxiesStyle { } @freezed -class TextScale with _$TextScale { +abstract class TextScale with _$TextScale { const factory TextScale({ @Default(false) bool enable, @Default(1.0) double scale, @@ -187,7 +184,7 @@ class TextScale with _$TextScale { } @freezed -class ThemeProps with _$ThemeProps { +abstract class ThemeProps with _$ThemeProps { const factory ThemeProps({ int? primaryColor, @Default(defaultPrimaryColors) List primaryColors, @@ -213,7 +210,7 @@ class ThemeProps with _$ThemeProps { } @freezed -class ScriptProps with _$ScriptProps { +abstract class ScriptProps with _$ScriptProps { const factory ScriptProps({ String? currentId, @Default([]) List