Optimize Android VPN

This commit is contained in:
wanghongenpin
2025-06-03 18:35:37 +08:00
parent 45262b353b
commit 6000d407dd
3 changed files with 84 additions and 27 deletions

View File

@@ -7,6 +7,7 @@ import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.net.ProxyInfo
import android.net.VpnService
import android.os.Build
import android.os.ParcelFileDescriptor
@@ -232,9 +233,9 @@ class ProxyVpnService : VpnService(), ProtectSocket {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
setMetered(false)
}
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// setHttpProxy(ProxyInfo.buildDirectProxy(proxyHost, proxyPort))
// }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
setHttpProxy(ProxyInfo.buildDirectProxy(proxyHost, proxyPort))
}
}.establish()
}

View File

@@ -4,6 +4,10 @@ import com.network.proxy.vpn.util.ProcessInfoManager
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/**
* 进程信息插件
@@ -27,13 +31,23 @@ class ProcessInfoPlugin : AndroidFlutterPlugin() {
channel.setMethodCallHandler { call, result ->
when (call.method) {
"getProcessByPort" -> {
// val host = call.argument<String>("host")
val host = call.argument<String>("host")
val port = call.argument<Int>("port")
val appInfo = processInfoManager.getProcessInfoByPort(port!!)
result.success(appInfo)
if (port != null) {
CoroutineScope(Dispatchers.IO).launch {
val appInfo = processInfoManager.getProcessInfoByPort(host, port)
withContext(Dispatchers.Main) {
result.success(appInfo)
}
}
} else {
result.error("INVALID_ARGUMENT", "Port is null", null)
}
}
else -> result.notImplemented()
else -> {
result.notImplemented()
}
}
}
}

View File

@@ -5,11 +5,19 @@ import android.net.ConnectivityManager
import android.os.Build
import android.os.Process
import android.system.OsConstants
import android.util.Log
import androidx.annotation.RequiresApi
import com.network.proxy.ProxyVpnService
import com.network.proxy.plugin.ProcessInfo
import com.network.proxy.vpn.Connection
import kotlinx.coroutines.CoroutineScope
import java.io.File
import java.net.InetSocketAddress
import java.nio.channels.SocketChannel
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/**
* 进程信息管理器,用于获取进程信息
@@ -32,24 +40,24 @@ class ProcessInfoManager private constructor() {
var activity: Context? = null
@RequiresApi(Build.VERSION_CODES.N)
fun setConnectionOwnerUid(connection: Connection) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
return
}
CoroutineScope(Dispatchers.IO).launch {
val sourceAddress =
InetSocketAddress(PacketUtil.intToIPAddress(connection.sourceIp), connection.sourcePort)
val destinationAddress = InetSocketAddress(
PacketUtil.intToIPAddress(connection.destinationIp), connection.destinationPort
)
val sourceAddress =
InetSocketAddress(PacketUtil.intToIPAddress(connection.sourceIp), connection.sourcePort)
val destinationAddress = InetSocketAddress(
PacketUtil.intToIPAddress(connection.destinationIp), connection.destinationPort
)
val uid = getProcessInfo(sourceAddress, destinationAddress)
val channel = connection.channel
if (uid != null && channel is SocketChannel) {
val localAddress = channel.localAddress as InetSocketAddress
val networkInfo =
NetworkInfo(uid, destinationAddress.hostString, destinationAddress.port)
localPortCache.put(localAddress.port, networkInfo)
val uid = getProcessInfoUid(sourceAddress, destinationAddress)
val channel = connection.channel
if (uid != null && uid != Process.INVALID_UID && channel is SocketChannel) {
val localAddress = channel.localAddress as InetSocketAddress
val networkInfo =
NetworkInfo(uid, destinationAddress.hostString, destinationAddress.port)
localPortCache.put(localAddress.port, networkInfo)
}
}
}
@@ -65,8 +73,8 @@ class ProcessInfoManager private constructor() {
}
}
private fun getProcessInfo(
@RequiresApi(Build.VERSION_CODES.N)
private fun getProcessInfoUid(
localAddress: InetSocketAddress, remoteAddress: InetSocketAddress
): Int? {
// Log.d(TAG, "getProcessInfo: $localAddress $remoteAddress")
@@ -74,11 +82,12 @@ class ProcessInfoManager private constructor() {
if (activity == null) {
return null
}
val connectivityManager: ConnectivityManager =
activity!!.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val uid = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
connectivityManager.getConnectionOwnerUid(
return connectivityManager.getConnectionOwnerUid(
OsConstants.IPPROTO_TCP, localAddress, remoteAddress
)
} else {
@@ -88,7 +97,7 @@ class ProcessInfoManager private constructor() {
InetSocketAddress::class.java,
InetSocketAddress::class.java
)
method.invoke(
return method.invoke(
connectivityManager, OsConstants.IPPROTO_TCP, localAddress, remoteAddress
) as Int
}
@@ -96,10 +105,15 @@ class ProcessInfoManager private constructor() {
if (uid != Process.INVALID_UID) {
return uid
}
Log.w(
"ProcessInfoManager",
"Failed to get UID for local address $localAddress and remote address $remoteAddress"
)
return null
}
fun getProcessInfoByPort(localPort: Int): ProcessInfo? {
suspend fun getProcessInfoByPort(host: String?, localPort: Int): ProcessInfo? {
val networkInfo = localPortCache.get(localPort)
if (networkInfo != null) {
val processInfo = getProcessInfo(networkInfo.uid)
@@ -109,6 +123,34 @@ class ProcessInfoManager private constructor() {
put("remotePort", networkInfo.remotePort)
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return withContext(Dispatchers.IO) {
val localAddress = InetSocketAddress(host, localPort)
val remoteAddress = InetSocketAddress(ProxyVpnService.host, ProxyVpnService.port)
val uid = getProcessInfoUid(localAddress, remoteAddress)
if (uid == null || uid == Process.INVALID_UID) {
return@withContext null
}
val processInfo = getProcessInfo(uid)
if (processInfo != null) {
localPortCache.put(
localPort, NetworkInfo(uid, remoteAddress.hostString, remoteAddress.port)
)
return@withContext processInfo
} else {
Log.w("ProcessInfoManager", "No process info found for UID: $uid")
null
}
}
} else {
Log.w("ProcessInfoManager", "Access to /proc/net/tcp is restricted on non-rooted devices.")
}
return null
}