add FOREGROUND_SERVICE for no_tun mode, not using vpn service (#1203)

1. add FOREGROUND_SERVICE related code, connection not to be **blocked by android system** when apps running in background
2. no_tun mode not enabling vpnservice, makeing other app to use vpnservice

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
21paradox
2025-08-09 18:34:45 +08:00
committed by GitHub
parent 8ffc2f12e4
commit 7de4b33dd1
5 changed files with 113 additions and 11 deletions

View File

@@ -1,6 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<application <application
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
@@ -18,6 +22,12 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<service
android:name=".MainForegroundService"
android:foregroundServiceType="dataSync"
android:enabled="true"
android:exported="false">
</service>
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"

View File

@@ -1,3 +1,20 @@
package com.kkrainbow.easytier package com.kkrainbow.easytier
class MainActivity : TauriActivity() import android.content.Intent
import android.os.Build
import android.os.Bundle
class MainActivity : TauriActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initService()
}
private fun initService() {
val serviceIntent = Intent(this, MainForegroundService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent)
} else {
startService(serviceIntent)
}
}
}

View File

@@ -0,0 +1,64 @@
package com.kkrainbow.easytier
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Intent
import android.content.pm.ServiceInfo
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import android.util.Log
class MainForegroundService : Service() {
companion object {
const val CHANNEL_ID = "easytier_channel"
const val NOTIFICATION_ID = 1355
// You can add more constants if needed
}
override fun onCreate() {
super.onCreate()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
createNotificationChannel()
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("easytier Running")
.setContentText("easytier is available on localhost")
.setSmallIcon(android.R.drawable.ic_menu_manage)
.build()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(
NOTIFICATION_ID,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
)
} else {
startForeground(NOTIFICATION_ID, notification)
}
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
}
override fun onBind(intent: Intent?): IBinder? = null
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
val channel = NotificationChannel(
CHANNEL_ID,
"easytier notice",
NotificationManager.IMPORTANCE_DEFAULT
)
val manager = getSystemService(NotificationManager::class.java)
manager?.createNotificationChannel(channel)
} catch (e: Exception) {
Log.e("MainForegroundService", "Failed to create notification channel", e)
}
}
}
}

View File

@@ -115,6 +115,11 @@ function getRoutesForVpn(routes: Route[]): string[] {
async function onNetworkInstanceChange() { async function onNetworkInstanceChange() {
console.error('vpn service watch network instance change ids', JSON.stringify(networkStore.networkInstanceIds)) console.error('vpn service watch network instance change ids', JSON.stringify(networkStore.networkInstanceIds))
const insts = networkStore.networkInstanceIds const insts = networkStore.networkInstanceIds
const no_tun = networkStore.isNoTunEnabled(insts[0])
if (no_tun) {
await doStopVpn()
return
}
if (!insts) { if (!insts) {
await doStopVpn() await doStopVpn()
return return
@@ -132,14 +137,6 @@ async function onNetworkInstanceChange() {
return return
} }
// if use no tun mode, stop the vpn service
const no_tun = networkStore.isNoTunEnabled(insts[0])
if (no_tun) {
console.error('no tun mode, stop vpn service')
await doStopVpn()
return
}
let network_length = curNetworkInfo?.my_node_info?.virtual_ipv4.network_length let network_length = curNetworkInfo?.my_node_info?.virtual_ipv4.network_length
if (!network_length) { if (!network_length) {
network_length = 24 network_length = 24
@@ -187,12 +184,26 @@ async function watchNetworkInstance() {
console.error('vpn service watch network instance') console.error('vpn service watch network instance')
} }
function isNoTunEnabled(instanceId: string | undefined) {
if (!instanceId) {
return false
}
const no_tun = networkStore.isNoTunEnabled(instanceId)
if (no_tun) {
return true
}
return false
}
export async function initMobileVpnService() { export async function initMobileVpnService() {
await registerVpnServiceListener() await registerVpnServiceListener()
await watchNetworkInstance() await watchNetworkInstance()
} }
export async function prepareVpnService() { export async function prepareVpnService(instanceId: string) {
if (isNoTunEnabled(instanceId)) {
return
}
console.log('prepare vpn') console.log('prepare vpn')
const prepare_ret = await prepare_vpn() const prepare_ret = await prepare_vpn()
console.log('prepare vpn', JSON.stringify((prepare_ret))) console.log('prepare vpn', JSON.stringify((prepare_ret)))

View File

@@ -102,7 +102,7 @@ networkStore.$subscribe(async () => {
async function runNetworkCb(cfg: NetworkTypes.NetworkConfig, cb: () => void) { async function runNetworkCb(cfg: NetworkTypes.NetworkConfig, cb: () => void) {
if (type() === 'android') { if (type() === 'android') {
await prepareVpnService() await prepareVpnService(cfg.instance_id)
networkStore.clearNetworkInstances() networkStore.clearNetworkInstances()
} }
else { else {