package com.novoyuuparosk.iperf3gui import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.MethodChannel /** * Kotlin binding layer for native iperf3 via JNI. * * This class loads the native library (libiperf3jni.so) and exposes * iperf3 functionality to Flutter via MethodChannel callbacks. * * iperf3 runs natively inside the app process — no Process.start() needed, * no SELinux restrictions. Output is streamed live to the Flutter UI via * the IperfCallback interface. */ object IperfRunner : FlutterPlugin { private var channel: MethodChannel? = null init { // Load native library at class initialization try { android.util.Log.i("IperfRunner", "Loading native library...") System.loadLibrary("iperf3jni") android.util.Log.i("IperfRunner", "Native library loaded successfully") } catch (e: UnsatisfiedLinkError) { android.util.Log.e("IperfRunner", "Failed to load native library: ${e.message}") e.printStackTrace() } } override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { channel = MethodChannel(binding.binaryMessenger, "com.novoyuuparosk.iperf3gui/iperf") channel?.setMethodCallHandler { call, result -> when (call.method) { "runIperf" -> { val args = call.argument>("args")?.toTypedArray() if (args != null) { runIperfLive(args, object : IperfCallback { override fun onOutput(line: String) { channel?.invokeMethod("onOutput", mapOf("output" to line)) } override fun onError(error: String) { channel?.invokeMethod("onError", mapOf("error" to error)) } override fun onComplete() { channel?.invokeMethod("onComplete", emptyMap()) } }) result.success(null) } else { result.error("INVALID_ARGS", "args must be a String array", null) } } "stopIperf" -> { forceStopIperfTest(object : IperfCallback { override fun onOutput(line: String) { channel?.invokeMethod("onOutput", mapOf("output" to line)) } override fun onError(error: String) { channel?.invokeMethod("onError", mapOf("error" to error)) } override fun onComplete() { channel?.invokeMethod("onComplete", emptyMap()) } }) result.success(null) } "isTestRunning" -> { result.success(isTestRunning()) } else -> { result.notImplemented() } } } } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { channel = null } // ───────────────────────────────────────────────────────────────── // Native method bindings // ───────────────────────────────────────────────────────────────── /** * Start an iperf3 test with the given arguments. * This calls the native C function via JNI. */ external fun runIperfLive(arguments: Array, callback: IperfCallback) /** * Gracefully stop a running iperf3 test. */ external fun forceStopIperfTest(callback: IperfCallback) /** * Check if an iperf3 test is currently running. * @return true if a test is in progress, false otherwise */ external fun isTestRunning(): Boolean /** * Callback interface for iperf3 test output. * * Used by the native layer to stream test results back to Flutter * in real-time. */ interface IperfCallback { /** Called with each line of iperf3 output (stdout/stderr) */ fun onOutput(line: String) /** Called when an error occurs */ fun onError(error: String) /** Called when the test completes (success or failure) */ fun onComplete() } }