Files
amnezia-client/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt
T

632 lines
22 KiB
Kotlin
Raw Normal View History

2021-09-30 15:56:48 +03:00
package org.amnezia.vpn
2024-06-18 20:46:21 +03:00
import android.annotation.SuppressLint
2024-03-04 18:08:55 +03:00
import android.app.ActivityManager
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
2024-05-12 18:04:14 +03:00
import android.app.NotificationManager
import android.content.BroadcastReceiver
2024-03-04 18:08:55 +03:00
import android.content.Context
2021-09-30 15:56:48 +03:00
import android.content.Intent
2023-11-24 21:51:09 +03:00
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST
2024-02-14 19:35:40 +03:00
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED
2023-11-24 21:51:09 +03:00
import android.net.VpnService
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.Message
import android.os.Messenger
2024-05-12 18:04:14 +03:00
import android.os.PowerManager
import android.os.Process
2023-11-24 21:51:09 +03:00
import androidx.annotation.MainThread
import androidx.core.app.ServiceCompat
2024-05-12 18:04:14 +03:00
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
2024-09-30 13:38:48 +03:00
import java.net.UnknownHostException
2024-03-04 18:08:55 +03:00
import java.util.concurrent.ConcurrentHashMap
2023-11-24 21:51:09 +03:00
import kotlin.LazyThreadSafetyMode.NONE
import kotlinx.coroutines.CoroutineExceptionHandler
2022-04-26 23:49:20 +05:30
import kotlinx.coroutines.CoroutineScope
2023-11-24 21:51:09 +03:00
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.TimeoutCancellationException
2023-11-24 21:51:09 +03:00
import kotlinx.coroutines.cancel
import kotlinx.coroutines.cancelAndJoin
2023-11-29 17:08:53 +03:00
import kotlinx.coroutines.delay
2023-11-24 21:51:09 +03:00
import kotlinx.coroutines.flow.MutableStateFlow
2024-03-04 18:08:55 +03:00
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.first
2022-04-26 23:49:20 +05:30
import kotlinx.coroutines.launch
2023-12-04 18:23:08 +03:00
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
2023-11-24 21:51:09 +03:00
import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
import org.amnezia.vpn.protocol.ProtocolState.CONNECTING
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTING
import org.amnezia.vpn.protocol.ProtocolState.RECONNECTING
import org.amnezia.vpn.protocol.ProtocolState.UNKNOWN
2023-11-28 22:27:00 +03:00
import org.amnezia.vpn.protocol.VpnException
2023-11-24 21:51:09 +03:00
import org.amnezia.vpn.protocol.VpnStartException
import org.amnezia.vpn.protocol.putStatus
import org.amnezia.vpn.util.LoadLibraryException
import org.amnezia.vpn.util.Log
2024-01-20 16:40:12 +03:00
import org.amnezia.vpn.util.Prefs
import org.amnezia.vpn.util.net.NetworkState
2024-05-12 18:04:14 +03:00
import org.amnezia.vpn.util.net.TrafficStats
2023-11-24 21:51:09 +03:00
import org.json.JSONException
2021-09-30 15:56:48 +03:00
import org.json.JSONObject
2022-04-26 23:49:20 +05:30
2023-11-24 21:51:09 +03:00
private const val TAG = "AmneziaVpnService"
2021-09-30 15:56:48 +03:00
2024-05-12 18:04:14 +03:00
const val ACTION_DISCONNECT = "org.amnezia.vpn.action.disconnect"
2024-06-18 20:46:21 +03:00
const val ACTION_CONNECT = "org.amnezia.vpn.action.connect"
2024-05-12 18:04:14 +03:00
2024-03-04 18:08:55 +03:00
const val MSG_VPN_CONFIG = "VPN_CONFIG"
const val MSG_ERROR = "ERROR"
const val MSG_SAVE_LOGS = "SAVE_LOGS"
const val MSG_CLIENT_NAME = "CLIENT_NAME"
2024-01-20 16:40:12 +03:00
2023-11-24 21:51:09 +03:00
const val AFTER_PERMISSION_CHECK = "AFTER_PERMISSION_CHECK"
private const val PREFS_CONFIG_KEY = "LAST_CONF"
2024-03-04 18:08:55 +03:00
private const val PREFS_SERVER_NAME = "LAST_SERVER_NAME"
private const val PREFS_SERVER_INDEX = "LAST_SERVER_INDEX"
2024-05-12 18:04:14 +03:00
// private const val STATISTICS_SENDING_TIMEOUT = 1000L
private const val TRAFFIC_STATS_UPDATE_TIMEOUT = 1000L
private const val DISCONNECT_TIMEOUT = 5000L
private const val STOP_SERVICE_TIMEOUT = 5000L
2021-09-30 15:56:48 +03:00
2024-06-18 20:46:21 +03:00
@SuppressLint("Registered")
open class AmneziaVpnService : VpnService() {
2022-04-26 23:49:20 +05:30
2023-11-24 21:51:09 +03:00
private lateinit var mainScope: CoroutineScope
private lateinit var connectionScope: CoroutineScope
2023-11-24 21:51:09 +03:00
private var isServiceBound = false
2024-06-18 20:46:21 +03:00
private var vpnProto: VpnProto? = null
private var protocolState = MutableStateFlow(UNKNOWN)
2024-03-04 18:08:55 +03:00
private var serverName: String? = null
private var serverIndex: Int = -1
2021-09-30 15:56:48 +03:00
2023-11-24 21:51:09 +03:00
private val isConnected
get() = protocolState.value == CONNECTED
2021-09-30 15:56:48 +03:00
2023-11-24 21:51:09 +03:00
private val isDisconnected
get() = protocolState.value == DISCONNECTED
2022-04-26 23:49:20 +05:30
private val isUnknown
get() = protocolState.value == UNKNOWN
2023-11-24 21:51:09 +03:00
private var connectionJob: Job? = null
private var disconnectionJob: Job? = null
2024-05-12 18:04:14 +03:00
private var trafficStatsUpdateJob: Job? = null
// private var statisticsSendingJob: Job? = null
private lateinit var networkState: NetworkState
2024-05-12 18:04:14 +03:00
private lateinit var trafficStats: TrafficStats
2024-06-18 20:46:21 +03:00
private var controlReceiver: BroadcastReceiver? = null
2024-05-12 18:04:14 +03:00
private var notificationStateReceiver: BroadcastReceiver? = null
private var screenOnReceiver: BroadcastReceiver? = null
private var screenOffReceiver: BroadcastReceiver? = null
2024-03-04 18:08:55 +03:00
private val clientMessengers = ConcurrentHashMap<Messenger, IpcMessenger>()
private val isActivityConnected
get() = clientMessengers.any { it.value.name == ACTIVITY_MESSENGER_NAME }
2021-09-30 15:56:48 +03:00
2023-11-24 21:51:09 +03:00
private val connectionExceptionHandler = CoroutineExceptionHandler { _, e ->
connectionJob?.cancel()
connectionJob = null
disconnectionJob?.cancel()
disconnectionJob = null
2023-11-24 21:51:09 +03:00
protocolState.value = DISCONNECTED
when (e) {
is IllegalArgumentException,
2023-11-28 22:27:00 +03:00
is VpnStartException,
is VpnException -> onError(e.message ?: e.toString())
2022-04-26 23:49:20 +05:30
2023-11-24 21:51:09 +03:00
is JSONException,
is BadConfigException -> onError("VPN config format error: ${e.message}")
2021-09-30 15:56:48 +03:00
2023-11-24 21:51:09 +03:00
is LoadLibraryException -> onError("${e.message}. Caused: ${e.cause?.message}")
2021-09-30 15:56:48 +03:00
2024-09-30 13:38:48 +03:00
is UnknownHostException -> onError("Unknown host")
2023-11-24 21:51:09 +03:00
else -> throw e
}
2022-02-09 15:23:20 +03:00
}
2023-11-24 21:51:09 +03:00
private val actionMessageHandler: Handler by lazy(NONE) {
object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
val action = msg.extractIpcMessage<Action>()
Log.d(TAG, "Handle action: $action")
when (action) {
Action.REGISTER_CLIENT -> {
2024-03-04 18:08:55 +03:00
val clientName = msg.data.getString(MSG_CLIENT_NAME)
val messenger = IpcMessenger(msg.replyTo, clientName)
clientMessengers[msg.replyTo] = messenger
Log.d(TAG, "Messenger client '$clientName' was registered")
2024-05-12 18:04:14 +03:00
// if (clientName == ACTIVITY_MESSENGER_NAME && isConnected) launchSendingStatistics()
2024-03-04 18:08:55 +03:00
}
Action.UNREGISTER_CLIENT -> {
clientMessengers.remove(msg.replyTo)?.let {
Log.d(TAG, "Messenger client '${it.name}' was unregistered")
2024-05-12 18:04:14 +03:00
// if (it.name == ACTIVITY_MESSENGER_NAME) stopSendingStatistics()
2024-03-04 18:08:55 +03:00
}
2023-11-24 21:51:09 +03:00
}
2021-09-30 15:56:48 +03:00
2023-11-24 21:51:09 +03:00
Action.CONNECT -> {
2024-03-04 18:08:55 +03:00
connect(msg.data.getString(MSG_VPN_CONFIG))
2023-11-24 21:51:09 +03:00
}
2021-10-13 18:33:43 +03:00
2023-11-24 21:51:09 +03:00
Action.DISCONNECT -> {
disconnect()
}
2023-11-24 21:51:09 +03:00
Action.REQUEST_STATUS -> {
2024-03-04 18:08:55 +03:00
clientMessengers[msg.replyTo]?.let { clientMessenger ->
clientMessenger.send {
ServiceEvent.STATUS.packToMessage {
putStatus(this@AmneziaVpnService.protocolState.value)
}
2023-11-24 21:51:09 +03:00
}
}
}
2024-01-20 16:40:12 +03:00
2024-05-12 18:04:14 +03:00
Action.NOTIFICATION_PERMISSION_GRANTED -> {
enableNotification()
}
2024-01-20 16:40:12 +03:00
Action.SET_SAVE_LOGS -> {
2024-03-04 18:08:55 +03:00
Log.saveLogs = msg.data.getBoolean(MSG_SAVE_LOGS)
2024-01-20 16:40:12 +03:00
}
2022-12-14 18:52:19 +03:00
}
2021-10-13 18:33:43 +03:00
}
}
2022-02-09 15:23:20 +03:00
}
2021-10-09 20:17:19 +03:00
2023-11-24 21:51:09 +03:00
private val vpnServiceMessenger: Messenger by lazy(NONE) {
Messenger(actionMessageHandler)
2022-02-09 15:23:20 +03:00
}
2021-10-09 20:17:19 +03:00
2023-11-24 21:51:09 +03:00
/**
* Notification setup
*/
private val foregroundServiceTypeCompat
get() = when {
2024-02-14 19:35:40 +03:00
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED
2023-11-24 21:51:09 +03:00
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> FOREGROUND_SERVICE_TYPE_MANIFEST
else -> 0
}
2024-05-12 18:04:14 +03:00
private val serviceNotification: ServiceNotification by lazy(NONE) { ServiceNotification(this) }
2021-09-30 15:56:48 +03:00
2023-11-24 21:51:09 +03:00
/**
* Service overloaded methods
*/
override fun onCreate() {
super.onCreate()
2024-01-20 16:40:12 +03:00
Log.d(TAG, "Create Amnezia VPN service")
mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
connectionScope = CoroutineScope(SupervisorJob() + Dispatchers.IO + connectionExceptionHandler)
2024-03-04 18:08:55 +03:00
loadServerData()
2023-11-24 21:51:09 +03:00
launchProtocolStateHandler()
networkState = NetworkState(this, ::reconnect)
2024-05-12 18:04:14 +03:00
trafficStats = TrafficStats()
registerBroadcastReceivers()
2022-02-09 15:23:20 +03:00
}
2021-10-09 20:17:19 +03:00
2023-11-24 21:51:09 +03:00
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val isAlwaysOn = intent != null && intent.action == SERVICE_INTERFACE
2023-11-24 21:51:09 +03:00
if (isAlwaysOn) {
2024-01-20 16:40:12 +03:00
Log.d(TAG, "Start service via Always-on")
2024-03-04 18:08:55 +03:00
connect()
2023-11-24 21:51:09 +03:00
} else if (intent?.getBooleanExtra(AFTER_PERMISSION_CHECK, false) == true) {
2024-01-20 16:40:12 +03:00
Log.d(TAG, "Start service after permission check")
2024-03-04 18:08:55 +03:00
connect()
2023-11-24 21:51:09 +03:00
} else {
2024-01-20 16:40:12 +03:00
Log.d(TAG, "Start service")
2024-03-04 18:08:55 +03:00
connect(intent?.getStringExtra(MSG_VPN_CONFIG))
}
2024-05-12 18:04:14 +03:00
ServiceCompat.startForeground(
2024-06-18 20:46:21 +03:00
this, NOTIFICATION_ID,
serviceNotification.buildNotification(serverName, vpnProto?.label, protocolState.value),
2024-05-12 18:04:14 +03:00
foregroundServiceTypeCompat
)
2023-11-24 21:51:09 +03:00
return START_REDELIVER_INTENT
}
2021-09-30 15:56:48 +03:00
2023-11-24 21:51:09 +03:00
override fun onBind(intent: Intent?): IBinder? {
Log.d(TAG, "onBind by $intent")
if (intent?.action == SERVICE_INTERFACE) return super.onBind(intent)
2023-11-24 21:51:09 +03:00
isServiceBound = true
return vpnServiceMessenger.binder
2022-02-09 15:23:20 +03:00
}
2021-10-18 16:51:45 +03:00
2023-11-24 21:51:09 +03:00
override fun onUnbind(intent: Intent?): Boolean {
Log.d(TAG, "onUnbind by $intent")
if (intent?.action != SERVICE_INTERFACE) {
2024-03-04 18:08:55 +03:00
if (clientMessengers.isEmpty()) {
isServiceBound = false
if (isUnknown || isDisconnected) stopService()
}
2022-04-26 23:49:20 +05:30
}
2023-12-05 16:57:53 +03:00
return true
}
override fun onRebind(intent: Intent?) {
Log.d(TAG, "onRebind by $intent")
if (intent?.action != SERVICE_INTERFACE) {
2023-12-05 16:57:53 +03:00
isServiceBound = true
}
super.onRebind(intent)
2022-02-09 15:23:20 +03:00
}
2021-10-18 16:51:45 +03:00
2023-11-24 21:51:09 +03:00
override fun onRevoke() {
2024-01-20 16:40:12 +03:00
Log.d(TAG, "onRevoke")
// Calls to onRevoke() method may not happen on the main thread of the process
2023-11-24 21:51:09 +03:00
mainScope.launch {
disconnect()
2021-09-30 15:56:48 +03:00
}
2022-02-09 15:23:20 +03:00
}
2023-11-24 21:51:09 +03:00
override fun onDestroy() {
2024-01-20 16:40:12 +03:00
Log.d(TAG, "Destroy service")
2024-05-12 18:04:14 +03:00
unregisterBroadcastReceivers()
2023-12-04 18:23:08 +03:00
runBlocking {
disconnect()
disconnectionJob?.join()
2023-12-04 18:23:08 +03:00
}
connectionScope.cancel()
2023-11-24 21:51:09 +03:00
mainScope.cancel()
super.onDestroy()
2022-02-09 15:23:20 +03:00
}
2021-10-13 18:33:43 +03:00
private fun stopService() {
2024-01-20 16:40:12 +03:00
Log.d(TAG, "Stop service")
// the coroutine below will be canceled during the onDestroy call
mainScope.launch {
delay(STOP_SERVICE_TIMEOUT)
Log.w(TAG, "Stop service timeout, kill process")
Process.killProcess(Process.myPid())
}
stopSelf()
}
2024-05-12 18:04:14 +03:00
private fun registerBroadcastReceivers() {
Log.d(TAG, "Register broadcast receivers")
2024-06-18 20:46:21 +03:00
controlReceiver = registerBroadcastReceiver(
arrayOf(ACTION_CONNECT, ACTION_DISCONNECT), ContextCompat.RECEIVER_NOT_EXPORTED
) {
it?.action?.let { action ->
2024-10-18 12:52:24 +03:00
Log.v(TAG, "Broadcast request received: $action")
2024-06-18 20:46:21 +03:00
when (action) {
ACTION_CONNECT -> connect()
ACTION_DISCONNECT -> disconnect()
else -> Log.w(TAG, "Unknown action received: $action")
}
}
2024-05-12 18:04:14 +03:00
}
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
registerBroadcastReceiver(
arrayOf(
NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED,
NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED
)
) {
val state = it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)
2024-10-18 12:52:24 +03:00
Log.v(TAG, "Notification state changed: ${it?.action}, blocked = $state")
2024-05-12 18:04:14 +03:00
if (state == false) {
enableNotification()
} else {
disableNotification()
}
}
} else null
registerScreenStateBroadcastReceivers()
}
private fun registerScreenStateBroadcastReceivers() {
if (serviceNotification.isNotificationEnabled()) {
Log.d(TAG, "Register screen state broadcast receivers")
screenOnReceiver = registerBroadcastReceiver(Intent.ACTION_SCREEN_ON) {
if (isConnected && serviceNotification.isNotificationEnabled()) startTrafficStatsUpdateJob()
}
screenOffReceiver = registerBroadcastReceiver(Intent.ACTION_SCREEN_OFF) {
stopTrafficStatsUpdateJob()
}
}
}
private fun unregisterScreenStateBroadcastReceivers() {
Log.d(TAG, "Unregister screen state broadcast receivers")
unregisterBroadcastReceiver(screenOnReceiver)
unregisterBroadcastReceiver(screenOffReceiver)
screenOnReceiver = null
screenOffReceiver = null
}
private fun unregisterBroadcastReceivers() {
Log.d(TAG, "Unregister broadcast receivers")
2024-06-18 20:46:21 +03:00
unregisterBroadcastReceiver(controlReceiver)
2024-05-12 18:04:14 +03:00
unregisterBroadcastReceiver(notificationStateReceiver)
unregisterScreenStateBroadcastReceivers()
2024-06-18 20:46:21 +03:00
controlReceiver = null
2024-05-12 18:04:14 +03:00
notificationStateReceiver = null
}
2022-02-09 15:23:20 +03:00
/**
2023-11-24 21:51:09 +03:00
* Methods responsible for processing VPN connection
2022-02-09 15:23:20 +03:00
*/
2023-11-24 21:51:09 +03:00
private fun launchProtocolStateHandler() {
mainScope.launch {
2024-03-04 18:08:55 +03:00
// drop first default UNKNOWN state
protocolState.drop(1).collect { protocolState ->
2024-01-20 16:40:12 +03:00
Log.d(TAG, "Protocol state changed: $protocolState")
2024-05-12 18:04:14 +03:00
2024-06-18 20:46:21 +03:00
serviceNotification.updateNotification(serverName, vpnProto?.label, protocolState)
2024-05-12 18:04:14 +03:00
clientMessengers.send {
ServiceEvent.STATUS_CHANGED.packToMessage {
putStatus(protocolState)
}
}
2024-06-18 20:46:21 +03:00
VpnStateStore.store { VpnState(protocolState, serverName, serverIndex, vpnProto) }
2024-05-12 18:04:14 +03:00
2023-11-24 21:51:09 +03:00
when (protocolState) {
CONNECTED -> {
networkState.bindNetworkListener()
2024-05-12 18:04:14 +03:00
// if (isActivityConnected) launchSendingStatistics()
launchTrafficStatsUpdate()
2023-11-24 21:51:09 +03:00
}
2021-09-30 15:56:48 +03:00
2023-11-24 21:51:09 +03:00
DISCONNECTED -> {
networkState.unbindNetworkListener()
2024-05-12 18:04:14 +03:00
stopTrafficStatsUpdateJob()
// stopSendingStatistics()
if (!isServiceBound) stopService()
2021-10-23 23:06:50 +03:00
}
2023-11-24 21:51:09 +03:00
2023-11-29 17:08:53 +03:00
DISCONNECTING -> {
networkState.unbindNetworkListener()
2024-05-12 18:04:14 +03:00
stopTrafficStatsUpdateJob()
// stopSendingStatistics()
}
RECONNECTING -> {
2024-05-12 18:04:14 +03:00
stopTrafficStatsUpdateJob()
// stopSendingStatistics()
2023-11-29 17:08:53 +03:00
}
CONNECTING, UNKNOWN -> {}
}
}
}
}
2024-05-12 18:04:14 +03:00
/* @MainThread
2023-11-29 17:08:53 +03:00
private fun launchSendingStatistics() {
2024-05-12 18:04:14 +03:00
if (isServiceBound && isConnected) {
2023-11-29 17:08:53 +03:00
statisticsSendingJob = mainScope.launch {
while (true) {
clientMessenger.send {
ServiceEvent.STATISTICS_UPDATE.packToMessage {
putStatistics(protocol?.statistics ?: Statistics.EMPTY_STATISTICS)
}
}
delay(STATISTICS_SENDING_TIMEOUT)
2021-10-23 23:06:50 +03:00
}
}
2024-05-12 18:04:14 +03:00
}
2022-02-09 15:23:20 +03:00
}
2023-10-16 12:05:35 -04:00
2023-11-29 17:08:53 +03:00
@MainThread
private fun stopSendingStatistics() {
statisticsSendingJob?.cancel()
2024-05-12 18:04:14 +03:00
} */
@MainThread
private fun enableNotification() {
registerScreenStateBroadcastReceivers()
2024-06-18 20:46:21 +03:00
serviceNotification.updateNotification(serverName, vpnProto?.label, protocolState.value)
2024-05-12 18:04:14 +03:00
launchTrafficStatsUpdate()
}
@MainThread
private fun disableNotification() {
unregisterScreenStateBroadcastReceivers()
stopTrafficStatsUpdateJob()
}
@MainThread
private fun launchTrafficStatsUpdate() {
stopTrafficStatsUpdateJob()
if (isConnected &&
serviceNotification.isNotificationEnabled() &&
getSystemService<PowerManager>()?.isInteractive != false
) {
2024-10-18 12:52:24 +03:00
Log.v(TAG, "Launch traffic stats update")
2024-05-12 18:04:14 +03:00
trafficStats.reset()
startTrafficStatsUpdateJob()
}
}
@MainThread
private fun startTrafficStatsUpdateJob() {
if (trafficStatsUpdateJob == null && trafficStats.isSupported()) {
Log.d(TAG, "Start traffic stats update")
trafficStatsUpdateJob = mainScope.launch {
while (true) {
trafficStats.getSpeed().let { speed ->
if (isConnected) {
serviceNotification.updateSpeed(speed)
}
}
delay(TRAFFIC_STATS_UPDATE_TIMEOUT)
}
}
}
}
@MainThread
private fun stopTrafficStatsUpdateJob() {
Log.d(TAG, "Stop traffic stats update")
trafficStatsUpdateJob?.cancel()
trafficStatsUpdateJob = null
2023-11-29 17:08:53 +03:00
}
2023-11-24 21:51:09 +03:00
@MainThread
2024-03-04 18:08:55 +03:00
private fun connect(vpnConfig: String? = null) {
if (vpnConfig == null) {
connectToVpn(Prefs.load(PREFS_CONFIG_KEY))
} else {
Prefs.save(PREFS_CONFIG_KEY, vpnConfig)
connectToVpn(vpnConfig)
}
}
@MainThread
private fun connectToVpn(vpnConfig: String) {
2023-11-24 21:51:09 +03:00
if (isConnected || protocolState.value == CONNECTING) return
2023-10-17 14:35:13 -04:00
2024-01-20 16:40:12 +03:00
Log.d(TAG, "Start VPN connection")
2023-11-24 21:51:09 +03:00
val config = parseConfigToJson(vpnConfig)
2024-03-04 18:08:55 +03:00
saveServerData(config)
2023-11-24 21:51:09 +03:00
if (config == null) {
onError("Invalid VPN config")
protocolState.value = DISCONNECTED
return
2022-02-09 15:23:20 +03:00
}
2023-10-17 14:35:13 -04:00
2024-06-18 20:46:21 +03:00
try {
vpnProto = VpnProto.get(config.getString("protocol"))
} catch (e: Exception) {
onError("Invalid VPN config: ${e.message}")
protocolState.value = DISCONNECTED
return
}
protocolState.value = CONNECTING
2023-11-24 21:51:09 +03:00
if (!checkPermission()) {
protocolState.value = DISCONNECTED
return
2022-02-09 15:23:20 +03:00
}
2023-10-17 14:35:13 -04:00
connectionJob = connectionScope.launch {
2023-11-24 21:51:09 +03:00
disconnectionJob?.join()
disconnectionJob = null
2024-06-18 20:46:21 +03:00
vpnProto?.protocol?.let { protocol ->
protocol.initialize(applicationContext, protocolState, ::onError)
protocol.startVpn(config, Builder(), ::protect)
}
2023-10-07 09:01:29 -04:00
}
2022-02-09 15:23:20 +03:00
}
2021-09-30 15:56:48 +03:00
2023-11-24 21:51:09 +03:00
@MainThread
private fun disconnect() {
if (isUnknown || isDisconnected || protocolState.value == DISCONNECTING) return
2022-04-01 10:05:58 +05:30
2024-01-20 16:40:12 +03:00
Log.d(TAG, "Stop VPN connection")
2023-11-24 21:51:09 +03:00
protocolState.value = DISCONNECTING
disconnectionJob = connectionScope.launch {
connectionJob?.cancelAndJoin()
2023-11-24 21:51:09 +03:00
connectionJob = null
2024-06-18 20:46:21 +03:00
vpnProto?.protocol?.stopVpn()
try {
withTimeout(DISCONNECT_TIMEOUT) {
// waiting for disconnect state
protocolState.first { it == DISCONNECTED }
}
} catch (e: TimeoutCancellationException) {
Log.w(TAG, "Disconnect timeout")
stopService()
}
2023-11-24 21:51:09 +03:00
}
2022-02-09 15:23:20 +03:00
}
2021-10-09 20:17:19 +03:00
@MainThread
private fun reconnect() {
if (!isConnected) return
2024-01-20 16:40:12 +03:00
Log.d(TAG, "Reconnect VPN")
protocolState.value = RECONNECTING
connectionJob = connectionScope.launch {
vpnProto?.protocol?.reconnectVpn(Builder(), ::protect)
}
}
2023-11-24 21:51:09 +03:00
/**
* Utils methods
*/
private fun onError(msg: String) {
Log.e(TAG, msg)
mainScope.launch {
2024-03-04 18:08:55 +03:00
clientMessengers.send {
ServiceEvent.ERROR.packToMessage {
2024-03-04 18:08:55 +03:00
putString(MSG_ERROR, msg)
}
}
2022-02-09 15:23:20 +03:00
}
}
2024-03-04 18:08:55 +03:00
private fun parseConfigToJson(vpnConfig: String): JSONObject? =
if (vpnConfig.isBlank()) {
2023-11-24 21:51:09 +03:00
null
2024-03-04 18:08:55 +03:00
} else {
try {
JSONObject(vpnConfig)
} catch (e: JSONException) {
onError("Invalid VPN config json format: ${e.message}")
null
}
2022-04-26 23:49:20 +05:30
}
2023-11-24 21:51:09 +03:00
2024-03-04 18:08:55 +03:00
private fun saveServerData(config: JSONObject?) {
serverName = config?.opt("description") as String?
serverIndex = config?.opt("serverIndex") as Int? ?: -1
2024-05-12 18:04:14 +03:00
Log.d(TAG, "Save server data: ($serverIndex, $serverName)")
2024-03-04 18:08:55 +03:00
Prefs.save(PREFS_SERVER_NAME, serverName)
Prefs.save(PREFS_SERVER_INDEX, serverIndex)
}
private fun loadServerData() {
serverName = Prefs.load<String>(PREFS_SERVER_NAME).ifBlank { null }
if (serverName != null) serverIndex = Prefs.load(PREFS_SERVER_INDEX)
2024-05-12 18:04:14 +03:00
Log.d(TAG, "Load server data: ($serverIndex, $serverName)")
2024-03-04 18:08:55 +03:00
}
2023-11-24 21:51:09 +03:00
private fun checkPermission(): Boolean =
if (prepare(applicationContext) != null) {
Intent(this, VpnRequestActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
2024-06-18 20:46:21 +03:00
putExtra(EXTRA_PROTOCOL, vpnProto)
2023-11-24 21:51:09 +03:00
}.also {
startActivity(it)
2022-04-26 23:49:20 +05:30
}
2023-11-24 21:51:09 +03:00
false
} else {
true
2022-04-26 23:49:20 +05:30
}
2024-03-04 18:08:55 +03:00
companion object {
2024-06-18 20:46:21 +03:00
fun isRunning(context: Context, processName: String): Boolean =
2024-05-12 18:04:14 +03:00
context.getSystemService<ActivityManager>()!!.runningAppProcesses.any {
2024-06-18 20:46:21 +03:00
it.processName == processName && it.importance <= IMPORTANCE_FOREGROUND_SERVICE
2024-05-12 18:04:14 +03:00
}
2024-03-04 18:08:55 +03:00
}
2022-02-09 15:23:20 +03:00
}