fix: auto tunneling and backup
Fixes a bug where android backups can cause app crashes due to pin lock feature keystore. Fixes a bug where auto tunneling to SSID tunnel was not working correctly. Fixes a mobile data tunneling bug which was causing mobile data tunneling to not perform correctly. Additional strictmode improvements.
This commit is contained in:
parent
6448386f76
commit
1d74d0984e
|
@ -51,7 +51,7 @@
|
|||
</queries>
|
||||
<application
|
||||
android:name=".WireGuardAutoTunnel"
|
||||
android:allowBackup="true"
|
||||
android:allowBackup="false"
|
||||
android:banner="@drawable/ic_banner"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
|
|
|
@ -17,6 +17,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
|||
import dagger.hilt.components.SingletonComponent
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import javax.inject.Provider
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
|
@ -51,9 +52,9 @@ class TunnelModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideVpnService(
|
||||
amneziaBackend: org.amnezia.awg.backend.Backend,
|
||||
@Userspace userspaceBackend: Backend,
|
||||
@Kernel kernelBackend: Backend,
|
||||
amneziaBackend: Provider<org.amnezia.awg.backend.Backend>,
|
||||
@Userspace userspaceBackend: Provider<Backend>,
|
||||
@Kernel kernelBackend: Provider<Backend>,
|
||||
appDataRepository: AppDataRepository,
|
||||
@ApplicationScope applicationScope: CoroutineScope,
|
||||
@IoDispatcher ioDispatcher: CoroutineDispatcher
|
||||
|
|
|
@ -390,19 +390,14 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
|||
}
|
||||
|
||||
watcherState.isMobileDataConditionMet() -> {
|
||||
Timber.i("$autoTunnel - tunnel on on mobile data condition met")
|
||||
Timber.i("$autoTunnel - tunnel on mobile data condition met")
|
||||
val mobileDataTunnel = getMobileDataTunnel()
|
||||
val tunnel =
|
||||
mobileDataTunnel ?: appDataRepository.getPrimaryOrFirstTunnel()
|
||||
if (isTunnelDown()) return@collectLatest serviceManager.startVpnServiceForeground(
|
||||
context,
|
||||
tunnel?.id,
|
||||
)
|
||||
if (tunnelConfig?.isMobileDataTunnel == false && mobileDataTunnel != null) {
|
||||
Timber.i("$autoTunnel - tunnel connected on mobile data is not preferred condition met, switching to preferred")
|
||||
if (isTunnelDown() || tunnelConfig?.isMobileDataTunnel == false) {
|
||||
serviceManager.startVpnServiceForeground(
|
||||
context,
|
||||
mobileDataTunnel.id,
|
||||
tunnel?.id,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -417,8 +412,8 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
|||
tunnelConfig == null) {
|
||||
Timber.i("$autoTunnel - tunnel on ssid not associated with current tunnel condition met")
|
||||
getSsidTunnel(watcherState.currentNetworkSSID)?.let {
|
||||
Timber.i("Found tunnel associated with this SSID, bringing tunnel up")
|
||||
if (isTunnelDown()) serviceManager.startVpnServiceForeground(
|
||||
Timber.i("Found tunnel associated with this SSID, bringing tunnel up: ${it.name}")
|
||||
if (isTunnelDown() || tunnelConfig?.id != it.id) serviceManager.startVpnServiceForeground(
|
||||
context,
|
||||
it.id,
|
||||
)
|
||||
|
|
|
@ -27,13 +27,14 @@ import kotlinx.coroutines.withContext
|
|||
import org.amnezia.awg.backend.Tunnel
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
class WireGuardTunnel
|
||||
@Inject
|
||||
constructor(
|
||||
private val userspaceAmneziaBackend: org.amnezia.awg.backend.Backend,
|
||||
@Userspace private val userspaceBackend: Backend,
|
||||
@Kernel private val kernelBackend: Backend,
|
||||
private val userspaceAmneziaBackend: Provider<org.amnezia.awg.backend.Backend>,
|
||||
@Userspace private val userspaceBackend: Provider<Backend>,
|
||||
@Kernel private val kernelBackend: Provider<Backend>,
|
||||
private val appDataRepository: AppDataRepository,
|
||||
@ApplicationScope private val applicationScope: CoroutineScope,
|
||||
@IoDispatcher private val ioDispatcher: CoroutineDispatcher
|
||||
|
@ -44,7 +45,7 @@ constructor(
|
|||
|
||||
private var statsJob: Job? = null
|
||||
|
||||
private var backend: Backend = userspaceBackend
|
||||
private lateinit var backend: Backend;
|
||||
|
||||
private var backendIsWgUserspace = true
|
||||
|
||||
|
@ -52,15 +53,16 @@ constructor(
|
|||
|
||||
init {
|
||||
applicationScope.launch(ioDispatcher) {
|
||||
backend = userspaceBackend.get()
|
||||
appDataRepository.settings.getSettingsFlow().collect {
|
||||
if (it.isKernelEnabled && (backendIsWgUserspace || backendIsAmneziaUserspace)) {
|
||||
Timber.i("Setting kernel backend")
|
||||
backend = kernelBackend
|
||||
backend = kernelBackend.get()
|
||||
backendIsWgUserspace = false
|
||||
backendIsAmneziaUserspace = false
|
||||
} else if (!it.isKernelEnabled && !it.isAmneziaEnabled && !backendIsWgUserspace) {
|
||||
Timber.i("Setting WireGuard userspace backend")
|
||||
backend = userspaceBackend
|
||||
backend = userspaceBackend.get()
|
||||
backendIsWgUserspace = true
|
||||
backendIsAmneziaUserspace = false
|
||||
} else if (it.isAmneziaEnabled && !backendIsAmneziaUserspace) {
|
||||
|
@ -81,7 +83,8 @@ constructor(
|
|||
TunnelConfig.configFromAmQuick(it.wgQuick)
|
||||
}
|
||||
}
|
||||
val state = userspaceAmneziaBackend.setState(this, tunnelState.toAmState(), config)
|
||||
val state =
|
||||
userspaceAmneziaBackend.get().setState(this, tunnelState.toAmState(), config)
|
||||
TunnelState.from(state)
|
||||
} else {
|
||||
Timber.i("Using Wg backend")
|
||||
|
@ -156,7 +159,9 @@ constructor(
|
|||
}
|
||||
|
||||
override fun getState(): TunnelState {
|
||||
return if (backendIsAmneziaUserspace) TunnelState.from(userspaceAmneziaBackend.getState(this))
|
||||
return if (backendIsAmneziaUserspace) TunnelState.from(
|
||||
userspaceAmneziaBackend.get().getState(this),
|
||||
)
|
||||
else TunnelState.from(backend.getState(this))
|
||||
}
|
||||
|
||||
|
@ -187,7 +192,11 @@ constructor(
|
|||
private fun startTunnelStatisticsJob() = applicationScope.launch(ioDispatcher) {
|
||||
while (true) {
|
||||
if (backendIsAmneziaUserspace) {
|
||||
emitBackendStatistics(AmneziaStatistics(userspaceAmneziaBackend.getStatistics(this@WireGuardTunnel)))
|
||||
emitBackendStatistics(
|
||||
AmneziaStatistics(
|
||||
userspaceAmneziaBackend.get().getStatistics(this@WireGuardTunnel),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
emitBackendStatistics(WireGuardStatistics(backend.getStatistics(this@WireGuardTunnel)))
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ import androidx.compose.material3.Surface
|
|||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
@ -70,7 +71,6 @@ import androidx.navigation.NavController
|
|||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.wireguard.android.backend.WgQuickBackend
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
||||
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
|
||||
|
@ -106,6 +106,7 @@ fun SettingsScreen(
|
|||
val pinExists = remember { mutableStateOf(PinManager.pinExists()) }
|
||||
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
val kernelSupport by viewModel.kernelSupport.collectAsStateWithLifecycle()
|
||||
|
||||
val fineLocationState = rememberPermissionState(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
var currentText by remember { mutableStateOf("") }
|
||||
|
@ -117,6 +118,10 @@ fun SettingsScreen(
|
|||
val screenPadding = 5.dp
|
||||
val fillMaxWidth = .85f
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.checkKernelSupport()
|
||||
}
|
||||
|
||||
val startForResult =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
|
@ -591,7 +596,7 @@ fun SettingsScreen(
|
|||
viewModel.onToggleAmnezia()
|
||||
},
|
||||
)
|
||||
if (WgQuickBackend.hasKernelSupport()) {
|
||||
if (kernelSupport) {
|
||||
ConfigurationToggle(
|
||||
stringResource(R.string.use_kernel),
|
||||
enabled =
|
||||
|
@ -601,8 +606,10 @@ fun SettingsScreen(
|
|||
checked = uiState.settings.isKernelEnabled,
|
||||
padding = screenPadding,
|
||||
onCheckChanged = {
|
||||
viewModel.onToggleKernelMode().onFailure {
|
||||
appViewModel.showSnackbarMessage(it.getMessage(context))
|
||||
scope.launch {
|
||||
viewModel.onToggleKernelMode().onFailure {
|
||||
appViewModel.showSnackbarMessage(it.getMessage(context))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
@ -9,5 +9,5 @@ data class SettingsUiState(
|
|||
val tunnels: List<TunnelConfig> = emptyList(),
|
||||
val vpnState: VpnState = VpnState(),
|
||||
val isLocationDisclosureShown: Boolean = true,
|
||||
val isBatteryOptimizeDisableShown: Boolean = false
|
||||
val isBatteryOptimizeDisableShown: Boolean = false,
|
||||
)
|
||||
|
|
|
@ -5,23 +5,31 @@ import android.location.LocationManager
|
|||
import androidx.core.location.LocationManagerCompat
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.wireguard.android.backend.WgQuickBackend
|
||||
import com.wireguard.android.util.RootShell
|
||||
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
||||
import com.zaneschepke.wireguardautotunnel.data.domain.Settings
|
||||
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
|
||||
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnService
|
||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||
import com.zaneschepke.wireguardautotunnel.util.FileUtils
|
||||
import com.zaneschepke.wireguardautotunnel.util.WgTunnelExceptions
|
||||
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.combine
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
@HiltViewModel
|
||||
class SettingsViewModel
|
||||
|
@ -29,11 +37,15 @@ class SettingsViewModel
|
|||
constructor(
|
||||
private val appDataRepository: AppDataRepository,
|
||||
private val serviceManager: ServiceManager,
|
||||
private val rootShell: RootShell,
|
||||
private val rootShell: Provider<RootShell>,
|
||||
private val fileUtils: FileUtils,
|
||||
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
|
||||
vpnService: VpnService
|
||||
) : ViewModel() {
|
||||
|
||||
private val _kernelSupport = MutableStateFlow(false)
|
||||
val kernelSupport = _kernelSupport.asStateFlow()
|
||||
|
||||
val uiState =
|
||||
combine(
|
||||
appDataRepository.settings.getSettingsFlow(),
|
||||
|
@ -181,27 +193,29 @@ constructor(
|
|||
)
|
||||
}
|
||||
|
||||
fun onToggleKernelMode(): Result<Unit> {
|
||||
if (!uiState.value.settings.isKernelEnabled) {
|
||||
try {
|
||||
rootShell.start()
|
||||
Timber.i("Root shell accepted!")
|
||||
saveSettings(
|
||||
uiState.value.settings.copy(
|
||||
isKernelEnabled = true,
|
||||
isAmneziaEnabled = false,
|
||||
),
|
||||
)
|
||||
suspend fun onToggleKernelMode(): Result<Unit> {
|
||||
return withContext(ioDispatcher) {
|
||||
if (!uiState.value.settings.isKernelEnabled) {
|
||||
try {
|
||||
rootShell.get().start()
|
||||
Timber.i("Root shell accepted!")
|
||||
saveSettings(
|
||||
uiState.value.settings.copy(
|
||||
isKernelEnabled = true,
|
||||
isAmneziaEnabled = false,
|
||||
),
|
||||
)
|
||||
|
||||
} catch (e: RootShell.RootShellException) {
|
||||
Timber.e(e)
|
||||
} catch (e: RootShell.RootShellException) {
|
||||
Timber.e(e)
|
||||
saveKernelMode(on = false)
|
||||
return@withContext Result.failure(WgTunnelExceptions.RootDenied())
|
||||
}
|
||||
} else {
|
||||
saveKernelMode(on = false)
|
||||
return Result.failure(WgTunnelExceptions.RootDenied())
|
||||
}
|
||||
} else {
|
||||
saveKernelMode(on = false)
|
||||
Result.success(Unit)
|
||||
}
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
fun onToggleRestartOnPing() = viewModelScope.launch {
|
||||
|
@ -211,4 +225,13 @@ constructor(
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun checkKernelSupport() = viewModelScope.launch {
|
||||
val kernelSupport = withContext(ioDispatcher) {
|
||||
WgQuickBackend.hasKernelSupport()
|
||||
}
|
||||
_kernelSupport.update {
|
||||
kernelSupport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
object Constants {
|
||||
const val VERSION_NAME = "3.4.5"
|
||||
const val VERSION_NAME = "3.4.6"
|
||||
const val JVM_TARGET = "17"
|
||||
const val VERSION_CODE = 34500
|
||||
const val VERSION_CODE = 34600
|
||||
const val TARGET_SDK = 34
|
||||
const val MIN_SDK = 26
|
||||
const val APP_ID = "com.zaneschepke.wireguardautotunnel"
|
||||
const val APP_NAME = "wgtunnel"
|
||||
const val COMPOSE_COMPILER_EXTENSION_VERSION = "1.5.11"
|
||||
const val COMPOSE_COMPILER_EXTENSION_VERSION = "1.5.14"
|
||||
|
||||
|
||||
const val STORE_PASS_VAR = "SIGNING_STORE_PASSWORD"
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
What's new:
|
||||
- Fixes auto tunneling bugs
|
||||
- Fixes android backup bug
|
||||
- Bump versions
|
|
@ -3,7 +3,7 @@ accompanist = "0.34.0"
|
|||
activityCompose = "1.9.0"
|
||||
amneziawgAndroid = "1.2.0"
|
||||
androidx-junit = "1.1.5"
|
||||
appcompat = "1.6.1"
|
||||
appcompat = "1.7.0"
|
||||
biometricKtx = "1.2.0-alpha05"
|
||||
coreGoogleShortcuts = "1.1.0"
|
||||
coreKtx = "1.13.1"
|
||||
|
@ -23,14 +23,14 @@ roomVersion = "2.6.1"
|
|||
timber = "5.0.1"
|
||||
tunnel = "1.0.20230706"
|
||||
androidGradlePlugin = "8.4.1"
|
||||
kotlin = "1.9.23"
|
||||
ksp = "1.9.23-1.0.19"
|
||||
kotlin = "1.9.24"
|
||||
ksp = "1.9.24-1.0.20"
|
||||
composeBom = "2024.05.00"
|
||||
compose = "1.6.7"
|
||||
zxingAndroidEmbedded = "4.3.0"
|
||||
|
||||
#plugins
|
||||
gradlePlugins-kotlinxSerialization = "1.9.23"
|
||||
gradlePlugins-kotlinxSerialization = "1.9.24"
|
||||
material = "1.12.0"
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue