Add android proxy service

This commit is contained in:
problematicconsumer
2023-09-09 16:02:21 +03:30
parent 3c261d6533
commit c1bff88775
7 changed files with 80 additions and 7 deletions

View File

@@ -8,7 +8,6 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.hiddify.hiddify.bg.ServiceConnection import com.hiddify.hiddify.bg.ServiceConnection
import com.hiddify.hiddify.bg.ServiceNotification import com.hiddify.hiddify.bg.ServiceNotification
import com.hiddify.hiddify.bg.VPNService
import com.hiddify.hiddify.constant.Alert import com.hiddify.hiddify.constant.Alert
import com.hiddify.hiddify.constant.Status import com.hiddify.hiddify.constant.Status
import io.flutter.embedding.android.FlutterFragmentActivity import io.flutter.embedding.android.FlutterFragmentActivity
@@ -56,15 +55,15 @@ class MainActivity : FlutterFragmentActivity(), ServiceConnection.Callback {
return return
} }
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
// if (Settings.rebuildServiceMode()) { if (Settings.rebuildServiceMode()) {
// reconnect() reconnect()
// } }
if (prepare()) { if (prepare()) {
Log.d(TAG, "VPN permission required") Log.d(TAG, "VPN permission required")
return@launch return@launch
} }
val intent = Intent(Application.application, VPNService::class.java) val intent = Intent(Application.application, Settings.serviceClass())
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
ContextCompat.startForegroundService(Application.application, intent) ContextCompat.startForegroundService(Application.application, intent)
} }

View File

@@ -1,7 +1,12 @@
package com.hiddify.hiddify package com.hiddify.hiddify
import android.content.Context import android.content.Context
import com.hiddify.hiddify.bg.ProxyService
import com.hiddify.hiddify.bg.VPNService
import com.hiddify.hiddify.constant.ServiceMode
import com.hiddify.hiddify.constant.SettingsKey import com.hiddify.hiddify.constant.SettingsKey
import org.json.JSONObject
import java.io.File
object Settings { object Settings {
@@ -24,6 +29,11 @@ object Settings {
get() = preferences.getString(SettingsKey.ACTIVE_CONFIG_PATH, "") ?: "" get() = preferences.getString(SettingsKey.ACTIVE_CONFIG_PATH, "") ?: ""
set(value) = preferences.edit().putString(SettingsKey.ACTIVE_CONFIG_PATH, value).apply() set(value) = preferences.edit().putString(SettingsKey.ACTIVE_CONFIG_PATH, value).apply()
var serviceMode: String
get() = preferences.getString(SettingsKey.SERVICE_MODE, ServiceMode.NORMAL)
?: ServiceMode.NORMAL
set(value) = preferences.edit().putString(SettingsKey.SERVICE_MODE, value).apply()
var configOptions: String var configOptions: String
get() = preferences.getString(SettingsKey.CONFIG_OPTIONS, "") ?: "" get() = preferences.getString(SettingsKey.CONFIG_OPTIONS, "") ?: ""
set(value) = preferences.edit().putString(SettingsKey.CONFIG_OPTIONS, value).apply() set(value) = preferences.edit().putString(SettingsKey.CONFIG_OPTIONS, value).apply()
@@ -39,5 +49,41 @@ object Settings {
var startedByUser: Boolean var startedByUser: Boolean
get() = preferences.getBoolean(SettingsKey.STARTED_BY_USER, false) get() = preferences.getBoolean(SettingsKey.STARTED_BY_USER, false)
set(value) = preferences.edit().putBoolean(SettingsKey.STARTED_BY_USER, value).apply() set(value) = preferences.edit().putBoolean(SettingsKey.STARTED_BY_USER, value).apply()
fun serviceClass(): Class<*> {
return when (serviceMode) {
ServiceMode.VPN -> VPNService::class.java
else -> ProxyService::class.java
}
}
suspend fun rebuildServiceMode(): Boolean {
var newMode = ServiceMode.NORMAL
try {
if (needVPNService()) {
newMode = ServiceMode.VPN
}
} catch (_: Exception) {
}
if (serviceMode == newMode) {
return false
}
serviceMode = newMode
return true
}
private suspend fun needVPNService(): Boolean {
val filePath = activeConfigPath
if (filePath.isBlank()) return false
val content = JSONObject(File(filePath).readText())
val inbounds = content.getJSONArray("inbounds")
for (index in 0 until inbounds.length()) {
val inbound = inbounds.getJSONObject(index)
if (inbound.getString("type") == "tun") {
return true
}
}
return false
}
} }

View File

@@ -30,6 +30,10 @@ interface PlatformInterfaceWrapper : PlatformInterface {
error("invalid argument") error("invalid argument")
} }
override fun closeTun() {
error("invalid argument")
}
override fun useProcFS(): Boolean { override fun useProcFS(): Boolean {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.Q return Build.VERSION.SDK_INT < Build.VERSION_CODES.Q
} }

View File

@@ -0,0 +1,17 @@
package com.hiddify.hiddify.bg
import android.app.Service
import android.content.Intent
class ProxyService : Service(), PlatformInterfaceWrapper {
private val service = BoxService(this, this)
override fun onStartCommand(intent: Intent, flags: Int, startId: Int) =
service.onStartCommand(intent, flags, startId)
override fun onBind(intent: Intent) = service.onBind(intent)
override fun onDestroy() = service.onDestroy()
override fun writeLog(message: String) = service.writeLog(message)
}

View File

@@ -36,7 +36,7 @@ class ServiceConnection(
fun connect() { fun connect() {
val intent = runBlocking { val intent = runBlocking {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
Intent(context, VPNService::class.java).setAction(Action.SERVICE) Intent(context, Settings.serviceClass()).setAction(Action.SERVICE)
} }
} }
context.bindService(intent, this, AppCompatActivity.BIND_AUTO_CREATE) context.bindService(intent, this, AppCompatActivity.BIND_AUTO_CREATE)
@@ -56,7 +56,7 @@ class ServiceConnection(
} }
val intent = runBlocking { val intent = runBlocking {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
Intent(context, VPNService::class.java).setAction(Action.SERVICE) Intent(context, Settings.serviceClass()).setAction(Action.SERVICE)
} }
} }
context.bindService(intent, this, AppCompatActivity.BIND_AUTO_CREATE) context.bindService(intent, this, AppCompatActivity.BIND_AUTO_CREATE)

View File

@@ -0,0 +1,6 @@
package com.hiddify.hiddify.constant
object ServiceMode {
const val NORMAL = "normal"
const val VPN = "vpn"
}

View File

@@ -4,6 +4,7 @@ object SettingsKey {
private const val KEY_PREFIX = "flutter." private const val KEY_PREFIX = "flutter."
const val ACTIVE_CONFIG_PATH = "${KEY_PREFIX}active_config_path" const val ACTIVE_CONFIG_PATH = "${KEY_PREFIX}active_config_path"
const val SERVICE_MODE = "${KEY_PREFIX}service_mode"
const val CONFIG_OPTIONS = "config_options_json" const val CONFIG_OPTIONS = "config_options_json"