diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/scripting/IRemoteIPC.kt b/app/src/main/kotlin/me/rhunk/snapenhance/scripting/IRemoteIPC.kt deleted file mode 100644 index 76010dd2..00000000 --- a/app/src/main/kotlin/me/rhunk/snapenhance/scripting/IRemoteIPC.kt +++ /dev/null @@ -1,19 +0,0 @@ -package me.rhunk.snapenhance.scripting - -import java.util.concurrent.ConcurrentHashMap - -class IRemoteIPC : IPCInterface() { - private val listeners = ConcurrentHashMap>() - - 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) { - 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 new file mode 100644 index 00000000..6e7d8082 --- /dev/null +++ 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>> // 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) { + 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 index 7e8df9a6..c7bc1c7b 100644 --- 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() - private val cachedModuleInfo = mutableMapOf() + 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 { 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) { - 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) { + override fun sendIPCMessage(channel: String, eventName: String, args: Array) { 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 index 4239b4e2..8c85068f 100644 --- 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 index 9826de15..3890653b 100644 --- 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) -> 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 index e4484fcf..5aeca312 100644 --- 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() - 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) { - 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) { + listener(args) + } + }) + } - override fun emit(eventName: String, args: Array) { - sendIPCMessage(eventName, 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 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 {