mirror of
https://github.com/MatsuriDayo/NekoBoxForAndroid.git
synced 2025-12-19 22:50:05 +08:00
optimize binder usage
This commit is contained in:
parent
17c6844107
commit
962668c6f1
@ -6,7 +6,7 @@ interface ISagerNetService {
|
||||
int getState();
|
||||
String getProfileName();
|
||||
|
||||
void registerCallback(in ISagerNetServiceCallback cb);
|
||||
void registerCallback(in ISagerNetServiceCallback cb, int id);
|
||||
oneway void unregisterCallback(in ISagerNetServiceCallback cb);
|
||||
|
||||
int urlTest();
|
||||
|
||||
@ -7,7 +7,6 @@ oneway interface ISagerNetServiceCallback {
|
||||
void stateChanged(int state, String profileName, String msg);
|
||||
void missingPlugin(String profileName, String pluginName);
|
||||
void routeAlert(int type, String routeName);
|
||||
void updateWakeLockStatus(boolean acquired);
|
||||
void cbSpeedUpdate(in SpeedDisplayData stats);
|
||||
void cbTrafficUpdate(in TrafficData stats);
|
||||
void cbLogUpdate(String str);
|
||||
|
||||
@ -36,7 +36,7 @@ import io.nekohasekai.sagernet.database.DataStore
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
class QuickToggleShortcut : Activity(), SagerConnection.Callback {
|
||||
private val connection = SagerConnection()
|
||||
private val connection = SagerConnection(SagerConnection.CONNECTION_ID_SHORTCUT)
|
||||
private var profileId = -1L
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
||||
@ -13,7 +13,6 @@ import io.nekohasekai.sagernet.aidl.ISagerNetService
|
||||
import io.nekohasekai.sagernet.aidl.ISagerNetServiceCallback
|
||||
import io.nekohasekai.sagernet.bg.proto.ProxyInstance
|
||||
import io.nekohasekai.sagernet.database.DataStore
|
||||
import io.nekohasekai.sagernet.database.ProxyEntity
|
||||
import io.nekohasekai.sagernet.database.SagerDatabase
|
||||
import io.nekohasekai.sagernet.ktx.*
|
||||
import io.nekohasekai.sagernet.plugin.PluginManager
|
||||
@ -73,20 +72,22 @@ class BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
val callbackIdMap = mutableMapOf<ISagerNetServiceCallback, Int>()
|
||||
|
||||
override val coroutineContext = Dispatchers.Main.immediate + Job()
|
||||
|
||||
override fun getState(): Int = (data?.state ?: State.Idle).ordinal
|
||||
override fun getProfileName(): String = data?.proxy?.profile?.displayName() ?: "Idle"
|
||||
|
||||
override fun registerCallback(cb: ISagerNetServiceCallback) {
|
||||
override fun registerCallback(cb: ISagerNetServiceCallback, id: Int) {
|
||||
callbacks.register(cb)
|
||||
cb.updateWakeLockStatus(data?.proxy?.service?.wakeLock != null)
|
||||
callbackIdMap[cb] = id
|
||||
}
|
||||
|
||||
private val boardcastMutex = Mutex()
|
||||
private val broadcastMutex = Mutex()
|
||||
|
||||
suspend fun broadcast(work: (ISagerNetServiceCallback) -> Unit) {
|
||||
boardcastMutex.withLock {
|
||||
broadcastMutex.withLock {
|
||||
val count = callbacks.beginBroadcast()
|
||||
try {
|
||||
repeat(count) {
|
||||
@ -103,6 +104,7 @@ class BaseService {
|
||||
}
|
||||
|
||||
override fun unregisterCallback(cb: ISagerNetServiceCallback) {
|
||||
callbackIdMap.remove(cb)
|
||||
callbacks.unregister(cb)
|
||||
}
|
||||
|
||||
@ -155,9 +157,8 @@ class BaseService {
|
||||
val success = data.proxy!!.box.selectOutbound(tag)
|
||||
if (success) runOnDefaultDispatcher {
|
||||
data.proxy!!.looper?.selectMain(ent.id)
|
||||
data.binder.broadcast {
|
||||
it.stateChanged(-1, ServiceNotification.genTitle(ent), null)
|
||||
}
|
||||
val title = ServiceNotification.genTitle(ent)
|
||||
data.notification?.postNotificationTitle(title)
|
||||
}
|
||||
}
|
||||
return
|
||||
@ -267,14 +268,10 @@ class BaseService {
|
||||
wakeLock?.apply {
|
||||
release()
|
||||
wakeLock = null
|
||||
data.binder.broadcast {
|
||||
it.updateWakeLockStatus(false)
|
||||
}
|
||||
data.notification?.postNotificationWakeLockStatus(false)
|
||||
} ?: apply {
|
||||
acquireWakeLock()
|
||||
data.binder.broadcast {
|
||||
it.updateWakeLockStatus(true)
|
||||
}
|
||||
data.notification?.postNotificationWakeLockStatus(true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,13 +283,9 @@ class BaseService {
|
||||
|
||||
if (DataStore.acquireWakeLock) {
|
||||
acquireWakeLock()
|
||||
data.binder.broadcast {
|
||||
it.updateWakeLockStatus(true)
|
||||
}
|
||||
data.notification?.postNotificationWakeLockStatus(true)
|
||||
} else {
|
||||
data.binder.broadcast {
|
||||
it.updateWakeLockStatus(false)
|
||||
}
|
||||
data.notification?.postNotificationWakeLockStatus(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,8 +15,10 @@ import io.nekohasekai.sagernet.aidl.TrafficData
|
||||
import io.nekohasekai.sagernet.database.DataStore
|
||||
import io.nekohasekai.sagernet.ktx.runOnMainDispatcher
|
||||
|
||||
class SagerConnection(private var listenForDeath: Boolean = false) : ServiceConnection,
|
||||
IBinder.DeathRecipient {
|
||||
class SagerConnection(
|
||||
private val connectionId: Int,
|
||||
private var listenForDeath: Boolean = false
|
||||
) : ServiceConnection, IBinder.DeathRecipient {
|
||||
|
||||
companion object {
|
||||
val serviceClass
|
||||
@ -25,6 +27,10 @@ class SagerConnection(private var listenForDeath: Boolean = false) : ServiceConn
|
||||
Key.MODE_VPN -> VpnService::class // Key.MODE_TRANS -> TransproxyService::class
|
||||
else -> throw UnknownError()
|
||||
}.java
|
||||
|
||||
val CONNECTION_ID_SHORTCUT = 0
|
||||
val CONNECTION_ID_TILE = 1
|
||||
val CONNECTION_ID_MAINACTIVITY = 2
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
@ -90,9 +96,6 @@ class SagerConnection(private var listenForDeath: Boolean = false) : ServiceConn
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateWakeLockStatus(acquired: Boolean) {
|
||||
}
|
||||
|
||||
override fun cbLogUpdate(str: String?) {
|
||||
DataStore.postLogListener?.let {
|
||||
if (str != null) {
|
||||
@ -114,7 +117,7 @@ class SagerConnection(private var listenForDeath: Boolean = false) : ServiceConn
|
||||
try {
|
||||
if (listenForDeath) binder.linkToDeath(this, 0)
|
||||
check(!callbackRegistered)
|
||||
service.registerCallback(serviceCallback)
|
||||
service.registerCallback(serviceCallback, connectionId)
|
||||
callbackRegistered = true
|
||||
} catch (e: RemoteException) {
|
||||
e.printStackTrace()
|
||||
|
||||
@ -13,9 +13,7 @@ import androidx.core.app.NotificationManagerCompat
|
||||
import io.nekohasekai.sagernet.Action
|
||||
import io.nekohasekai.sagernet.R
|
||||
import io.nekohasekai.sagernet.SagerNet
|
||||
import io.nekohasekai.sagernet.aidl.ISagerNetServiceCallback
|
||||
import io.nekohasekai.sagernet.aidl.SpeedDisplayData
|
||||
import io.nekohasekai.sagernet.aidl.TrafficData
|
||||
import io.nekohasekai.sagernet.database.DataStore
|
||||
import io.nekohasekai.sagernet.database.ProxyEntity
|
||||
import io.nekohasekai.sagernet.database.SagerDatabase
|
||||
@ -35,7 +33,7 @@ import io.nekohasekai.sagernet.utils.Theme
|
||||
* See also: https://github.com/aosp-mirror/platform_frameworks_base/commit/070d142993403cc2c42eca808ff3fafcee220ac4
|
||||
*/
|
||||
class ServiceNotification(
|
||||
private val service: BaseService.Interface, profileName: String,
|
||||
private val service: BaseService.Interface, title: String,
|
||||
channel: String, visible: Boolean = false,
|
||||
) : BroadcastReceiver() {
|
||||
companion object {
|
||||
@ -50,81 +48,63 @@ class ServiceNotification(
|
||||
}
|
||||
}
|
||||
|
||||
val showDirectSpeed = DataStore.showDirectSpeed
|
||||
|
||||
private val callback: ISagerNetServiceCallback by lazy {
|
||||
object : ISagerNetServiceCallback.Stub() {
|
||||
override fun cbSpeedUpdate(stats: SpeedDisplayData) {
|
||||
builder.apply {
|
||||
if (showDirectSpeed) {
|
||||
val speedDetail = (service as Context).getString(
|
||||
R.string.speed_detail, service.getString(
|
||||
R.string.speed, Formatter.formatFileSize(service, stats.txRateProxy)
|
||||
), service.getString(
|
||||
R.string.speed, Formatter.formatFileSize(service, stats.rxRateProxy)
|
||||
), service.getString(
|
||||
R.string.speed,
|
||||
Formatter.formatFileSize(service, stats.txRateDirect)
|
||||
), service.getString(
|
||||
R.string.speed,
|
||||
Formatter.formatFileSize(service, stats.rxRateDirect)
|
||||
)
|
||||
)
|
||||
setStyle(NotificationCompat.BigTextStyle().bigText(speedDetail))
|
||||
setContentText(speedDetail)
|
||||
} else {
|
||||
val speedSimple = (service as Context).getString(
|
||||
R.string.traffic, service.getString(
|
||||
R.string.speed, Formatter.formatFileSize(service, stats.txRateProxy)
|
||||
), service.getString(
|
||||
R.string.speed, Formatter.formatFileSize(service, stats.rxRateProxy)
|
||||
)
|
||||
)
|
||||
setContentText(speedSimple)
|
||||
}
|
||||
setSubText(
|
||||
service.getString(
|
||||
R.string.traffic,
|
||||
Formatter.formatFileSize(service, stats.txTotal),
|
||||
Formatter.formatFileSize(service, stats.rxTotal)
|
||||
)
|
||||
fun postNotificationSpeedUpdate(stats: SpeedDisplayData) {
|
||||
builder.apply {
|
||||
if (showDirectSpeed) {
|
||||
val speedDetail = (service as Context).getString(
|
||||
R.string.speed_detail, service.getString(
|
||||
R.string.speed, Formatter.formatFileSize(service, stats.txRateProxy)
|
||||
), service.getString(
|
||||
R.string.speed, Formatter.formatFileSize(service, stats.rxRateProxy)
|
||||
), service.getString(
|
||||
R.string.speed,
|
||||
Formatter.formatFileSize(service, stats.txRateDirect)
|
||||
), service.getString(
|
||||
R.string.speed,
|
||||
Formatter.formatFileSize(service, stats.rxRateDirect)
|
||||
)
|
||||
}
|
||||
update()
|
||||
}
|
||||
|
||||
override fun cbTrafficUpdate(stats: TrafficData?) {
|
||||
}
|
||||
|
||||
override fun stateChanged(state: Int, profileName: String?, msg: String?) {
|
||||
if (state == -1) {
|
||||
builder.setContentTitle(profileName)
|
||||
}
|
||||
}
|
||||
|
||||
override fun missingPlugin(profileName: String?, pluginName: String?) {
|
||||
}
|
||||
|
||||
override fun routeAlert(type: Int, routeName: String?) {
|
||||
}
|
||||
|
||||
override fun updateWakeLockStatus(acquired: Boolean) {
|
||||
updateActions(acquired)
|
||||
builder.priority =
|
||||
if (acquired) NotificationCompat.PRIORITY_HIGH else NotificationCompat.PRIORITY_LOW
|
||||
update()
|
||||
}
|
||||
|
||||
override fun cbLogUpdate(str: String?) {
|
||||
)
|
||||
setStyle(NotificationCompat.BigTextStyle().bigText(speedDetail))
|
||||
setContentText(speedDetail)
|
||||
} else {
|
||||
val speedSimple = (service as Context).getString(
|
||||
R.string.traffic, service.getString(
|
||||
R.string.speed, Formatter.formatFileSize(service, stats.txRateProxy)
|
||||
), service.getString(
|
||||
R.string.speed, Formatter.formatFileSize(service, stats.rxRateProxy)
|
||||
)
|
||||
)
|
||||
setContentText(speedSimple)
|
||||
}
|
||||
setSubText(
|
||||
service.getString(
|
||||
R.string.traffic,
|
||||
Formatter.formatFileSize(service, stats.txTotal),
|
||||
Formatter.formatFileSize(service, stats.rxTotal)
|
||||
)
|
||||
)
|
||||
}
|
||||
update()
|
||||
}
|
||||
private var callbackRegistered = false
|
||||
|
||||
fun postNotificationTitle(newTitle: String) {
|
||||
builder.setContentTitle(newTitle)
|
||||
update()
|
||||
}
|
||||
|
||||
fun postNotificationWakeLockStatus(acquired: Boolean) {
|
||||
updateActions(acquired)
|
||||
builder.priority =
|
||||
if (acquired) NotificationCompat.PRIORITY_HIGH else NotificationCompat.PRIORITY_LOW
|
||||
update()
|
||||
}
|
||||
|
||||
private val showDirectSpeed = DataStore.showDirectSpeed
|
||||
|
||||
private val builder = NotificationCompat.Builder(service as Context, channel)
|
||||
.setWhen(0)
|
||||
.setTicker(service.getString(R.string.forward_success))
|
||||
.setContentTitle(profileName)
|
||||
.setContentTitle(title)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setContentIntent(SagerNet.configureIntent(service))
|
||||
.setSmallIcon(R.drawable.ic_service_active)
|
||||
@ -147,7 +127,7 @@ class ServiceNotification(
|
||||
show()
|
||||
}
|
||||
|
||||
fun updateActions(wakeLockAcquired: Boolean) {
|
||||
private fun updateActions(wakeLockAcquired: Boolean) {
|
||||
service as Context
|
||||
|
||||
builder.clearActions()
|
||||
@ -188,15 +168,11 @@ class ServiceNotification(
|
||||
if (service.data.state == BaseService.State.Connected) updateCallback(intent.action == Intent.ACTION_SCREEN_ON)
|
||||
}
|
||||
|
||||
var listenPostSpeed = false
|
||||
|
||||
private fun updateCallback(screenOn: Boolean) {
|
||||
if (DataStore.speedInterval == 0) return
|
||||
if (screenOn) {
|
||||
service.data.binder.registerCallback(callback)
|
||||
callbackRegistered = true
|
||||
} else if (callbackRegistered) { // unregister callback to save battery
|
||||
service.data.binder.unregisterCallback(callback)
|
||||
callbackRegistered = false
|
||||
}
|
||||
listenPostSpeed = screenOn
|
||||
}
|
||||
|
||||
private fun show() = (service as Service).startForeground(notificationId, builder.build())
|
||||
|
||||
@ -37,7 +37,7 @@ class TileService : BaseTileService(), SagerConnection.Callback {
|
||||
}
|
||||
private var tapPending = false
|
||||
|
||||
private val connection = SagerConnection()
|
||||
private val connection = SagerConnection(SagerConnection.CONNECTION_ID_TILE)
|
||||
override fun stateChanged(state: BaseService.State, profileName: String?, msg: String?) =
|
||||
updateTile(state) { profileName }
|
||||
|
||||
|
||||
@ -3,14 +3,13 @@ package io.nekohasekai.sagernet.bg.proto
|
||||
import io.nekohasekai.sagernet.aidl.SpeedDisplayData
|
||||
import io.nekohasekai.sagernet.aidl.TrafficData
|
||||
import io.nekohasekai.sagernet.bg.BaseService
|
||||
import io.nekohasekai.sagernet.bg.SagerConnection
|
||||
import io.nekohasekai.sagernet.database.DataStore
|
||||
import io.nekohasekai.sagernet.database.ProfileManager
|
||||
import io.nekohasekai.sagernet.fmt.TAG_BYPASS
|
||||
import io.nekohasekai.sagernet.fmt.TAG_PROXY
|
||||
import io.nekohasekai.sagernet.ktx.Logs
|
||||
import kotlinx.coroutines.*
|
||||
import kotlin.time.DurationUnit
|
||||
import kotlin.time.toDuration
|
||||
|
||||
class TrafficLooper
|
||||
(
|
||||
@ -64,9 +63,10 @@ class TrafficLooper
|
||||
}
|
||||
|
||||
private suspend fun loop() {
|
||||
val delayMs = DataStore.speedInterval
|
||||
val delayMs = DataStore.speedInterval.toLong()
|
||||
val showDirectSpeed = DataStore.showDirectSpeed
|
||||
if (delayMs == 0) return
|
||||
val profileTrafficStatistics = DataStore.profileTrafficStatistics
|
||||
if (delayMs == 0L) return
|
||||
|
||||
var trafficUpdater: TrafficUpdater? = null
|
||||
var proxy: ProxyInstance?
|
||||
@ -77,7 +77,7 @@ class TrafficLooper
|
||||
var itemBypass: TrafficUpdater.TrafficLooperData? = null
|
||||
|
||||
while (sc.isActive) {
|
||||
delay(delayMs.toDuration(DurationUnit.MILLISECONDS))
|
||||
delay(delayMs)
|
||||
proxy = data.proxy ?: continue
|
||||
|
||||
if (trafficUpdater == null) {
|
||||
@ -134,30 +134,23 @@ class TrafficLooper
|
||||
itemMain!!.rx - itemMainBase!!.rx
|
||||
)
|
||||
|
||||
// traffic
|
||||
val traffic = mutableMapOf<Long, TrafficData>()
|
||||
if (DataStore.profileTrafficStatistics) {
|
||||
proxy.config.trafficMap.forEach { (_, ents) ->
|
||||
for (ent in ents) {
|
||||
val item = items[ent.id] ?: continue
|
||||
ent.rx = item.rx
|
||||
ent.tx = item.tx
|
||||
// ProfileManager.updateProfile(ent) // update DB
|
||||
traffic[ent.id] = TrafficData(
|
||||
id = ent.id,
|
||||
rx = ent.rx,
|
||||
tx = ent.tx,
|
||||
) // display
|
||||
// broadcast (MainActivity)
|
||||
data.binder.broadcast { b ->
|
||||
if (data.binder.callbackIdMap[b] == SagerConnection.CONNECTION_ID_MAINACTIVITY) {
|
||||
b.cbSpeedUpdate(speed)
|
||||
if (profileTrafficStatistics) {
|
||||
items.forEach { (id, item) ->
|
||||
b.cbTrafficUpdate(
|
||||
TrafficData(id = id, rx = item.rx, tx = item.tx) // display
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// broadcast
|
||||
data.binder.broadcast { b ->
|
||||
b.cbSpeedUpdate(speed)
|
||||
for (t in traffic) {
|
||||
b.cbTrafficUpdate(t.value)
|
||||
}
|
||||
// ServiceNotification
|
||||
data.notification?.apply {
|
||||
if (listenPostSpeed) postNotificationSpeedUpdate(speed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,12 @@ import moe.matsuri.nb4a.utils.Util
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import org.json.JSONObject
|
||||
|
||||
fun ShadowsocksBean.fixPluginName() {
|
||||
if (plugin.startsWith("simple-obfs")) {
|
||||
plugin = plugin.replaceFirst("simple-obfs", "obfs-local")
|
||||
}
|
||||
}
|
||||
|
||||
fun parseShadowsocks(url: String): ShadowsocksBean {
|
||||
|
||||
if (url.substringBefore("#").contains("@")) {
|
||||
@ -31,6 +37,7 @@ fun parseShadowsocks(url: String): ShadowsocksBean {
|
||||
password = link.password
|
||||
plugin = link.queryParameter("plugin") ?: ""
|
||||
name = link.fragment
|
||||
fixPluginName()
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,6 +50,7 @@ fun parseShadowsocks(url: String): ShadowsocksBean {
|
||||
password = methodAndPswd.substringAfter(":")
|
||||
plugin = link.queryParameter("plugin") ?: ""
|
||||
name = link.fragment
|
||||
fixPluginName()
|
||||
}
|
||||
} else {
|
||||
// v2rayN style
|
||||
|
||||
@ -381,7 +381,7 @@ class MainActivity : ThemedActivity(),
|
||||
}
|
||||
}
|
||||
|
||||
val connection = SagerConnection(true)
|
||||
val connection = SagerConnection(SagerConnection.CONNECTION_ID_MAINACTIVITY, true)
|
||||
override fun onServiceConnected(service: ISagerNetService) = changeState(
|
||||
try {
|
||||
BaseService.State.values()[service.state]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user