parent
09b669f54b
commit
bc811f74ef
|
@ -2,11 +2,13 @@ package com.zaneschepke.wireguardautotunnel.service.foreground
|
|||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.NetworkCapabilities
|
||||
import android.os.IBinder
|
||||
import android.os.PowerManager
|
||||
import androidx.core.app.ServiceCompat
|
||||
import androidx.lifecycle.LifecycleService
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.wireguard.android.util.RootShell
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.data.domain.Settings
|
||||
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
|
||||
|
@ -23,6 +25,7 @@ import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
|||
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
|
||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.cancelWithMessage
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.getCurrentWifiName
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.isMatchingToWildcardList
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.isReachable
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.onNotRunning
|
||||
|
@ -46,6 +49,9 @@ import javax.inject.Provider
|
|||
class AutoTunnelService : LifecycleService() {
|
||||
private val foregroundId = 122
|
||||
|
||||
@Inject
|
||||
lateinit var rootShell: Provider<RootShell>
|
||||
|
||||
@Inject
|
||||
lateinit var wifiService: NetworkService<WifiService>
|
||||
|
||||
|
@ -397,6 +403,14 @@ class AutoTunnelService : LifecycleService() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateWifi(connected: Boolean) {
|
||||
autoTunnelStateFlow.update {
|
||||
it.copy(
|
||||
isWifiConnected = connected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun watchForEthernetConnectivityChanges() {
|
||||
withContext(ioDispatcher) {
|
||||
Timber.i("Starting ethernet data watcher")
|
||||
|
@ -428,21 +442,13 @@ class AutoTunnelService : LifecycleService() {
|
|||
when (status) {
|
||||
is NetworkStatus.Available -> {
|
||||
Timber.i("Gained Wi-Fi connection")
|
||||
autoTunnelStateFlow.update {
|
||||
it.copy(
|
||||
isWifiConnected = true,
|
||||
)
|
||||
}
|
||||
updateWifi(true)
|
||||
}
|
||||
|
||||
is NetworkStatus.CapabilitiesChanged -> {
|
||||
Timber.i("Wifi capabilities changed")
|
||||
autoTunnelStateFlow.update {
|
||||
it.copy(
|
||||
isWifiConnected = true,
|
||||
)
|
||||
}
|
||||
val ssid = wifiService.getNetworkName(status.networkCapabilities)
|
||||
updateWifi(true)
|
||||
val ssid = getWifiSSID(status.networkCapabilities)
|
||||
ssid?.let { name ->
|
||||
if (name.contains(Constants.UNREADABLE_SSID)) {
|
||||
Timber.w("SSID unreadable: missing permissions")
|
||||
|
@ -459,11 +465,7 @@ class AutoTunnelService : LifecycleService() {
|
|||
}
|
||||
|
||||
is NetworkStatus.Unavailable -> {
|
||||
autoTunnelStateFlow.update {
|
||||
it.copy(
|
||||
isWifiConnected = false,
|
||||
)
|
||||
}
|
||||
updateWifi(false)
|
||||
Timber.i("Lost Wi-Fi connection")
|
||||
}
|
||||
}
|
||||
|
@ -471,6 +473,16 @@ class AutoTunnelService : LifecycleService() {
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun getWifiSSID(networkCapabilities: NetworkCapabilities): String? {
|
||||
return withContext(ioDispatcher) {
|
||||
try {
|
||||
rootShell.get().getCurrentWifiName()
|
||||
} catch (_: Exception) {
|
||||
wifiService.getNetworkName(networkCapabilities)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getMobileDataTunnel(): TunnelConfig? {
|
||||
return appDataRepository.tunnels.findByMobileDataTunnel().firstOrNull()
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.zaneschepke.wireguardautotunnel.ui.screens.settings
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context.POWER_SERVICE
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
|
@ -101,7 +100,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
|
|||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val isRunningOnTv = context.isRunningOnTv()
|
||||
|
||||
val kernelSupport by viewModel.kernelSupport.collectAsStateWithLifecycle()
|
||||
val settingsUiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
val fineLocationState = rememberPermissionState(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
var currentText by remember { mutableStateOf("") }
|
||||
|
@ -115,10 +114,6 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
|
|||
val screenPadding = 5.dp
|
||||
val fillMaxWidth = .85f
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.checkKernelSupport()
|
||||
}
|
||||
|
||||
LaunchedEffect(uiState.settings.trustedNetworkSSIDs) {
|
||||
currentText = ""
|
||||
}
|
||||
|
@ -127,7 +122,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
|
|||
rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult(),
|
||||
) { result: ActivityResult ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
result.data
|
||||
// Handle the Intent
|
||||
}
|
||||
|
@ -262,6 +257,18 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
|
|||
)
|
||||
}
|
||||
|
||||
fun onAutoTunnelWifiChecked() {
|
||||
when (false) {
|
||||
isBackgroundLocationGranted -> showLocationDialog = true
|
||||
fineLocationState.status.isGranted -> showLocationDialog = true
|
||||
viewModel.isLocationEnabled(context) ->
|
||||
showLocationServicesAlertDialog = true
|
||||
else -> {
|
||||
viewModel.onToggleTunnelOnWifi()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Top,
|
||||
|
@ -311,18 +318,8 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
|
|||
checked = uiState.settings.isTunnelOnWifiEnabled,
|
||||
padding = screenPadding,
|
||||
onCheckChanged = { checked ->
|
||||
if (!checked) viewModel.onToggleTunnelOnWifi()
|
||||
if (checked) {
|
||||
when (false) {
|
||||
isBackgroundLocationGranted -> showLocationDialog = true
|
||||
fineLocationState.status.isGranted -> showLocationDialog = true
|
||||
viewModel.isLocationEnabled(context) ->
|
||||
showLocationServicesAlertDialog = true
|
||||
else -> {
|
||||
viewModel.onToggleTunnelOnWifi()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!checked || settingsUiState.isRooted) viewModel.onToggleTunnelOnWifi().also { return@ConfigurationToggle }
|
||||
onAutoTunnelWifiChecked()
|
||||
},
|
||||
modifier =
|
||||
if (uiState.settings.isAutoTunnelEnabled) {
|
||||
|
@ -491,7 +488,7 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel(), appViewModel:
|
|||
uiState.settings.isAutoTunnelEnabled ||
|
||||
uiState.settings.isAlwaysOnVpnEnabled ||
|
||||
(uiState.vpnState.status == TunnelState.UP) ||
|
||||
!kernelSupport
|
||||
!settingsUiState.isKernelAvailable
|
||||
),
|
||||
checked = uiState.settings.isKernelEnabled,
|
||||
padding = screenPadding,
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package com.zaneschepke.wireguardautotunnel.ui.screens.settings
|
||||
|
||||
data class SettingsUiState(
|
||||
val isRooted: Boolean = false,
|
||||
val isKernelAvailable: Boolean = false,
|
||||
)
|
|
@ -13,13 +13,14 @@ import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
|
|||
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
|
||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||
import com.zaneschepke.wireguardautotunnel.util.FileUtils
|
||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -38,8 +39,16 @@ constructor(
|
|||
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _kernelSupport = MutableStateFlow(false)
|
||||
val kernelSupport = _kernelSupport.asStateFlow()
|
||||
private val _uiState = MutableStateFlow(SettingsUiState())
|
||||
val uiState = _uiState.onStart {
|
||||
_uiState.update {
|
||||
it.copy(isKernelAvailable = isKernelSupported(), isRooted = isRooted())
|
||||
}
|
||||
}.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(Constants.SUBSCRIPTION_TIMEOUT),
|
||||
SettingsUiState(),
|
||||
)
|
||||
private val settings = appDataRepository.settings.getSettingsFlow()
|
||||
.stateIn(viewModelScope, SharingStarted.Eagerly, Settings())
|
||||
|
||||
|
@ -211,13 +220,9 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun checkKernelSupport() = viewModelScope.launch {
|
||||
val kernelSupport =
|
||||
withContext(ioDispatcher) {
|
||||
WgQuickBackend.hasKernelSupport()
|
||||
}
|
||||
_kernelSupport.update {
|
||||
kernelSupport
|
||||
private suspend fun isKernelSupported(): Boolean {
|
||||
return withContext(ioDispatcher) {
|
||||
WgQuickBackend.hasKernelSupport()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -231,6 +236,17 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun isRooted(): Boolean {
|
||||
return try {
|
||||
withContext(ioDispatcher) {
|
||||
rootShell.get().start()
|
||||
}
|
||||
true
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun requestRoot(): Result<Unit> {
|
||||
return withContext(ioDispatcher) {
|
||||
kotlin.runCatching {
|
||||
|
|
|
@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import com.zaneschepke.logcatter.LogReader
|
||||
import com.zaneschepke.logcatter.model.LogMessage
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.module.MainDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||
|
@ -15,7 +16,6 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.launchShareFile
|
|||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Job
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.zaneschepke.wireguardautotunnel.util.extensions
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.wireguard.android.util.RootShell
|
||||
import com.wireguard.config.Peer
|
||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.HandshakeStatus
|
||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.TunnelStatistics
|
||||
|
@ -78,3 +79,9 @@ fun Config.toWgQuickString(): String {
|
|||
}
|
||||
return lines.joinToString(System.lineSeparator())
|
||||
}
|
||||
|
||||
fun RootShell.getCurrentWifiName(): String? {
|
||||
val response = mutableListOf<String>()
|
||||
this.run(response, "dumpsys wifi | grep -o \"SSID: [^,]*\" | cut -d ' ' -f2- | tr -d '\"'")
|
||||
return response.lastOrNull()
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ pinLockCompose = "1.0.4"
|
|||
roomVersion = "2.6.1"
|
||||
timber = "5.0.1"
|
||||
tunnel = "1.2.1"
|
||||
androidGradlePlugin = "8.7.0"
|
||||
androidGradlePlugin = "8.7.1"
|
||||
kotlin = "2.0.21"
|
||||
ksp = "2.0.21-1.0.25"
|
||||
composeBom = "2024.09.03"
|
||||
|
|
Loading…
Reference in New Issue