mirror of
https://github.com/MatsuriDayo/NekoBoxForAndroid.git
synced 2025-12-18 22:20:06 +08:00
implement front proxy & selector
This commit is contained in:
parent
c2f39c3bbc
commit
b6a5f334ba
@ -47,7 +47,7 @@ class BaseService {
|
||||
val receiver = broadcastReceiver { _, intent ->
|
||||
when (intent.action) {
|
||||
Intent.ACTION_SHUTDOWN -> service.persistStats()
|
||||
Action.RELOAD -> service.forceLoad()
|
||||
Action.RELOAD -> service.reload()
|
||||
Action.SWITCH_WAKE_LOCK -> runOnDefaultDispatcher { service.switchWakeLock() }
|
||||
else -> service.stopRunner()
|
||||
}
|
||||
@ -82,7 +82,7 @@ class BaseService {
|
||||
cb.updateWakeLockStatus(data?.proxy?.service?.wakeLock != null)
|
||||
}
|
||||
|
||||
val boardcastMutex = Mutex()
|
||||
private val boardcastMutex = Mutex()
|
||||
|
||||
suspend fun broadcast(work: (ISagerNetServiceCallback) -> Unit) {
|
||||
boardcastMutex.withLock {
|
||||
@ -143,10 +143,23 @@ class BaseService {
|
||||
fun onBind(intent: Intent): IBinder? =
|
||||
if (intent.action == Action.SERVICE) data.binder else null
|
||||
|
||||
fun forceLoad() {
|
||||
fun reload() {
|
||||
if (DataStore.selectedProxy == 0L) {
|
||||
stopRunner(false, (this as Context).getString(R.string.profile_empty))
|
||||
}
|
||||
if (canReloadSelector()) {
|
||||
var tag = ""
|
||||
data.proxy!!.config.trafficMap.forEach { (t, list) ->
|
||||
if (list.map { it.id }.contains(DataStore.selectedProxy)) {
|
||||
tag = t
|
||||
}
|
||||
}
|
||||
if (tag.isNotBlank()) {
|
||||
val success = data.proxy!!.box.selectOutbound(tag)
|
||||
Logs.d("selectOutbound $tag $success")
|
||||
}
|
||||
return
|
||||
}
|
||||
val s = data.state
|
||||
when {
|
||||
s == State.Stopped -> startRunner()
|
||||
@ -155,6 +168,18 @@ class BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
fun canReloadSelector(): Boolean {
|
||||
if ((data.proxy?.config?.selectorGroupId ?: -1L) < 0) return false
|
||||
val ent = SagerDatabase.proxyDao.getById(DataStore.selectedProxy) ?: return false
|
||||
val tmpBox = ProxyInstance(ent)
|
||||
tmpBox.buildConfigTmp()
|
||||
if (tmpBox.lastSelectorGroupId == data.proxy?.lastSelectorGroupId) {
|
||||
return true
|
||||
// TODO if profile changed?
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
suspend fun startProcesses() {
|
||||
data.proxy!!.launch()
|
||||
}
|
||||
|
||||
@ -8,18 +8,27 @@ import io.nekohasekai.sagernet.ktx.runOnIoDispatcher
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import moe.matsuri.nb4a.utils.JavaUtil
|
||||
|
||||
class ProxyInstance(profile: ProxyEntity, val service: BaseService.Interface) :
|
||||
class ProxyInstance(profile: ProxyEntity, var service: BaseService.Interface? = null) :
|
||||
BoxInstance(profile) {
|
||||
|
||||
var lastSelectorGroupId = -1L
|
||||
|
||||
// for TrafficLooper
|
||||
private var looper: TrafficLooper? = null
|
||||
|
||||
override fun buildConfig() {
|
||||
super.buildConfig()
|
||||
lastSelectorGroupId = super.config.selectorGroupId
|
||||
//
|
||||
Logs.d(config.config)
|
||||
if (BuildConfig.DEBUG) Logs.d(JavaUtil.gson.toJson(config.trafficMap))
|
||||
}
|
||||
|
||||
// only use this in temporary instance
|
||||
fun buildConfigTmp() {
|
||||
buildConfig()
|
||||
}
|
||||
|
||||
override suspend fun init() {
|
||||
super.init()
|
||||
pluginConfigs.forEach { (_, plugin) ->
|
||||
@ -32,7 +41,7 @@ class ProxyInstance(profile: ProxyEntity, val service: BaseService.Interface) :
|
||||
box.setAsMain()
|
||||
super.launch()
|
||||
runOnIoDispatcher {
|
||||
looper = TrafficLooper(service.data, this)
|
||||
looper = service?.let { TrafficLooper(it.data, this) }
|
||||
looper?.start()
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,6 +54,7 @@ class ConfigBuildResult(
|
||||
var mainEntId: Long,
|
||||
var trafficMap: Map<String, List<ProxyEntity>>,
|
||||
val alerts: List<Pair<Int, String>>,
|
||||
val selectorGroupId: Long,
|
||||
) {
|
||||
data class IndexEntity(var chain: LinkedHashMap<Int, ProxyEntity>)
|
||||
}
|
||||
@ -84,16 +85,19 @@ fun buildConfig(
|
||||
listOf(),
|
||||
proxy.id, //
|
||||
mapOf(TAG_PROXY to listOf(proxy)), //
|
||||
listOf()
|
||||
listOf(),
|
||||
-1L
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val trafficMap = HashMap<String, MutableList<ProxyEntity>>()
|
||||
val globalOutbounds = ArrayList<Long>()
|
||||
val group = SagerDatabase.groupDao.getById(proxy.groupId)
|
||||
var optionsToMerge = ""
|
||||
|
||||
fun ProxyEntity.resolveChain(): MutableList<ProxyEntity> {
|
||||
val frontProxy = group?.frontProxy?.let { SagerDatabase.proxyDao.getById(it) }
|
||||
val bean = requireBean()
|
||||
if (bean is ChainBean) {
|
||||
val beans = SagerDatabase.proxyDao.getEntities(bean.proxies)
|
||||
@ -103,18 +107,26 @@ fun buildConfig(
|
||||
val item = beansMap[proxyId] ?: continue
|
||||
beanList.addAll(item.resolveChain())
|
||||
}
|
||||
return beanList.asReversed()
|
||||
return if (frontProxy == null) {
|
||||
beanList.asReversed()
|
||||
} else {
|
||||
beanList.add(0, frontProxy)
|
||||
beanList.asReversed()
|
||||
}
|
||||
}
|
||||
return if (frontProxy == null) {
|
||||
mutableListOf(this)
|
||||
} else {
|
||||
mutableListOf(this, frontProxy)
|
||||
}
|
||||
return mutableListOf(this)
|
||||
}
|
||||
|
||||
val proxies = proxy.resolveChain()
|
||||
val extraRules = if (forTest) listOf() else SagerDatabase.rulesDao.enabledRules()
|
||||
val extraProxies =
|
||||
if (forTest) mapOf() else SagerDatabase.proxyDao.getEntities(extraRules.mapNotNull { rule ->
|
||||
(if (forTest) mapOf() else SagerDatabase.proxyDao.getEntities(extraRules.mapNotNull { rule ->
|
||||
rule.outbound.takeIf { it > 0 && it != proxy.id }
|
||||
}.toHashSet().toList()).associate { it.id to it.resolveChain() }
|
||||
|
||||
}.toHashSet().toList()).associate { it.id to it.resolveChain() }).toMutableMap()
|
||||
val buildSelector = !forTest && group?.isSelector == true
|
||||
val uidListDNSRemote = mutableListOf<Int>()
|
||||
val uidListDNSDirect = mutableListOf<Int>()
|
||||
val domainListDNSRemote = mutableListOf<String>()
|
||||
@ -444,8 +456,23 @@ fun buildConfig(
|
||||
return chainTagOut
|
||||
}
|
||||
|
||||
val tagProxy = buildChain(0, proxies)
|
||||
// build outbounds
|
||||
val tagMap = mutableMapOf<Long, String>()
|
||||
if (buildSelector) {
|
||||
val list = group?.id?.let { SagerDatabase.proxyDao.getByGroup(it) }
|
||||
list?.forEach {
|
||||
tagMap[it.id] = buildChain(it.id, it.resolveChain())
|
||||
}
|
||||
outbounds.add(0, Outbound_SelectorOptions().apply {
|
||||
type = "selector"
|
||||
tag = TAG_PROXY
|
||||
default_ = tagMap[proxy.id]
|
||||
outbounds = tagMap.values.toList()
|
||||
}.asMap())
|
||||
} else {
|
||||
buildChain(0, proxy.resolveChain())
|
||||
}
|
||||
// build outbounds from route item
|
||||
extraProxies.forEach { (key, entities) ->
|
||||
tagMap[key] = buildChain(key, entities)
|
||||
}
|
||||
@ -521,10 +548,10 @@ fun buildConfig(
|
||||
}
|
||||
|
||||
outbound = when (val outId = rule.outbound) {
|
||||
0L -> tagProxy
|
||||
0L -> TAG_PROXY
|
||||
-1L -> TAG_BYPASS
|
||||
-2L -> TAG_BLOCK
|
||||
else -> if (outId == proxy.id) tagProxy else tagMap[outId]
|
||||
else -> if (outId == proxy.id) TAG_PROXY else tagMap[outId]
|
||||
?: throw Exception("invalid rule")
|
||||
}
|
||||
})
|
||||
@ -569,7 +596,7 @@ fun buildConfig(
|
||||
for (dns in remoteDns) {
|
||||
if (!dns.isIpAddress()) continue
|
||||
route.rules.add(Rule_DefaultOptions().apply {
|
||||
outbound = tagProxy
|
||||
outbound = TAG_PROXY
|
||||
ip_cidr = listOf(dns)
|
||||
})
|
||||
}
|
||||
@ -721,7 +748,8 @@ fun buildConfig(
|
||||
externalIndexMap,
|
||||
proxy.id,
|
||||
trafficMap,
|
||||
alerts
|
||||
alerts,
|
||||
if (buildSelector) group!!.id else -1L
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@ import (
|
||||
|
||||
sblog "github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/outbound"
|
||||
)
|
||||
|
||||
var mainInstance *BoxInstance
|
||||
@ -70,7 +71,8 @@ type BoxInstance struct {
|
||||
cancel context.CancelFunc
|
||||
state int
|
||||
|
||||
v2api *boxapi.SbV2rayServer
|
||||
v2api *boxapi.SbV2rayServer
|
||||
selector *outbound.Selector
|
||||
|
||||
ForTest bool
|
||||
}
|
||||
@ -111,6 +113,13 @@ func NewSingBoxInstance(config string) (b *BoxInstance, err error) {
|
||||
platformFormatter := platformFormatter_.Interface().(*sblog.Formatter)
|
||||
platformFormatter.DisableColors = true
|
||||
|
||||
// selector
|
||||
if proxy, ok := b.Router().Outbound("proxy"); ok {
|
||||
if selector, ok := proxy.(*outbound.Selector); ok {
|
||||
b.selector = selector
|
||||
}
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
@ -182,6 +191,13 @@ func (b *BoxInstance) QueryStats(tag, direct string) int64 {
|
||||
return b.v2api.QueryStats(fmt.Sprintf("outbound>>>%s>>>traffic>>>%s", tag, direct))
|
||||
}
|
||||
|
||||
func (b *BoxInstance) SelectOutbound(tag string) bool {
|
||||
if b.selector != nil {
|
||||
return b.selector.SelectOutbound(tag)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func UrlTest(i *BoxInstance, link string, timeout int32) (int32, error) {
|
||||
if i == nil {
|
||||
// test current
|
||||
|
||||
Loading…
Reference in New Issue
Block a user