Skip to content

Commit

Permalink
refactor: add official interface callback
Browse files Browse the repository at this point in the history
copy from sing-box-for-android

Closes #19

refactor: use defaultNetwork instead of underlying network

Signed-off-by: HystericalDragon <HystericalDragons@proton.me>

fix #33

Signed-off-by: HystericalDragon <HystericalDragons@proton.me>
  • Loading branch information
xchacha20-poly1305 committed Feb 5, 2024
1 parent 36e5a15 commit de7be95
Show file tree
Hide file tree
Showing 15 changed files with 453 additions and 139 deletions.
22 changes: 12 additions & 10 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,23 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>

<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />


<uses-feature
android:name="android.software.leanback"
Expand Down Expand Up @@ -271,16 +273,16 @@
android:name="io.nekohasekai.sagernet.bg.ProxyService"
android:exported="false"
android:process=":bg"
android:foregroundServiceType="specialUse">
android:foregroundServiceType="specialUse|location">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="proxy" />
android:value="proxy"/>
</service>

<service
android:name="io.nekohasekai.sagernet.bg.VpnService"
android:exported="false"
android:foregroundServiceType="specialUse"
android:foregroundServiceType="specialUse|location"
android:label="@string/app_name"
android:permission="android.permission.BIND_VPN_SERVICE"
android:process=":bg">
Expand All @@ -290,7 +292,7 @@
</intent-filter>
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="vpn" />
android:value="vpn"/>
</service>

<service
Expand Down
8 changes: 3 additions & 5 deletions app/src/main/java/io/nekohasekai/sagernet/SagerNet.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.net.ConnectivityManager
import android.net.Network
import android.net.wifi.WifiManager
import android.os.Build
import android.os.PowerManager
Expand All @@ -18,13 +17,14 @@ import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import go.Seq
import io.nekohasekai.sagernet.bg.DefaultNetworkListener
import io.nekohasekai.sagernet.bg.DefaultNetworkMonitor
import io.nekohasekai.sagernet.bg.SagerConnection
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.ktx.Logs
import io.nekohasekai.sagernet.ktx.runOnDefaultDispatcher
import io.nekohasekai.sagernet.ui.MainActivity
import io.nekohasekai.sagernet.utils.CrashHandler
import io.nekohasekai.sagernet.utils.DefaultNetworkListener
import io.nekohasekai.sagernet.utils.PackageCache
import io.nekohasekai.sagernet.utils.Theme
import kotlinx.coroutines.DEBUG_PROPERTY_NAME
Expand Down Expand Up @@ -88,7 +88,7 @@ class SagerNet : Application(),
Theme.applyNightTheme()
runOnDefaultDispatcher {
DefaultNetworkListener.start(this) {
underlyingNetwork = it
DefaultNetworkMonitor.defaultNetwork = it
}
}
}
Expand Down Expand Up @@ -198,8 +198,6 @@ class SagerNet : Application(),
fun stopService() =
application.sendBroadcast(Intent(Action.CLOSE).setPackage(application.packageName))

var underlyingNetwork: Network? = null

}


Expand Down
20 changes: 3 additions & 17 deletions app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.SagerDatabase
import io.nekohasekai.sagernet.ktx.*
import io.nekohasekai.sagernet.plugin.PluginManager
import io.nekohasekai.sagernet.utils.DefaultNetworkListener
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
Expand Down Expand Up @@ -206,6 +205,7 @@ class BaseService {
}

suspend fun startProcesses() {
DefaultNetworkMonitor.start()
data.proxy!!.launch()
}

Expand All @@ -222,7 +222,7 @@ class BaseService {
wakeLock = null
}
runOnDefaultDispatcher {
DefaultNetworkListener.stop(this)
DefaultNetworkMonitor.stop()
}
}

Expand Down Expand Up @@ -267,21 +267,7 @@ class BaseService {
var upstreamInterfaceName: String?

suspend fun preInit() {
DefaultNetworkListener.start(this) {
SagerNet.connectivity.getLinkProperties(it)?.also { link ->
SagerNet.underlyingNetwork = it
DataStore.vpnService?.updateUnderlyingNetwork()
//
val oldName = upstreamInterfaceName
if (oldName != link.interfaceName) {
upstreamInterfaceName = link.interfaceName
}
if (oldName != null && upstreamInterfaceName != null && oldName != upstreamInterfaceName) {
Logs.d("Network changed: $oldName -> $upstreamInterfaceName")
LibcoreUtil.resetAllConnections(true)
}
}
}
DataStore.vpnService?.updateUnderlyingNetwork()
}

var wakeLock: PowerManager.WakeLock?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.nekohasekai.sagernet.utils
package io.nekohasekai.sagernet.bg

import android.annotation.TargetApi
import android.net.ConnectivityManager
Expand All @@ -9,7 +9,6 @@ import android.os.Build
import android.os.Handler
import android.os.Looper
import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.ktx.Logs
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
Expand Down Expand Up @@ -76,8 +75,12 @@ object DefaultNetworkListener {
}
}

suspend fun start(key: Any, listener: (Network?) -> Unit) =
networkActor.send(NetworkMessage.Start(key, listener))
suspend fun start(key: Any, listener: (Network?) -> Unit) = networkActor.send(
NetworkMessage.Start(
key,
listener
)
)

suspend fun get() = if (fallback) @TargetApi(23) {
SagerNet.connectivity.activeNetwork
Expand All @@ -91,17 +94,29 @@ object DefaultNetworkListener {

// NB: this runs in ConnectivityThread, and this behavior cannot be changed until API 26
private object Callback : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) =
runBlocking { networkActor.send(NetworkMessage.Put(network)) }
override fun onAvailable(network: Network) = runBlocking {
networkActor.send(
NetworkMessage.Put(
network
)
)
}

override fun onCapabilitiesChanged(
network: Network, networkCapabilities: NetworkCapabilities
) { // it's a good idea to refresh capabilities
network: Network,
networkCapabilities: NetworkCapabilities
) {
// it's a good idea to refresh capabilities
runBlocking { networkActor.send(NetworkMessage.Update(network)) }
}

override fun onLost(network: Network) =
runBlocking { networkActor.send(NetworkMessage.Lost(network)) }
override fun onLost(network: Network) = runBlocking {
networkActor.send(
NetworkMessage.Lost(
network
)
)
}
}

private var fallback = false
Expand All @@ -126,37 +141,40 @@ object DefaultNetworkListener {
* Source: https://android.googlesource.com/platform/frameworks/base/+/2df4c7d/services/core/java/com/android/server/ConnectivityService.java#887
*/
private fun register() {
try {
fallback = false
when (Build.VERSION.SDK_INT) {
in 31..Int.MAX_VALUE -> @TargetApi(31) {
SagerNet.connectivity.registerBestMatchingNetworkCallback(
request, Callback, mainHandler
)
}

in 28 until 31 -> @TargetApi(28) { // we want REQUEST here instead of LISTEN
SagerNet.connectivity.requestNetwork(request, Callback, mainHandler)
}

in 26 until 28 -> @TargetApi(26) {
SagerNet.connectivity.registerDefaultNetworkCallback(Callback, mainHandler)
}

in 24 until 26 -> @TargetApi(24) {
SagerNet.connectivity.registerDefaultNetworkCallback(Callback)
}

else -> {
SagerNet.connectivity.requestNetwork(request, Callback)
// known bug on API 23: https://stackoverflow.com/a/33509180/2245107
}
when (Build.VERSION.SDK_INT) {
in 31..Int.MAX_VALUE -> @TargetApi(31) {
SagerNet.connectivity.registerBestMatchingNetworkCallback(
request,
Callback,
mainHandler
)
}

in 28 until 31 -> @TargetApi(28) { // we want REQUEST here instead of LISTEN
SagerNet.connectivity.requestNetwork(request, Callback, mainHandler)
}

in 26 until 28 -> @TargetApi(26) {
SagerNet.connectivity.registerDefaultNetworkCallback(Callback, mainHandler)
}

in 24 until 26 -> @TargetApi(24) {
SagerNet.connectivity.registerDefaultNetworkCallback(Callback)
}

else -> try {
fallback = false
SagerNet.connectivity.requestNetwork(request, Callback)
} catch (e: RuntimeException) {
fallback =
true // known bug on API 23: https://stackoverflow.com/a/33509180/2245107
}
} catch (e: Exception) {
Logs.w(e)
fallback = true
}
}

private fun unregister() = SagerNet.connectivity.unregisterNetworkCallback(Callback)
}
private fun unregister() {
runCatching {
SagerNet.connectivity.unregisterNetworkCallback(Callback)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.nekohasekai.sagernet.bg

import android.net.Network
import android.os.Build
import libcore.InterfaceUpdateListener
import io.nekohasekai.sagernet.SagerNet
import java.net.NetworkInterface

object DefaultNetworkMonitor {
var defaultNetwork: Network? = null
private var listener: InterfaceUpdateListener? = null

suspend fun start() {
DefaultNetworkListener.start(this) {
defaultNetwork = it
checkDefaultInterfaceUpdate(it)
}
defaultNetwork = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
SagerNet.connectivity.activeNetwork
} else {
DefaultNetworkListener.get()
}
}

suspend fun stop() {
DefaultNetworkListener.stop(this)
}

fun setListener(listener: InterfaceUpdateListener?) {
this.listener = listener
checkDefaultInterfaceUpdate(defaultNetwork)
}

private fun checkDefaultInterfaceUpdate(
newNetwork: Network?
) {
val listener = listener ?: return
if (newNetwork != null) {
val interfaceName =
(SagerNet.connectivity.getLinkProperties(newNetwork) ?: return).interfaceName
for (times in 0 until 10) {
var interfaceIndex: Int
try {
interfaceIndex = NetworkInterface.getByName(interfaceName).index
} catch (e: Exception) {
Thread.sleep(100)
continue
}
listener.updateDefaultInterface(interfaceName, interfaceIndex)
}
} else {
listener.updateDefaultInterface("", -1)
}
}
}
6 changes: 3 additions & 3 deletions app/src/main/java/io/nekohasekai/sagernet/bg/VpnService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,9 @@ class VpnService : BaseVpnService(),

fun updateUnderlyingNetwork(builder: Builder? = null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
SagerNet.underlyingNetwork?.let {
builder?.setUnderlyingNetworks(arrayOf(SagerNet.underlyingNetwork))
?: setUnderlyingNetworks(arrayOf(SagerNet.underlyingNetwork))
DefaultNetworkMonitor.defaultNetwork?.let {
builder?.setUnderlyingNetworks(arrayOf(DefaultNetworkMonitor.defaultNetwork))
?: setUnderlyingNetworks(arrayOf(DefaultNetworkMonitor.defaultNetwork))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package io.nekohasekai.sagernet.group
import io.nekohasekai.sagernet.IPv6Mode
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.bg.DefaultNetworkMonitor
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.GroupManager
import io.nekohasekai.sagernet.database.ProxyGroup
Expand Down Expand Up @@ -63,13 +63,13 @@ abstract class GroupUpdater {
lookupJobs.add(GlobalScope.launch(lookupPool) {
try {
val results = if (
SagerNet.underlyingNetwork != null &&
DefaultNetworkMonitor.defaultNetwork != null &&
DataStore.enableFakeDns &&
DataStore.serviceState.started &&
DataStore.serviceMode == Key.MODE_VPN
) {
// FakeDNS
SagerNet.underlyingNetwork!!
DefaultNetworkMonitor.defaultNetwork!!
.getAllByName(profile.serverAddress)
.filterNotNull()
} else {
Expand Down
Loading

0 comments on commit de7be95

Please sign in to comment.