mirror of
https://github.com/MetaCubeX/ClashMetaForAndroid.git
synced 2024-11-25 06:46:09 +03:00
Refactor: refactor network observer
This commit is contained in:
parent
7e46d75159
commit
7809c30052
@ -62,9 +62,9 @@ class TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.De
|
|||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
network.onEvent { e ->
|
network.onEvent { n ->
|
||||||
if (Build.VERSION.SDK_INT in 22..28) @TargetApi(22) {
|
if (Build.VERSION.SDK_INT in 22..28) @TargetApi(22) {
|
||||||
setUnderlyingNetworks(e.network?.let { arrayOf(it) })
|
setUnderlyingNetworks(n?.let { arrayOf(it) })
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
|
@ -1,105 +1,127 @@
|
|||||||
package com.github.kr328.clash.service.clash.module
|
package com.github.kr328.clash.service.clash.module
|
||||||
|
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Intent
|
|
||||||
import android.net.*
|
import android.net.*
|
||||||
import android.os.PowerManager
|
import android.os.Build
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import com.github.kr328.clash.common.log.Log
|
import com.github.kr328.clash.common.log.Log
|
||||||
import com.github.kr328.clash.core.Clash
|
import com.github.kr328.clash.core.Clash
|
||||||
import com.github.kr328.clash.service.util.resolveDns
|
import com.github.kr328.clash.service.util.resolveDns
|
||||||
import kotlinx.coroutines.NonCancellable
|
import kotlinx.coroutines.NonCancellable
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.selects.select
|
import kotlinx.coroutines.channels.trySendBlocking
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class NetworkObserveModule(service: Service) :
|
class NetworkObserveModule(service: Service) : Module<Network?>(service) {
|
||||||
Module<NetworkObserveModule.NetworkChanged>(service) {
|
private data class Action(val type: Type, val network: Network) {
|
||||||
data class NetworkChanged(val network: Network?)
|
enum class Type { Available, Lost, Changed }
|
||||||
|
}
|
||||||
|
|
||||||
private val connectivity = service.getSystemService<ConnectivityManager>()!!
|
private val connectivity = service.getSystemService<ConnectivityManager>()!!
|
||||||
private val networks: Channel<Network?> = Channel(Channel.CONFLATED)
|
private val actions = Channel<Action>(Channel.UNLIMITED)
|
||||||
private val request = NetworkRequest.Builder().apply {
|
private val request = NetworkRequest.Builder().apply {
|
||||||
addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
|
addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
|
||||||
addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||||
addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
|
addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
private val callback = object : ConnectivityManager.NetworkCallback() {
|
private val callback = object : ConnectivityManager.NetworkCallback() {
|
||||||
private var network: Network? = null
|
|
||||||
|
|
||||||
override fun onAvailable(network: Network) {
|
override fun onAvailable(network: Network) {
|
||||||
if (this.network != network)
|
actions.trySendBlocking(Action(Action.Type.Available, network))
|
||||||
networks.trySend(network)
|
}
|
||||||
|
|
||||||
this.network = network
|
override fun onLost(network: Network) {
|
||||||
|
actions.trySendBlocking(Action(Action.Type.Lost, network))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) {
|
override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) {
|
||||||
if (this.network == network)
|
actions.trySendBlocking(Action(Action.Type.Changed, network))
|
||||||
networks.trySend(network)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun register(): Result<Unit> {
|
|
||||||
return runCatching {
|
|
||||||
connectivity.registerNetworkCallback(request, callback)
|
|
||||||
}.onFailure {
|
|
||||||
Log.w("Observe network change: $it", it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun unregister(): Result<Unit> {
|
|
||||||
return runCatching {
|
|
||||||
connectivity.unregisterNetworkCallback(callback)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun run() {
|
override suspend fun run() {
|
||||||
val screenToggle = receiveBroadcast(false, Channel.CONFLATED) {
|
try {
|
||||||
addAction(Intent.ACTION_SCREEN_ON)
|
connectivity.registerNetworkCallback(request, callback)
|
||||||
addAction(Intent.ACTION_SCREEN_OFF)
|
} catch (e: Exception) {
|
||||||
}
|
Log.w("Observe network failed: $e", e)
|
||||||
|
|
||||||
if (service.getSystemService<PowerManager>()?.isInteractive != false) {
|
return
|
||||||
register()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
var current: Network? = null
|
||||||
|
val networks = mutableSetOf<Network>()
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
val quit = select<Boolean> {
|
val action = actions.receive()
|
||||||
screenToggle.onReceive {
|
|
||||||
when (it.action) {
|
when (action.type) {
|
||||||
Intent.ACTION_SCREEN_ON ->
|
Action.Type.Available -> {
|
||||||
register().isFailure
|
networks.add(action.network)
|
||||||
Intent.ACTION_SCREEN_OFF ->
|
}
|
||||||
unregister().isFailure
|
Action.Type.Lost -> {
|
||||||
else ->
|
networks.remove(action.network)
|
||||||
false
|
}
|
||||||
|
Action.Type.Changed -> {
|
||||||
|
if (current == action.network) {
|
||||||
|
val dns = connectivity.resolveDns(action.network)
|
||||||
|
|
||||||
|
Clash.notifyDnsChanged(dns)
|
||||||
|
|
||||||
|
Log.d("Current network changed: ${action.network}: $dns")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
networks.onReceive {
|
|
||||||
val dns = connectivity.resolveDns(it)
|
|
||||||
|
|
||||||
Clash.notifyDnsChanged(dns)
|
continue
|
||||||
|
|
||||||
Log.d("Network changed, system dns = $dns")
|
|
||||||
|
|
||||||
enqueueEvent(NetworkChanged(it))
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (quit) {
|
|
||||||
return
|
current = networks.maxByOrNull {
|
||||||
|
connectivity.getNetworkCapabilities(it)?.let { cap ->
|
||||||
|
TRANSPORT_PRIORITY.indexOfFirst { cap.hasTransport(it) }
|
||||||
|
} ?: -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val dns = connectivity.resolveDns(current)
|
||||||
|
|
||||||
|
Clash.notifyDnsChanged(dns)
|
||||||
|
|
||||||
|
enqueueEvent(current)
|
||||||
|
|
||||||
|
Log.d("Available network changed: $current of $networks: $dns")
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
withContext(NonCancellable) {
|
withContext(NonCancellable) {
|
||||||
unregister()
|
enqueueEvent(null)
|
||||||
|
|
||||||
Clash.notifyDnsChanged(emptyList())
|
Clash.notifyDnsChanged(emptyList())
|
||||||
|
|
||||||
|
runCatching {
|
||||||
|
connectivity.unregisterNetworkCallback(callback)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TRANSPORT_PRIORITY = sequence {
|
||||||
|
yield(NetworkCapabilities.TRANSPORT_CELLULAR)
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||||
|
yield(NetworkCapabilities.TRANSPORT_LOWPAN)
|
||||||
|
}
|
||||||
|
|
||||||
|
yield(NetworkCapabilities.TRANSPORT_BLUETOOTH)
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
yield(NetworkCapabilities.TRANSPORT_WIFI_AWARE)
|
||||||
|
}
|
||||||
|
|
||||||
|
yield(NetworkCapabilities.TRANSPORT_WIFI)
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
yield(NetworkCapabilities.TRANSPORT_USB)
|
||||||
|
}
|
||||||
|
|
||||||
|
yield(NetworkCapabilities.TRANSPORT_ETHERNET)
|
||||||
|
}.toList()
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user