Advanced AOSP Subsystems
3 min read

NDK Overview

Introduction to the Android NDK

The Android Native Development Kit (NDK) is a toolset that allows developers to implement parts of their apps using C and C++. While originally designed for application developers, understanding the NDK is crucial for AOSP engineers to grasp the boundaries between app-facing native APIs and internal system code.

NDK vs. AOSP Native Code

It is vital to distinguish between writing code using the NDK and writing code within the AOSP source tree.

  • NDK Code: Built using the NDK toolchain outside of the AOSP build system (typically via Gradle and CMake). It targets a specific API level and relies solely on stable, public APIs. Apps bundled this way can run on various Android versions.
  • AOSP Native Code: Built using the Android build system (Soong/Android.bp). This code lives inside the platform (e.g., /frameworks/native or /system/core). It can access internal, private APIs and structures, but it is tightly coupled to the specific AOSP version it was built for.

Stable APIs vs. Platform-Internal APIs

The NDK provides a "stable API surface." If an app compiles against an NDK header (like <android/log.h> or <EGL/egl.h>), Google guarantees that the compiled binary will continue to work on future Android versions.

Conversely, platform-internal APIs (like libbinder internals, SurfaceFlinger specifics, or private Bionic libc functions) are unstable. They can, and do, change between Android releases. NDK apps are strictly forbidden from linking against non-NDK platform libraries (enforced by linker namespaces since Android 7.0).

Application Binary Interfaces (ABI)

Android devices run on different hardware architectures. The NDK compiles C/C++ code into machine code for specific Application Binary Interfaces (ABIs).

  • arm64-v8a: The 64-bit ARM architecture. This is the dominant architecture for modern Android smartphones and tablets.
  • armeabi-v7a: The 32-bit ARM architecture. Deprecated and being phased out (modern Play Store rules require 64-bit support), but historically significant.
  • x86_64: The 64-bit x86 architecture. Primarily used for the Android Emulator and some ChromeOS devices running Android apps.
  • x86: The 32-bit x86 architecture (deprecated).

When building an NDK library, you typically build "fat binaries" (APKs containing .so files for multiple ABIs) or use App Bundles to deliver the correct ABI to the user's device.

The NDK Sysroot

The sysroot is a directory within the NDK that contains the headers and libraries targetable by your native code. It acts as a miniature, standardized operating system environment for the compiler.

When you include a standard C header (e.g., <stdio.h>), the compiler looks inside the NDK's sysroot, not your host machine's /usr/include. The sysroot contains:

  • Standard C/C++ headers.
  • Android-specific headers (e.g., <android/asset_manager.h>).
  • Stub shared libraries (.so files) that the linker uses to resolve symbols at build time. The actual implementations are provided by the Android OS at runtime.