diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/Hysteria2Bean.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/Hysteria2Bean.kt deleted file mode 100644 index c4fc6582..00000000 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/Hysteria2Bean.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.v2ray.ang.dto - -data class Hysteria2Bean( - val server: String?, - val auth: String?, - val lazy: Boolean? = true, - val obfs: ObfsBean? = null, - val socks5: Socks5Bean? = null, - val http: Socks5Bean? = null, - val tls: TlsBean? = null, - val transport: TransportBean? = null, - val bandwidth: BandwidthBean? = null, -) { - data class ObfsBean( - val type: String?, - val salamander: SalamanderBean? - ) { - data class SalamanderBean( - val password: String?, - ) - } - - data class Socks5Bean( - val listen: String?, - ) - - data class TlsBean( - val sni: String?, - val insecure: Boolean?, - val pinSHA256: String?, - ) - - data class TransportBean( - val type: String?, - val udp: TransportUdpBean? - ) { - data class TransportUdpBean( - val hopInterval: String?, - ) - } - - data class BandwidthBean( - val down: String?, - val up: String?, - ) -} \ No newline at end of file diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/NetworkType.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/NetworkType.kt index 0e8091b9..9931bfe6 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/NetworkType.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/NetworkType.kt @@ -10,7 +10,8 @@ enum class NetworkType(val type: String) { H2("h2"), //QUIC("quic"), - GRPC("grpc"); + GRPC("grpc"), + HYSTERIA("hysteria"); companion object { fun fromString(type: String?) = entries.find { it.type == type } ?: TCP diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt index c1659a9c..335c2003 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt @@ -76,7 +76,7 @@ data class V2rayConfig( /*DNS*/ val network: String? = null, var address: Any? = null, - val port: Int? = null, + var port: Int? = null, /*Freedom*/ var domainStrategy: String? = null, val redirect: String? = null, @@ -160,7 +160,8 @@ data class V2rayConfig( var quicSettings: QuicSettingBean? = null, var realitySettings: TlsSettingsBean? = null, var grpcSettings: GrpcSettingsBean? = null, - var hy2steriaSettings: Hy2steriaSettingsBean? = null, + var hysteriaSettings: HysteriaSettingsBean? = null, + var udpmasks: List? = null, val dsSettings: Any? = null, var sockopt: SockoptBean? = null ) { @@ -247,7 +248,8 @@ data class V2rayConfig( var dialerProxy: String? = null, var domainStrategy: String? = null, var happyEyeballs: HappyEyeballsBean? = null, - ) + ) + data class HappyEyeballsBean( var prioritizeIPv6: Boolean? = null, var maxConcurrentTry: Int? = 4, @@ -293,18 +295,27 @@ data class V2rayConfig( var health_check_timeout: Int? = null ) - data class Hy2steriaSettingsBean( - var password: String? = null, - var use_udp_extension: Boolean? = true, - var congestion: Hy2CongestionBean? = null + data class HysteriaSettingsBean( + var version: Int, + var auth: String? = null, + var up: String? = null, + var down: String? = null, + var udphop: HysteriaUdpHopBean? = null ) { - data class Hy2CongestionBean( - var type: String? = "bbr", - var up_mbps: Int? = null, - var down_mbps: Int? = null, + data class HysteriaUdpHopBean( + var port: String? = null, + var interval: Int? = null ) } + data class UdpMasksBean( + var type: String, + var settings: UdpMasksSettingsBean? = null + ) { + data class UdpMasksSettingsBean( + var password: String? = null + ) + } } data class MuxBean( diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt index 2ba5d9de..6d1fb4d3 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt @@ -158,6 +158,8 @@ open class FmtBase { config.authority.let { if (it.isNotNullEmpty()) dicQuery["authority"] = it.orEmpty() } config.serviceName.let { if (it.isNotNullEmpty()) dicQuery["serviceName"] = it.orEmpty() } } + + else -> {} } return dicQuery diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt index 58d80fab..dea4bb5d 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt @@ -1,11 +1,12 @@ package com.v2ray.ang.fmt import com.v2ray.ang.AppConfig -import com.v2ray.ang.AppConfig.LOOPBACK import com.v2ray.ang.dto.EConfigType -import com.v2ray.ang.dto.Hysteria2Bean +import com.v2ray.ang.dto.NetworkType import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.dto.V2rayConfig.OutboundBean +import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean +import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.UdpMasksBean.UdpMasksSettingsBean import com.v2ray.ang.extension.idnHost import com.v2ray.ang.extension.isNotNullEmpty import com.v2ray.ang.handler.MmkvManager @@ -30,6 +31,7 @@ object Hysteria2Fmt : FmtBase() { config.serverPort = uri.port.toString() config.password = uri.userInfo config.security = AppConfig.TLS + config.network = NetworkType.HYSTERIA.type if (!uri.rawQuery.isNullOrEmpty()) { val queryParam = getQueryParam(uri) @@ -80,64 +82,6 @@ object Hysteria2Fmt : FmtBase() { return toUri(config, config.password, dicQuery) } - /** - * Converts a ProfileItem object to a Hysteria2Bean object. - * - * @param config the ProfileItem object to convert - * @param socksPort the port number for the socks5 proxy - * @return the converted Hysteria2Bean object, or null if conversion fails - */ - fun toNativeConfig(config: ProfileItem, socksPort: Int): Hysteria2Bean? { - - val obfs = if (config.obfsPassword.isNullOrEmpty()) null else - Hysteria2Bean.ObfsBean( - type = "salamander", - salamander = Hysteria2Bean.ObfsBean.SalamanderBean( - password = config.obfsPassword - ) - ) - - val transport = if (config.portHopping.isNullOrEmpty()) null else - Hysteria2Bean.TransportBean( - type = "udp", - udp = Hysteria2Bean.TransportBean.TransportUdpBean( - hopInterval = (config.portHoppingInterval?.takeIf { it.isNotEmpty() } ?: "30") + "s" - ) - ) - - val bandwidth = if (config.bandwidthDown.isNullOrEmpty() || config.bandwidthUp.isNullOrEmpty()) null else - Hysteria2Bean.BandwidthBean( - down = config.bandwidthDown, - up = config.bandwidthUp, - ) - - val server = - if (config.portHopping.isNullOrEmpty()) - config.getServerAddressAndPort() - else - Utils.getIpv6Address(config.server) + ":" + config.portHopping - - val bean = Hysteria2Bean( - server = server, - auth = config.password, - obfs = obfs, - transport = transport, - bandwidth = bandwidth, - socks5 = Hysteria2Bean.Socks5Bean( - listen = "$LOOPBACK:${socksPort}", - ), - http = Hysteria2Bean.Socks5Bean( - listen = "$LOOPBACK:${socksPort}", - ), - tls = Hysteria2Bean.TlsBean( - sni = config.sni ?: config.server, - insecure = config.insecure, - pinSHA256 = if (config.pinSHA256.isNullOrEmpty()) null else config.pinSHA256 - ) - ) - return bean - } - /** * Converts a ProfileItem object to an OutboundBean object. * @@ -145,7 +89,33 @@ object Hysteria2Fmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.HYSTERIA2) + val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.HYSTERIA2) ?: return null + profileItem.network = NetworkType.HYSTERIA.type + profileItem.alpn = "h3" + + outboundBean.settings?.let { server -> + server.address = getServerAddress(profileItem) + server.port = profileItem.serverPort.orEmpty().toInt() + } + + val sni = outboundBean.streamSettings?.let { + V2rayConfigManager.populateTransportSettings(it, profileItem) + } + + outboundBean.streamSettings?.let { + V2rayConfigManager.populateTlsSettings(it, profileItem, sni) + } + + if (profileItem.obfsPassword.isNotNullEmpty()) { + outboundBean.streamSettings?.udpmasks = mutableListOf( + StreamSettingsBean.UdpMasksBean( + type = "salamander", + settings = UdpMasksSettingsBean( + password = profileItem.obfsPassword + ) + ) + ) + } return outboundBean } } \ No newline at end of file diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt index 3b54e90f..eef2ea33 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt @@ -110,13 +110,6 @@ object AngConfigManager { if (guid == null) return -1 val result = V2rayConfigManager.getV2rayConfig(context, guid) if (result.status) { - val config = MmkvManager.decodeServerConfig(guid) - if (config?.configType == EConfigType.HYSTERIA2) { - val socksPort = Utils.findFreePort(listOf(100 + SettingsManager.getSocksPort(), 0)) - val hy2Config = Hysteria2Fmt.toNativeConfig(config, socksPort) - Utils.setClipboard(context, JsonUtil.toJsonPretty(hy2Config) + "\n" + result.content) - return 0 - } Utils.setClipboard(context, result.content) } else { return -1 diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/PluginServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/PluginServiceManager.kt deleted file mode 100644 index d29bc992..00000000 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/PluginServiceManager.kt +++ /dev/null @@ -1,141 +0,0 @@ -package com.v2ray.ang.handler - -import android.content.Context -import android.os.SystemClock -import android.util.Log -import com.v2ray.ang.AppConfig -import com.v2ray.ang.dto.EConfigType -import com.v2ray.ang.dto.ProfileItem -import com.v2ray.ang.fmt.Hysteria2Fmt -import com.v2ray.ang.service.ProcessService -import com.v2ray.ang.util.JsonUtil -import com.v2ray.ang.util.Utils -import java.io.File - -object PluginServiceManager { - private const val HYSTERIA2 = "libhysteria2.so" - - private val procService: ProcessService by lazy { - ProcessService() - } - - /** - * Run the plugin based on the provided configuration. - * - * @param context The context to use. - * @param config The profile configuration. - * @param socksPort The port information. - */ - fun runPlugin(context: Context, config: ProfileItem?, socksPort: Int?) { - Log.i(AppConfig.TAG, "Starting plugin execution") - - if (config == null) { - Log.w(AppConfig.TAG, "Cannot run plugin: config is null") - return - } - - try { - if (config.configType == EConfigType.HYSTERIA2) { - if (socksPort == null) { - Log.w(AppConfig.TAG, "Cannot run plugin: socksPort is null") - return - } - Log.i(AppConfig.TAG, "Running Hysteria2 plugin") - val configFile = genConfigHy2(context, config, socksPort) ?: return - val cmd = genCmdHy2(context, configFile) - - procService.runProcess(context, cmd) - } - } catch (e: Exception) { - Log.e(AppConfig.TAG, "Error running plugin", e) - } - } - - /** - * Stop the running plugin. - */ - fun stopPlugin() { - stopHy2() - } - - /** - * Perform a real ping using Hysteria2. - * - * @param context The context to use. - * @param config The profile configuration. - * @return The ping delay in milliseconds, or -1 if it fails. - */ - fun realPingHy2(context: Context, config: ProfileItem?): Long { - Log.i(AppConfig.TAG, "realPingHy2") - val retFailure = -1L - - if (config?.configType?.equals(EConfigType.HYSTERIA2) == true) { - val socksPort = Utils.findFreePort(listOf(0)) - val configFile = genConfigHy2(context, config, socksPort) ?: return retFailure - val cmd = genCmdHy2(context, configFile) - - val proc = ProcessService() - proc.runProcess(context, cmd) - Thread.sleep(1000L) - val delay = SpeedtestManager.testConnection(context, socksPort) - proc.stopProcess() - - return delay.first - } - return retFailure - } - - /** - * Generate the configuration file for Hysteria2. - * - * @param context The context to use. - * @param config The profile configuration. - * @param socksPort The port information. - * @return The generated configuration file. - */ - private fun genConfigHy2(context: Context, config: ProfileItem, socksPort: Int): File? { - Log.i(AppConfig.TAG, "runPlugin $HYSTERIA2") - - val hy2Config = Hysteria2Fmt.toNativeConfig(config, socksPort) ?: return null - - val configFile = File(context.noBackupFilesDir, "hy2_${SystemClock.elapsedRealtime()}.json") - Log.i(AppConfig.TAG, "runPlugin ${configFile.absolutePath}") - - configFile.parentFile?.mkdirs() - configFile.writeText(JsonUtil.toJson(hy2Config)) - Log.i(AppConfig.TAG, JsonUtil.toJson(hy2Config)) - - return configFile - } - - /** - * Generate the command to run Hysteria2. - * - * @param context The context to use. - * @param configFile The configuration file. - * @return The command to run Hysteria2. - */ - private fun genCmdHy2(context: Context, configFile: File): MutableList { - return mutableListOf( - File(context.applicationInfo.nativeLibraryDir, HYSTERIA2).absolutePath, - "--disable-update-check", - "--config", - configFile.absolutePath, - "--log-level", - "warn", - "client" - ) - } - - /** - * Stop the Hysteria2 process. - */ - private fun stopHy2() { - try { - Log.i(AppConfig.TAG, "$HYSTERIA2 destroy") - procService.stopProcess() - } catch (e: Exception) { - Log.e(AppConfig.TAG, "Failed to stop Hysteria2 process", e) - } - } -} \ No newline at end of file diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayServiceManager.kt index 1cbe24f3..60d99f74 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayServiceManager.kt @@ -168,7 +168,6 @@ object V2RayServiceManager { NotificationManager.showNotification(currentConfig) NotificationManager.startSpeedNotification(currentConfig) - PluginServiceManager.runPlugin(service, config, result.socksPort) } catch (e: Exception) { Log.e(AppConfig.TAG, "Failed to startup service", e) return false @@ -202,7 +201,6 @@ object V2RayServiceManager { } catch (e: Exception) { Log.e(AppConfig.TAG, "Failed to unregister broadcast receiver", e) } - PluginServiceManager.stopPlugin() return true } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt index 5597089b..486c3f0d 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt @@ -16,6 +16,7 @@ import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean import com.v2ray.ang.dto.V2rayConfig.RoutingBean.RulesBean import com.v2ray.ang.extension.isNotNullEmpty import com.v2ray.ang.fmt.HttpFmt +import com.v2ray.ang.fmt.Hysteria2Fmt import com.v2ray.ang.fmt.ShadowsocksFmt import com.v2ray.ang.fmt.SocksFmt import com.v2ray.ang.fmt.TrojanFmt @@ -160,12 +161,8 @@ object V2rayConfigManager { getInbounds(v2rayConfig) - if (config.configType == EConfigType.HYSTERIA2) { - result.socksPort = getPlusOutbounds(v2rayConfig, config) ?: return result - } else { - getOutbounds(v2rayConfig, config) ?: return result - getMoreOutbounds(v2rayConfig, config.subscriptionId) - } + getOutbounds(v2rayConfig, config) ?: return result + getMoreOutbounds(v2rayConfig, config.subscriptionId) getRouting(v2rayConfig) @@ -196,7 +193,6 @@ object V2rayConfigManager { val validConfigs = configList.asSequence().filter { it.server.isNotNullEmpty() } .filter { !Utils.isPureIpAddress(it.server!!) || Utils.isValidUrl(it.server!!) } .filter { it.configType != EConfigType.CUSTOM } - .filter { it.configType != EConfigType.HYSTERIA2 } .filter { it.configType != EConfigType.POLICYGROUP } .toList() @@ -270,12 +266,8 @@ object V2rayConfigManager { val v2rayConfig = initV2rayConfig(context) ?: return result - if (config.configType == EConfigType.HYSTERIA2) { - result.socksPort = getPlusOutbounds(v2rayConfig, config) ?: return result - } else { - getOutbounds(v2rayConfig, config) ?: return result - getMoreOutbounds(v2rayConfig, config.subscriptionId) - } + getOutbounds(v2rayConfig, config) ?: return result + getMoreOutbounds(v2rayConfig, config.subscriptionId) v2rayConfig.log.loglevel = MmkvManager.decodeSettingsString(AppConfig.PREF_LOGLEVEL) ?: "warning" v2rayConfig.inbounds.clear() @@ -667,44 +659,6 @@ object V2rayConfigManager { return true } - /** - * Configures special outbound settings for Hysteria2 protocol. - * - * Creates a SOCKS outbound connection on a free port for protocols requiring special handling. - * - * @param v2rayConfig The V2ray configuration object to be modified - * @param config The profile item containing connection details - * @return The port number for the SOCKS connection, or null if there was an error - */ - private fun getPlusOutbounds(v2rayConfig: V2rayConfig, config: ProfileItem): Int? { - try { - val socksPort = Utils.findFreePort(listOf(100 + SettingsManager.getSocksPort(), 0)) - - val outboundNew = OutboundBean( - mux = null, - protocol = EConfigType.SOCKS.name.lowercase(), - settings = OutSettingsBean( - servers = listOf( - OutSettingsBean.ServersBean( - address = AppConfig.LOOPBACK, - port = socksPort - ) - ) - ) - ) - if (v2rayConfig.outbounds.isNotEmpty()) { - v2rayConfig.outbounds[0] = outboundNew - } else { - v2rayConfig.outbounds.add(outboundNew) - } - - return socksPort - } catch (e: Exception) { - Log.e(AppConfig.TAG, "Failed to configure plusOutbound", e) - return null - } - } - /** * Configures additional outbound connections for proxy chaining. * @@ -1046,7 +1000,7 @@ object V2rayConfigManager { EConfigType.VLESS -> VlessFmt.toOutbound(profileItem) EConfigType.TROJAN -> TrojanFmt.toOutbound(profileItem) EConfigType.WIREGUARD -> WireguardFmt.toOutbound(profileItem) - EConfigType.HYSTERIA2 -> null + EConfigType.HYSTERIA2 -> Hysteria2Fmt.toOutbound(profileItem) EConfigType.HTTP -> HttpFmt.toOutbound(profileItem) EConfigType.POLICYGROUP -> null } @@ -1079,8 +1033,7 @@ object V2rayConfigManager { EConfigType.SHADOWSOCKS, EConfigType.SOCKS, EConfigType.HTTP, - EConfigType.TROJAN, - EConfigType.HYSTERIA2 -> + EConfigType.TROJAN -> return OutboundBean( protocol = configType.name.lowercase(), settings = OutSettingsBean( @@ -1098,6 +1051,15 @@ object V2rayConfigManager { ) ) + EConfigType.HYSTERIA2 -> + return OutboundBean( + protocol = "hysteria", + settings = OutSettingsBean( + servers = null + ), + streamSettings = StreamSettingsBean() + ) + EConfigType.CUSTOM -> null EConfigType.POLICYGROUP -> null } @@ -1127,7 +1089,7 @@ object V2rayConfigManager { val xhttpExtra = profileItem.xhttpExtra var sni: String? = null - streamSettings.network = if (transport.isEmpty()) NetworkType.TCP.type else transport + streamSettings.network = transport.ifEmpty { NetworkType.TCP.type } when (streamSettings.network) { NetworkType.TCP.type -> { val tcpSetting = StreamSettingsBean.TcpSettingsBean() @@ -1216,6 +1178,23 @@ object V2rayConfigManager { sni = authority streamSettings.grpcSettings = grpcSetting } + + NetworkType.HYSTERIA.type -> { + val hysteriaSetting = StreamSettingsBean.HysteriaSettingsBean( + version = 2, + auth = profileItem.password.orEmpty(), + up = profileItem.bandwidthUp?.ifEmpty { "0" }.orEmpty(), + down = profileItem.bandwidthDown?.ifEmpty { "0" }.orEmpty(), + udphop = null + ) + if (profileItem.portHopping.isNotNullEmpty()) { + hysteriaSetting.udphop = StreamSettingsBean.HysteriaSettingsBean.HysteriaUdpHopBean( + port = profileItem.portHopping, + interval = profileItem.portHoppingInterval?.ifEmpty { "30" }.orEmpty().toInt() + ) + } + streamSettings.hysteriaSettings = hysteriaSetting + } } return sni } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/NativePlugin.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/NativePlugin.kt deleted file mode 100644 index 7a072cd0..00000000 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/NativePlugin.kt +++ /dev/null @@ -1,32 +0,0 @@ -/****************************************************************************** - * * - * Copyright (C) 2021 by nekohasekai * - * Copyright (C) 2021 by Max Lv * - * Copyright (C) 2021 by Mygod Studio * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - * * - ******************************************************************************/ - -package com.v2ray.ang.plugin - -import android.content.pm.ResolveInfo - -class NativePlugin(resolveInfo: ResolveInfo) : ResolvedPlugin(resolveInfo) { - init { - check(resolveInfo.providerInfo != null) - } - - override val componentInfo get() = resolveInfo.providerInfo!! -} diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/Plugin.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/Plugin.kt deleted file mode 100644 index 04294ac6..00000000 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/Plugin.kt +++ /dev/null @@ -1,43 +0,0 @@ -/****************************************************************************** - * * - * Copyright (C) 2021 by nekohasekai * - * Copyright (C) 2021 by Max Lv * - * Copyright (C) 2021 by Mygod Studio * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - * * - ******************************************************************************/ - -package com.v2ray.ang.plugin - -import android.graphics.drawable.Drawable - -abstract class Plugin { - abstract val id: String - abstract val label: CharSequence - abstract val version: Int - abstract val versionName: String - open val icon: Drawable? get() = null - open val defaultConfig: String? get() = null - open val packageName: String get() = "" - open val directBootAware: Boolean get() = true - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - return id == (other as Plugin).id - } - - override fun hashCode() = id.hashCode() -} diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/PluginContract.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/PluginContract.kt deleted file mode 100644 index 5aa253f8..00000000 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/PluginContract.kt +++ /dev/null @@ -1,33 +0,0 @@ -/****************************************************************************** - * * - * Copyright (C) 2021 by nekohasekai * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - * * - ******************************************************************************/ - -package com.v2ray.ang.plugin - -object PluginContract { - - const val ACTION_NATIVE_PLUGIN = "io.nekohasekai.sagernet.plugin.ACTION_NATIVE_PLUGIN" - const val EXTRA_ENTRY = "io.nekohasekai.sagernet.plugin.EXTRA_ENTRY" - const val METADATA_KEY_ID = "io.nekohasekai.sagernet.plugin.id" - const val METADATA_KEY_EXECUTABLE_PATH = "io.nekohasekai.sagernet.plugin.executable_path" - const val METHOD_GET_EXECUTABLE = "sagernet:getExecutable" - - const val COLUMN_PATH = "path" - const val COLUMN_MODE = "mode" - const val SCHEME = "plugin" -} diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/PluginList.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/PluginList.kt deleted file mode 100644 index 2495eb45..00000000 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/PluginList.kt +++ /dev/null @@ -1,54 +0,0 @@ -/****************************************************************************** - * * - * Copyright (C) 2021 by nekohasekai * - * Copyright (C) 2021 by Max Lv * - * Copyright (C) 2021 by Mygod Studio * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - * * - ******************************************************************************/ - -package com.v2ray.ang.plugin - -import android.content.Intent -import android.content.pm.PackageManager -import com.v2ray.ang.AngApplication - -class PluginList : ArrayList() { - init { - addAll( - AngApplication.application.packageManager.queryIntentContentProviders( - Intent(PluginContract.ACTION_NATIVE_PLUGIN), PackageManager.GET_META_DATA - ) - .filter { it.providerInfo.exported }.map { NativePlugin(it) }) - } - - val lookup = mutableMapOf().apply { - for (plugin in this@PluginList.toList()) { - fun check(old: Plugin?) { - if (old != null && old != plugin) { - this@PluginList.remove(old) - } - /* if (old != null && old !== plugin) { - val packages = this@PluginList.filter { it.id == plugin.id } - .joinToString { it.packageName } - val message = "Conflicting plugins found from: $packages" - Toast.makeText(SagerNet.application, message, Toast.LENGTH_LONG).show() - throw IllegalStateException(message) - }*/ - } - check(put(plugin.id, plugin)) - } - } -} diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/PluginManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/PluginManager.kt deleted file mode 100644 index 2e6ad91f..00000000 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/PluginManager.kt +++ /dev/null @@ -1,232 +0,0 @@ -/****************************************************************************** - * * - * Copyright (C) 2021 by nekohasekai * - * Copyright (C) 2021 by Max Lv * - * Copyright (C) 2021 by Mygod Studio * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - * * - ******************************************************************************/ - -package com.v2ray.ang.plugin - -import android.annotation.SuppressLint -import android.content.BroadcastReceiver -import android.content.ContentResolver -import android.content.Intent -import android.content.pm.ComponentInfo -import android.content.pm.PackageManager -import android.content.pm.ProviderInfo -import android.database.Cursor -import android.net.Uri -import android.os.Build -import android.system.Os -import androidx.core.os.bundleOf -import com.v2ray.ang.AngApplication -import com.v2ray.ang.extension.listenForPackageChanges -import com.v2ray.ang.extension.toast -import com.v2ray.ang.plugin.PluginContract.METADATA_KEY_ID -import java.io.File -import java.io.FileNotFoundException - -object PluginManager { - - class PluginNotFoundException(val plugin: String) : FileNotFoundException(plugin) - - private var receiver: BroadcastReceiver? = null - private var cachedPlugins: PluginList? = null - fun fetchPlugins() = synchronized(this) { - if (receiver == null) receiver = AngApplication.application.listenForPackageChanges { - synchronized(this) { - receiver = null - cachedPlugins = null - } - } - if (cachedPlugins == null) cachedPlugins = PluginList() - cachedPlugins!! - } - - private fun buildUri(id: String, authority: String) = Uri.Builder() - .scheme(PluginContract.SCHEME) - .authority(authority) - .path("/$id") - .build() - - data class InitResult( - val path: String, - ) - - @Throws(Throwable::class) - fun init(pluginId: String): InitResult? { - if (pluginId.isEmpty()) return null - var throwable: Throwable? = null - - try { - val result = initNative(pluginId) - if (result != null) return result - } catch (t: Throwable) { - if (throwable == null) throwable = t //Logs.w(t) - } - - throw throwable ?: PluginNotFoundException(pluginId) - } - - private fun initNative(pluginId: String): InitResult? { - var flags = PackageManager.GET_META_DATA - if (Build.VERSION.SDK_INT >= 24) { - flags = - flags or PackageManager.MATCH_DIRECT_BOOT_UNAWARE or PackageManager.MATCH_DIRECT_BOOT_AWARE - } - var providers = AngApplication.application.packageManager.queryIntentContentProviders( - Intent(PluginContract.ACTION_NATIVE_PLUGIN, buildUri(pluginId, "com.github.dyhkwong.AngApplication")), flags - ) - .filter { it.providerInfo.exported } - if (providers.isEmpty()) { - providers = AngApplication.application.packageManager.queryIntentContentProviders( - Intent(PluginContract.ACTION_NATIVE_PLUGIN, buildUri(pluginId, "io.nekohasekai.AngApplication")), flags - ) - .filter { it.providerInfo.exported } - } - if (providers.isEmpty()) { - providers = AngApplication.application.packageManager.queryIntentContentProviders( - Intent(PluginContract.ACTION_NATIVE_PLUGIN, buildUri(pluginId, "moe.matsuri.lite")), flags - ) - .filter { it.providerInfo.exported } - } - if (providers.isEmpty()) { - providers = AngApplication.application.packageManager.queryIntentContentProviders( - Intent(PluginContract.ACTION_NATIVE_PLUGIN, buildUri(pluginId, "fr.husi")), flags - ) - .filter { it.providerInfo.exported } - } - if (providers.isEmpty()) { - providers = AngApplication.application.packageManager.queryIntentContentProviders( - Intent(PluginContract.ACTION_NATIVE_PLUGIN), PackageManager.GET_META_DATA - ).filter { - it.providerInfo.exported && - it.providerInfo.metaData.containsKey(METADATA_KEY_ID) && - it.providerInfo.metaData.getString(METADATA_KEY_ID) == pluginId - } - if (providers.size > 1) { - providers = listOf(providers[0]) // What if there is more than one? - } - } - if (providers.isEmpty()) return null - if (providers.size > 1) { - val message = - "Conflicting plugins found from: ${providers.joinToString { it.providerInfo.packageName }}" - AngApplication.application.toast(message) - throw IllegalStateException(message) - } - val provider = providers.single().providerInfo - var failure: Throwable? = null - try { - initNativeFaster(provider)?.also { return InitResult(it) } - } catch (t: Throwable) { - // Logs.w("Initializing native plugin faster mode failed") - failure = t - } - - val uri = Uri.Builder().apply { - scheme(ContentResolver.SCHEME_CONTENT) - authority(provider.authority) - }.build() - try { - return initNativeFast( - AngApplication.application.contentResolver, - pluginId, - uri - )?.let { InitResult(it) } - } catch (t: Throwable) { - // Logs.w("Initializing native plugin fast mode failed") - failure?.also { t.addSuppressed(it) } - failure = t - } - - try { - return initNativeSlow( - AngApplication.application.contentResolver, - pluginId, - uri - )?.let { InitResult(it) } - } catch (t: Throwable) { - failure.also { t.addSuppressed(it) } - throw t - } - } - - private fun initNativeFaster(provider: ProviderInfo): String? { - return provider.loadString(PluginContract.METADATA_KEY_EXECUTABLE_PATH) - ?.let { relativePath -> - File(provider.applicationInfo.nativeLibraryDir).resolve(relativePath).apply { - check(canExecute()) - }.absolutePath - } - } - - private fun initNativeFast(cr: ContentResolver, pluginId: String, uri: Uri): String? { - return cr.call(uri, PluginContract.METHOD_GET_EXECUTABLE, null, bundleOf()) - ?.getString(PluginContract.EXTRA_ENTRY)?.also { - check(File(it).canExecute()) - } - } - - @SuppressLint("Recycle") - private fun initNativeSlow(cr: ContentResolver, pluginId: String, uri: Uri): String? { - var initialized = false - fun entryNotFound(): Nothing = - throw IndexOutOfBoundsException("Plugin entry binary not found") - - val pluginDir = File(AngApplication.application.noBackupFilesDir, "plugin") - (cr.query( - uri, - arrayOf(PluginContract.COLUMN_PATH, PluginContract.COLUMN_MODE), - null, - null, - null - ) - ?: return null).use { cursor -> - if (!cursor.moveToFirst()) entryNotFound() - pluginDir.deleteRecursively() - if (!pluginDir.mkdirs()) throw FileNotFoundException("Unable to create plugin directory") - val pluginDirPath = pluginDir.absolutePath + '/' - do { - val path = cursor.getString(0) - val file = File(pluginDir, path) - check(file.absolutePath.startsWith(pluginDirPath)) - cr.openInputStream(uri.buildUpon().path(path).build())!!.use { inStream -> - file.outputStream().use { outStream -> inStream.copyTo(outStream) } - } - Os.chmod( - file.absolutePath, when (cursor.getType(1)) { - Cursor.FIELD_TYPE_INTEGER -> cursor.getInt(1) - Cursor.FIELD_TYPE_STRING -> cursor.getString(1).toInt(8) - else -> throw IllegalArgumentException("File mode should be of type int") - } - ) - if (path == pluginId) initialized = true - } while (cursor.moveToNext()) - } - if (!initialized) entryNotFound() - return File(pluginDir, pluginId).absolutePath - } - - fun ComponentInfo.loadString(key: String) = when (val value = metaData.getString(key)) { - is String -> value -// is Int -> AngApplication.application.packageManager.getResourcesForApplication(applicationInfo) -// .getString(value) - - null -> null - } -} diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/ResolvedPlugin.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/ResolvedPlugin.kt deleted file mode 100644 index 6e6861cf..00000000 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/ResolvedPlugin.kt +++ /dev/null @@ -1,51 +0,0 @@ -/****************************************************************************** - * * - * Copyright (C) 2021 by nekohasekai * - * Copyright (C) 2021 by Max Lv * - * Copyright (C) 2021 by Mygod Studio * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - * * - ******************************************************************************/ - -package com.v2ray.ang.plugin - -import android.content.pm.ComponentInfo -import android.content.pm.PackageManager -import android.content.pm.ResolveInfo -import android.graphics.drawable.Drawable -import android.os.Build -import com.v2ray.ang.AngApplication -import com.v2ray.ang.plugin.PluginManager.loadString - -abstract class ResolvedPlugin(protected val resolveInfo: ResolveInfo) : Plugin() { - protected abstract val componentInfo: ComponentInfo - - override val id by lazy { componentInfo.loadString(PluginContract.METADATA_KEY_ID)!! } - override val version by lazy { - getPackageInfo(componentInfo.packageName).versionCode - } - override val versionName: String by lazy { - getPackageInfo(componentInfo.packageName).versionName!! - } - override val label: CharSequence get() = resolveInfo.loadLabel(AngApplication.application.packageManager) - override val icon: Drawable get() = resolveInfo.loadIcon(AngApplication.application.packageManager) - override val packageName: String get() = componentInfo.packageName - override val directBootAware get() = Build.VERSION.SDK_INT < 24 || componentInfo.directBootAware - - fun getPackageInfo(packageName: String) = AngApplication.application.packageManager.getPackageInfo( - packageName, if (Build.VERSION.SDK_INT >= 28) PackageManager.GET_SIGNING_CERTIFICATES - else @Suppress("DEPRECATION") PackageManager.GET_SIGNATURES - )!! -} diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt index 68934f11..5c13514a 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt @@ -10,7 +10,6 @@ import com.v2ray.ang.AppConfig.MSG_MEASURE_CONFIG_SUCCESS import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.extension.serializable import com.v2ray.ang.handler.MmkvManager -import com.v2ray.ang.handler.PluginServiceManager import com.v2ray.ang.handler.SettingsManager import com.v2ray.ang.handler.V2RayNativeManager import com.v2ray.ang.handler.V2rayConfigManager @@ -129,16 +128,10 @@ class V2RayTestService : Service() { private fun startRealPing(guid: String): Long { val retFailure = -1L - val config = MmkvManager.decodeServerConfig(guid) ?: return retFailure - if (config.configType == EConfigType.HYSTERIA2) { - val delay = PluginServiceManager.realPingHy2(this, config) - return delay - } else { - val configResult = V2rayConfigManager.getV2rayConfig4Speedtest(this, guid) - if (!configResult.status) { - return retFailure - } - return V2RayNativeManager.measureOutboundDelay(configResult.content, SettingsManager.getDelayTestUrl()) + val configResult = V2rayConfigManager.getV2rayConfig4Speedtest(this, guid) + if (!configResult.status) { + return retFailure } + return V2RayNativeManager.measureOutboundDelay(configResult.content, SettingsManager.getDelayTestUrl()) } } \ No newline at end of file