diff --git a/service/src/main/java/com/github/kr328/clash/service/ClashNotification.kt b/service/src/main/java/com/github/kr328/clash/service/ClashNotification.kt index 52d6b4c..e40a3e9 100644 --- a/service/src/main/java/com/github/kr328/clash/service/ClashNotification.kt +++ b/service/src/main/java/com/github/kr328/clash/service/ClashNotification.kt @@ -114,8 +114,7 @@ class ClashNotification(private val context: ClashService, enableRefresh: Boolea delay(1000) } - } - finally { + } finally { Log.d("Clash Notification Stopped") } } diff --git a/service/src/main/java/com/github/kr328/clash/service/ClashService.kt b/service/src/main/java/com/github/kr328/clash/service/ClashService.kt index 3a0974f..36ba67e 100644 --- a/service/src/main/java/com/github/kr328/clash/service/ClashService.kt +++ b/service/src/main/java/com/github/kr328/clash/service/ClashService.kt @@ -9,8 +9,10 @@ import android.os.IBinder import com.github.kr328.clash.core.Clash import com.github.kr328.clash.service.data.ClashDatabase import com.github.kr328.clash.service.util.* -import kotlinx.coroutines.* +import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex class ClashService : BaseService() { @@ -98,7 +100,7 @@ class ClashService : BaseService() { } private suspend fun reloadProfile() { - if ( !loadLock.tryLock() ) + if (!loadLock.tryLock()) return try { diff --git a/service/src/main/java/com/github/kr328/clash/service/TunService.kt b/service/src/main/java/com/github/kr328/clash/service/TunService.kt index 2576720..c36b8df 100644 --- a/service/src/main/java/com/github/kr328/clash/service/TunService.kt +++ b/service/src/main/java/com/github/kr328/clash/service/TunService.kt @@ -7,7 +7,6 @@ import com.github.kr328.clash.core.utils.Log import com.github.kr328.clash.service.net.DefaultNetworkChannel import com.github.kr328.clash.service.util.broadcastNetworkChanged import kotlinx.coroutines.* -import kotlinx.coroutines.channels.broadcast import java.net.InetAddress class TunService : VpnService(), CoroutineScope by MainScope() { @@ -71,18 +70,22 @@ class TunService : VpnService(), CoroutineScope by MainScope() { Log.i("Network changed to ${d?.second}") - if ( d == null ) { + if (d == null) { setUnderlyingNetworks(null) continue } setUnderlyingNetworks(arrayOf(d.first)) - if ( settings.get(Settings.AUTO_ADD_SYSTEM_DNS) ) { + if (settings.get(Settings.AUTO_ADD_SYSTEM_DNS)) { withContext(Dispatchers.Default) { - Clash.appendDns(d.second.dnsServers - .map(InetAddress::getHostName) - .filter(String::isNotBlank)) + val dnsServers = d.second?.dnsServers ?: emptyList() + + Clash.appendDns( + dnsServers + .map(InetAddress::getHostName) + .filter(String::isNotBlank) + ) } } diff --git a/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileProxyEntity.kt b/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileProxyEntity.kt index c706430..4c37090 100644 --- a/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileProxyEntity.kt +++ b/service/src/main/java/com/github/kr328/clash/service/data/ClashProfileProxyEntity.kt @@ -1,6 +1,8 @@ package com.github.kr328.clash.service.data -import androidx.room.* +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.ForeignKey @Entity( tableName = "profile_select_proxies", diff --git a/service/src/main/java/com/github/kr328/clash/service/net/DefaultNetworkChannel.kt b/service/src/main/java/com/github/kr328/clash/service/net/DefaultNetworkChannel.kt index 91643f1..ad0dd30 100644 --- a/service/src/main/java/com/github/kr328/clash/service/net/DefaultNetworkChannel.kt +++ b/service/src/main/java/com/github/kr328/clash/service/net/DefaultNetworkChannel.kt @@ -1,29 +1,41 @@ -// from https://github.com/shadowsocks/shadowsocks-android/blob/master/core/src/main/java/com/github/shadowsocks/net/DefaultNetworkListener.kt package com.github.kr328.clash.service.net import android.content.Context import android.net.* -import com.github.kr328.clash.core.utils.Log import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.sync.Mutex class DefaultNetworkChannel(val context: Context, scope: CoroutineScope) : - CoroutineScope by scope, Channel?> by Channel(Channel.CONFLATED) { + CoroutineScope by scope, + Channel?> by Channel(Channel.CONFLATED) { + private val sendLock = Mutex() private val connectivity = context.getSystemService(ConnectivityManager::class.java)!! private val callback = object : ConnectivityManager.NetworkCallback() { - var current: Network? = null + private val capabilitiesCache = mutableMapOf() - override fun onLost(network: Network) { - if ( current == network ) - offer(null) + override fun onAvailable(network: Network) { + sendDefaultNetwork() } - override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) { - if ( linkProperties.routes.any { it.isDefaultRoute } ) { - offer(network to linkProperties) - current = network - } + override fun onLost(network: Network) { + sendDefaultNetwork() + + capabilitiesCache.remove(network) + } + + override fun onCapabilitiesChanged( + network: Network, + networkCapabilities: NetworkCapabilities? + ) { + val cap = capabilitiesCache[network] + + if (cap?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + != networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + ) + sendDefaultNetwork() + + capabilitiesCache[network] = networkCapabilities } } @@ -35,54 +47,55 @@ class DefaultNetworkChannel(val context: Context, scope: CoroutineScope) : connectivity.unregisterNetworkCallback(callback) } -// private fun sendDefaultNetwork() { -// if (detectDelayLock.tryLock()) { -// launch { -// delay(1000) -// -// val network = detectDefaultNetwork() -// val linkProperties = network?.let { connectivity.getLinkProperties(it) } -// -// if ( network != null && linkProperties != null ) -// send(network to linkProperties) -// else -// send(null) -// -// detectDelayLock.unlock() -// } -// } -// } -// -// private suspend fun detectDefaultNetwork(): Network? = withContext(Dispatchers.Default) { -// return@withContext try { -// connectivity.allNetworks -// .asSequence() -// .mapNotNull { network -> -// connectivity.getNetworkCapabilities(network)?.let { it to network } -// } -// .filterNot { -// it.first.hasTransport(NetworkCapabilities.TRANSPORT_VPN) || -// !it.first.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) -// } -// .sortedBy { -// when { -// it.first.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> 0 -// it.first.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> 1 -// it.first.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> 2 -// it.first.hasTransport(NetworkCapabilities.TRANSPORT_LOWPAN) -> 3 -// it.first.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> 4 -// else -> 5 -// } + if (it.first.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) -// -1000 -// else -// 0 -// } -// .map { -// it.second -// } -// .firstOrNull() -// } catch (e: Exception) { -// null -// } -// } + private fun sendDefaultNetwork() { + if (!sendLock.tryLock()) + return + + launch { + delay(1000) + + val network = detectDefaultNetwork() + val link = network?.let(connectivity::getLinkProperties) + + if (network != null) + send(network to link) + else + send(null) + + sendLock.unlock() + } + } + + private suspend fun detectDefaultNetwork() = withContext(Dispatchers.Default) { + try { + connectivity.allNetworks + .asSequence() + .mapNotNull { network -> + connectivity.getNetworkCapabilities(network)?.let { it to network } + } + .filterNot { + it.first.hasTransport(NetworkCapabilities.TRANSPORT_VPN) || + !it.first.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + } + .sortedBy { + when { + it.first.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> 0 + it.first.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> 1 + it.first.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> 2 + it.first.hasTransport(NetworkCapabilities.TRANSPORT_LOWPAN) -> 3 + it.first.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> 4 + else -> 5 + } + if (it.first.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) + -1000 + else + 0 + } + .map { + it.second + } + .firstOrNull() + } catch (e: Exception) { + null + } + } } \ No newline at end of file diff --git a/service/src/main/java/com/github/kr328/clash/service/util/BroadcastUtils.kt b/service/src/main/java/com/github/kr328/clash/service/util/BroadcastUtils.kt index b438263..136b5a9 100644 --- a/service/src/main/java/com/github/kr328/clash/service/util/BroadcastUtils.kt +++ b/service/src/main/java/com/github/kr328/clash/service/util/BroadcastUtils.kt @@ -3,9 +3,7 @@ package com.github.kr328.clash.service.util import android.content.Context import android.content.Intent import com.github.kr328.clash.service.Intents -import com.github.kr328.clash.service.data.ClashDatabase import com.github.kr328.clash.service.data.ClashProfileEntity -import com.github.kr328.clash.service.data.ClashProfileProxyEntity fun Context.sendBroadcastSelf(intent: Intent) { this.sendBroadcast(intent.setPackage(this.packageName))