Skip to content

Commit

Permalink
perf(search): ⚡ 优化搜索逻辑,可以使用app名称或包名.
Browse files Browse the repository at this point in the history
refactor(vpnservice): ♻️ 修改vpn服务为绑定activity,vpn服务随应用结束服务自动关闭.
perf(search): ⚡ Optimize the search logic, you can use the app name or package name.
refactor(vpnservice): ♻️ Modify the vpn service to bind activity, and vpn service will automatically close with the end of the application service.
  • Loading branch information
ys1231 committed Sep 3, 2024
1 parent dcc454c commit cd1e3ad
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 201 deletions.
1 change: 0 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_VPN_SERVICE"
android:process=":background"
android:foregroundServiceType="specialUse">
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="vpn" />
<intent-filter>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package cn.ys1231.appproxy.IyueService

import android.R.id.message
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
Expand All @@ -9,38 +8,34 @@ import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.net.VpnService
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.os.ParcelFileDescriptor
import android.util.Log
import androidx.core.app.NotificationCompat
import cn.ys1231.appproxy.MainActivity
import cn.ys1231.appproxy.R
import cn.ys1231.appproxy.data.Utils
import com.google.gson.Gson
import engine.Engine
import engine.Key
import java.util.concurrent.CountDownLatch


class IyueVPNService : VpnService() {
private val TAG = "iyue->${this.javaClass.simpleName}"
private var vpnInterface: ParcelFileDescriptor? = null
private var vpnThread: Thread? = null
private var proxyData: Map<String, Any>? = null
private var utils: Utils? = null

// 移除死循环,并添加一个停止信号处理逻辑
private val stopSignal = CountDownLatch(1)
private val TAG = "iyue->${this.javaClass.simpleName} "

companion object {
const val ACTION_STOP_SERVICE = "cn.ys1231.appproxy.STOP_VPN_SERVICE"
private var vpnInterface: ParcelFileDescriptor? = null
private var isRunning = false
private val binder = VPNServiceBinder()

inner class VPNServiceBinder : Binder() {
fun getService(): IyueVPNService = this@IyueVPNService
}

override fun onCreate() {
super.onCreate()
// 初始化操作
Log.d(TAG, "onCreate: IyueVPNService ")
utils = Utils(this)
Log.d(TAG, "onCreate: VPNServiceBinder")

// 创建通知渠道(Android Oreo 及以上版本)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelId = "iyue_vpn_channel"
Expand All @@ -57,31 +52,30 @@ class IyueVPNService : VpnService() {
}
}

override fun onBind(intent: Intent?): IBinder {
Log.d(TAG, "onBind: VPNServiceBinder")
return binder
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent?.action == ACTION_STOP_SERVICE) {
stopVpn()
utils?.setVpnStatus(false)
utils?.setProxyName("")
// stopSelf()
return START_NOT_STICKY
}
proxyData = intent?.extras?.getSerializable("data") as Map<String, Any>
Log.d(TAG, "onStartCommand: $proxyData")
vpnThread = object : Thread() {
override fun run() {
try {
utils?.setVpnStatus(true)
startVpn(proxyData!!)
} catch (e: Exception) {
Log.e(TAG, "vpnThread: fail", e)
}
}
}
vpnThread?.start()
Log.d(TAG, "onStartCommand: ${intent.toString()}")
return START_NOT_STICKY
}

fun startVpnService(data: Map<String, Any>) {
Log.d(TAG, "startVpnService: $data")

// {proxyPort=8080, proxyPass=, proxyName=test, proxyType=http, proxyUser=, appProxyPackageList=[com.android.chrome], proxyHost=192.168.0.1}
val proxyName = data["proxyName"].toString()
val proxyHost = data["proxyHost"].toString()
val proxyPort = (data["proxyPort"] as String).toInt()
val proxyType = data["proxyType"].toString()
val proxyUser = data["proxyUser"].toString()
val proxyPass = data["proxyPass"].toString()

// 创建并显示前台服务通知
val notificationIntent = Intent(this, MainActivity::class.java)
.putExtra("iyue_vpn_channel", true)
.putExtra("proxyData", proxyData!!["proxyName"] as String)
val pendingIntent = PendingIntent.getActivity(
this,
0,
Expand All @@ -90,21 +84,14 @@ class IyueVPNService : VpnService() {
)

val notification = NotificationCompat.Builder(this, "iyue_vpn_channel")
.setContentTitle("${applicationInfo.loadLabel(packageManager)}: ${proxyData!!["proxyName"]}")
.setContentText("${proxyData!!["proxyType"]}: ${proxyData!!["proxyHost"]}:${proxyData!!["proxyPort"]}")
.setSmallIcon(R.mipmap.vpn)
.setContentTitle("${applicationInfo.loadLabel(packageManager)}: $proxyName")
.setContentText("$proxyType: $proxyHost:$proxyPort")
.setSmallIcon(R.mipmap.vpn, 3)
.setContentIntent(pendingIntent)
.build()

startForeground(1, notification) // 使用一个唯一ID(例如1)来启动前台服务

return START_NOT_STICKY
}

private fun startVpn(data: Map<String, Any>) {
startForeground(1, notification)

Log.d(TAG, "startVpn: IyueVPNService: $data")
// {proxyPort=8080, proxyPass=, proxyName=test, proxyType=http, proxyUser=, appProxyPackageList=[com.android.chrome], proxyHost=192.168.0.1}
val builder = Builder()
.addAddress("10.0.0.2", 24)
.addRoute("0.0.0.0", 0)
Expand All @@ -128,12 +115,6 @@ class IyueVPNService : VpnService() {
Log.e(TAG, "vpnInterface: create establish error ")
return
}
val proxyName = data["proxyName"].toString()
val proxyHost = data["proxyHost"].toString()
val proxyPort = (data["proxyPort"] as String).toInt()
val proxyType = data["proxyType"].toString()
val proxyUser = data["proxyUser"].toString()
val proxyPass = data["proxyPass"].toString()

val key = Key()
key.mark = 0
Expand All @@ -149,55 +130,51 @@ class IyueVPNService : VpnService() {
key.tcpModerateReceiveBuffer = false
Engine.insert(key)
Engine.start()
Log.d(TAG, "startEngine: ${key.toString()}")
utils?.setProxyName(proxyName)
stopSignal.await()
Log.d(TAG, "startEngine: $key")
isRunning = true
// stopSignal.await()
} catch (e: Exception) {
Log.e(TAG, "startEngine: error ${e.message}")
} finally {
}
}

fun stopVpnService() {
Log.d(TAG, "stopVpnService: vpnInterface $vpnInterface")
try {
if (vpnInterface != null) {
// 不能主动停止,会触发重复关闭fd 导致app崩溃
// Engine.stop()
// Engine.stop()
vpnInterface?.close()
vpnInterface = null
isRunning = false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
stopForeground(STOP_FOREGROUND_REMOVE)
}
}
Log.d(TAG, "stopEngine: success!")
}

}

private fun stopVpn() {
Log.d(TAG, "stopVpn: vpnInterface $vpnInterface")
try {
// 通知startVpn中的等待线程可以结束
stopSignal.countDown();
if (vpnThread != null && vpnThread?.isAlive == true) {
vpnThread?.interrupt()
} else {
Log.w(TAG, "vpnThread is either null or not alive, interrupt is not called.")
}
} catch (e: Exception) {
Log.e(TAG, "stopVpn: ${e.message}")
Log.e(TAG, "stopVpnService: ${e.message}")
}

}

override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy: ")
fun isRunning(): Boolean {
return isRunning
}

private fun jsonToList(jsonString: String): List<String> {
val gson = Gson()
return gson.fromJson(jsonString, Array<String>::class.java).toList()
}

public fun isVpnRunning(): Boolean {
return vpnInterface != null
override fun onUnbind(intent: Intent?): Boolean {
Log.d(TAG, "onUnbind: IyueVPNService ")
stopVpnService()
return super.onUnbind(intent)
}

public fun getProxyData(): String? {
return proxyData!!["proxyName"]?.toString()
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy: IyueVPNService ")
}

}
Loading

0 comments on commit cd1e3ad

Please sign in to comment.