optimize binder usage

This commit is contained in:
arm64v8a 2023-03-26 14:03:39 +09:00
parent 17c6844107
commit 962668c6f1
10 changed files with 106 additions and 134 deletions

View File

@ -6,7 +6,7 @@ interface ISagerNetService {
int getState(); int getState();
String getProfileName(); String getProfileName();
void registerCallback(in ISagerNetServiceCallback cb); void registerCallback(in ISagerNetServiceCallback cb, int id);
oneway void unregisterCallback(in ISagerNetServiceCallback cb); oneway void unregisterCallback(in ISagerNetServiceCallback cb);
int urlTest(); int urlTest();

View File

@ -7,7 +7,6 @@ oneway interface ISagerNetServiceCallback {
void stateChanged(int state, String profileName, String msg); void stateChanged(int state, String profileName, String msg);
void missingPlugin(String profileName, String pluginName); void missingPlugin(String profileName, String pluginName);
void routeAlert(int type, String routeName); void routeAlert(int type, String routeName);
void updateWakeLockStatus(boolean acquired);
void cbSpeedUpdate(in SpeedDisplayData stats); void cbSpeedUpdate(in SpeedDisplayData stats);
void cbTrafficUpdate(in TrafficData stats); void cbTrafficUpdate(in TrafficData stats);
void cbLogUpdate(String str); void cbLogUpdate(String str);

View File

@ -36,7 +36,7 @@ import io.nekohasekai.sagernet.database.DataStore
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
class QuickToggleShortcut : Activity(), SagerConnection.Callback { class QuickToggleShortcut : Activity(), SagerConnection.Callback {
private val connection = SagerConnection() private val connection = SagerConnection(SagerConnection.CONNECTION_ID_SHORTCUT)
private var profileId = -1L private var profileId = -1L
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {

View File

@ -13,7 +13,6 @@ import io.nekohasekai.sagernet.aidl.ISagerNetService
import io.nekohasekai.sagernet.aidl.ISagerNetServiceCallback import io.nekohasekai.sagernet.aidl.ISagerNetServiceCallback
import io.nekohasekai.sagernet.bg.proto.ProxyInstance import io.nekohasekai.sagernet.bg.proto.ProxyInstance
import io.nekohasekai.sagernet.database.DataStore import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.ProxyEntity
import io.nekohasekai.sagernet.database.SagerDatabase import io.nekohasekai.sagernet.database.SagerDatabase
import io.nekohasekai.sagernet.ktx.* import io.nekohasekai.sagernet.ktx.*
import io.nekohasekai.sagernet.plugin.PluginManager 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 val coroutineContext = Dispatchers.Main.immediate + Job()
override fun getState(): Int = (data?.state ?: State.Idle).ordinal override fun getState(): Int = (data?.state ?: State.Idle).ordinal
override fun getProfileName(): String = data?.proxy?.profile?.displayName() ?: "Idle" override fun getProfileName(): String = data?.proxy?.profile?.displayName() ?: "Idle"
override fun registerCallback(cb: ISagerNetServiceCallback) { override fun registerCallback(cb: ISagerNetServiceCallback, id: Int) {
callbacks.register(cb) 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) { suspend fun broadcast(work: (ISagerNetServiceCallback) -> Unit) {
boardcastMutex.withLock { broadcastMutex.withLock {
val count = callbacks.beginBroadcast() val count = callbacks.beginBroadcast()
try { try {
repeat(count) { repeat(count) {
@ -103,6 +104,7 @@ class BaseService {
} }
override fun unregisterCallback(cb: ISagerNetServiceCallback) { override fun unregisterCallback(cb: ISagerNetServiceCallback) {
callbackIdMap.remove(cb)
callbacks.unregister(cb) callbacks.unregister(cb)
} }
@ -155,9 +157,8 @@ class BaseService {
val success = data.proxy!!.box.selectOutbound(tag) val success = data.proxy!!.box.selectOutbound(tag)
if (success) runOnDefaultDispatcher { if (success) runOnDefaultDispatcher {
data.proxy!!.looper?.selectMain(ent.id) data.proxy!!.looper?.selectMain(ent.id)
data.binder.broadcast { val title = ServiceNotification.genTitle(ent)
it.stateChanged(-1, ServiceNotification.genTitle(ent), null) data.notification?.postNotificationTitle(title)
}
} }
} }
return return
@ -267,14 +268,10 @@ class BaseService {
wakeLock?.apply { wakeLock?.apply {
release() release()
wakeLock = null wakeLock = null
data.binder.broadcast { data.notification?.postNotificationWakeLockStatus(false)
it.updateWakeLockStatus(false)
}
} ?: apply { } ?: apply {
acquireWakeLock() acquireWakeLock()
data.binder.broadcast { data.notification?.postNotificationWakeLockStatus(true)
it.updateWakeLockStatus(true)
}
} }
} }
@ -286,13 +283,9 @@ class BaseService {
if (DataStore.acquireWakeLock) { if (DataStore.acquireWakeLock) {
acquireWakeLock() acquireWakeLock()
data.binder.broadcast { data.notification?.postNotificationWakeLockStatus(true)
it.updateWakeLockStatus(true)
}
} else { } else {
data.binder.broadcast { data.notification?.postNotificationWakeLockStatus(false)
it.updateWakeLockStatus(false)
}
} }
} }

View File

@ -15,8 +15,10 @@ import io.nekohasekai.sagernet.aidl.TrafficData
import io.nekohasekai.sagernet.database.DataStore import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.ktx.runOnMainDispatcher import io.nekohasekai.sagernet.ktx.runOnMainDispatcher
class SagerConnection(private var listenForDeath: Boolean = false) : ServiceConnection, class SagerConnection(
IBinder.DeathRecipient { private val connectionId: Int,
private var listenForDeath: Boolean = false
) : ServiceConnection, IBinder.DeathRecipient {
companion object { companion object {
val serviceClass val serviceClass
@ -25,6 +27,10 @@ class SagerConnection(private var listenForDeath: Boolean = false) : ServiceConn
Key.MODE_VPN -> VpnService::class // Key.MODE_TRANS -> TransproxyService::class Key.MODE_VPN -> VpnService::class // Key.MODE_TRANS -> TransproxyService::class
else -> throw UnknownError() else -> throw UnknownError()
}.java }.java
val CONNECTION_ID_SHORTCUT = 0
val CONNECTION_ID_TILE = 1
val CONNECTION_ID_MAINACTIVITY = 2
} }
interface Callback { interface Callback {
@ -90,9 +96,6 @@ class SagerConnection(private var listenForDeath: Boolean = false) : ServiceConn
} }
} }
override fun updateWakeLockStatus(acquired: Boolean) {
}
override fun cbLogUpdate(str: String?) { override fun cbLogUpdate(str: String?) {
DataStore.postLogListener?.let { DataStore.postLogListener?.let {
if (str != null) { if (str != null) {
@ -114,7 +117,7 @@ class SagerConnection(private var listenForDeath: Boolean = false) : ServiceConn
try { try {
if (listenForDeath) binder.linkToDeath(this, 0) if (listenForDeath) binder.linkToDeath(this, 0)
check(!callbackRegistered) check(!callbackRegistered)
service.registerCallback(serviceCallback) service.registerCallback(serviceCallback, connectionId)
callbackRegistered = true callbackRegistered = true
} catch (e: RemoteException) { } catch (e: RemoteException) {
e.printStackTrace() e.printStackTrace()

View File

@ -13,9 +13,7 @@ import androidx.core.app.NotificationManagerCompat
import io.nekohasekai.sagernet.Action import io.nekohasekai.sagernet.Action
import io.nekohasekai.sagernet.R import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.SagerNet import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.aidl.ISagerNetServiceCallback
import io.nekohasekai.sagernet.aidl.SpeedDisplayData import io.nekohasekai.sagernet.aidl.SpeedDisplayData
import io.nekohasekai.sagernet.aidl.TrafficData
import io.nekohasekai.sagernet.database.DataStore import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.ProxyEntity import io.nekohasekai.sagernet.database.ProxyEntity
import io.nekohasekai.sagernet.database.SagerDatabase 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 * See also: https://github.com/aosp-mirror/platform_frameworks_base/commit/070d142993403cc2c42eca808ff3fafcee220ac4
*/ */
class ServiceNotification( class ServiceNotification(
private val service: BaseService.Interface, profileName: String, private val service: BaseService.Interface, title: String,
channel: String, visible: Boolean = false, channel: String, visible: Boolean = false,
) : BroadcastReceiver() { ) : BroadcastReceiver() {
companion object { companion object {
@ -50,81 +48,63 @@ class ServiceNotification(
} }
} }
val showDirectSpeed = DataStore.showDirectSpeed fun postNotificationSpeedUpdate(stats: SpeedDisplayData) {
builder.apply {
private val callback: ISagerNetServiceCallback by lazy { if (showDirectSpeed) {
object : ISagerNetServiceCallback.Stub() { val speedDetail = (service as Context).getString(
override fun cbSpeedUpdate(stats: SpeedDisplayData) { R.string.speed_detail, service.getString(
builder.apply { R.string.speed, Formatter.formatFileSize(service, stats.txRateProxy)
if (showDirectSpeed) { ), service.getString(
val speedDetail = (service as Context).getString( R.string.speed, Formatter.formatFileSize(service, stats.rxRateProxy)
R.string.speed_detail, service.getString( ), service.getString(
R.string.speed, Formatter.formatFileSize(service, stats.txRateProxy) R.string.speed,
), service.getString( Formatter.formatFileSize(service, stats.txRateDirect)
R.string.speed, Formatter.formatFileSize(service, stats.rxRateProxy) ), service.getString(
), service.getString( R.string.speed,
R.string.speed, Formatter.formatFileSize(service, stats.rxRateDirect)
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)
)
) )
} )
update() setStyle(NotificationCompat.BigTextStyle().bigText(speedDetail))
} setContentText(speedDetail)
} else {
override fun cbTrafficUpdate(stats: TrafficData?) { val speedSimple = (service as Context).getString(
} R.string.traffic, service.getString(
R.string.speed, Formatter.formatFileSize(service, stats.txRateProxy)
override fun stateChanged(state: Int, profileName: String?, msg: String?) { ), service.getString(
if (state == -1) { R.string.speed, Formatter.formatFileSize(service, stats.rxRateProxy)
builder.setContentTitle(profileName) )
} )
} setContentText(speedSimple)
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?) {
} }
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) private val builder = NotificationCompat.Builder(service as Context, channel)
.setWhen(0) .setWhen(0)
.setTicker(service.getString(R.string.forward_success)) .setTicker(service.getString(R.string.forward_success))
.setContentTitle(profileName) .setContentTitle(title)
.setOnlyAlertOnce(true) .setOnlyAlertOnce(true)
.setContentIntent(SagerNet.configureIntent(service)) .setContentIntent(SagerNet.configureIntent(service))
.setSmallIcon(R.drawable.ic_service_active) .setSmallIcon(R.drawable.ic_service_active)
@ -147,7 +127,7 @@ class ServiceNotification(
show() show()
} }
fun updateActions(wakeLockAcquired: Boolean) { private fun updateActions(wakeLockAcquired: Boolean) {
service as Context service as Context
builder.clearActions() builder.clearActions()
@ -188,15 +168,11 @@ class ServiceNotification(
if (service.data.state == BaseService.State.Connected) updateCallback(intent.action == Intent.ACTION_SCREEN_ON) if (service.data.state == BaseService.State.Connected) updateCallback(intent.action == Intent.ACTION_SCREEN_ON)
} }
var listenPostSpeed = false
private fun updateCallback(screenOn: Boolean) { private fun updateCallback(screenOn: Boolean) {
if (DataStore.speedInterval == 0) return if (DataStore.speedInterval == 0) return
if (screenOn) { listenPostSpeed = screenOn
service.data.binder.registerCallback(callback)
callbackRegistered = true
} else if (callbackRegistered) { // unregister callback to save battery
service.data.binder.unregisterCallback(callback)
callbackRegistered = false
}
} }
private fun show() = (service as Service).startForeground(notificationId, builder.build()) private fun show() = (service as Service).startForeground(notificationId, builder.build())

View File

@ -37,7 +37,7 @@ class TileService : BaseTileService(), SagerConnection.Callback {
} }
private var tapPending = false 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?) = override fun stateChanged(state: BaseService.State, profileName: String?, msg: String?) =
updateTile(state) { profileName } updateTile(state) { profileName }

View File

@ -3,14 +3,13 @@ package io.nekohasekai.sagernet.bg.proto
import io.nekohasekai.sagernet.aidl.SpeedDisplayData import io.nekohasekai.sagernet.aidl.SpeedDisplayData
import io.nekohasekai.sagernet.aidl.TrafficData import io.nekohasekai.sagernet.aidl.TrafficData
import io.nekohasekai.sagernet.bg.BaseService import io.nekohasekai.sagernet.bg.BaseService
import io.nekohasekai.sagernet.bg.SagerConnection
import io.nekohasekai.sagernet.database.DataStore import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.ProfileManager import io.nekohasekai.sagernet.database.ProfileManager
import io.nekohasekai.sagernet.fmt.TAG_BYPASS import io.nekohasekai.sagernet.fmt.TAG_BYPASS
import io.nekohasekai.sagernet.fmt.TAG_PROXY import io.nekohasekai.sagernet.fmt.TAG_PROXY
import io.nekohasekai.sagernet.ktx.Logs import io.nekohasekai.sagernet.ktx.Logs
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlin.time.DurationUnit
import kotlin.time.toDuration
class TrafficLooper class TrafficLooper
( (
@ -64,9 +63,10 @@ class TrafficLooper
} }
private suspend fun loop() { private suspend fun loop() {
val delayMs = DataStore.speedInterval val delayMs = DataStore.speedInterval.toLong()
val showDirectSpeed = DataStore.showDirectSpeed val showDirectSpeed = DataStore.showDirectSpeed
if (delayMs == 0) return val profileTrafficStatistics = DataStore.profileTrafficStatistics
if (delayMs == 0L) return
var trafficUpdater: TrafficUpdater? = null var trafficUpdater: TrafficUpdater? = null
var proxy: ProxyInstance? var proxy: ProxyInstance?
@ -77,7 +77,7 @@ class TrafficLooper
var itemBypass: TrafficUpdater.TrafficLooperData? = null var itemBypass: TrafficUpdater.TrafficLooperData? = null
while (sc.isActive) { while (sc.isActive) {
delay(delayMs.toDuration(DurationUnit.MILLISECONDS)) delay(delayMs)
proxy = data.proxy ?: continue proxy = data.proxy ?: continue
if (trafficUpdater == null) { if (trafficUpdater == null) {
@ -134,30 +134,23 @@ class TrafficLooper
itemMain!!.rx - itemMainBase!!.rx itemMain!!.rx - itemMainBase!!.rx
) )
// traffic // broadcast (MainActivity)
val traffic = mutableMapOf<Long, TrafficData>() data.binder.broadcast { b ->
if (DataStore.profileTrafficStatistics) { if (data.binder.callbackIdMap[b] == SagerConnection.CONNECTION_ID_MAINACTIVITY) {
proxy.config.trafficMap.forEach { (_, ents) -> b.cbSpeedUpdate(speed)
for (ent in ents) { if (profileTrafficStatistics) {
val item = items[ent.id] ?: continue items.forEach { (id, item) ->
ent.rx = item.rx b.cbTrafficUpdate(
ent.tx = item.tx TrafficData(id = id, rx = item.rx, tx = item.tx) // display
// ProfileManager.updateProfile(ent) // update DB )
traffic[ent.id] = TrafficData( }
id = ent.id,
rx = ent.rx,
tx = ent.tx,
) // display
} }
} }
} }
// broadcast // ServiceNotification
data.binder.broadcast { b -> data.notification?.apply {
b.cbSpeedUpdate(speed) if (listenPostSpeed) postNotificationSpeedUpdate(speed)
for (t in traffic) {
b.cbTrafficUpdate(t.value)
}
} }
} }
} }

View File

@ -6,6 +6,12 @@ import moe.matsuri.nb4a.utils.Util
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.json.JSONObject import org.json.JSONObject
fun ShadowsocksBean.fixPluginName() {
if (plugin.startsWith("simple-obfs")) {
plugin = plugin.replaceFirst("simple-obfs", "obfs-local")
}
}
fun parseShadowsocks(url: String): ShadowsocksBean { fun parseShadowsocks(url: String): ShadowsocksBean {
if (url.substringBefore("#").contains("@")) { if (url.substringBefore("#").contains("@")) {
@ -31,6 +37,7 @@ fun parseShadowsocks(url: String): ShadowsocksBean {
password = link.password password = link.password
plugin = link.queryParameter("plugin") ?: "" plugin = link.queryParameter("plugin") ?: ""
name = link.fragment name = link.fragment
fixPluginName()
} }
} }
@ -43,6 +50,7 @@ fun parseShadowsocks(url: String): ShadowsocksBean {
password = methodAndPswd.substringAfter(":") password = methodAndPswd.substringAfter(":")
plugin = link.queryParameter("plugin") ?: "" plugin = link.queryParameter("plugin") ?: ""
name = link.fragment name = link.fragment
fixPluginName()
} }
} else { } else {
// v2rayN style // v2rayN style

View File

@ -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( override fun onServiceConnected(service: ISagerNetService) = changeState(
try { try {
BaseService.State.values()[service.state] BaseService.State.values()[service.state]