🛠️ Compiling iPerf3 in Android with CMake & JNI — A Practical Guide

iPerf3 is the gold standard for measuring TCP and UDP throughput — but it’s a native C tool. What if you want to embed it inside an Android app, with full power and none of the complexity?
That’s what I did with CellularLab, my open-source iPerf3 client for Android. It features a native iPerf3 engine compiled using CMake, a polished Kotlin UI, and even AI-powered log analysis via Gemini.
In this post, I’ll walk you through:
Setting up iPerf3 inside your Android project
Compiling it with CMake
Calling native functions via JNI
Gotchas and tips
Let’s go 👇
📦 Why Use Native iPerf?
While wrappers exist, I needed:
💥 Full control over TCP/UDP parameters
🔄 Real-time log streaming into Kotlin
📊 Accurate field testing on mobile
⚙️ Flexibility to patch or update iPerf logic directly
Embedding iPerf3 natively means you're not limited by broken wrappers or permissions.

📁 Directory Structure
Here’s the relevant part of my Android main/ source:
Abhishek/
├── cpp/
│ └── iperf/
│ ├── iperf-3.19/ # Cloned iPerf3 C source
│ ├── iperf_jni.c # JNI bridge file
│ └── iperf_config_android.h # Android config header
I downloaded and placed iPerf3.19 source inside src/main/cpp/iperf/iperf-3.19.
⚙️ CMakeLists.txt – Linking It All Together
Your cpp/CMakeLists.txt should:
Set the correct iPerf version path
Inject Android-specific config
Compile all necessary
.cfilesLink Android NDK dependencies
Here’s a trimmed-down example:
cmakeCopyEditcmake_minimum_required(VERSION 3.21)
project("cellularlab")
# Set paths
set(IPERF_SRC_DIR ${CMAKE_SOURCE_DIR}/iperf/iperf-3.19)
set(JNI_SRC_FILE ${CMAKE_SOURCE_DIR}/iperf/iperf_jni.c)
# Inject config headers
configure_file(
${CMAKE_SOURCE_DIR}/iperf/iperf_config_android.h
${IPERF_SRC_DIR}/iperf_config.h
)
configure_file(
${IPERF_SRC_DIR}/version.h.in
${IPERF_SRC_DIR}/version.h @ONLY
)
# Declare shared library
add_library(cellularlab SHARED
${JNI_SRC_FILE}
${IPERF_SRC_DIR}/iperf_api.c
${IPERF_SRC_DIR}/iperf_tcp.c
${IPERF_SRC_DIR}/iperf_udp.c
${IPERF_SRC_DIR}/net.c
${IPERF_SRC_DIR}/cjson.c
...
)
target_include_directories(cellularlab PRIVATE
${IPERF_SRC_DIR}
${CMAKE_SOURCE_DIR}/iperf
)
target_compile_definitions(cellularlab PRIVATE HAVE_PTHREAD)
# Link to Android NDK system libraries
find_package(Threads REQUIRED)
target_link_libraries(cellularlab
Threads::Threads
log
android
)
🧠 The JNI Layer: iperf_jni.c
This file bridges native C code to Kotlin via the IperfRunner.kt class. It:
Converts arguments from Kotlin to native
argvInitializes an
iperf_testsessionRuns it using
iperf_run_client()Pipes stdout logs to Java for live UI display
Key JNI functions:
Java_com_abhishek_cellularlab_tests_iperf_IperfRunner_runIperfLive(...)
Java_com_abhishek_cellularlab_tests_iperf_IperfRunner_forceStopIperfTest(...)
Log forwarding is done via:
(*env)->CallVoidMethod(env, callback, onOutput, line);
Which enables smooth integration with Android UI.
🔁 Kotlin Binding
Your Kotlin IperfRunner.kt will look like:
external fun runIperfLive(arguments: Array<String>, callback: IperfCallback)
external fun forceStopIperfTest(callback: IperfCallback)
interface IperfCallback {
fun onOutput(line: String)
fun onError(error: String)
fun onComplete()
}
This gives you live log streaming, error reporting, and lifecycle control from the UI.
🔼 Upgrading iPerf Version
Want to upgrade from 3.19 → 3.20 or higher?
Here’s what to do:
Replace the contents of
/cpp/iperf/iperf-3.19with the new versionUpdate version in
CMakeLists.txt:
cmakeCopyEditset(IPERF_SRC_DIR ${CMAKE_SOURCE_DIR}/iperf/iperf-3.20)
Ensure any new
.cfiles are included in theadd_library()blockRebuild the project — you're done ✅
Optional: Keep a backup of iperf_jni.c and test compatibility (in case iPerf internal structures change).
🛠️ build.gradle – Native Hookup
Make sure externalNativeBuild is enabled:
kotlinCopyEditexternalNativeBuild {
cmake {
path = file("src/main/cpp/CMakeLists.txt")
version = "4.0.2"
}
}
ndk {
abiFilters += listOf("armeabi-v7a", "arm64-v8a")
}
📦 Output
When run inside the app:
iPerf logs are captured in real-time via a pipe
They're streamed into the Kotlin UI
Optional: Saved and analyzed using Gemini AI in my case
This makes the app production-grade for networking teams, labs, and enthusiasts.
✅ Summary
Compiling iPerf3 into an Android app unlocks real-time, native performance testing:
Customizable behavior
AI-powered log analysis
Transparent, extensible, and powerful
⭐ Want to Try It?
Here's my full open-source implementation:
👉 GitHub: Abhi5h3k/CellularLab
Feel free to fork, experiment, and adapt to your needs.
