You're in
Iker Hurtado's pro blog
Developer | Entrepreneur | Investor
Software engineer (entrepreneur and investor at times). These days doing performant frontend and graphics on the web platform at Barcelona Supercomputing Center

Android native libraries and APIs

22 Jan 2015   |   iker hurtado  
Share on Twitter Share on Google+ Share on Facebook
In this post I review the main native libraries that NDK makes available to allow us to create Android apps with only or large proportion of C/C++ code

Android NDK include several C/C++ libraries in order to enable us to develop natively nearly any feature -mainly the latest versions.

It provides a set of system headers for stable native APIs that are guaranteed to be supported in all later releases of the platform. In it, each API corresponds to a set of headers files, and a shared library file that contains the corresponding implementation, and which must be linked against by your native code.

In the NDK documentation are listed the stable APIs/ABIs exposed by the Android NDK. The headers corresponding to a given API level are located under: NDK_DIR/platforms/android-api-level/arch-arm/usr/include

To use this libraries, we must include the lib header (examplelib.h) in our source file and tell the build system that our native module needs to be linked to /system/lib/libexamplelib.so at load-time (by adding LOCAL_LDLIBS := -lexamplelib to the Android.mk file).

Note that the build system automatically links the C library, the Math library and the C++ support library to your native code, there is no need to indicate it explicitly (in LOCAL_LDLIBS).

In the rest of the post I will study some of more important native libraries.

Bionic libc

The more basic and common C library for a system is known as C standard library (ANSI C standard) or succinctly libc. The POSIX C library specification declares, on top of the standard C library, the additional functionality that should be included on POSIX compliant systems.

The Bionic libc is the POSIX standard C library written by Google for the Android operating system; it's a derivation of BSD standard C library.

Bionic is not in any way binary-compatible with other C libraries.

Bionic provides C standard library macros, type definitions, functions, and small number of Android-specific features of these functionality domains: Memory Management, File Input and Output, String Manipulation, Mathematics, Date and Time, Process Control, Signal Handling, Socket Networking, Multithreading, Users and Groups, System Configuration and Name Service Switch.

Bionic was specifically designed for mobile computing, so not every function in the standard C library is supported. The list of missing functionality is available within the header files.

More information about this in the book Pro Android C++ with the NDK

Other basic utility libraries

Logging library

NDK brings a Android-specific logging library. We can to write helpful wrapper macros for our own usage.

The only utility header (android/log.h) contains various definitions that can be used to send log messages to the kernel from your native code. The file itself contains many informative comments on how to use it.

To use this lib, our native module should link to /system/lib/liblog.so by the addition of -llog to LOCAL_LDLIBS build system var.

ZLib compression library

NDK brings a compression utility library called ZLib

The headers are: zlib.h and zconf.h. To use this lib, our native module should link to /system/lib/libz.so by the addition of -lz to LOCAL_LDLIBS build system var.

More information in ZLib page documentation.

The Android native application APIs

The NDK carries several basic APIs in order to make easier write Android native applications. This APIs are Android specific, unlike Bionic or OpenGL APIs that are mostly standard.

All the corresponding functions are provided by the libandroid.so library. To link it dynamically, -landroid must be added to LOCAL_LDLIBS var in Android.mk file.

The first and more important header is the android/native_activity.h (facilities for activity lifecycle management) that I go in depth to here: Native activity, app glue lib, lifecycle and threads.

The next API allows to listen to input events and sensors directly from native code. The headers are: android/looper.h, android/input.h, android/keycodes.h, android/sensor.h

If you are using the NDK app glue lib, its header android_native_app_glue.h brings the native_activity.h (which in turn has other included) and android/looper.h included.

Accessing app files

In Android, assets allow Android apps to include various types of files, including text and binary ones packaged into the APK archive. A recent added feature: Opaque Binary Blob (OBB) files support, allows to distribute large amount data outside of the APK.

The NDK storage manager library allows us to access any resources stored in the app; both the assets folder files (by assets folder relative paths) as Opaque Binary Blob files.

The relevant headers for this feature are: android/configuration.h, android/asset_manager.h, android/storage_manager.h and android/obb.h

The resources accessible (for Java framework) through compile-time generated IDs (inside the res/ folder) are not directly available to native programming.
More detailed information about this topic in the book Android NDK: Begginer's Guide

Native Window API

This API allows a native window management and enables to directly access and manipulate the pixel buffer of the native window.

Header files to include: android/native_window.h, android/native_window_jni.h

The steps for drawing directly in the window buffer:

1. Set the window buffer format and size. The dimensions and the pixel format of the native window should match the image data that will be rendered.

int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, 
            int32_t width, int32_t height, int32_t format);

Example: ANativeWindow_setBuffersGeometry(pApp->window, 
            0, 0, WINDOW_FORMAT_RGBX_8888);
This function updates the native window buffer associated with the native window referred by the window input argument. The window size and format are changed according to the rest of the input arguments. If the size or the format is set to 0 , then the native window's base value will be used.

2. Lock the window's next drawing surface:

int32_t ANativeWindow_lock(ANativeWindow* window, 
           ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);

Example: if (ANativeWindow_lock(lWindow, &mWindowBuffer, NULL) >= 0)
The ANativeWindow_lock function is used to lock the native window buffer and to obtain a pointer to the raw pixel buffer. Native code can then use this pointer to access and manipulate the pixel buffer. After this call is returned, the input argument outBuffer will refer to the window buffer for drawing.

3. Clear the buffer: This is optional. Sometimes we may just want to overwrite a part of the window buffer. Example:

memset(mWindowBuffer.bits, 0, 
       mWindowBuffer.stride * mWindowBuffer.height * sizeof(uint32_t*));

4. Draw something to the buffer. The buffer structure data will be useful for drawing in it:

typedef struct ANativeWindow_Buffer {
    int32_t width; // The number of pixels show horizontally
    int32_t height; // The number of pixels shown vertically
    int32_t stride; // The number of *pixels* that a line 
    // in the buffer takes in memory.  This may be >= width
    int32_t format; // The format of the buffer
    void* bits; // The actual bits.
} ANativeWindow_Buffer;

5. Unlock the window's drawing surface and post the new buffer to display. This is done with this function:

int32_t ANativeWindow_unlockAndPost(ANativeWindow* window);

Other functions:

In order to prevent the native window instance from being deleted, the native code can acquire a reference to it using the ANativeWindow_acquire function.

void ANativeWindow_acquire(ANativeWindow* window);
Every call to this function should be balanced by a call to ANativeWindow_release function.

As mentioned earlier, to prevent memory leaks, each native window reference should be released using the ANativeWindow_release function.

void ANativeWindow_release(ANativeWindow* window);

The native window API provides a set of functions for the native code to obtain information regarding the native window such as the dimensions and the pixel format. These are:

ANativeWindow_getWidth, ANativeWindow_getHeight, ANativeWindow_getFormat

ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface);
It takes a JNIEnv interface pointer and a Surface object (Java env object) reference and returns a pointer to the native window instance. The function also acquires a reference on the returned native window instance, and it needs to be released through the ANativeWindow_release function to prevent memory leaks.

Graphics and Multimedia native libraries

In addition to Bionic and Android specific native libraries, there are several more native libraries in order to facilitate -mainly- graphic and multimedia native programming.

I list the libraries in order of appearance in NDK:

OpenGL ES library

This library enables the native and efficient programming of the GPU in a Android device through OpenGL ES cross-platform API. Over time NDK has been supporting growing versions of OpenGL ES: 1.0, 2.0 and 3.0.

The coming months I will study thoroughly and by practice this library, so I have one section in this blog dedicated to this topic.

The jnigraphics library

This is a tiny library that exposes a stable, C-based interface that allows native code to reliably access the pixel buffers of Java bitmap objects. It's a library to manipulate efficiently images and after return them (by JNI) to the Java framework, I think.

EGL Graphics library

EGL is an interface between some rendering APIs, such as OpenGL ES, and the underlying native platform window system. In practice, EGL provides a native platform interface to allocate and manage OpenGL ES surfaces.

I have a post dedicate to this topic: Android NDK EGL Graphics Library

OpenSL ES library

Like OpenGL ES library, this library enables native, efficient and standard programming of sound in a Android device. OpenSL ES is a cross-platform, hardware-accelerated audio API tuned for embedded systems.

These two books cover this sound API: Android NDK Beginner’s Guide and Android Native Development Kit Cookbook.

OpenMAX AL library

Like before, this library enables native, efficient and standard programming of multimedia in a Android device. Form Khronos group page:

OpenMAX™ is a royalty-free, cross-platform API that provides comprehensive streaming media codec and application portability by enabling accelerated multimedia components to be developed, integrated and programmed across multiple operating systems and silicon platforms. The OpenMAX API will be shipped with processors to enable library and codec implementers to rapidly and effectively make use of the full acceleration potential of new silicon - regardless of the underlying hardware architecture.

ranchu  |  08:10 - 13 May
Hi Iker, Thanks for the post. Is the sensors API are part of libandroid.so ? Does is iclude gps API too ? Thanks, Ran