1
1
package com.zaneschepke.wireguardautotunnel.core.service.autotunnel
2
2
3
3
import android.content.Intent
4
- import android.net.NetworkCapabilities
5
4
import android.os.IBinder
6
5
import android.os.PowerManager
7
6
import androidx.core.app.ServiceCompat
8
7
import androidx.lifecycle.LifecycleService
9
8
import androidx.lifecycle.lifecycleScope
10
- import com.wireguard.android.util.RootShell
9
+ import com.zaneschepke.networkmonitor.NetworkMonitor
10
+ import com.zaneschepke.networkmonitor.NetworkStatus
11
11
import com.zaneschepke.wireguardautotunnel.R
12
- import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
13
- import com.zaneschepke.wireguardautotunnel.di.AppShell
14
- import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
15
- import com.zaneschepke.wireguardautotunnel.di.MainImmediateDispatcher
16
- import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
17
- import com.zaneschepke.wireguardautotunnel.domain.state.AutoTunnelState
18
- import com.zaneschepke.wireguardautotunnel.domain.state.NetworkState
19
- import com.zaneschepke.wireguardautotunnel.core.network.InternetConnectivityMonitor
20
- import com.zaneschepke.wireguardautotunnel.core.network.NetworkMonitor
21
- import com.zaneschepke.wireguardautotunnel.domain.state.ConnectivityState
22
- import com.zaneschepke.wireguardautotunnel.domain.enums.NotificationAction
23
12
import com.zaneschepke.wireguardautotunnel.core.notification.NotificationManager
24
13
import com.zaneschepke.wireguardautotunnel.core.notification.WireGuardNotification
14
+ import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
25
15
import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelManager
16
+ import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
17
+ import com.zaneschepke.wireguardautotunnel.di.MainImmediateDispatcher
26
18
import com.zaneschepke.wireguardautotunnel.domain.entity.AppSettings
27
19
import com.zaneschepke.wireguardautotunnel.domain.enums.BackendState
20
+ import com.zaneschepke.wireguardautotunnel.domain.enums.NotificationAction
28
21
import com.zaneschepke.wireguardautotunnel.domain.events.AutoTunnelEvent
29
22
import com.zaneschepke.wireguardautotunnel.domain.events.KillSwitchEvent
23
+ import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
24
+ import com.zaneschepke.wireguardautotunnel.domain.state.AutoTunnelState
25
+ import com.zaneschepke.wireguardautotunnel.domain.state.NetworkState
30
26
import com.zaneschepke.wireguardautotunnel.util.Constants
31
27
import com.zaneschepke.wireguardautotunnel.util.extensions.Tunnels
32
- import com.zaneschepke.wireguardautotunnel.util.extensions.getCurrentWifiName
33
28
import dagger.hilt.android.AndroidEntryPoint
34
29
import kotlinx.coroutines.CompletableDeferred
35
30
import kotlinx.coroutines.CoroutineDispatcher
31
+ import kotlinx.coroutines.ExperimentalCoroutinesApi
36
32
import kotlinx.coroutines.FlowPreview
37
33
import kotlinx.coroutines.flow.Flow
38
34
import kotlinx.coroutines.flow.MutableStateFlow
39
35
import kotlinx.coroutines.flow.combine
40
36
import kotlinx.coroutines.flow.debounce
41
37
import kotlinx.coroutines.flow.distinctUntilChanged
38
+ import kotlinx.coroutines.flow.flatMapLatest
39
+ import kotlinx.coroutines.flow.flowOn
42
40
import kotlinx.coroutines.flow.map
43
41
import kotlinx.coroutines.flow.update
44
42
import kotlinx.coroutines.launch
@@ -49,10 +47,6 @@ import javax.inject.Provider
49
47
@AndroidEntryPoint
50
48
class AutoTunnelService : LifecycleService () {
51
49
52
- @Inject
53
- @AppShell
54
- lateinit var rootShell: Provider <RootShell >
55
-
56
50
@Inject
57
51
lateinit var networkMonitor: NetworkMonitor
58
52
@@ -161,49 +155,54 @@ class AutoTunnelService : LifecycleService() {
161
155
}
162
156
}
163
157
164
- private suspend fun buildNetworkState (connectivityState : ConnectivityState ): NetworkState {
158
+ private fun buildNetworkState (networkStatus : NetworkStatus ): NetworkState {
165
159
return with (autoTunnelStateFlow.value.networkState) {
166
- val wifiName = when {
167
- connectivityState.wifiAvailable &&
168
- (wifiName == null || wifiName == Constants .UNREADABLE_SSID || networkMonitor.didWifiChangeSinceLastCapabilitiesQuery) -> {
169
- networkMonitor.getWifiCapabilities()?.let { getWifiName(it) } ? : wifiName
160
+ val wifiName = when (networkStatus) {
161
+ is NetworkStatus .Connected -> {
162
+ networkStatus.wifiSsid
170
163
}
171
- ! connectivityState.wifiAvailable -> null
172
- else -> wifiName
164
+ else -> null
173
165
}
174
166
copy(
175
- isWifiConnected = connectivityState.wifiAvailable ,
176
- isMobileDataConnected = connectivityState.cellularAvailable ,
177
- isEthernetConnected = isEthernetConnected ,
167
+ isWifiConnected = networkStatus.wifiConnected ,
168
+ isMobileDataConnected = networkStatus.cellularConnected ,
169
+ isEthernetConnected = networkStatus.ethernetConnected ,
178
170
wifiName = wifiName,
179
171
)
180
172
}
181
173
}
182
174
175
+ @OptIn(ExperimentalCoroutinesApi ::class )
183
176
private fun startAutoTunnelStateJob () = lifecycleScope.launch(ioDispatcher) {
184
177
combine(
185
178
combineSettings(),
186
- networkMonitor.status.map {
187
- buildNetworkState(it)
188
- }.distinctUntilChanged(),
179
+ appDataRepository.get().settings.flow
180
+ .distinctUntilChanged { old, new -> old.isKernelEnabled == new.isKernelEnabled } // Only emit when isKernelEnabled changes
181
+ .flatMapLatest { settings ->
182
+ networkMonitor.getNetworkStatusFlow(true , settings.isKernelEnabled)
183
+ .flowOn(ioDispatcher)
184
+ .map { buildNetworkState(it) }
185
+ }
186
+ .distinctUntilChanged(),
189
187
) { double, networkState ->
190
- AutoTunnelState (tunnelManager.activeTunnels.value, networkState, double.first, double.second)
188
+ AutoTunnelState (
189
+ tunnelManager.activeTunnels.value,
190
+ networkState,
191
+ double.first,
192
+ double.second,
193
+ )
191
194
}.collect { state ->
192
195
autoTunnelStateFlow.update {
193
- it.copy(activeTunnels = state.activeTunnels, networkState = state.networkState, settings = state.settings, tunnels = state.tunnels)
196
+ it.copy(
197
+ activeTunnels = state.activeTunnels,
198
+ networkState = state.networkState,
199
+ settings = state.settings,
200
+ tunnels = state.tunnels,
201
+ )
194
202
}
195
203
}
196
204
}
197
205
198
- private suspend fun getWifiName (wifiCapabilities : NetworkCapabilities ): String? {
199
- val setting = appDataRepository.get().settings.get()
200
- return if (setting.isWifiNameByShellEnabled) {
201
- rootShell.get().getCurrentWifiName()
202
- } else {
203
- InternetConnectivityMonitor .getNetworkName(wifiCapabilities, this @AutoTunnelService)
204
- }
205
- }
206
-
207
206
private fun combineSettings (): Flow <Pair <AppSettings , Tunnels >> {
208
207
return combine(
209
208
appDataRepository.get().settings.flow,
@@ -240,7 +239,7 @@ class AutoTunnelService : LifecycleService() {
240
239
Timber .d(" Starting with debounce delay of: ${settings.debounceDelaySeconds} seconds" )
241
240
autoTunnelStateFlow.debounce(settings.debounceDelayMillis()).collect { watcherState ->
242
241
if (watcherState == defaultState) return @collect
243
- Timber .d(" New auto tunnel state emitted" )
242
+ Timber .d(" New auto tunnel state emitted ${watcherState.networkState} " )
244
243
when (val event = watcherState.asAutoTunnelEvent()) {
245
244
is AutoTunnelEvent .Start -> (event.tunnelConf ? : appDataRepository.get().getPrimaryOrFirstTunnel())?.let {
246
245
tunnelManager.startTunnel(it)
0 commit comments