Skip to main content

Command Palette

Search for a command to run...

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

Updated
3 min read
🛠️ Compiling iPerf3 in Android with CMake & JNI — A Practical Guide

💻 Software engineer by profession, builder by passion.

Crafting tools that solve real-world problems.

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.

Gemini AI

📁 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 .c files

  • Link 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 argv

  • Initializes an iperf_test session

  • Runs 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:

  1. Replace the contents of /cpp/iperf/iperf-3.19 with the new version

  2. Update version in CMakeLists.txt:

cmakeCopyEditset(IPERF_SRC_DIR ${CMAKE_SOURCE_DIR}/iperf/iperf-3.20)
  1. Ensure any new .c files are included in the add_library() block

  2. Rebuild 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.