4.4 KiB
iperf3-gui
A Flutter-based Android GUI wrapper for iperf3, the popular network performance measurement tool.
Architecture
lib/ # Flutter/Dart application layer
├── main.dart # App entry point (MaterialApp)
├── domain/ # Business logic (platform-independent)
│ ├── models/
│ │ ├── test_configuration.dart # Server, port, duration, interval, parallel streams
│ │ └── iperf3_result.dart # Raw output + exit code
│ └── services/
│ ├── iperf3_service.dart # Dart → MethodChannel bridge (async with Completer)
│ └── iperf3_binary_path_provider.dart # Deprecated (kept for reference)
└── ui/ # Presentation layer
└── features/
└── test_run/
├── test_run_screen.dart # Server/port inputs + GO button
└── test_run_view_model.dart # State machine: idle → running → completed/error
android/app/src/main/
├── kotlin/ # Kotlin plugin layer
│ └── com/novoyuuparosk/iperf3gui/
│ ├── MainActivity.kt # Registers IperfRunner as FlutterPlugin
│ └── IperfRunner.kt # MethodChannel handler → native JNI calls
└── cpp/ # Native iperf3 library
├── CMakeLists.txt # Compiles iperf3 + cJSON + JNI into libiperf3jni.so
├── iperf_jni.c # JNI bridge: Java String[] → iperf3 API
├── iperf/ # iperf3 3.19 C source tree (see below)
├── iperf_config.h # Generated Android-compatible config header
└── iperf_config_android.h # Android-specific compile options
Data Flow
User taps GO
→ Dart TestRunViewModel.runTest(config)
→ Iperf3Service.run(config) via MethodChannel
→ Kotlin IperfRunner.runIperfLive(args, callback)
→ JNI C: java_string_array_to_argv() → iperf_parse_arguments()
→ iperf_run_client(test)
→ Reader thread pipes stdout → CallVoidMethod → Dart onOutput callbacks
→ On completion: Dart Completer resolves with Iperf3Result
Key Design Decisions
JNI Over Process Execution
Android SELinux Enforcing mode blocks execve() from the untrusted_app domain, making Process.start() of external binaries fail with Permission denied. Compiling iperf3 as a shared library linked into the app process eliminates this restriction entirely — iperf3 code runs in-process with no domain transitions needed.
Callback Mechanism
The JNI bridge uses CallVoidMethod on stored jmethodIDs to invoke the Kotlin IperfCallback interface, NOT function pointer casts. Direct function pointer casts caused SIGBUS crashes on ARM due to JNINativeInterface method ID layout differences.
Output Streaming
iperf3 output is redirected via pipe to a background reader thread that attaches to the Java VM via AttachCurrentThread and invokes callbacks via CallVoidMethod. The reader thread frees the state struct on exit; the JNI caller owns the callback object's lifetime.
Async Pattern
Dart uses Completer<Iperf3Result> to await native completion. The MethodChannel callback handler resolves the completer on onComplete, with a timeout fallback that calls stopIperf on the native layer.
iperf3 Source
- Version: 3.19
- Location:
android/app/src/main/cpp/iperf/ - Files: Full C source tree including cJSON, headers, man pages, PEM keys
- Build: CMake compiles into
libiperf3jni.soforarmeabi-v7aandarm64-v8a - Config:
iperf_config.hgenerated fromiperf_config.h.inwith Android-compatible POSIX/threading macros
Native JNI API
| Kotlin Method | JNI Signature | Purpose |
|---|---|---|
runIperfLive(String[], IperfCallback) |
runIperfLive |
Start test with args and callback |
forceStopIperfTest(IperfCallback) |
forceStopIperfTest |
Graceful stop via shutdown() |
isTestRunning() |
isTestRunning |
Check if test is in progress |
Building
Prerequisites:
- Flutter 3.47+
- Android SDK with platform android-36
- JDK 17
- NDK r28+
- CMake 3.22.1
flutter pub get
flutter build apk --debug
adb install build/app/outputs/flutter-apk/app-debug.apk
Dependencies
- Dart:
provider,path_provider - Android: CMake external native build
- Native: iperf3 3.19, cJSON (single file)
License
MIT — bundled iperf3 3.19 is BSD-3-Clause licensed.