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:
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 {