parent
fe45a2fad9
commit
25b986ef2f
|
@ -0,0 +1,15 @@
|
||||||
|
package com.zaneschepke.wireguardautotunnel.module
|
||||||
|
|
||||||
|
import javax.inject.Qualifier
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class Wifi
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class MobileData
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class Ethernet
|
|
@ -13,15 +13,19 @@ import dagger.hilt.android.scopes.ServiceScoped
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(ServiceComponent::class)
|
@InstallIn(ServiceComponent::class)
|
||||||
abstract class ServiceModule {
|
abstract class ServiceModule {
|
||||||
@Binds
|
|
||||||
@ServiceScoped
|
|
||||||
abstract fun provideWifiService(wifiService: WifiService): NetworkService<WifiService>
|
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
|
@Wifi
|
||||||
@ServiceScoped
|
@ServiceScoped
|
||||||
abstract fun provideMobileDataService(mobileDataService: MobileDataService): NetworkService<MobileDataService>
|
abstract fun provideWifiService(wifiService: WifiService): NetworkService
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
|
@MobileData
|
||||||
@ServiceScoped
|
@ServiceScoped
|
||||||
abstract fun provideEthernetService(ethernetService: EthernetService): NetworkService<EthernetService>
|
abstract fun provideMobileDataService(mobileDataService: MobileDataService): NetworkService
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Ethernet
|
||||||
|
@ServiceScoped
|
||||||
|
abstract fun provideEthernetService(ethernetService: EthernetService): NetworkService
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel
|
package com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.NetworkCapabilities
|
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import androidx.core.app.ServiceCompat
|
import androidx.core.app.ServiceCompat
|
||||||
|
@ -13,17 +12,16 @@ import com.zaneschepke.wireguardautotunnel.data.domain.Settings
|
||||||
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
|
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.module.AppShell
|
import com.zaneschepke.wireguardautotunnel.module.AppShell
|
||||||
|
import com.zaneschepke.wireguardautotunnel.module.Ethernet
|
||||||
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
|
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
|
||||||
import com.zaneschepke.wireguardautotunnel.module.MainImmediateDispatcher
|
import com.zaneschepke.wireguardautotunnel.module.MainImmediateDispatcher
|
||||||
|
import com.zaneschepke.wireguardautotunnel.module.MobileData
|
||||||
|
import com.zaneschepke.wireguardautotunnel.module.Wifi
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel.model.AutoTunnelEvent
|
import com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel.model.AutoTunnelEvent
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel.model.AutoTunnelState
|
import com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel.model.AutoTunnelState
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel.model.NetworkState
|
import com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel.model.NetworkState
|
||||||
import com.zaneschepke.wireguardautotunnel.service.network.EthernetService
|
|
||||||
import com.zaneschepke.wireguardautotunnel.service.network.MobileDataService
|
|
||||||
import com.zaneschepke.wireguardautotunnel.service.network.NetworkService
|
import com.zaneschepke.wireguardautotunnel.service.network.NetworkService
|
||||||
import com.zaneschepke.wireguardautotunnel.service.network.NetworkStatus
|
|
||||||
import com.zaneschepke.wireguardautotunnel.service.network.WifiService
|
|
||||||
import com.zaneschepke.wireguardautotunnel.service.notification.NotificationAction
|
import com.zaneschepke.wireguardautotunnel.service.notification.NotificationAction
|
||||||
import com.zaneschepke.wireguardautotunnel.service.notification.NotificationService
|
import com.zaneschepke.wireguardautotunnel.service.notification.NotificationService
|
||||||
import com.zaneschepke.wireguardautotunnel.service.notification.WireGuardNotification
|
import com.zaneschepke.wireguardautotunnel.service.notification.WireGuardNotification
|
||||||
|
@ -31,7 +29,6 @@ import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
||||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.TunnelConfigs
|
import com.zaneschepke.wireguardautotunnel.util.extensions.TunnelConfigs
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.cancelWithMessage
|
import com.zaneschepke.wireguardautotunnel.util.extensions.cancelWithMessage
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.getCurrentWifiName
|
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.isReachable
|
import com.zaneschepke.wireguardautotunnel.util.extensions.isReachable
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.onNotRunning
|
import com.zaneschepke.wireguardautotunnel.util.extensions.onNotRunning
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
@ -64,13 +61,16 @@ class AutoTunnelService : LifecycleService() {
|
||||||
lateinit var rootShell: Provider<RootShell>
|
lateinit var rootShell: Provider<RootShell>
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var wifiService: NetworkService<WifiService>
|
@Wifi
|
||||||
|
lateinit var wifiService: NetworkService
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var mobileDataService: NetworkService<MobileDataService>
|
@MobileData
|
||||||
|
lateinit var mobileDataService: NetworkService
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var ethernetService: NetworkService<EthernetService>
|
@Ethernet
|
||||||
|
lateinit var ethernetService: NetworkService
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var appDataRepository: Provider<AppDataRepository>
|
lateinit var appDataRepository: Provider<AppDataRepository>
|
||||||
|
@ -242,6 +242,7 @@ class AutoTunnelService : LifecycleService() {
|
||||||
) { double, networkState ->
|
) { double, networkState ->
|
||||||
AutoTunnelState(tunnelService.get().vpnState.value, networkState, double.first, double.second)
|
AutoTunnelState(tunnelService.get().vpnState.value, networkState, double.first, double.second)
|
||||||
}.collect { state ->
|
}.collect { state ->
|
||||||
|
Timber.d("Network state: ${state.networkState}")
|
||||||
autoTunnelStateFlow.update {
|
autoTunnelStateFlow.update {
|
||||||
it.copy(vpnState = state.vpnState, networkState = state.networkState, settings = state.settings, tunnels = state.tunnels)
|
it.copy(vpnState = state.vpnState, networkState = state.networkState, settings = state.settings, tunnels = state.tunnels)
|
||||||
}
|
}
|
||||||
|
@ -256,19 +257,14 @@ class AutoTunnelService : LifecycleService() {
|
||||||
@OptIn(FlowPreview::class)
|
@OptIn(FlowPreview::class)
|
||||||
private fun combineNetworkEventsJob(): Flow<NetworkState> {
|
private fun combineNetworkEventsJob(): Flow<NetworkState> {
|
||||||
return combine(
|
return combine(
|
||||||
wifiService.networkStatus,
|
wifiService.status,
|
||||||
mobileDataService.networkStatus,
|
mobileDataService.status,
|
||||||
ethernetService.networkStatus,
|
) { wifi, mobileData ->
|
||||||
) { wifi, mobileData, ethernet ->
|
|
||||||
NetworkState(
|
NetworkState(
|
||||||
wifi.isConnected,
|
wifi.available,
|
||||||
mobileData.isConnected,
|
mobileData.available,
|
||||||
ethernet.isConnected,
|
false,
|
||||||
when (wifi) {
|
wifi.name
|
||||||
is NetworkStatus.CapabilitiesChanged -> getWifiSSID(wifi.networkCapabilities)
|
|
||||||
is NetworkStatus.Available -> autoTunnelStateFlow.value.networkState.wifiName
|
|
||||||
is NetworkStatus.Unavailable -> null
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}.distinctUntilChanged().filterNot { it.isWifiConnected && it.wifiName == null }.debounce(500L)
|
}.distinctUntilChanged().filterNot { it.isWifiConnected && it.wifiName == null }.debounce(500L)
|
||||||
}
|
}
|
||||||
|
@ -285,28 +281,6 @@ class AutoTunnelService : LifecycleService() {
|
||||||
}.distinctUntilChanged()
|
}.distinctUntilChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getWifiSSID(networkCapabilities: NetworkCapabilities): String? {
|
|
||||||
return withContext(ioDispatcher) {
|
|
||||||
val settings = settings()
|
|
||||||
if (settings.isWifiNameByShellEnabled) return@withContext rootShell.get().getCurrentWifiName()
|
|
||||||
wifiService.getNetworkName(networkCapabilities)
|
|
||||||
}.also {
|
|
||||||
if (it?.contains(Constants.UNREADABLE_SSID) == true) {
|
|
||||||
Timber.w("SSID unreadable: missing permissions")
|
|
||||||
} else {
|
|
||||||
Timber.i("Detected valid SSID")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun settings(): Settings {
|
|
||||||
return if (autoTunnelStateFlow.value == defaultState) {
|
|
||||||
appDataRepository.get().settings.getSettings()
|
|
||||||
} else {
|
|
||||||
autoTunnelStateFlow.value.settings
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(FlowPreview::class)
|
@OptIn(FlowPreview::class)
|
||||||
private fun startAutoTunnelJob() = lifecycleScope.launch(ioDispatcher) {
|
private fun startAutoTunnelJob() = lifecycleScope.launch(ioDispatcher) {
|
||||||
Timber.i("Starting auto-tunnel network event watcher")
|
Timber.i("Starting auto-tunnel network event watcher")
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.service.network
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.ConnectivityManager
|
|
||||||
import android.net.Network
|
|
||||||
import android.net.NetworkCapabilities
|
|
||||||
import android.net.NetworkRequest
|
|
||||||
import android.net.wifi.WifiManager
|
|
||||||
import android.os.Build
|
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
|
||||||
import kotlinx.coroutines.flow.catch
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.flow.onStart
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
abstract class BaseNetworkService<T : BaseNetworkService<T>>(
|
|
||||||
val context: Context,
|
|
||||||
networkCapability: Int,
|
|
||||||
) : NetworkService<T> {
|
|
||||||
private val connectivityManager =
|
|
||||||
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
|
||||||
|
|
||||||
val wifiManager =
|
|
||||||
context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
|
|
||||||
|
|
||||||
private fun checkHasCapability(networkCapability: Int): Boolean {
|
|
||||||
val network = connectivityManager.activeNetwork
|
|
||||||
val networkCapabilities = connectivityManager.getNetworkCapabilities(network)
|
|
||||||
return networkCapabilities?.hasTransport(networkCapability) == true
|
|
||||||
}
|
|
||||||
|
|
||||||
override val networkStatus =
|
|
||||||
callbackFlow {
|
|
||||||
val networkStatusCallback =
|
|
||||||
when (Build.VERSION.SDK_INT) {
|
|
||||||
in Build.VERSION_CODES.S..Int.MAX_VALUE -> {
|
|
||||||
object :
|
|
||||||
ConnectivityManager.NetworkCallback(
|
|
||||||
FLAG_INCLUDE_LOCATION_INFO,
|
|
||||||
) {
|
|
||||||
override fun onAvailable(network: Network) {
|
|
||||||
trySend(NetworkStatus.Available(network))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLost(network: Network) {
|
|
||||||
trySend(NetworkStatus.Unavailable())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
|
||||||
trySend(
|
|
||||||
NetworkStatus.CapabilitiesChanged(
|
|
||||||
network,
|
|
||||||
networkCapabilities,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
object : ConnectivityManager.NetworkCallback() {
|
|
||||||
override fun onAvailable(network: Network) {
|
|
||||||
trySend(NetworkStatus.Available(network))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLost(network: Network) {
|
|
||||||
trySend(NetworkStatus.Unavailable())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
|
||||||
trySend(
|
|
||||||
NetworkStatus.CapabilitiesChanged(
|
|
||||||
network,
|
|
||||||
networkCapabilities,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val request =
|
|
||||||
NetworkRequest.Builder()
|
|
||||||
.addTransportType(networkCapability)
|
|
||||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
|
||||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
|
|
||||||
.build()
|
|
||||||
connectivityManager.registerNetworkCallback(request, networkStatusCallback)
|
|
||||||
|
|
||||||
awaitClose { connectivityManager.unregisterNetworkCallback(networkStatusCallback) }
|
|
||||||
}.onStart {
|
|
||||||
// needed for services that are not yet available as it will impact later combine flows if we don't emit
|
|
||||||
emit(NetworkStatus.Unavailable())
|
|
||||||
}.catch {
|
|
||||||
Timber.e(it)
|
|
||||||
emit(NetworkStatus.Unavailable())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <Result> Flow<NetworkStatus>.map(
|
|
||||||
crossinline onUnavailable: suspend () -> Result,
|
|
||||||
crossinline onAvailable: suspend (network: Network) -> Result,
|
|
||||||
crossinline onCapabilitiesChanged:
|
|
||||||
suspend (network: Network, networkCapabilities: NetworkCapabilities) -> Result,
|
|
||||||
): Flow<Result> = map { status ->
|
|
||||||
when (status) {
|
|
||||||
is NetworkStatus.Unavailable -> onUnavailable()
|
|
||||||
is NetworkStatus.Available -> onAvailable(status.network)
|
|
||||||
is NetworkStatus.CapabilitiesChanged ->
|
|
||||||
onCapabilitiesChanged(
|
|
||||||
status.network,
|
|
||||||
status.networkCapabilities,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +1,64 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.service.network
|
package com.zaneschepke.wireguardautotunnel.service.network
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.Network
|
||||||
import android.net.NetworkCapabilities
|
import android.net.NetworkCapabilities
|
||||||
|
import android.net.NetworkRequest
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onStart
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class EthernetService
|
class EthernetService
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
) :
|
) : NetworkService {
|
||||||
BaseNetworkService<EthernetService>(context, NetworkCapabilities.TRANSPORT_ETHERNET) {
|
|
||||||
|
|
||||||
override fun isNetworkSecure(): Boolean {
|
private val connectivityManager =
|
||||||
return true
|
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
|
||||||
|
override val status = callbackFlow {
|
||||||
|
val networkStatusCallback = object : ConnectivityManager.NetworkCallback() {
|
||||||
|
override fun onAvailable(network: Network) {
|
||||||
|
trySend(NetworkStatus.Available(network))
|
||||||
|
}
|
||||||
|
override fun onLost(network: Network) {
|
||||||
|
trySend(NetworkStatus.Unavailable())
|
||||||
|
}
|
||||||
|
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
||||||
|
trySend(
|
||||||
|
NetworkStatus.CapabilitiesChanged(
|
||||||
|
network,
|
||||||
|
networkCapabilities,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val request =
|
||||||
|
NetworkRequest.Builder()
|
||||||
|
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
|
||||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
|
||||||
|
.build()
|
||||||
|
connectivityManager.registerNetworkCallback(request, networkStatusCallback)
|
||||||
|
|
||||||
|
awaitClose { connectivityManager.unregisterNetworkCallback(networkStatusCallback) }
|
||||||
|
}.onStart {
|
||||||
|
// needed for services that are not yet available as it will impact later combine flows if we don't emit
|
||||||
|
emit(NetworkStatus.Unavailable())
|
||||||
|
}.catch {
|
||||||
|
Timber.e(it)
|
||||||
|
emit(NetworkStatus.Unavailable())
|
||||||
|
}.map {
|
||||||
|
when (it) {
|
||||||
|
is NetworkStatus.Available, is NetworkStatus.CapabilitiesChanged -> Status(true, null)
|
||||||
|
is NetworkStatus.Unavailable -> Status(false, null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,63 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.service.network
|
package com.zaneschepke.wireguardautotunnel.service.network
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.Network
|
||||||
import android.net.NetworkCapabilities
|
import android.net.NetworkCapabilities
|
||||||
|
import android.net.NetworkRequest
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onStart
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class MobileDataService
|
class MobileDataService
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
) :
|
) : NetworkService {
|
||||||
BaseNetworkService<MobileDataService>(context, NetworkCapabilities.TRANSPORT_CELLULAR) {
|
private val connectivityManager =
|
||||||
override fun isNetworkSecure(): Boolean {
|
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
return false
|
|
||||||
|
override val status = callbackFlow {
|
||||||
|
val networkStatusCallback = object : ConnectivityManager.NetworkCallback() {
|
||||||
|
override fun onAvailable(network: Network) {
|
||||||
|
trySend(NetworkStatus.Available(network))
|
||||||
|
}
|
||||||
|
override fun onLost(network: Network) {
|
||||||
|
trySend(NetworkStatus.Unavailable())
|
||||||
|
}
|
||||||
|
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
||||||
|
trySend(
|
||||||
|
NetworkStatus.CapabilitiesChanged(
|
||||||
|
network,
|
||||||
|
networkCapabilities,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val request =
|
||||||
|
NetworkRequest.Builder()
|
||||||
|
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
|
||||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
|
||||||
|
.build()
|
||||||
|
connectivityManager.registerNetworkCallback(request, networkStatusCallback)
|
||||||
|
|
||||||
|
awaitClose { connectivityManager.unregisterNetworkCallback(networkStatusCallback) }
|
||||||
|
}.onStart {
|
||||||
|
// needed for services that are not yet available as it will impact later combine flows if we don't emit
|
||||||
|
emit(NetworkStatus.Unavailable())
|
||||||
|
}.catch {
|
||||||
|
Timber.e(it)
|
||||||
|
emit(NetworkStatus.Unavailable())
|
||||||
|
}.map {
|
||||||
|
when (it) {
|
||||||
|
is NetworkStatus.Available, is NetworkStatus.CapabilitiesChanged -> Status(true, null)
|
||||||
|
is NetworkStatus.Unavailable -> Status(false, null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,27 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.service.network
|
package com.zaneschepke.wireguardautotunnel.service.network
|
||||||
|
|
||||||
|
import android.net.Network
|
||||||
import android.net.NetworkCapabilities
|
import android.net.NetworkCapabilities
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
interface NetworkService<T> {
|
interface NetworkService {
|
||||||
fun getNetworkName(networkCapabilities: NetworkCapabilities): String? {
|
val status: Flow<Status>
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isNetworkSecure(): Boolean
|
inline fun <Result> Flow<NetworkStatus>.map(
|
||||||
|
crossinline onUnavailable: suspend () -> Result,
|
||||||
val networkStatus: Flow<NetworkStatus>
|
crossinline onAvailable: suspend (network: Network) -> Result,
|
||||||
|
crossinline onCapabilitiesChanged:
|
||||||
|
suspend (network: Network, networkCapabilities: NetworkCapabilities) -> Result,
|
||||||
|
): Flow<Result> = map { status ->
|
||||||
|
when (status) {
|
||||||
|
is NetworkStatus.Unavailable -> onUnavailable()
|
||||||
|
is NetworkStatus.Available -> onAvailable(status.network)
|
||||||
|
is NetworkStatus.CapabilitiesChanged ->
|
||||||
|
onCapabilitiesChanged(
|
||||||
|
status.network,
|
||||||
|
status.networkCapabilities,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.zaneschepke.wireguardautotunnel.service.network
|
||||||
|
|
||||||
|
data class Status(
|
||||||
|
val available: Boolean,
|
||||||
|
val name: String?,
|
||||||
|
)
|
|
@ -1,22 +1,134 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.service.network
|
package com.zaneschepke.wireguardautotunnel.service.network
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.ConnectivityManager.NetworkCallback.FLAG_INCLUDE_LOCATION_INFO
|
||||||
|
import android.net.Network
|
||||||
import android.net.NetworkCapabilities
|
import android.net.NetworkCapabilities
|
||||||
|
import android.net.NetworkRequest
|
||||||
import android.net.wifi.SupplicantState
|
import android.net.wifi.SupplicantState
|
||||||
|
import android.net.wifi.WifiManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import com.wireguard.android.util.RootShell
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.repository.SettingsRepository
|
||||||
|
import com.zaneschepke.wireguardautotunnel.module.AppShell
|
||||||
|
import com.zaneschepke.wireguardautotunnel.util.extensions.getCurrentWifiName
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.onStart
|
||||||
|
import kotlinx.coroutines.flow.transform
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Provider
|
||||||
|
|
||||||
class WifiService
|
class WifiService
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
) :
|
private val settingsRepository: SettingsRepository,
|
||||||
BaseNetworkService<WifiService>(context, NetworkCapabilities.TRANSPORT_WIFI) {
|
@AppShell private val rootShell: Provider<RootShell>
|
||||||
|
) : NetworkService {
|
||||||
|
|
||||||
override fun getNetworkName(networkCapabilities: NetworkCapabilities): String? {
|
val mutex = Mutex()
|
||||||
|
|
||||||
|
private var ssid : String? = null
|
||||||
|
private var available : Boolean = false
|
||||||
|
|
||||||
|
private val connectivityManager =
|
||||||
|
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
|
||||||
|
override val status = callbackFlow {
|
||||||
|
val networkStatusCallback =
|
||||||
|
when (Build.VERSION.SDK_INT) {
|
||||||
|
in Build.VERSION_CODES.S..Int.MAX_VALUE -> {
|
||||||
|
object :
|
||||||
|
ConnectivityManager.NetworkCallback(
|
||||||
|
FLAG_INCLUDE_LOCATION_INFO,
|
||||||
|
) {
|
||||||
|
override fun onAvailable(network: Network) {
|
||||||
|
trySend(NetworkStatus.Available(network))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLost(network: Network) {
|
||||||
|
trySend(NetworkStatus.Unavailable())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
||||||
|
trySend(
|
||||||
|
NetworkStatus.CapabilitiesChanged(
|
||||||
|
network,
|
||||||
|
networkCapabilities,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
object : ConnectivityManager.NetworkCallback() {
|
||||||
|
override fun onAvailable(network: Network) {
|
||||||
|
trySend(NetworkStatus.Available(network))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLost(network: Network) {
|
||||||
|
trySend(NetworkStatus.Unavailable())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
||||||
|
trySend(
|
||||||
|
NetworkStatus.CapabilitiesChanged(
|
||||||
|
network,
|
||||||
|
networkCapabilities,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val request =
|
||||||
|
NetworkRequest.Builder()
|
||||||
|
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
|
||||||
|
.build()
|
||||||
|
connectivityManager.registerNetworkCallback(request, networkStatusCallback)
|
||||||
|
|
||||||
|
awaitClose { connectivityManager.unregisterNetworkCallback(networkStatusCallback) }
|
||||||
|
}.onStart {
|
||||||
|
// needed for services that are not yet available as it will impact later combine flows if we don't emit
|
||||||
|
emit(NetworkStatus.Unavailable())
|
||||||
|
}.catch {
|
||||||
|
Timber.e(it)
|
||||||
|
emit(NetworkStatus.Unavailable())
|
||||||
|
}.transform {
|
||||||
|
when(it) {
|
||||||
|
is NetworkStatus.Available -> mutex.withLock {
|
||||||
|
available = true
|
||||||
|
}
|
||||||
|
is NetworkStatus.CapabilitiesChanged -> mutex.withLock {
|
||||||
|
if(available) {
|
||||||
|
available = false
|
||||||
|
Timber.d("Getting SSID from capabilities")
|
||||||
|
ssid = getNetworkName(it.networkCapabilities)
|
||||||
|
}
|
||||||
|
emit(Status(true, ssid))
|
||||||
|
}
|
||||||
|
is NetworkStatus.Unavailable -> emit(Status(false, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getNetworkName(networkCapabilities: NetworkCapabilities): String? {
|
||||||
|
if(settingsRepository.getSettings().isWifiNameByShellEnabled) return rootShell.get().getCurrentWifiName()
|
||||||
var ssid = networkCapabilities.getWifiName()
|
var ssid = networkCapabilities.getWifiName()
|
||||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S) {
|
||||||
|
val wifiManager =
|
||||||
|
context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
val info = wifiManager.connectionInfo
|
val info = wifiManager.connectionInfo
|
||||||
if (info.supplicantState === SupplicantState.COMPLETED) {
|
if (info.supplicantState === SupplicantState.COMPLETED) {
|
||||||
ssid = info.ssid
|
ssid = info.ssid
|
||||||
|
@ -24,9 +136,4 @@ constructor(
|
||||||
}
|
}
|
||||||
return ssid?.trim('"')
|
return ssid?.trim('"')
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isNetworkSecure(): Boolean {
|
|
||||||
// TODO
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue