Add android proxy service
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.hiddify.hiddify.constant
|
||||||
|
|
||||||
|
object ServiceMode {
|
||||||
|
const val NORMAL = "normal"
|
||||||
|
const val VPN = "vpn"
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user