Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: service handling #1944

Merged
merged 1 commit into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class BootReceiver : BroadcastReceiver() {
applicationScope.launch {
if (settingsRepository.isAutoStartEnabled()) {
if (tunnelManager.getState() != Tunnel.State.Down) return@launch
tunnelManager.start(true)
tunnelManager.start()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class VpnQuickTile : TileService(), LifecycleOwner {
lifecycleScope.launch {
when (tunnelManager.getState()) {
Tunnel.State.Up -> tunnelManager.stop()
Tunnel.State.Down -> tunnelManager.start(true)
Tunnel.State.Down -> tunnelManager.start()
else -> Unit
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import net.nymtech.nymvpn.util.extensions.toUserMessage
import net.nymtech.vpn.backend.Backend
import net.nymtech.vpn.backend.Tunnel
import net.nymtech.vpn.model.BackendEvent
import net.nymtech.vpn.util.exceptions.NymVpnInitializeException
import net.nymtech.vpn.util.exceptions.BackendException
import nym_vpn_lib.AccountLinks
import nym_vpn_lib.AccountStateSummary
import nym_vpn_lib.BandwidthEvent
Expand Down Expand Up @@ -73,7 +73,7 @@ class NymTunnelManager @Inject constructor(
}
}

override suspend fun start(fromBackground: Boolean) {
override suspend fun start() {
runCatching {
// clear any error states
emitBackendUiEvent(null)
Expand All @@ -86,12 +86,12 @@ class NymTunnelManager @Inject constructor(
backendEvent = ::onBackendEvent,
credentialMode = settingsRepository.isCredentialMode(),
)
backend.get().start(tunnel, fromBackground, context.toUserAgent())
backend.get().start(tunnel, context.toUserAgent())
}.onFailure {
if (it is NymVpnInitializeException) {
if (it is BackendException) {
when (it) {
is NymVpnInitializeException.VpnAlreadyRunning -> Timber.w("Vpn already running")
is NymVpnInitializeException.VpnPermissionDenied -> launchVpnPermissionNotification()
is BackendException.VpnAlreadyRunning -> Timber.w("Vpn already running")
is BackendException.VpnPermissionDenied -> launchVpnPermissionNotification()
}
} else {
Timber.e(it)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import nym_vpn_lib.AccountStateSummary

interface TunnelManager {
suspend fun stop()
suspend fun start(fromBackground: Boolean = true)
suspend fun start()
suspend fun storeMnemonic(mnemonic: String)
suspend fun isMnemonicStored(): Boolean
suspend fun removeMnemonic()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ class ShortcutActivity : ComponentActivity() {
when (intent.action) {
ShortcutAction.START_MIXNET.name -> {
settingsRepository.setVpnMode(Tunnel.Mode.FIVE_HOP_MIXNET)
tunnelManager.start(true)
tunnelManager.start()
}
ShortcutAction.START_WG.name -> {
settingsRepository.setVpnMode(Tunnel.Mode.TWO_HOP_MIXNET)
tunnelManager.start(true)
tunnelManager.start()
}
ShortcutAction.STOP.name -> tunnelManager.stop()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ constructor(
}

fun onConnect() = viewModelScope.launch {
tunnelManager.start(false)
tunnelManager.start()
}

fun onDisconnect() = viewModelScope.launch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ interface Backend {

suspend fun removeMnemonic()

suspend fun start(tunnel: Tunnel, background: Boolean, userAgent: UserAgent)
suspend fun start(tunnel: Tunnel, userAgent: UserAgent)

suspend fun stop()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,22 @@ import com.getkeepsafe.relinker.ReLinker.LoadListener
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.nymtech.vpn.model.BackendEvent
import net.nymtech.vpn.model.Country
import net.nymtech.vpn.service.network.NetworkConnectivityService
import net.nymtech.vpn.service.network.NetworkService
import net.nymtech.vpn.service.network.NetworkStatus
import net.nymtech.vpn.util.Action
import net.nymtech.vpn.util.Constants
import net.nymtech.vpn.util.Constants.LOG_LEVEL
import net.nymtech.vpn.util.LifecycleVpnService
import net.nymtech.vpn.util.NotificationManager
import net.nymtech.vpn.util.SingletonHolder
import net.nymtech.vpn.util.exceptions.NymVpnInitializeException
import net.nymtech.vpn.util.exceptions.BackendException
import net.nymtech.vpn.util.extensions.asTunnelState
import net.nymtech.vpn.util.extensions.startServiceByClass
import net.nymtech.vpn.util.extensions.waitForTrue
import nym_vpn_lib.AccountLinks
import nym_vpn_lib.AccountStateSummary
import nym_vpn_lib.AndroidTunProvider
Expand All @@ -53,7 +53,6 @@ import nym_vpn_lib.waitForRegisterDevice
import nym_vpn_lib.waitForUpdateAccount
import nym_vpn_lib.waitForUpdateDevice
import timber.log.Timber
import java.util.concurrent.TimeoutException
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.properties.Delegates

Expand Down Expand Up @@ -100,6 +99,7 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat
override suspend fun init(environment: Tunnel.Environment, credentialMode: Boolean?) {
return withContext(ioDispatcher) {
runCatching {
Os.setenv("RUST_LOG", LOG_LEVEL, true)
initLogger(null)
initEnvironment(environment)
nym_vpn_lib.configureLib(storagePath, credentialMode)
Expand All @@ -110,12 +110,12 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat
}
}

private fun onNetworkStateChange(networkStatus: NetworkStatus) {
private fun onNetworkStatusChange(networkStatus: NetworkStatus) {
this.networkStatus = networkStatus
updateObservers()
}

private fun addObserver(observer: ConnectivityObserver) {
private fun addConnectivityObserver(observer: ConnectivityObserver) {
observers.add(observer)
updateObservers()
}
Expand All @@ -136,26 +136,9 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat
observers.remove(observer)
}

suspend fun waitForInit() {
runCatching {
val startTime = System.currentTimeMillis()
val timeout = 5000L
while (System.currentTimeMillis() - startTime < timeout) {
if (initialized.get()) {
return
}
delay(10)
}
throw TimeoutException("Failed to initialize backend")
}.onFailure {
Timber.e(it)
}
}

private suspend fun initEnvironment(environment: Tunnel.Environment) {
withContext(ioDispatcher) {
runCatching {
Os.setenv("RUST_LOG", LOG_LEVEL, true)
initEnvironment(environment.networkName())
}.onFailure {
Timber.w("Failed to setup environment, defaulting to bundle mainnet")
Expand All @@ -167,15 +150,15 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat
@Throws(VpnException::class)
override suspend fun getAccountSummary(): AccountStateSummary {
return withContext(ioDispatcher) {
waitForInit()
initialized.waitForTrue()
nym_vpn_lib.getAccountState()
}
}

@Throws(VpnException::class)
override suspend fun getAccountLinks(): AccountLinks {
return withContext(ioDispatcher) {
waitForInit()
initialized.waitForTrue()
nym_vpn_lib.getAccountLinks(getCurrentLocaleCountryCode())
}
}
Expand All @@ -194,7 +177,7 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat
override suspend fun storeMnemonic(mnemonic: String) {
withContext(ioDispatcher) {
try {
waitForInit()
initialized.waitForTrue()
storeAccountMnemonic(mnemonic)
waitForUpdateAccount()
waitForUpdateDevice()
Expand All @@ -209,22 +192,22 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat
@Throws(VpnException::class)
override suspend fun isMnemonicStored(): Boolean {
return withContext(ioDispatcher) {
waitForInit()
initialized.waitForTrue()
isAccountMnemonicStored()
}
}

override suspend fun getDeviceIdentity(): String {
return withContext(ioDispatcher) {
waitForInit()
initialized.waitForTrue()
nym_vpn_lib.getDeviceIdentity()
}
}

@Throws(VpnException::class)
override suspend fun removeMnemonic() {
withContext(ioDispatcher) {
waitForInit()
initialized.waitForTrue()
forgetAccount()
}
}
Expand All @@ -239,44 +222,45 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat

override suspend fun getSystemMessages(): List<SystemMessage> {
return withContext(ioDispatcher) {
waitForInit()
initialized.waitForTrue()
nym_vpn_lib.getSystemMessages()
}
}

@Throws(NymVpnInitializeException::class)
override suspend fun start(tunnel: Tunnel, background: Boolean, userAgent: UserAgent) {
override suspend fun start(tunnel: Tunnel, userAgent: UserAgent) {
withContext(ioDispatcher) {
waitForInit()
initialized.waitForTrue()
val state = getState()
// TODO handle changes to tunnel while tunnel is up in future
if (state != Tunnel.State.Down) throw NymVpnInitializeException.VpnAlreadyRunning()
if (state != Tunnel.State.Down) throw BackendException.VpnAlreadyRunning()
[email protected] = tunnel
onStateChange(Tunnel.State.InitializingClient)
if (android.net.VpnService.prepare(context) != null) throw NymVpnInitializeException.VpnPermissionDenied()
startVpn(tunnel, background, userAgent)
if (android.net.VpnService.prepare(context) != null) throw BackendException.VpnPermissionDenied()
startVpn(tunnel, userAgent)
}
}

private suspend fun startVpn(tunnel: Tunnel, background: Boolean, userAgent: UserAgent) {
private suspend fun startServices() {
if (!vpnService.isCompleted) context.startServiceByClass(VpnService::class.java)
if (!stateMachineService.isCompleted) context.startServiceByClass(StateMachineService::class.java)
val vpnService = vpnService.await()
val stateMachineService = stateMachineService.await()
vpnService.setOwner(this)
stateMachineService.setOwner(this)
}

private suspend fun startVpn(tunnel: Tunnel, userAgent: UserAgent) {
withContext(ioDispatcher) {
if (!initialized.get()) init(tunnel.environment, tunnel.credentialMode)
if (!vpnService.isCompleted) context.startServiceByClass(background, VpnService::class.java)
context.startServiceByClass(background, StateMachineService::class.java)
val vpnService = vpnService.await()
val stateMachineService = stateMachineService.await()
val backend = this@NymBackend
vpnService.setOwner(backend)
stateMachineService.setOwner(backend)
initialized.waitForTrue()
startServices()
try {
startVpn(
VpnConfig(
tunnel.entryPoint,
tunnel.exitPoint,
isTwoHop(tunnel.mode),
vpnService,
tunnel.mode.isTwoHop(),
vpnService.await(),
storagePath,
backend,
this@NymBackend,
tunnel.credentialMode,
null,
userAgent,
Expand All @@ -297,7 +281,7 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat
@OptIn(ExperimentalCoroutinesApi::class)
override suspend fun stop() {
withContext(ioDispatcher) {
waitForInit()
initialized.waitForTrue()
runCatching {
stopVpn()
vpnService.getCompleted().stopSelf()
Expand All @@ -312,11 +296,6 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat
return state
}

private fun isTwoHop(mode: Tunnel.Mode): Boolean = when (mode) {
Tunnel.Mode.TWO_HOP_MIXNET -> true
else -> false
}

override fun onEvent(event: TunnelEvent) {
when (event) {
is TunnelEvent.MixnetState -> {
Expand All @@ -336,6 +315,8 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat

internal class StateMachineService : LifecycleService() {

private lateinit var networkService: NetworkService

private var owner: NymBackend? = null
private var wakeLock: PowerManager.WakeLock? = null

Expand All @@ -349,6 +330,8 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat
}

override fun onCreate() {
super.onCreate()
networkService = NetworkConnectivityService(this)
stateMachineService.complete(this)
val notificationManager = NotificationManager.getInstance(this)
ServiceCompat.startForeground(
Expand All @@ -357,14 +340,15 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat
notificationManager.createStateMachineNotification(),
SYSTEM_EXEMPT_SERVICE_TYPE_ID,
)
lifecycleScope.launch {
NetworkConnectivityService(this@StateMachineService).networkStatus.collect {
Timber.d("New network event: $it")
owner?.onNetworkStateChange(it)
}
}
startNetworkStatusMonitor()
initWakeLock()
super.onCreate()
}

private fun startNetworkStatusMonitor() = lifecycleScope.launch {
networkService.networkStatus.collect {
Timber.d("New network event: $it")
owner?.onNetworkStatusChange(it)
}
}

override fun onDestroy() {
Expand Down Expand Up @@ -403,17 +387,13 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat
private var startId by Delegates.notNull<Int>()
private val notificationManager = NotificationManager.getInstance(this)

companion object {
private const val VPN_NOTIFICATION_ID = 222
}

private val builder: Builder
get() = Builder()

override fun onCreate() {
super.onCreate()
Timber.d("Vpn service created")
vpnService.complete(this)
super.onCreate()
}

override fun onDestroy() {
Expand All @@ -427,11 +407,7 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat
this.startId = startId
vpnService.complete(this)
intent?.let {
if (it.action == Action.START_FOREGROUND.name) {
startForeground(startId, notificationManager.createVpnRunningNotification())
} else {
notificationManager.notify(notificationManager.createVpnRunningNotification(), VPN_NOTIFICATION_ID)
}
startForeground(startId, notificationManager.createVpnRunningNotification())
}
return super.onStartCommand(intent, flags, startId)
}
Expand Down Expand Up @@ -487,7 +463,7 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat
}

override fun addConnectivityObserver(observer: ConnectivityObserver) {
owner?.addObserver(observer)
owner?.addConnectivityObserver(observer)
}

override fun removeConnectivityObserver(observer: ConnectivityObserver) {
Expand Down
Loading
Loading