Neha Shukla
Neha Shukla

Reputation: 3660

Compile native C++ shared object with Android NDK

I have generated a .so file via C++ on Linus and got .so file generated now I have cross compile this .so file for Android ARM so I have to comiple it via Android NDK so that new genrated .so can be used in my android project .

So can anyone help me where I have to put Linux generated .so file in my Android project and what to add in Make file(Android.mk) so that it can generate new .so file with existing methods in my previous Linux generated .so file.

I hope my question is clear to you all, if not please tell me.

Please help me. Thanks in advance

Upvotes: 2

Views: 3627

Answers (1)

jww
jww

Reputation: 102205

Here are the steps to build a native C++ shared object.

  1. Add native support to your project. See Android's Add native support
  2. Add your C++ files to the JNI folder
  3. Create an Android.mk, add it to the the JNI folder, and add the architectures you want to support. For example:

    APP_ABI   := armeabi x86 mips armeabi-v7a
    
  1. Pick a C++ runtime library. See the CPLUSPLUS.README in the NDK. I picked STL Port for the runtime (the GNU runtime was toxic due to its license).

  2. Create an Appication.mk, add it to the the JNI folder, and add the magic. For example:

    LOCAL_PATH := $(call my-dir)
    
    # NDK_DEBUG_IMPORTS := 1
    
    #########################################################
    # STLport library
    include $(CLEAR_VARS)
    
    STLPORT_INCL     := /opt/android-ndk-r9/sources/cxx-stl/stlport/stlport
    STLPORT_LIB      := /opt/android-ndk-r9/sources/cxx-stl/stlport/libs/$(TARGET_ARCH_ABI)
    
    LOCAL_MODULE := stlport_shared
    LOCAL_SRC_FILES := $(STLPORT_LIB)/libstlport_shared.so
    
    LOCAL_EXPORT_CPPFLAGS :=
    LOCAL_EXPORT_C_INCLUDES := $(STLPORT_INCL)
    
    include $(PREBUILT_SHARED_LIBRARY)
    
    LOCAL_SHARED_LIBRARIES  := stlport_shared
    
    #########################################################
    # Crypto++ library
    include $(CLEAR_VARS)
    
    CRYPTOPP_INCL   := /usr/local/cryptopp/android-$(TARGET_ARCH_ABI)/include
    CRYPTOPP_LIB    := /usr/local/cryptopp/android-$(TARGET_ARCH_ABI)/lib
    
    LOCAL_MODULE       := cryptopp
    LOCAL_SRC_FILES    := $(CRYPTOPP_LIB)/libcryptopp.so
    
    LOCAL_EXPORT_CPPFLAGS := -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function
    LOCAL_EXPORT_C_INCLUDES := $(CRYPTOPP_INCL) $(CRYPTOPP_INCL)/cryptopp
    
    include $(PREBUILT_SHARED_LIBRARY)
    
    LOCAL_SHARED_LIBRARIES  := cryptopp
    
    #########################################################
    # PRNG library
    include $(CLEAR_VARS)
    
    APP_STL         := stlport_shared
    APP_MODULES     := prng stlport_shared cryptopp
    
    # My ass... LOCAL_EXPORT_C_INCLUDES is useless
    LOCAL_C_INCLUDES   := $(STLPORT_INCL) $(CRYPTOPP_INCL)
    
    LOCAL_CPP_FEATURES := rtti exceptions
    
    LOCAL_CPP_FLAGS    := -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function
    LOCAL_CPP_FLAGS    += -Wl,--exclude-libs,ALL
    
    LOCAL_LDLIBS            := -llog -landroid
    LOCAL_SHARED_LIBRARIES  := cryptopp stlport_shared
    
    LOCAL_MODULE    := prng
    LOCAL_SRC_FILES := libprng.cpp
    
    include $(BUILD_SHARED_LIBRARY)
    

My library depends on STLport. The stuff following "STLport library" ensures my library is compiled against stlport_shared.so, and the stlport_shared.so is copied into the APK.

My library also depends upon a cross-compiled version of Crypto++. Crypto++ was also compiled/linked against stlport_shared.so. The stuff following "Crypto++ library" ensures my library is compiled against libcryptopp.so, and the libcryptopp.so is copied into the APK.

Finally, my library is called out. My library is the stuff following "PRNG library" (its a one file test project). It builds libprng.so, and ensures libprng.so is copied into the APK.

You'll also need Android classes. Here's what mine looks like.

package com.cryptopp.prng;

public class PRNG {

    static {
        System.loadLibrary("stlport_shared");
        System.loadLibrary("cryptopp");
        System.loadLibrary("prng");
    }

    private static native int CryptoPP_Reseed(byte[] bytes);

    private static native int CryptoPP_GetBytes(byte[] bytes);

    private static Object lock = new Object();

    // Class method. Returns the number of bytes consumed from the seed.
    public static int Reseed(byte[] seed) {         
        synchronized (lock) {
            return CryptoPP_Reseed(seed);
        }
    }

    // Class method. Returns the number of bytes generated.
    public static int GetBytes(byte[] bytes) {
        synchronized (lock) {
            return CryptoPP_GetBytes(bytes);
        }
    }

    // Instance method. Returns the number of bytes consumed from the seed.
    public int reseed(byte[] seed) {
        synchronized (lock) {
            return CryptoPP_Reseed(seed);
        }
    }

    // Instance method. Returns the number of bytes generated.
    public int getBytes(byte[] bytes) {
        synchronized (lock) {
            return CryptoPP_GetBytes(bytes);
        }
    }
}

The Android modified build system really sucks. Its sufficiently different from standard make-based projects and poorly documented. But that's what Android offers, so that's what you have to use. Eclipse's Android native support is built around it.


If interested, here's what the wrapper header file looks like. You can use javah to generate it from your DEX file (compiled Java classes).

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_cryptopp_prng_PRNG */

#ifndef _Included_com_cryptopp_prng_PRNG
#define _Included_com_cryptopp_prng_PRNG
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_cryptopp_prng_PRNG
 * Method:    CryptoPP_Reseed
 * Signature: ([B)I
 */
JNIEXPORT jint JNICALL Java_com_cryptopp_prng_PRNG_CryptoPP_1Reseed
  (JNIEnv *, jclass, jbyteArray);

/*
 * Class:     com_cryptopp_prng_PRNG
 * Method:    CryptoPP_GetBytes
 * Signature: ([B)I
 */
JNIEXPORT jint JNICALL Java_com_cryptopp_prng_PRNG_CryptoPP_1GetBytes
  (JNIEnv *, jclass, jbyteArray);

#ifdef __cplusplus
}
#endif
#endif

Upvotes: 5

Related Questions