commit 19a0ab8398c9c8fd2ddb6dce8afcc3bc24375d11
parent 98b3f2bfc9833323f958eed8c214cb93099b3a45
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Sun, 17 Sep 2023 19:35:36 +0200

feat(scripting): ipc global channels

Diffstat:
Dapp/src/main/kotlin/me/rhunk/snapenhance/scripting/IRemoteIPC.kt | 20--------------------
Aapp/src/main/kotlin/me/rhunk/snapenhance/scripting/RemoteManagerIPC.kt | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapp/src/main/kotlin/me/rhunk/snapenhance/scripting/RemoteScriptManager.kt | 33+++++++++++----------------------
Mcore/src/main/aidl/me/rhunk/snapenhance/bridge/scripting/IScripting.aidl | 4++--
Mcore/src/main/kotlin/me/rhunk/snapenhance/scripting/IPCInterface.kt | 5+++++
Mcore/src/main/kotlin/me/rhunk/snapenhance/scripting/core/CoreScriptRuntime.kt | 49++++++++++++++++++++++++++++---------------------
6 files changed, 100 insertions(+), 65 deletions(-)

diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/scripting/IRemoteIPC.kt b/app/src/main/kotlin/me/rhunk/snapenhance/scripting/IRemoteIPC.kt @@ -1,19 +0,0 @@ -package me.rhunk.snapenhance.scripting - -import java.util.concurrent.ConcurrentHashMap - -class IRemoteIPC : IPCInterface() { - private val listeners = ConcurrentHashMap<String, MutableSet<Listener>>() - - fun removeListener(eventName: String, listener: Listener) { - listeners[eventName]?.remove(listener) - } - - override fun on(eventName: String, listener: Listener) { - listeners.getOrPut(eventName) { mutableSetOf() }.add(listener) - } - - override fun emit(eventName: String, args: Array<out String?>) { - listeners[eventName]?.toList()?.forEach { it(args) } - } -}- \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/scripting/RemoteManagerIPC.kt b/app/src/main/kotlin/me/rhunk/snapenhance/scripting/RemoteManagerIPC.kt @@ -0,0 +1,53 @@ +package me.rhunk.snapenhance.scripting + +import android.os.DeadObjectException +import me.rhunk.snapenhance.bridge.scripting.IPCListener +import me.rhunk.snapenhance.core.logger.AbstractLogger +import me.rhunk.snapenhance.scripting.type.ModuleInfo +import java.util.concurrent.ConcurrentHashMap + +typealias IPCListeners = ConcurrentHashMap<String, MutableMap<String, MutableSet<IPCListener>>> // channel, eventName -> listeners + +class RemoteManagerIPC( + private val moduleInfo: ModuleInfo, + private val logger: AbstractLogger, + private val ipcListeners: IPCListeners = ConcurrentHashMap(), +) : IPCInterface() { + companion object { + private const val TAG = "RemoteManagerIPC" + } + + override fun on(eventName: String, listener: Listener) { + onBroadcast(moduleInfo.name, eventName, listener) + } + + override fun emit(eventName: String, vararg args: String?) { + emit(moduleInfo.name, eventName, *args) + } + + override fun onBroadcast(channel: String, eventName: String, listener: Listener) { + ipcListeners.getOrPut(channel) { mutableMapOf() }.getOrPut(eventName) { mutableSetOf() }.add(object: IPCListener.Stub() { + override fun onMessage(args: Array<out String?>) { + try { + listener(args) + } catch (doe: DeadObjectException) { + ipcListeners[channel]?.get(eventName)?.remove(this) + } catch (t: Throwable) { + logger.error("Failed to receive message for channel: $channel, event: $eventName", t, TAG) + } + } + }) + } + + override fun broadcast(channel: String, eventName: String, vararg args: String?) { + ipcListeners[channel]?.get(eventName)?.toList()?.forEach { + try { + it.onMessage(args) + } catch (doe: DeadObjectException) { + ipcListeners[channel]?.get(eventName)?.remove(it) + } catch (t: Throwable) { + logger.error("Failed to send message for channel: $channel, event: $eventName", t, TAG) + } + } + } +}+ \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/scripting/RemoteScriptManager.kt b/app/src/main/kotlin/me/rhunk/snapenhance/scripting/RemoteScriptManager.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.scripting import android.net.Uri -import android.os.DeadObjectException import androidx.documentfile.provider.DocumentFile import me.rhunk.snapenhance.RemoteSideContext import me.rhunk.snapenhance.bridge.scripting.IPCListener @@ -14,14 +13,10 @@ class RemoteScriptManager( private val context: RemoteSideContext, ) : IScripting.Stub() { val runtime = ScriptRuntime(context.log) - val remoteIpc = IRemoteIPC() - private fun getScriptFolder() - = DocumentFile.fromTreeUri(context.androidContext, Uri.parse(context.config.root.scripting.moduleFolder.get())) - //private fun hasHotReload() = context.config.root.scripting.hotReload.get() private val reloadListeners = mutableListOf<ReloadListener>() - private val cachedModuleInfo = mutableMapOf<String, ModuleInfo>() + private val ipcListeners = IPCListeners() fun sync() { getScriptFileNames().forEach { name -> @@ -40,8 +35,8 @@ class RemoteScriptManager( } fun init() { - runtime.buildModuleObject = { - putConst("ipc", this, remoteIpc) + runtime.buildModuleObject = { module -> + putConst("ipc", this, RemoteManagerIPC(module.moduleInfo, context.log, ipcListeners)) } sync() @@ -56,6 +51,8 @@ class RemoteScriptManager( return context.androidContext.contentResolver.openInputStream(file.uri)?.use(callback) ?: callback(null) } + private fun getScriptFolder() = DocumentFile.fromTreeUri(context.androidContext, Uri.parse(context.config.root.scripting.moduleFolder.get())) + private fun getScriptFileNames(): List<String> { return (getScriptFolder() ?: return emptyList()).listFiles().filter { it.name?.endsWith(".js") ?: false }.map { it.name!! } } @@ -78,23 +75,15 @@ class RemoteScriptManager( reloadListeners.add(listener) } - override fun registerIPCListener(eventName: String, listener: IPCListener) { - remoteIpc.on(eventName, object: Listener { - override fun invoke(args: Array<out String?>) { - try { - listener.onMessage(args) - } catch (e: DeadObjectException) { - remoteIpc.removeListener(eventName, this) - } catch (t: Throwable) { - context.log.error("Failed to invoke $eventName", t) - } - } - }) + override fun registerIPCListener(channel: String, eventName: String, listener: IPCListener) { + ipcListeners.getOrPut(channel) { mutableMapOf() }.getOrPut(eventName) { mutableSetOf() }.add(listener) } - override fun sendIPCMessage(eventName: String, args: Array<out String>) { + override fun sendIPCMessage(channel: String, eventName: String, args: Array<out String>) { runCatching { - remoteIpc.emit(eventName, args) + ipcListeners[channel]?.get(eventName)?.toList()?.forEach { + it.onMessage(args) + } }.onFailure { context.log.error("Failed to send message for $eventName", it) } diff --git a/core/src/main/aidl/me/rhunk/snapenhance/bridge/scripting/IScripting.aidl b/core/src/main/aidl/me/rhunk/snapenhance/bridge/scripting/IScripting.aidl @@ -10,7 +10,7 @@ interface IScripting { void registerReloadListener(ReloadListener listener); - void registerIPCListener(String eventName, IPCListener listener); + void registerIPCListener(String channel, String eventName, IPCListener listener); - void sendIPCMessage(String eventName, in String[] args); + void sendIPCMessage(String channel, String eventName, in String[] args); } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/scripting/IPCInterface.kt b/core/src/main/kotlin/me/rhunk/snapenhance/scripting/IPCInterface.kt @@ -5,8 +5,13 @@ typealias Listener = (Array<out String?>) -> Unit abstract class IPCInterface { abstract fun on(eventName: String, listener: Listener) + abstract fun onBroadcast(channel: String, eventName: String, listener: Listener) + abstract fun emit(eventName: String, vararg args: String?) + abstract fun broadcast(channel: String, eventName: String, vararg args: String?) @Suppress("unused") fun emit(eventName: String) = emit(eventName, *emptyArray()) + @Suppress("unused") + fun emit(channel: String, eventName: String) = broadcast(channel, eventName, *emptyArray()) } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/scripting/core/CoreScriptRuntime.kt b/core/src/main/kotlin/me/rhunk/snapenhance/scripting/core/CoreScriptRuntime.kt @@ -18,7 +18,6 @@ class CoreScriptRuntime( private val scriptHookers = mutableListOf<ScriptHooker>() - fun connect(scriptingInterface: IScripting) { scriptingInterface.apply { registerReloadListener(object: ReloadListener.Stub() { @@ -27,31 +26,39 @@ class CoreScriptRuntime( } }) - ipcInterface = object: IPCInterface() { - override fun on(eventName: String, listener: Listener) { - registerIPCListener(eventName, object: IPCListener.Stub() { - override fun onMessage(args: Array<out String?>) { - listener(args) - } - }) - } + buildModuleObject = { module -> + putConst("ipc", this, object: IPCInterface() { + override fun onBroadcast(channel: String, eventName: String, listener: Listener) { + registerIPCListener(channel, eventName, object: IPCListener.Stub() { + override fun onMessage(args: Array<out String?>) { + listener(args) + } + }) + } + + override fun on(eventName: String, listener: Listener) { + onBroadcast(module.moduleInfo.name, eventName, listener) + } + + override fun emit(eventName: String, vararg args: String?) { + broadcast(module.moduleInfo.name, eventName, *args) + } - override fun emit(eventName: String, args: Array<out String?>) { - sendIPCMessage(eventName, args) + override fun broadcast(channel: String, eventName: String, vararg args: String?) { + sendIPCMessage(channel, eventName, args) + } + }) + putFunction("findClass") { + val className = it?.get(0).toString() + classLoader.loadClass(className) } + putConst("hooker", this, ScriptHooker(module.moduleInfo, logger, classLoader).also { + scriptHookers.add(it) + }) } } - buildModuleObject = { module -> - putConst("ipc", this, ipcInterface) - putFunction("findClass") { - val className = it?.get(0).toString() - classLoader.loadClass(className) - } - putConst("hooker", this, ScriptHooker(module.moduleInfo, logger, classLoader).also { - scriptHookers.add(it) - }) - } + scriptingInterface.enabledScripts.forEach { path -> runCatching {