管理端全部功能跑通

This commit is contained in:
robin
2026-02-27 10:35:22 +08:00
parent 4d275c921d
commit 150799f41d
263 changed files with 22664 additions and 4053 deletions

View File

@@ -3,7 +3,6 @@ package com.aliyun.ams.httpdns
import android.content.Context
import android.util.Log
import androidx.annotation.NonNull
import com.alibaba.sdk.android.httpdns.HttpDns
import com.alibaba.sdk.android.httpdns.HttpDnsService
import com.alibaba.sdk.android.httpdns.InitConfig
@@ -15,18 +14,18 @@ import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
class AliyunHttpDnsPlugin : FlutterPlugin, MethodCallHandler {
private lateinit var channel: MethodChannel
private var appContext: Context? = null
// Cached service keyed by accountId to avoid re-creating
private var service: HttpDnsService? = null
private var accountId: String? = null
private var secretKey: String? = null
private var aesSecretKey: String? = null
// Desired states collected before build()
private var appId: String? = null
private var secretKey: String? = null
private var primaryServiceHost: String? = null
private var backupServiceHost: String? = null
private var servicePort: Int? = null
private var desiredPersistentCacheEnabled: Boolean? = null
private var desiredDiscardExpiredAfterSeconds: Int? = null
private var desiredReuseExpiredIPEnabled: Boolean? = null
@@ -35,10 +34,6 @@ class AliyunHttpDnsPlugin : FlutterPlugin, MethodCallHandler {
private var desiredPreResolveAfterNetworkChanged: Boolean? = null
private var desiredIPRankingMap: Map<String, Int>? = null
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
appContext = flutterPluginBinding.applicationContext
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "aliyun_httpdns")
@@ -46,14 +41,7 @@ class AliyunHttpDnsPlugin : FlutterPlugin, MethodCallHandler {
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
// Log every incoming call with method name and raw arguments
try {
Log.i("AliyunHttpDns", "invoke method=${call.method}, args=${call.arguments}")
} catch (_: Throwable) {
Log.i("AliyunHttpDns", "invoke method=${call.method}, args=<unprintable>")
}
when (call.method) {
// Dart: init(accountId, secretKey?, aesSecretKey?) — only save states here
"initialize" -> {
val args = call.arguments as? Map<*, *> ?: emptyMap<String, Any>()
val ctx = appContext
@@ -61,261 +49,315 @@ class AliyunHttpDnsPlugin : FlutterPlugin, MethodCallHandler {
result.error("no_context", "Android context not attached", null)
return
}
val accountAny = args["accountId"]
val account = when (accountAny) {
is Int -> accountAny.toString()
is Long -> accountAny.toString()
is String -> accountAny
else -> null
}
val secret = (args["secretKey"] as? String)?.takeIf { it.isNotBlank() }
val aes = (args["aesSecretKey"] as? String)?.takeIf { it.isNotBlank() }
if (account.isNullOrBlank()) {
Log.i("AliyunHttpDns", "initialize missing accountId")
val appIdAny = args["appId"]
val parsedAppId = when (appIdAny) {
is Int -> appIdAny.toString()
is Long -> appIdAny.toString()
is String -> appIdAny.trim()
else -> ""
}
if (parsedAppId.isBlank()) {
Log.i("AliyunHttpDns", "initialize missing appId")
result.success(false)
return
}
// Save desired states only; actual build happens on 'build'
accountId = account
val primaryHostArg = (args["primaryServiceHost"] as? String)?.trim()
if (primaryHostArg.isNullOrBlank()) {
Log.i("AliyunHttpDns", "initialize missing primaryServiceHost")
result.success(false)
return
}
val secret = (args["secretKey"] as? String)?.takeIf { it.isNotBlank() }
val backup = (args["backupServiceHost"] as? String)?.trim()
val port = when (val portAny = args["servicePort"]) {
is Int -> portAny
is Long -> portAny.toInt()
is String -> portAny.toIntOrNull()
else -> null
}
appId = parsedAppId
secretKey = secret
aesSecretKey = aes
Log.i("AliyunHttpDns", "initialize saved state, account=$account")
primaryServiceHost = primaryHostArg
backupServiceHost = backup?.trim()?.takeIf { it.isNotEmpty() }
servicePort = if (port != null && port > 0) port else null
Log.i(
"AliyunHttpDns",
"initialize appId=$appId, primaryServiceHost=$primaryServiceHost, backupServiceHost=$backupServiceHost, servicePort=$servicePort"
)
result.success(true)
}
// Dart: setLogEnabled(enabled) — save desired
"setLogEnabled" -> {
val enabled = call.argument<Boolean>("enabled") == true
desiredLogEnabled = enabled
Log.i("AliyunHttpDns", "setLogEnabled desired=$enabled")
result.success(null)
}
// Dart: setHttpsRequestEnabled(enabled)
"setHttpsRequestEnabled" -> {
val enabled = call.argument<Boolean>("enabled") == true
desiredHttpsEnabled = enabled
Log.i("AliyunHttpDns", "https request desired=$enabled")
result.success(null)
}
// Dart: setPersistentCacheIPEnabled(enabled, discardExpiredAfterSeconds?) — save desired
"setPersistentCacheIPEnabled" -> {
val enabled = call.argument<Boolean>("enabled") == true
val discard = call.argument<Int>("discardExpiredAfterSeconds")
desiredPersistentCacheEnabled = enabled
desiredDiscardExpiredAfterSeconds = discard
Log.i("AliyunHttpDns", "persistent cache desired=$enabled discard=$discard")
result.success(null)
}
// Dart: setReuseExpiredIPEnabled(enabled) — save desired
"setReuseExpiredIPEnabled" -> {
val enabled = call.argument<Boolean>("enabled") == true
desiredReuseExpiredIPEnabled = enabled
Log.i("AliyunHttpDns", "reuse expired ip desired=$enabled")
result.success(null)
}
// Dart: setPreResolveAfterNetworkChanged(enabled) — save desired (applied at build via InitConfig)
"setPreResolveAfterNetworkChanged" -> {
val enabled = call.argument<Boolean>("enabled") == true
desiredPreResolveAfterNetworkChanged = enabled
Log.i("AliyunHttpDns", "preResolveAfterNetworkChanged desired=$enabled")
result.success(null)
}
// Dart: setIPRankingList(hostPortMap) — save desired
"setIPRankingList" -> {
val hostPortMap = call.argument<Map<String, Int>>("hostPortMap")
desiredIPRankingMap = hostPortMap
Log.i("AliyunHttpDns", "IP ranking list desired, hosts=${hostPortMap?.keys?.joinToString()}")
result.success(null)
}
// Dart: setPreResolveHosts(hosts, ipType)
"setPreResolveHosts" -> {
val hosts = call.argument<List<String>>("hosts") ?: emptyList()
val ipTypeStr = call.argument<String>("ipType") ?: "auto"
val type = when (ipTypeStr.lowercase()) {
"ipv4", "v4" -> RequestIpType.v4
"ipv6", "v6" -> RequestIpType.v6
"both", "64" -> RequestIpType.both
else -> RequestIpType.auto
}
val type = mapIpType(call.argument<String>("ipType") ?: "auto")
try {
service?.setPreResolveHosts(hosts, type)
Log.i("AliyunHttpDns", "preResolve set for ${hosts.size} hosts, type=$type")
} catch (t: Throwable) {
Log.i("AliyunHttpDns", "setPreResolveHosts failed: ${t.message}")
} catch (_: Throwable) {
}
result.success(null)
}
// Dart: getSessionId
"getSessionId" -> {
val sid = try { service?.getSessionId() } catch (_: Throwable) { null }
val sid = try {
service?.getSessionId()
} catch (_: Throwable) {
null
}
result.success(sid)
}
// Dart: cleanAllHostCache
"cleanAllHostCache" -> {
try {
// Best-effort: empty list to clear all
service?.cleanHostCache(ArrayList())
} catch (t: Throwable) {
Log.i("AliyunHttpDns", "cleanAllHostCache failed: ${t.message}")
} catch (_: Throwable) {
}
result.success(null)
}
// Dart: build() — construct InitConfig and acquire service using desired states
"build" -> {
val ctx = appContext
val account = accountId
if (ctx == null || account.isNullOrBlank()) {
val currentAppId = appId
if (ctx == null || currentAppId.isNullOrBlank()) {
result.success(false)
return
}
try {
desiredLogEnabled?.let { enabled ->
try {
HttpDnsLog.enable(enabled)
Log.i("AliyunHttpDns", "HttpDnsLog.enable($enabled)")
} catch (t: Throwable) {
Log.w("AliyunHttpDns", "HttpDnsLog.enable failed: ${t.message}")
}
}
val builder = InitConfig.Builder()
// Optional builder params
try { builder.javaClass.getMethod("setContext", Context::class.java).invoke(builder, ctx) } catch (_: Throwable) {}
try {
desiredLogEnabled?.let { HttpDnsLog.enable(it) }
val builder = InitConfig.Builder()
var hostConfigApplied = false
try {
builder.javaClass.getMethod("setContext", Context::class.java)
.invoke(builder, ctx)
} catch (_: Throwable) {
}
try {
if (!secretKey.isNullOrBlank()) {
builder.javaClass.getMethod("setSecretKey", String::class.java).invoke(builder, secretKey)
builder.javaClass.getMethod("setSecretKey", String::class.java)
.invoke(builder, secretKey)
}
} catch (_: Throwable) {}
} catch (_: Throwable) {
}
try {
if (!aesSecretKey.isNullOrBlank()) {
builder.javaClass.getMethod("setAesSecretKey", String::class.java).invoke(builder, aesSecretKey)
}
} catch (_: Throwable) {}
// Prefer HTTPS if requested
val enableHttps = desiredHttpsEnabled ?: true
builder.javaClass.getMethod("setEnableHttps", Boolean::class.javaPrimitiveType)
.invoke(builder, enableHttps)
} catch (_: Throwable) {
}
try {
desiredHttpsEnabled?.let { en ->
builder.javaClass.getMethod("setEnableHttps", Boolean::class.javaPrimitiveType).invoke(builder, en)
builder.javaClass.getMethod("setPrimaryServiceHost", String::class.java)
.invoke(builder, primaryServiceHost)
hostConfigApplied = true
} catch (t: Throwable) {
Log.w("AliyunHttpDns", "setPrimaryServiceHost failed: ${t.message}")
}
try {
backupServiceHost?.let {
builder.javaClass.getMethod("setBackupServiceHost", String::class.java)
.invoke(builder, it)
}
} catch (_: Throwable) {}
} catch (_: Throwable) {
}
try {
servicePort?.let {
builder.javaClass.getMethod("setServicePort", Int::class.javaPrimitiveType)
.invoke(builder, it)
}
} catch (_: Throwable) {
}
try {
desiredPersistentCacheEnabled?.let { enabled ->
val discardSeconds = desiredDiscardExpiredAfterSeconds
if (discardSeconds != null && discardSeconds >= 0) {
val expiredThresholdMillis = discardSeconds.toLong() * 1000L
builder.javaClass.getMethod("setEnableCacheIp", Boolean::class.javaPrimitiveType, Long::class.javaPrimitiveType)
.invoke(builder, enabled, expiredThresholdMillis)
val thresholdMillis = discardSeconds.toLong() * 1000L
builder.javaClass.getMethod(
"setEnableCacheIp",
Boolean::class.javaPrimitiveType,
Long::class.javaPrimitiveType
).invoke(builder, enabled, thresholdMillis)
} else {
builder.javaClass.getMethod("setEnableCacheIp", Boolean::class.javaPrimitiveType)
.invoke(builder, enabled)
builder.javaClass.getMethod(
"setEnableCacheIp",
Boolean::class.javaPrimitiveType
).invoke(builder, enabled)
}
}
} catch (_: Throwable) { }
} catch (_: Throwable) {
}
try {
desiredReuseExpiredIPEnabled?.let { enabled ->
desiredReuseExpiredIPEnabled?.let {
builder.javaClass.getMethod("setEnableExpiredIp", Boolean::class.javaPrimitiveType)
.invoke(builder, enabled)
.invoke(builder, it)
}
} catch (_: Throwable) { }
// Apply preResolve-after-network-changed
} catch (_: Throwable) {
}
try {
desiredPreResolveAfterNetworkChanged?.let { en ->
builder.javaClass.getMethod("setPreResolveAfterNetworkChanged", Boolean::class.javaPrimitiveType).invoke(builder, en)
desiredPreResolveAfterNetworkChanged?.let {
builder.javaClass.getMethod("setPreResolveAfterNetworkChanged", Boolean::class.javaPrimitiveType)
.invoke(builder, it)
}
} catch (_: Throwable) {}
// Apply IP ranking list
} catch (_: Throwable) {
}
try {
desiredIPRankingMap?.let { map ->
if (map.isNotEmpty()) {
// Create List<IPRankingBean>
val ipRankingBeanClass = Class.forName("com.alibaba.sdk.android.httpdns.ranking.IPRankingBean")
val constructor = ipRankingBeanClass.getConstructor(String::class.java, Int::class.javaPrimitiveType)
val beanClass = Class.forName("com.alibaba.sdk.android.httpdns.ranking.IPRankingBean")
val ctor = beanClass.getConstructor(String::class.java, Int::class.javaPrimitiveType)
val list = ArrayList<Any>()
map.forEach { (host, port) ->
val bean = constructor.newInstance(host, port)
list.add(bean)
list.add(ctor.newInstance(host, port))
}
val m = builder.javaClass.getMethod("setIPRankingList", List::class.java)
m.invoke(builder, list)
Log.i("AliyunHttpDns", "setIPRankingList applied with ${list.size} hosts")
builder.javaClass.getMethod("setIPRankingList", List::class.java)
.invoke(builder, list)
}
}
} catch (t: Throwable) {
Log.w("AliyunHttpDns", "setIPRankingList failed: ${t.message}")
} catch (_: Throwable) {
}
builder.buildFor(account)
if (!hostConfigApplied) {
Log.w("AliyunHttpDns", "build failed: sdk core does not support primaryServiceHost")
result.success(false)
return
}
builder.buildFor(currentAppId)
service = if (!secretKey.isNullOrBlank()) {
HttpDns.getService(ctx, account, secretKey)
HttpDns.getService(ctx, currentAppId, secretKey)
} else {
HttpDns.getService(ctx, account)
HttpDns.getService(ctx, currentAppId)
}
Log.i("AliyunHttpDns", "build completed for account=$account")
result.success(true)
} catch (t: Throwable) {
Log.i("AliyunHttpDns", "build failed: ${t.message}")
Log.w("AliyunHttpDns", "build failed: ${t.message}")
result.success(false)
}
}
// Dart: resolveHostSyncNonBlocking(hostname, ipType, sdnsParams?, cacheKey?)
"resolveHostSyncNonBlocking" -> {
val hostname = call.argument<String>("hostname")
if (hostname.isNullOrBlank()) {
result.success(mapOf("ipv4" to emptyList<String>(), "ipv6" to emptyList<String>()))
return
}
val ipTypeStr = call.argument<String>("ipType") ?: "auto"
val type = when (ipTypeStr.lowercase()) {
"ipv4", "v4" -> RequestIpType.v4
"ipv6", "v6" -> RequestIpType.v6
"both", "64" -> RequestIpType.both
else -> RequestIpType.auto
}
val type = mapIpType(call.argument<String>("ipType") ?: "auto")
try {
val svc = service ?: run {
val ctx = appContext
val acc = accountId
if (ctx != null && !acc.isNullOrBlank()) HttpDns.getService(ctx, acc) else null
val currentAppId = appId
if (ctx != null && !currentAppId.isNullOrBlank()) {
HttpDns.getService(ctx, currentAppId)
} else {
null
}
}
val r = svc?.getHttpDnsResultForHostSyncNonBlocking(hostname, type)
val v4 = r?.ips?.toList() ?: emptyList()
val v6 = r?.ipv6s?.toList() ?: emptyList()
// 记录解析结果,便于排查:包含 host、请求类型以及返回的 IPv4/IPv6 列表
Log.d(
"HttpdnsPlugin",
"resolve result host=" + hostname + ", type=" + type +
", ipv4=" + v4.joinToString(prefix = "[", postfix = "]") +
", ipv6=" + v6.joinToString(prefix = "[", postfix = "]")
)
result.success(mapOf("ipv4" to v4, "ipv6" to v6))
} catch (t: Throwable) {
Log.i("AliyunHttpDns", "resolveHostSyncNonBlocking failed: ${t.message}")
} catch (_: Throwable) {
result.success(mapOf("ipv4" to emptyList<String>(), "ipv6" to emptyList<String>()))
}
}
// Legacy methods removed: preResolve / clearCache handled at app layer if needed
"resolveHostV1" -> {
val hostname = call.argument<String>("hostname")
if (hostname.isNullOrBlank()) {
result.success(mapOf("ipv4" to emptyList<String>(), "ipv6" to emptyList<String>(), "ttl" to 0))
return
}
val qtype = (call.argument<String>("qtype") ?: "A").uppercase()
val cip = call.argument<String>("cip")?.trim()
val requestType = if (qtype == "AAAA") RequestIpType.v6 else RequestIpType.v4
try {
val svc = service ?: run {
val ctx = appContext
val currentAppId = appId
if (ctx != null && !currentAppId.isNullOrBlank()) {
HttpDns.getService(ctx, currentAppId)
} else {
null
}
}
val params = if (!cip.isNullOrEmpty()) mapOf("cip" to cip) else null
val r = svc?.getHttpDnsResultForHostSyncNonBlocking(hostname, requestType, params, hostname)
val v4 = r?.ips?.toList() ?: emptyList()
val v6 = r?.ipv6s?.toList() ?: emptyList()
result.success(
mapOf(
"ipv4" to v4,
"ipv6" to v6,
"ttl" to (r?.ttl ?: 0),
)
)
} catch (_: Throwable) {
result.success(mapOf("ipv4" to emptyList<String>(), "ipv6" to emptyList<String>(), "ttl" to 0))
}
}
else -> result.notImplemented()
}
}
private fun mapIpType(ipType: String): RequestIpType {
return when (ipType.lowercase()) {
"ipv4", "v4" -> RequestIpType.v4
"ipv6", "v6" -> RequestIpType.v6
"both", "64" -> RequestIpType.both
else -> RequestIpType.auto
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
service = null