From 962668c6f188e225d1335a490fd28042e01fc506 Mon Sep 17 00:00:00 2001 From: arm64v8a <48624112+arm64v8a@users.noreply.github.com> Date: Sun, 26 Mar 2023 14:03:39 +0900 Subject: [PATCH] optimize binder usage --- .../sagernet/aidl/ISagerNetService.aidl | 2 +- .../aidl/ISagerNetServiceCallback.aidl | 1 - .../sagernet/QuickToggleShortcut.kt | 2 +- .../io/nekohasekai/sagernet/bg/BaseService.kt | 33 ++--- .../sagernet/bg/SagerConnection.kt | 15 +- .../sagernet/bg/ServiceNotification.kt | 130 +++++++----------- .../io/nekohasekai/sagernet/bg/TileService.kt | 2 +- .../sagernet/bg/proto/TrafficLooper.kt | 43 +++--- .../fmt/shadowsocks/ShadowsocksFmt.kt | 8 ++ .../nekohasekai/sagernet/ui/MainActivity.kt | 2 +- 10 files changed, 105 insertions(+), 133 deletions(-) diff --git a/app/src/main/aidl/io/nekohasekai/sagernet/aidl/ISagerNetService.aidl b/app/src/main/aidl/io/nekohasekai/sagernet/aidl/ISagerNetService.aidl index bb7ce08f..725ff866 100644 --- a/app/src/main/aidl/io/nekohasekai/sagernet/aidl/ISagerNetService.aidl +++ b/app/src/main/aidl/io/nekohasekai/sagernet/aidl/ISagerNetService.aidl @@ -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(); diff --git a/app/src/main/aidl/io/nekohasekai/sagernet/aidl/ISagerNetServiceCallback.aidl b/app/src/main/aidl/io/nekohasekai/sagernet/aidl/ISagerNetServiceCallback.aidl index 8eb485d0..3b0a02d3 100644 --- a/app/src/main/aidl/io/nekohasekai/sagernet/aidl/ISagerNetServiceCallback.aidl +++ b/app/src/main/aidl/io/nekohasekai/sagernet/aidl/ISagerNetServiceCallback.aidl @@ -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); diff --git a/app/src/main/java/io/nekohasekai/sagernet/QuickToggleShortcut.kt b/app/src/main/java/io/nekohasekai/sagernet/QuickToggleShortcut.kt index 2cd9d7d3..cb65a1fe 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/QuickToggleShortcut.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/QuickToggleShortcut.kt @@ -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?) { diff --git a/app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt b/app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt index 1a80b088..886cf6cd 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt @@ -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() + 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) } } diff --git a/app/src/main/java/io/nekohasekai/sagernet/bg/SagerConnection.kt b/app/src/main/java/io/nekohasekai/sagernet/bg/SagerConnection.kt index 623b6461..0f40d4ec 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/bg/SagerConnection.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/bg/SagerConnection.kt @@ -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() diff --git a/app/src/main/java/io/nekohasekai/sagernet/bg/ServiceNotification.kt b/app/src/main/java/io/nekohasekai/sagernet/bg/ServiceNotification.kt index 09af2b5d..432da51a 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/bg/ServiceNotification.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/bg/ServiceNotification.kt @@ -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?) { + ) + 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() + } - override fun updateWakeLockStatus(acquired: Boolean) { - updateActions(acquired) - builder.priority = - if (acquired) NotificationCompat.PRIORITY_HIGH else NotificationCompat.PRIORITY_LOW - update() - } + fun postNotificationTitle(newTitle: String) { + builder.setContentTitle(newTitle) + update() + } - override fun cbLogUpdate(str: String?) { - } - } + fun postNotificationWakeLockStatus(acquired: Boolean) { + updateActions(acquired) + builder.priority = + if (acquired) NotificationCompat.PRIORITY_HIGH else NotificationCompat.PRIORITY_LOW + update() } - private var callbackRegistered = false + + 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()) diff --git a/app/src/main/java/io/nekohasekai/sagernet/bg/TileService.kt b/app/src/main/java/io/nekohasekai/sagernet/bg/TileService.kt index 5c56d9e5..54239e91 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/bg/TileService.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/bg/TileService.kt @@ -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 } diff --git a/app/src/main/java/io/nekohasekai/sagernet/bg/proto/TrafficLooper.kt b/app/src/main/java/io/nekohasekai/sagernet/bg/proto/TrafficLooper.kt index 9e879888..f9f3e793 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/bg/proto/TrafficLooper.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/bg/proto/TrafficLooper.kt @@ -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() - 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) } } } diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/shadowsocks/ShadowsocksFmt.kt b/app/src/main/java/io/nekohasekai/sagernet/fmt/shadowsocks/ShadowsocksFmt.kt index 5bd63184..aeecc30a 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/fmt/shadowsocks/ShadowsocksFmt.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/shadowsocks/ShadowsocksFmt.kt @@ -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 diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/MainActivity.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/MainActivity.kt index 2b1f8f9e..92bd6cd2 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/ui/MainActivity.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/ui/MainActivity.kt @@ -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]