mirror of
https://github.com/MatsuriDayo/NekoBoxForAndroid.git
synced 2025-12-18 22:20:06 +08:00
optimize code
This commit is contained in:
parent
d0c68e38ef
commit
ad0dd1d63f
@ -6,8 +6,6 @@ import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
@ -20,27 +18,21 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import go.Seq
|
||||
import io.nekohasekai.sagernet.bg.SagerConnection
|
||||
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.ui.MainActivity
|
||||
import io.nekohasekai.sagernet.utils.*
|
||||
import kotlinx.coroutines.DEBUG_PROPERTY_NAME
|
||||
import kotlinx.coroutines.DEBUG_PROPERTY_VALUE_ON
|
||||
import libcore.BoxPlatformInterface
|
||||
import libcore.Libcore
|
||||
import libcore.NB4AInterface
|
||||
import moe.matsuri.nb4a.NativeInterface
|
||||
import moe.matsuri.nb4a.utils.JavaUtil
|
||||
import moe.matsuri.nb4a.utils.LibcoreUtil
|
||||
import moe.matsuri.nb4a.utils.cleanWebview
|
||||
import java.net.InetSocketAddress
|
||||
import androidx.work.Configuration as WorkConfiguration
|
||||
|
||||
class SagerNet : Application(),
|
||||
BoxPlatformInterface,
|
||||
WorkConfiguration.Provider, NB4AInterface {
|
||||
WorkConfiguration.Provider {
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
super.attachBaseContext(base)
|
||||
@ -48,6 +40,8 @@ class SagerNet : Application(),
|
||||
application = this
|
||||
}
|
||||
|
||||
val nativeInterface = NativeInterface()
|
||||
|
||||
val externalAssets by lazy { getExternalFilesDir(null) ?: filesDir }
|
||||
val process = JavaUtil.getProcessName()
|
||||
val isMainProcess = process == BuildConfig.APPLICATION_ID
|
||||
@ -81,12 +75,9 @@ class SagerNet : Application(),
|
||||
externalAssets.absolutePath + "/",
|
||||
DataStore.logBufSize,
|
||||
DataStore.logLevel > 0,
|
||||
this
|
||||
nativeInterface, nativeInterface
|
||||
)
|
||||
|
||||
// libbox: platform interface
|
||||
Libcore.setBoxPlatformInterface(this)
|
||||
|
||||
if (isMainProcess) {
|
||||
Theme.apply(this)
|
||||
Theme.applyNightTheme()
|
||||
@ -133,11 +124,6 @@ class SagerNet : Application(),
|
||||
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 {
|
||||
{
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package io.nekohasekai.sagernet.bg.proto
|
||||
|
||||
import android.os.Build
|
||||
import android.os.SystemClock
|
||||
import io.nekohasekai.sagernet.SagerNet
|
||||
import io.nekohasekai.sagernet.bg.AbstractInstance
|
||||
@ -117,10 +116,8 @@ abstract class BoxInstance(
|
||||
|
||||
override fun launch() {
|
||||
// TODO move, this is not box
|
||||
val context =
|
||||
if (Build.VERSION.SDK_INT < 24 || SagerNet.user.isUserUnlocked) SagerNet.application else SagerNet.deviceStorage
|
||||
val cache = File(context.cacheDir, "tmpcfg")
|
||||
cache.mkdirs()
|
||||
val cacheDir = File(SagerNet.application.cacheDir, "tmpcfg")
|
||||
cacheDir.mkdirs()
|
||||
|
||||
for ((chain) in config.externalIndex) {
|
||||
chain.entries.forEachIndexed { index, (port, profile) ->
|
||||
@ -135,7 +132,7 @@ abstract class BoxInstance(
|
||||
|
||||
bean is TrojanGoBean -> {
|
||||
val configFile = File(
|
||||
cache, "trojan_go_" + SystemClock.elapsedRealtime() + ".json"
|
||||
cacheDir, "trojan_go_" + SystemClock.elapsedRealtime() + ".json"
|
||||
)
|
||||
configFile.parentFile?.mkdirs()
|
||||
configFile.writeText(config)
|
||||
@ -150,7 +147,7 @@ abstract class BoxInstance(
|
||||
|
||||
bean is NaiveBean -> {
|
||||
val configFile = File(
|
||||
cache, "naive_" + SystemClock.elapsedRealtime() + ".json"
|
||||
cacheDir, "naive_" + SystemClock.elapsedRealtime() + ".json"
|
||||
)
|
||||
|
||||
configFile.parentFile?.mkdirs()
|
||||
@ -161,7 +158,7 @@ abstract class BoxInstance(
|
||||
|
||||
if (bean.certificates.isNotBlank()) {
|
||||
val certFile = File(
|
||||
cache, "naive_" + SystemClock.elapsedRealtime() + ".crt"
|
||||
cacheDir, "naive_" + SystemClock.elapsedRealtime() + ".crt"
|
||||
)
|
||||
|
||||
certFile.parentFile?.mkdirs()
|
||||
@ -180,7 +177,7 @@ abstract class BoxInstance(
|
||||
|
||||
bean is HysteriaBean -> {
|
||||
val configFile = File(
|
||||
cache, "hysteria_" + SystemClock.elapsedRealtime() + ".json"
|
||||
cacheDir, "hysteria_" + SystemClock.elapsedRealtime() + ".json"
|
||||
)
|
||||
|
||||
configFile.parentFile?.mkdirs()
|
||||
@ -213,7 +210,7 @@ abstract class BoxInstance(
|
||||
any as JSONObject
|
||||
|
||||
val name = any.getString("name")
|
||||
val configFile = File(cache, name)
|
||||
val configFile = File(cacheDir, name)
|
||||
configFile.parentFile?.mkdirs()
|
||||
val content = any.getString("content")
|
||||
configFile.writeText(content)
|
||||
@ -243,10 +240,8 @@ abstract class BoxInstance(
|
||||
}
|
||||
|
||||
bean is TuicBean -> {
|
||||
val configFile = File(
|
||||
context.noBackupFilesDir,
|
||||
"tuic_" + SystemClock.elapsedRealtime() + ".json"
|
||||
)
|
||||
val configFile =
|
||||
File(cacheDir, "tuic_" + SystemClock.elapsedRealtime() + ".json")
|
||||
|
||||
configFile.parentFile?.mkdirs()
|
||||
configFile.writeText(config)
|
||||
|
||||
@ -422,24 +422,22 @@ fun buildConfig(
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
|
||||
// domain_strategy
|
||||
pastEntity?.requireBean()?.apply {
|
||||
// don't loopback
|
||||
if (currentDomainStrategy != "" && !serverAddress.isIpAddress()) {
|
||||
domainListDNSDirectForce.add("full:$serverAddress")
|
||||
}
|
||||
}
|
||||
currentOutbound["domain_strategy"] = if (forTest) "" else currentDomainStrategy
|
||||
|
||||
// custom JSON merge
|
||||
if (bean.customOutboundJson.isNotBlank()) {
|
||||
Util.mergeJSON(bean.customOutboundJson, currentOutbound)
|
||||
}
|
||||
}
|
||||
|
||||
pastEntity?.requireBean()?.apply {
|
||||
// don't loopback
|
||||
if (currentDomainStrategy != "" && !serverAddress.isIpAddress()) {
|
||||
domainListDNSDirectForce.add("full:$serverAddress")
|
||||
}
|
||||
}
|
||||
if (forTest) {
|
||||
currentDomainStrategy = ""
|
||||
}
|
||||
|
||||
currentOutbound["tag"] = tagOut
|
||||
currentOutbound["domain_strategy"] = currentDomainStrategy
|
||||
|
||||
// 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.
|
||||
|
||||
@ -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
|
||||
}
|
||||
96
app/src/main/java/moe/matsuri/nb4a/NativeInterface.kt
Normal file
96
app/src/main/java/moe/matsuri/nb4a/NativeInterface.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
if [ ! -z $ENV_NB4A ]; then
|
||||
export COMMIT_SING_BOX_EXTRA="9400fd007ad5a19b5892a7c42aae0951e1163747"
|
||||
export COMMIT_SING_BOX_EXTRA="3805838008319a97e4495f43e10a1d4c9c1e512a"
|
||||
fi
|
||||
|
||||
if [ ! -z $ENV_SING_BOX_EXTRA ]; then
|
||||
|
||||
@ -21,13 +21,10 @@ func init() {
|
||||
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) {
|
||||
return &androidUnderlyingTransportSing{name, underlyingResolver}, nil
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
type androidUnderlyingTransportSing struct {
|
||||
name string
|
||||
*androidUnderlyingTransport
|
||||
@ -37,12 +34,10 @@ func (t *androidUnderlyingTransportSing) Name() string { return t.name }
|
||||
|
||||
//
|
||||
|
||||
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 systemResolver = &net.Resolver{PreferGo: false} // Using System API, lookup from current network.
|
||||
var underlyingResolver = &androidUnderlyingTransport{} // Using System API, lookup from non-VPN network.
|
||||
|
||||
type androidUnderlyingTransport struct {
|
||||
systemResolver *net.Resolver
|
||||
}
|
||||
type androidUnderlyingTransport struct{}
|
||||
|
||||
func (t *androidUnderlyingTransport) Start() 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))
|
||||
}
|
||||
} else {
|
||||
ips2, err2 := t.systemResolver.LookupIP(context.Background(), network, domain)
|
||||
ips2, err2 := systemResolver.LookupIP(context.Background(), network, domain)
|
||||
if err2 != nil {
|
||||
err = err2
|
||||
return
|
||||
|
||||
@ -32,19 +32,17 @@ func ForceGc() {
|
||||
go runtime.GC()
|
||||
}
|
||||
|
||||
func SetLocalResolver(lr LocalResolver) {
|
||||
localResolver = lr
|
||||
}
|
||||
|
||||
func InitCore(process, cachePath, internalAssets, externalAssets string,
|
||||
maxLogSizeKb int32, logEnable bool,
|
||||
iif NB4AInterface,
|
||||
if1 NB4AInterface, if2 BoxPlatformInterface,
|
||||
) {
|
||||
defer device.DeferPanicToError("InitCore", func(err error) { log.Println(err) })
|
||||
isBgProcess := strings.HasSuffix(process, ":bg")
|
||||
|
||||
neko_common.RunMode = neko_common.RunMode_NekoBoxForAndroid
|
||||
intfNB4A = iif
|
||||
intfNB4A = if1
|
||||
intfBox = if2
|
||||
useProcfs = intfBox.UseProcFS()
|
||||
|
||||
// Working dir
|
||||
tmp := filepath.Join(cachePath, "../no_backup")
|
||||
@ -64,7 +62,7 @@ func InitCore(process, cachePath, internalAssets, externalAssets string,
|
||||
boxmain.DisableColor()
|
||||
|
||||
// nekoutils
|
||||
nekoutils.Selector_OnProxySelected = iif.Selector_OnProxySelected
|
||||
nekoutils.Selector_OnProxySelected = intfNB4A.Selector_OnProxySelected
|
||||
|
||||
// Set up some component
|
||||
go func() {
|
||||
|
||||
@ -16,6 +16,10 @@ type LocalResolver interface {
|
||||
|
||||
var localResolver LocalResolver // Android: passed from java (only when VPNService)
|
||||
|
||||
func SetLocalResolver(lr LocalResolver) {
|
||||
localResolver = lr
|
||||
}
|
||||
|
||||
type BoxPlatformInterface interface {
|
||||
AutoDetectInterfaceControl(fd int32) error
|
||||
OpenTun(singTunOptionsJson, tunPlatformOptionsJson string) (int, error)
|
||||
@ -24,8 +28,3 @@ type BoxPlatformInterface interface {
|
||||
PackageNameByUid(uid int32) (string, error)
|
||||
UIDByPackageName(packageName string) (int32, error)
|
||||
}
|
||||
|
||||
func SetBoxPlatformInterface(iif BoxPlatformInterface) {
|
||||
intfBox = iif
|
||||
useProcfs = intfBox.UseProcFS()
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user