@@ -5,26 +5,32 @@ import android.content.Intent
55import android.net.VpnService
66import android.os.ParcelFileDescriptor
77import androidx.core.app.NotificationCompat
8+ import com.netmonitor.app.Constants
89import com.netmonitor.app.MainActivity
910import com.netmonitor.app.NetMonitorApp
1011import com.netmonitor.app.R
1112import com.netmonitor.app.model.PacketInfo
13+ import com.netmonitor.app.util.PacketBus
1214import kotlinx.coroutines.*
1315import java.io.FileInputStream
1416import java.io.FileOutputStream
15- import java.nio.ByteBuffer
1617
18+ /* *
19+ * VPN 抓包服务
20+ *
21+ * 改进点:
22+ * - 移除 companion object 中的静态回调 onPacketCaptured,改用 PacketBus (SharedFlow)
23+ * 解决内存泄漏:不再持有 ViewModel/Activity 的引用
24+ * - VPN 配置参数统一引用 Constants
25+ * - 通知 ID 统一引用 Constants
26+ */
1727class PacketCaptureVpnService : VpnService () {
1828
1929 private var vpnInterface: ParcelFileDescriptor ? = null
2030 private var captureJob: Job ? = null
2131 private val scope = CoroutineScope (Dispatchers .IO + SupervisorJob ())
2232
23- override fun onStartCommand (
24- intent : Intent ? ,
25- flags : Int ,
26- startId : Int
27- ): Int {
33+ override fun onStartCommand (intent : Intent ? , flags : Int , startId : Int ): Int {
2834 when (intent?.action) {
2935 ACTION_START -> startCapture()
3036 ACTION_STOP -> stopCapture()
@@ -35,13 +41,13 @@ class PacketCaptureVpnService : VpnService() {
3541 private fun startCapture () {
3642 val builder = Builder ()
3743 .setSession(" NetMonitor VPN" )
38- .addAddress(" 10.0.0.2 " , 32 )
39- .addRoute(" 0.0.0.0 " , 0 )
40- .addRoute(" :: " , 0 )
41- .addDnsServer(" 8.8.8.8 " )
42- .addDnsServer(" 8.8.4.4 " )
43- .addDnsServer(" 2001:4860:4860::8888 " )
44- .setMtu(1500 )
44+ .addAddress(Constants . VPN_ADDRESS , 32 )
45+ .addRoute(Constants . VPN_ROUTE_V4 , 0 )
46+ .addRoute(Constants . VPN_ROUTE_V6 , 0 )
47+ .addDnsServer(Constants . VPN_DNS_PRIMARY )
48+ .addDnsServer(Constants . VPN_DNS_SECONDARY )
49+ .addDnsServer(Constants . VPN_DNS_V6 )
50+ .setMtu(Constants . VPN_MTU )
4551 .setBlocking(false )
4652
4753 // 排除自身流量,防止回环
@@ -51,7 +57,7 @@ class PacketCaptureVpnService : VpnService() {
5157
5258 vpnInterface = builder.establish() ? : return
5359
54- startForeground(NOTIFICATION_ID , createNotification())
60+ startForeground(Constants . NOTIFICATION_ID_CAPTURE , createNotification())
5561
5662 captureJob = scope.launch {
5763 val fd = vpnInterface!! .fileDescriptor
@@ -77,7 +83,7 @@ class PacketCaptureVpnService : VpnService() {
7783 }
7884 }
7985
80- private fun processPacket (data : ByteArray , length : Int ) {
86+ private suspend fun processPacket (data : ByteArray , length : Int ) {
8187 try {
8288 val version = (data[0 ].toInt() shr 4 ) and 0xF
8389 val srcIp: String
@@ -93,13 +99,13 @@ class PacketCaptureVpnService : VpnService() {
9399 protocol = data[9 ].toInt() and 0xFF
94100 headerLength = (data[0 ].toInt() and 0x0F ) * 4
95101 srcIp = " ${data[12 ].toInt() and 0xFF } " +
96- " .${data[13 ].toInt() and 0xFF } " +
97- " .${data[14 ].toInt() and 0xFF } " +
98- " .${data[15 ].toInt() and 0xFF } "
102+ " .${data[13 ].toInt() and 0xFF } " +
103+ " .${data[14 ].toInt() and 0xFF } " +
104+ " .${data[15 ].toInt() and 0xFF } "
99105 dstIp = " ${data[16 ].toInt() and 0xFF } " +
100- " .${data[17 ].toInt() and 0xFF } " +
101- " .${data[18 ].toInt() and 0xFF } " +
102- " .${data[19 ].toInt() and 0xFF } "
106+ " .${data[17 ].toInt() and 0xFF } " +
107+ " .${data[18 ].toInt() and 0xFF } " +
108+ " .${data[19 ].toInt() and 0xFF } "
103109 }
104110 6 -> {
105111 protocol = data[6 ].toInt() and 0xFF
@@ -115,18 +121,18 @@ class PacketCaptureVpnService : VpnService() {
115121 protoName = " TCP"
116122 if (length >= headerLength + 4 ) {
117123 srcPort = ((data[headerLength].toInt() and 0xFF ) shl 8 ) or
118- (data[headerLength + 1 ].toInt() and 0xFF )
124+ (data[headerLength + 1 ].toInt() and 0xFF )
119125 dstPort = ((data[headerLength + 2 ].toInt() and 0xFF ) shl 8 ) or
120- (data[headerLength + 3 ].toInt() and 0xFF )
126+ (data[headerLength + 3 ].toInt() and 0xFF )
121127 }
122128 }
123129 17 -> {
124130 protoName = " UDP"
125131 if (length >= headerLength + 4 ) {
126132 srcPort = ((data[headerLength].toInt() and 0xFF ) shl 8 ) or
127- (data[headerLength + 1 ].toInt() and 0xFF )
133+ (data[headerLength + 1 ].toInt() and 0xFF )
128134 dstPort = ((data[headerLength + 2 ].toInt() and 0xFF ) shl 8 ) or
129- (data[headerLength + 3 ].toInt() and 0xFF )
135+ (data[headerLength + 3 ].toInt() and 0xFF )
130136 }
131137 }
132138 1 -> protoName = " ICMP"
@@ -139,7 +145,8 @@ class PacketCaptureVpnService : VpnService() {
139145 else
140146 PacketInfo .Direction .INBOUND
141147
142- onPacketCaptured?.invoke(
148+ // ✅ 改用 PacketBus 替代静态回调,不再持有外部引用
149+ PacketBus .emit(
143150 PacketInfo (
144151 protocol = protoName,
145152 sourceIp = srcIp,
@@ -150,8 +157,7 @@ class PacketCaptureVpnService : VpnService() {
150157 direction = direction
151158 )
152159 )
153- } catch (_: Exception ) {
154- }
160+ } catch (_: Exception ) { }
155161 }
156162
157163 private fun formatIpv6 (data : ByteArray , offset : Int ): String {
@@ -173,24 +179,22 @@ class PacketCaptureVpnService : VpnService() {
173179 stopSelf()
174180 }
175181
176- private fun createNotification () =
177- NotificationCompat .Builder (
178- this , NetMonitorApp .CHANNEL_CAPTURE
179- )
180- .setContentTitle(" 抓包服务运行中" )
181- .setContentText(" 正在捕获网络数据包..." )
182- .setSmallIcon(R .drawable.ic_capture)
183- .setContentIntent(
184- PendingIntent .getActivity(
185- this , 0 ,
186- Intent (this , MainActivity ::class .java),
187- PendingIntent .FLAG_UPDATE_CURRENT or
188- PendingIntent .FLAG_IMMUTABLE
189- )
182+ private fun createNotification () = NotificationCompat .Builder (
183+ this , NetMonitorApp .CHANNEL_CAPTURE
184+ )
185+ .setContentTitle(getString(R .string.capture_running_title))
186+ .setContentText(getString(R .string.capture_running_text))
187+ .setSmallIcon(R .drawable.ic_capture)
188+ .setContentIntent(
189+ PendingIntent .getActivity(
190+ this , 0 ,
191+ Intent (this , MainActivity ::class .java),
192+ PendingIntent .FLAG_UPDATE_CURRENT or PendingIntent .FLAG_IMMUTABLE
190193 )
191- .setOngoing(true )
192- .setSilent(true )
193- .build()
194+ )
195+ .setOngoing(true )
196+ .setSilent(true )
197+ .build()
194198
195199 override fun onDestroy () {
196200 super .onDestroy()
@@ -204,8 +208,5 @@ class PacketCaptureVpnService : VpnService() {
204208 companion object {
205209 const val ACTION_START = " com.netmonitor.action.START_CAPTURE"
206210 const val ACTION_STOP = " com.netmonitor.action.STOP_CAPTURE"
207- const val NOTIFICATION_ID = 1002
208-
209- var onPacketCaptured: ((PacketInfo ) -> Unit )? = null
210211 }
211212}
0 commit comments