optimize code

This commit is contained in:
arm64v8a 2023-07-01 18:07:25 +09:00
parent d0c68e38ef
commit ad0dd1d63f
9 changed files with 133 additions and 164 deletions

View File

@ -6,8 +6,6 @@ import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.Network import android.net.Network
@ -20,27 +18,21 @@ import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import go.Seq import go.Seq
import io.nekohasekai.sagernet.bg.SagerConnection import io.nekohasekai.sagernet.bg.SagerConnection
import io.nekohasekai.sagernet.bg.ServiceNotification
import io.nekohasekai.sagernet.database.DataStore import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.SagerDatabase
import io.nekohasekai.sagernet.ktx.Logs import io.nekohasekai.sagernet.ktx.Logs
import io.nekohasekai.sagernet.ktx.runOnDefaultDispatcher import io.nekohasekai.sagernet.ktx.runOnDefaultDispatcher
import io.nekohasekai.sagernet.ui.MainActivity import io.nekohasekai.sagernet.ui.MainActivity
import io.nekohasekai.sagernet.utils.* import io.nekohasekai.sagernet.utils.*
import kotlinx.coroutines.DEBUG_PROPERTY_NAME import kotlinx.coroutines.DEBUG_PROPERTY_NAME
import kotlinx.coroutines.DEBUG_PROPERTY_VALUE_ON import kotlinx.coroutines.DEBUG_PROPERTY_VALUE_ON
import libcore.BoxPlatformInterface
import libcore.Libcore import libcore.Libcore
import libcore.NB4AInterface import moe.matsuri.nb4a.NativeInterface
import moe.matsuri.nb4a.utils.JavaUtil import moe.matsuri.nb4a.utils.JavaUtil
import moe.matsuri.nb4a.utils.LibcoreUtil
import moe.matsuri.nb4a.utils.cleanWebview import moe.matsuri.nb4a.utils.cleanWebview
import java.net.InetSocketAddress
import androidx.work.Configuration as WorkConfiguration import androidx.work.Configuration as WorkConfiguration
class SagerNet : Application(), class SagerNet : Application(),
BoxPlatformInterface, WorkConfiguration.Provider {
WorkConfiguration.Provider, NB4AInterface {
override fun attachBaseContext(base: Context) { override fun attachBaseContext(base: Context) {
super.attachBaseContext(base) super.attachBaseContext(base)
@ -48,6 +40,8 @@ class SagerNet : Application(),
application = this application = this
} }
val nativeInterface = NativeInterface()
val externalAssets by lazy { getExternalFilesDir(null) ?: filesDir } val externalAssets by lazy { getExternalFilesDir(null) ?: filesDir }
val process = JavaUtil.getProcessName() val process = JavaUtil.getProcessName()
val isMainProcess = process == BuildConfig.APPLICATION_ID val isMainProcess = process == BuildConfig.APPLICATION_ID
@ -81,12 +75,9 @@ class SagerNet : Application(),
externalAssets.absolutePath + "/", externalAssets.absolutePath + "/",
DataStore.logBufSize, DataStore.logBufSize,
DataStore.logLevel > 0, DataStore.logLevel > 0,
this nativeInterface, nativeInterface
) )
// libbox: platform interface
Libcore.setBoxPlatformInterface(this)
if (isMainProcess) { if (isMainProcess) {
Theme.apply(this) Theme.apply(this)
Theme.applyNightTheme() Theme.applyNightTheme()
@ -133,11 +124,6 @@ class SagerNet : Application(),
uiMode.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION uiMode.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION
} }
// /data/user_de available when not unlocked
val deviceStorage by lazy {
if (Build.VERSION.SDK_INT < 24) application else DeviceStorageApp(application)
}
val configureIntent: (Context) -> PendingIntent by lazy { val configureIntent: (Context) -> PendingIntent by lazy {
{ {
PendingIntent.getActivity( PendingIntent.getActivity(
@ -209,82 +195,4 @@ class SagerNet : Application(),
} }
// libbox interface
override fun autoDetectInterfaceControl(fd: Int) {
DataStore.vpnService?.protect(fd)
}
override fun openTun(singTunOptionsJson: String, tunPlatformOptionsJson: String): Long {
if (DataStore.vpnService == null) {
throw Exception("no VpnService")
}
return DataStore.vpnService!!.startVpn(singTunOptionsJson, tunPlatformOptionsJson).toLong()
}
override fun useProcFS(): Boolean {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.Q
}
@RequiresApi(Build.VERSION_CODES.Q)
override fun findConnectionOwner(
ipProto: Int, srcIp: String, srcPort: Int, destIp: String, destPort: Int
): Int {
return connectivity.getConnectionOwnerUid(
ipProto, InetSocketAddress(srcIp, srcPort), InetSocketAddress(destIp, destPort)
)
}
override fun packageNameByUid(uid: Int): String {
PackageCache.awaitLoadSync()
if (uid <= 1000L) {
return "android"
}
val packageNames = PackageCache.uidMap[uid]
if (!packageNames.isNullOrEmpty()) for (packageName in packageNames) {
return packageName
}
error("unknown uid $uid")
}
override fun uidByPackageName(packageName: String): Int {
PackageCache.awaitLoadSync()
return PackageCache[packageName] ?: 0
}
// nb4a interface
override fun useOfficialAssets(): Boolean {
return DataStore.rulesProvider == 0
}
override fun selector_OnProxySelected(selectorTag: String, tag: String) {
if (selectorTag != "proxy") {
Logs.d("other selector: $selectorTag")
return
}
LibcoreUtil.resetAllConnections(true)
DataStore.baseService?.apply {
runOnDefaultDispatcher {
val id = data.proxy!!.config.profileTagMap
.filterValues { it == tag }.keys.firstOrNull() ?: -1
val ent = SagerDatabase.proxyDao.getById(id) ?: return@runOnDefaultDispatcher
// traffic & title
data.proxy?.apply {
looper?.selectMain(id)
displayProfileName = ServiceNotification.genTitle(ent)
data.notification?.postNotificationTitle(displayProfileName)
}
// post binder
data.binder.broadcast { b ->
b.cbSelectorUpdate(id)
}
}
}
}
} }

View File

@ -1,6 +1,5 @@
package io.nekohasekai.sagernet.bg.proto package io.nekohasekai.sagernet.bg.proto
import android.os.Build
import android.os.SystemClock import android.os.SystemClock
import io.nekohasekai.sagernet.SagerNet import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.bg.AbstractInstance import io.nekohasekai.sagernet.bg.AbstractInstance
@ -117,10 +116,8 @@ abstract class BoxInstance(
override fun launch() { override fun launch() {
// TODO move, this is not box // TODO move, this is not box
val context = val cacheDir = File(SagerNet.application.cacheDir, "tmpcfg")
if (Build.VERSION.SDK_INT < 24 || SagerNet.user.isUserUnlocked) SagerNet.application else SagerNet.deviceStorage cacheDir.mkdirs()
val cache = File(context.cacheDir, "tmpcfg")
cache.mkdirs()
for ((chain) in config.externalIndex) { for ((chain) in config.externalIndex) {
chain.entries.forEachIndexed { index, (port, profile) -> chain.entries.forEachIndexed { index, (port, profile) ->
@ -135,7 +132,7 @@ abstract class BoxInstance(
bean is TrojanGoBean -> { bean is TrojanGoBean -> {
val configFile = File( val configFile = File(
cache, "trojan_go_" + SystemClock.elapsedRealtime() + ".json" cacheDir, "trojan_go_" + SystemClock.elapsedRealtime() + ".json"
) )
configFile.parentFile?.mkdirs() configFile.parentFile?.mkdirs()
configFile.writeText(config) configFile.writeText(config)
@ -150,7 +147,7 @@ abstract class BoxInstance(
bean is NaiveBean -> { bean is NaiveBean -> {
val configFile = File( val configFile = File(
cache, "naive_" + SystemClock.elapsedRealtime() + ".json" cacheDir, "naive_" + SystemClock.elapsedRealtime() + ".json"
) )
configFile.parentFile?.mkdirs() configFile.parentFile?.mkdirs()
@ -161,7 +158,7 @@ abstract class BoxInstance(
if (bean.certificates.isNotBlank()) { if (bean.certificates.isNotBlank()) {
val certFile = File( val certFile = File(
cache, "naive_" + SystemClock.elapsedRealtime() + ".crt" cacheDir, "naive_" + SystemClock.elapsedRealtime() + ".crt"
) )
certFile.parentFile?.mkdirs() certFile.parentFile?.mkdirs()
@ -180,7 +177,7 @@ abstract class BoxInstance(
bean is HysteriaBean -> { bean is HysteriaBean -> {
val configFile = File( val configFile = File(
cache, "hysteria_" + SystemClock.elapsedRealtime() + ".json" cacheDir, "hysteria_" + SystemClock.elapsedRealtime() + ".json"
) )
configFile.parentFile?.mkdirs() configFile.parentFile?.mkdirs()
@ -213,7 +210,7 @@ abstract class BoxInstance(
any as JSONObject any as JSONObject
val name = any.getString("name") val name = any.getString("name")
val configFile = File(cache, name) val configFile = File(cacheDir, name)
configFile.parentFile?.mkdirs() configFile.parentFile?.mkdirs()
val content = any.getString("content") val content = any.getString("content")
configFile.writeText(content) configFile.writeText(content)
@ -243,10 +240,8 @@ abstract class BoxInstance(
} }
bean is TuicBean -> { bean is TuicBean -> {
val configFile = File( val configFile =
context.noBackupFilesDir, File(cacheDir, "tuic_" + SystemClock.elapsedRealtime() + ".json")
"tuic_" + SystemClock.elapsedRealtime() + ".json"
)
configFile.parentFile?.mkdirs() configFile.parentFile?.mkdirs()
configFile.writeText(config) configFile.writeText(config)

View File

@ -422,24 +422,22 @@ fun buildConfig(
} catch (_: Exception) { } catch (_: Exception) {
} }
// custom JSON merge // domain_strategy
if (bean.customOutboundJson.isNotBlank()) {
Util.mergeJSON(bean.customOutboundJson, currentOutbound)
}
}
pastEntity?.requireBean()?.apply { pastEntity?.requireBean()?.apply {
// don't loopback // don't loopback
if (currentDomainStrategy != "" && !serverAddress.isIpAddress()) { if (currentDomainStrategy != "" && !serverAddress.isIpAddress()) {
domainListDNSDirectForce.add("full:$serverAddress") domainListDNSDirectForce.add("full:$serverAddress")
} }
} }
if (forTest) { currentOutbound["domain_strategy"] = if (forTest) "" else currentDomainStrategy
currentDomainStrategy = ""
// custom JSON merge
if (bean.customOutboundJson.isNotBlank()) {
Util.mergeJSON(bean.customOutboundJson, currentOutbound)
}
} }
currentOutbound["tag"] = tagOut currentOutbound["tag"] = tagOut
currentOutbound["domain_strategy"] = currentDomainStrategy
// External proxy need a dokodemo-door inbound to forward the traffic // External proxy need a dokodemo-door inbound to forward the traffic
// For external proxy software, their traffic must goes to v2ray-core to use protected fd. // For external proxy software, their traffic must goes to v2ray-core to use protected fd.

View File

@ -1,20 +0,0 @@
package io.nekohasekai.sagernet.utils
import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.app.Application
import android.content.Context
@SuppressLint("Registered")
@TargetApi(24)
class DeviceStorageApp(context: Context) : Application() {
init {
attachBaseContext(context.createDeviceProtectedStorageContext())
}
/**
* Thou shalt not get the REAL underlying application context which would no longer be operating under device
* protected storage.
*/
override fun getApplicationContext() = this
}

View File

@ -0,0 +1,96 @@
package moe.matsuri.nb4a
import android.os.Build
import androidx.annotation.RequiresApi
import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.bg.ServiceNotification
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.SagerDatabase
import io.nekohasekai.sagernet.ktx.Logs
import io.nekohasekai.sagernet.ktx.runOnDefaultDispatcher
import io.nekohasekai.sagernet.utils.PackageCache
import libcore.BoxPlatformInterface
import libcore.NB4AInterface
import moe.matsuri.nb4a.utils.LibcoreUtil
import java.net.InetSocketAddress
class NativeInterface : BoxPlatformInterface, NB4AInterface {
// libbox interface
override fun autoDetectInterfaceControl(fd: Int) {
DataStore.vpnService?.protect(fd)
}
override fun openTun(singTunOptionsJson: String, tunPlatformOptionsJson: String): Long {
if (DataStore.vpnService == null) {
throw Exception("no VpnService")
}
return DataStore.vpnService!!.startVpn(singTunOptionsJson, tunPlatformOptionsJson).toLong()
}
override fun useProcFS(): Boolean {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.Q
}
@RequiresApi(Build.VERSION_CODES.Q)
override fun findConnectionOwner(
ipProto: Int, srcIp: String, srcPort: Int, destIp: String, destPort: Int
): Int {
return SagerNet.connectivity.getConnectionOwnerUid(
ipProto, InetSocketAddress(srcIp, srcPort), InetSocketAddress(destIp, destPort)
)
}
override fun packageNameByUid(uid: Int): String {
PackageCache.awaitLoadSync()
if (uid <= 1000L) {
return "android"
}
val packageNames = PackageCache.uidMap[uid]
if (!packageNames.isNullOrEmpty()) for (packageName in packageNames) {
return packageName
}
error("unknown uid $uid")
}
override fun uidByPackageName(packageName: String): Int {
PackageCache.awaitLoadSync()
return PackageCache[packageName] ?: 0
}
// nb4a interface
override fun useOfficialAssets(): Boolean {
return DataStore.rulesProvider == 0
}
override fun selector_OnProxySelected(selectorTag: String, tag: String) {
if (selectorTag != "proxy") {
Logs.d("other selector: $selectorTag")
return
}
LibcoreUtil.resetAllConnections(true)
DataStore.baseService?.apply {
runOnDefaultDispatcher {
val id = data.proxy!!.config.profileTagMap
.filterValues { it == tag }.keys.firstOrNull() ?: -1
val ent = SagerDatabase.proxyDao.getById(id) ?: return@runOnDefaultDispatcher
// traffic & title
data.proxy?.apply {
looper?.selectMain(id)
displayProfileName = ServiceNotification.genTitle(ent)
data.notification?.postNotificationTitle(displayProfileName)
}
// post binder
data.binder.broadcast { b ->
b.cbSelectorUpdate(id)
}
}
}
}
}

View File

@ -1,5 +1,5 @@
if [ ! -z $ENV_NB4A ]; then if [ ! -z $ENV_NB4A ]; then
export COMMIT_SING_BOX_EXTRA="9400fd007ad5a19b5892a7c42aae0951e1163747" export COMMIT_SING_BOX_EXTRA="3805838008319a97e4495f43e10a1d4c9c1e512a"
fi fi
if [ ! -z $ENV_SING_BOX_EXTRA ]; then if [ ! -z $ENV_SING_BOX_EXTRA ]; then

View File

@ -21,13 +21,10 @@ func init() {
D.RegisterTransport([]string{"underlying"}, createUnderlyingTransport) D.RegisterTransport([]string{"underlying"}, createUnderlyingTransport)
} }
// CreateUnderlyingTransport for Android
func createUnderlyingTransport(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (D.Transport, error) { func createUnderlyingTransport(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (D.Transport, error) {
return &androidUnderlyingTransportSing{name, underlyingResolver}, nil return &androidUnderlyingTransportSing{name, underlyingResolver}, nil
} }
//
type androidUnderlyingTransportSing struct { type androidUnderlyingTransportSing struct {
name string name string
*androidUnderlyingTransport *androidUnderlyingTransport
@ -38,11 +35,9 @@ func (t *androidUnderlyingTransportSing) Name() string { return t.name }
// //
var systemResolver = &net.Resolver{PreferGo: false} // Using System API, lookup from current network. var systemResolver = &net.Resolver{PreferGo: false} // Using System API, lookup from current network.
var underlyingResolver = &androidUnderlyingTransport{systemResolver: systemResolver} // Using System API, lookup from non-VPN network. var underlyingResolver = &androidUnderlyingTransport{} // Using System API, lookup from non-VPN network.
type androidUnderlyingTransport struct { type androidUnderlyingTransport struct{}
systemResolver *net.Resolver
}
func (t *androidUnderlyingTransport) Start() error { return nil } func (t *androidUnderlyingTransport) Start() error { return nil }
func (t *androidUnderlyingTransport) Close() error { return nil } func (t *androidUnderlyingTransport) Close() error { return nil }
@ -96,7 +91,7 @@ func (t *androidUnderlyingTransport) Lookup(ctx context.Context, domain string,
ips = append(ips, netip.MustParseAddr(ip)) ips = append(ips, netip.MustParseAddr(ip))
} }
} else { } else {
ips2, err2 := t.systemResolver.LookupIP(context.Background(), network, domain) ips2, err2 := systemResolver.LookupIP(context.Background(), network, domain)
if err2 != nil { if err2 != nil {
err = err2 err = err2
return return

View File

@ -32,19 +32,17 @@ func ForceGc() {
go runtime.GC() go runtime.GC()
} }
func SetLocalResolver(lr LocalResolver) {
localResolver = lr
}
func InitCore(process, cachePath, internalAssets, externalAssets string, func InitCore(process, cachePath, internalAssets, externalAssets string,
maxLogSizeKb int32, logEnable bool, maxLogSizeKb int32, logEnable bool,
iif NB4AInterface, if1 NB4AInterface, if2 BoxPlatformInterface,
) { ) {
defer device.DeferPanicToError("InitCore", func(err error) { log.Println(err) }) defer device.DeferPanicToError("InitCore", func(err error) { log.Println(err) })
isBgProcess := strings.HasSuffix(process, ":bg") isBgProcess := strings.HasSuffix(process, ":bg")
neko_common.RunMode = neko_common.RunMode_NekoBoxForAndroid neko_common.RunMode = neko_common.RunMode_NekoBoxForAndroid
intfNB4A = iif intfNB4A = if1
intfBox = if2
useProcfs = intfBox.UseProcFS()
// Working dir // Working dir
tmp := filepath.Join(cachePath, "../no_backup") tmp := filepath.Join(cachePath, "../no_backup")
@ -64,7 +62,7 @@ func InitCore(process, cachePath, internalAssets, externalAssets string,
boxmain.DisableColor() boxmain.DisableColor()
// nekoutils // nekoutils
nekoutils.Selector_OnProxySelected = iif.Selector_OnProxySelected nekoutils.Selector_OnProxySelected = intfNB4A.Selector_OnProxySelected
// Set up some component // Set up some component
go func() { go func() {

View File

@ -16,6 +16,10 @@ type LocalResolver interface {
var localResolver LocalResolver // Android: passed from java (only when VPNService) var localResolver LocalResolver // Android: passed from java (only when VPNService)
func SetLocalResolver(lr LocalResolver) {
localResolver = lr
}
type BoxPlatformInterface interface { type BoxPlatformInterface interface {
AutoDetectInterfaceControl(fd int32) error AutoDetectInterfaceControl(fd int32) error
OpenTun(singTunOptionsJson, tunPlatformOptionsJson string) (int, error) OpenTun(singTunOptionsJson, tunPlatformOptionsJson string) (int, error)
@ -24,8 +28,3 @@ type BoxPlatformInterface interface {
PackageNameByUid(uid int32) (string, error) PackageNameByUid(uid int32) (string, error)
UIDByPackageName(packageName string) (int32, error) UIDByPackageName(packageName string) (int32, error)
} }
func SetBoxPlatformInterface(iif BoxPlatformInterface) {
intfBox = iif
useProcfs = intfBox.UseProcFS()
}