@@ -6,123 +6,150 @@ import android.os.Build
66import androidx.core.content.getSystemService
77import com.github.kr328.clash.common.log.Log
88import com.github.kr328.clash.core.Clash
9- import com.github.kr328.clash.service.util.resolveDns
9+ import com.github.kr328.clash.service.util.asSocketAddressText
1010import kotlinx.coroutines.NonCancellable
1111import kotlinx.coroutines.channels.Channel
12- import kotlinx.coroutines.channels.trySendBlocking
12+ import kotlinx.coroutines.selects.select
1313import kotlinx.coroutines.withContext
14+ import java.net.InetAddress
15+ import java.util.concurrent.ConcurrentHashMap
1416
15- class NetworkObserveModule (service : Service ) : Module<Network?>(service) {
16- private data class Action (val type : Type , val network : Network ) {
17- enum class Type { Available , Lost , Changed }
18- }
19-
17+ class NetworkObserveModule (service : Service ) : Module<Network>(service) {
2018 private val connectivity = service.getSystemService<ConnectivityManager >()!!
21- private val actions = Channel <Action > (Channel .UNLIMITED )
19+ private val networks : Channel <Network > = Channel (Channel .UNLIMITED )
2220 private val request = NetworkRequest .Builder ().apply {
2321 addCapability(NetworkCapabilities .NET_CAPABILITY_NOT_VPN )
2422 addCapability(NetworkCapabilities .NET_CAPABILITY_INTERNET )
23+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .P ) {
24+ addCapability(NetworkCapabilities .NET_CAPABILITY_FOREGROUND )
25+ }
2526 addCapability(NetworkCapabilities .NET_CAPABILITY_NOT_RESTRICTED )
2627 }.build()
28+
29+ private data class NetworkInfo (
30+ @Volatile var losingMs : Long = 0 ,
31+ @Volatile var dnsList : List <InetAddress > = emptyList()
32+ ) {
33+ fun isAvailable (): Boolean = losingMs < System .currentTimeMillis()
34+ }
35+
36+ private val networkInfos = ConcurrentHashMap <Network , NetworkInfo >()
37+
38+ @Volatile
39+ private var curDnsList = emptyList<String >()
40+
2741 private val callback = object : ConnectivityManager .NetworkCallback () {
2842 override fun onAvailable (network : Network ) {
29- actions.trySendBlocking(Action (Action .Type .Available , network))
43+ Log .i(" NetworkObserve onAvailable network=$network " )
44+ networkInfos[network] = NetworkInfo ()
45+ }
46+
47+ override fun onLosing (network : Network , maxMsToLive : Int ) {
48+ Log .i(" NetworkObserve onLosing network=$network " )
49+ networkInfos[network]?.losingMs = System .currentTimeMillis() + maxMsToLive
50+ notifyDnsChange()
51+
52+ networks.trySend(network)
3053 }
3154
3255 override fun onLost (network : Network ) {
33- actions.trySendBlocking(Action (Action .Type .Lost , network))
56+ Log .i(" NetworkObserve onLost network=$network " )
57+ networkInfos.remove(network)
58+ notifyDnsChange()
59+
60+ networks.trySend(network)
3461 }
3562
3663 override fun onLinkPropertiesChanged (network : Network , linkProperties : LinkProperties ) {
37- actions.trySendBlocking(Action (Action .Type .Changed , network))
64+ Log .i(" NetworkObserve onLinkPropertiesChanged network=$network $linkProperties " )
65+ networkInfos[network]?.dnsList = linkProperties.dnsServers
66+ notifyDnsChange()
67+
68+ networks.trySend(network)
69+ }
70+
71+ override fun onUnavailable () {
72+ Log .i(" NetworkObserve onUnavailable" )
3873 }
3974 }
4075
41- override suspend fun run () {
42- try {
76+ private fun register (): Boolean {
77+ Log .i(" NetworkObserve start register" )
78+ return try {
4379 connectivity.registerNetworkCallback(request, callback)
80+
81+ true
4482 } catch (e: Exception ) {
45- Log .w(" Observe network failed: $e " , e)
83+ Log .w(" NetworkObserve register failed" , e)
4684
47- return
85+ false
4886 }
87+ }
4988
89+ private fun unregister (): Boolean {
90+ Log .i(" NetworkObserve start unregister" )
5091 try {
51- val networks = mutableSetOf<Network >()
52-
53- while (true ) {
54- val action = actions.receive()
55-
56- val resolveDefault = when (action.type) {
57- Action .Type .Available -> {
58- networks.add(action.network)
92+ connectivity.unregisterNetworkCallback(callback)
93+ } catch (e: Exception ) {
94+ Log .w(" NetworkObserve unregister failed" , e)
95+ }
5996
60- true
61- }
62- Action .Type .Lost -> {
63- networks.remove(action.network)
97+ return false
98+ }
6499
65- true
66- }
67- Action .Type .Changed -> {
68- false
69- }
70- }
100+ private fun networkToInt (entry : Map .Entry <Network , NetworkInfo >): Int {
101+ val capabilities = connectivity.getNetworkCapabilities(entry.key)
102+ // calculate priority based on transport type, available state
103+ // lower value means higher priority
104+ // wifi > ethernet > usb tethering > bluetooth tethering > cellular > satellite > other
105+ return when {
106+ capabilities == null -> 100
107+ capabilities.hasTransport(NetworkCapabilities .TRANSPORT_VPN ) -> 90
108+ capabilities.hasTransport(NetworkCapabilities .TRANSPORT_WIFI ) -> 0
109+ capabilities.hasTransport(NetworkCapabilities .TRANSPORT_ETHERNET ) -> 1
110+ Build .VERSION .SDK_INT >= Build .VERSION_CODES .S && capabilities.hasTransport(NetworkCapabilities .TRANSPORT_USB ) -> 2
111+ capabilities.hasTransport(NetworkCapabilities .TRANSPORT_BLUETOOTH ) -> 3
112+ capabilities.hasTransport(NetworkCapabilities .TRANSPORT_CELLULAR ) -> 4
113+ Build .VERSION .SDK_INT >= Build .VERSION_CODES .VANILLA_ICE_CREAM && capabilities.hasTransport(NetworkCapabilities .TRANSPORT_SATELLITE ) -> 5
114+ // TRANSPORT_LOWPAN / TRANSPORT_THREAD / TRANSPORT_WIFI_AWARE are not for general internet access, which will not set as default route.
115+ else -> 6
116+ } + (if (entry.value.isAvailable()) 0 else 10 )
117+ }
71118
72- val dns = networks.flatMap { network ->
73- connectivity?.resolveDns(network) ? : emptyList()
74- }
119+ private fun notifyDnsChange () {
120+ val dnsList = (networkInfos.asSequence().minByOrNull { networkToInt(it) }?.value?.dnsList
121+ ? : emptyList()).map { x -> x.asSocketAddressText(53 ) }
122+ val prevDnsList = curDnsList
123+ if (dnsList.isNotEmpty() && prevDnsList != dnsList) {
124+ Log .i(" notifyDnsChange $prevDnsList -> $dnsList " )
125+ curDnsList = dnsList
126+ Clash .notifyDnsChanged(dnsList)
127+ }
128+ }
75129
76- Clash .notifyDnsChanged(dns)
130+ override suspend fun run () {
131+ register()
77132
78- Log .d(" DNS: $dns " )
133+ try {
134+ while (true ) {
135+ val quit = select {
136+ networks.onReceive {
137+ enqueueEvent(it)
79138
80- if (resolveDefault) {
81- val network = networks.maxByOrNull { net ->
82- connectivity.getNetworkCapabilities(net)?.let { cap ->
83- TRANSPORT_PRIORITY .indexOfFirst { cap.hasTransport(it) }
84- } ? : - 1
139+ false
85140 }
86-
87- enqueueEvent(network)
88-
89- Log .d(" Network: $network of $networks " )
141+ }
142+ if (quit) {
143+ return
90144 }
91145 }
92146 } finally {
93147 withContext(NonCancellable ) {
94- enqueueEvent( null )
148+ unregister( )
95149
150+ Log .i(" NetworkObserve dns = []" )
96151 Clash .notifyDnsChanged(emptyList())
97-
98- runCatching {
99- connectivity.unregisterNetworkCallback(callback)
100- }
101152 }
102153 }
103154 }
104-
105- companion object {
106- private val TRANSPORT_PRIORITY = sequence {
107- yield (NetworkCapabilities .TRANSPORT_CELLULAR )
108-
109- if (Build .VERSION .SDK_INT >= 27 ) {
110- yield (NetworkCapabilities .TRANSPORT_LOWPAN )
111- }
112-
113- yield (NetworkCapabilities .TRANSPORT_BLUETOOTH )
114-
115- if (Build .VERSION .SDK_INT >= 26 ) {
116- yield (NetworkCapabilities .TRANSPORT_WIFI_AWARE )
117- }
118-
119- yield (NetworkCapabilities .TRANSPORT_WIFI )
120-
121- if (Build .VERSION .SDK_INT >= 31 ) {
122- yield (NetworkCapabilities .TRANSPORT_USB )
123- }
124-
125- yield (NetworkCapabilities .TRANSPORT_ETHERNET )
126- }.toList()
127- }
128155}
0 commit comments