Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile native library that relies on other native libraries to run using Android NDK?

I have a large library of C files that relies on OpenSSL and I'm very new to working with native libraries, and especially new to working with android. I've managed to implement this library on iOS with no problems.

I've been looking around online for tutorials / information on how to do this, but so far I haven't found anything I can understand. It's all very confusing to me.

Essentially I'm trying to figure out how to compile the library of C files into a native lib using NDK and use the compiled library with imy android application.

From what I understand, I need a custom cmake file for android itself, but I don't know where to start with that and the documentation I've found has been incredibly difficult to follow.

So, I need some help understanding what the process would be of compiling the library in the following manor:

  1. Compile the library, linking it with openssl and libcrypto, using Gradle and CMakeLists.txt and link the final library with my Android Studio project.
  2. Be able to make calls to the native functions within these libraries from my Java code. (I understand that this requires a JNI Java Wrapper)

(I've already managed to build libssl and libcrypto for all archs that android requires, but I'm not sure what to do with the .a/.so files.)

Any help on this matter would be incredibly appreciated.

Thanks

EDIT:

I managed to generate some library files with the following.

File Structure:

.idea
app
build
gradle
jni    <--- I made this folder specifically fo this.
    Android.mk <-- This is the important file.
    include
        openssl
            <openssl header files>
    libs
        arm64-v8a
            libcrypto.a
            libcrypto.so
            libssl.a
            libssl.so
        armeabi
            libcrypto.a
            libcrypto.so
            libssl.a
            libssl.so
        armeabi--v7a
            libcrypto.a
            libcrypto.so
            libssl.a
            libssl.so
        mips
            libcrypto.a
            libcrypto.so
            libssl.a
            libssl.so
        mips64
            libcrypto.a
            libcrypto.so
            libssl.a
            libssl.so
        x86
            libcrypto.a
            libcrypto.so
            libssl.a
            libssl.so
        x86_64
            libcrypto.a
            libcrypto.so
            libssl.a
            libssl.so
    src <--- These are example files that i used to build this with.
        myc_files.c
        myother_c_files.c
        myc_heades.h
        myother_c_headers.h

I then used the following in the Android.mk to generate some libraries by running ndk-build.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

# Select from arm64-v8a armeabi armeabi-v7a mips mips64 x86 x86_64
ARCH := armeabi

APP_UNIFIED_HEADERS := true

#APP_ABI := $(ARCH)
# TARGET_ARCH := $(ARCH)
TARGET_PLATFORM := android-14

SRC_DIR := $(LOCAL_PATH)/src
HDR_DIR := $(LOCAL_PATH)/include
LIB_DIR := $(LOCAL_PATH)/libs

LOCAL_C_INCLUDES := $(HDR_DIR)
LOCAL_MODULE := myclib
LOCAL_SRC_FILES := $(SRC_DIR)/myc_files.c $(SRC_DIR)/myother_c_files.c
LOCAL_LDLIBS += -L$(LIB_DIR)/$(ARCH) -lssl -lcrypto

include $(BUILD_STATIC_LIBRARY)

The libraries were put into PROJECT/obj/local and I have no idea where to go from here, or if that Android.mk file actually properly built the libraries.

like image 227
Nathan F. Avatar asked Jun 01 '17 02:06

Nathan F.


People also ask

What compiler does Android NDK use?

Code written in C/C++ can be compiled to ARM, or x86 native code (or their 64-bit variants) using the Android Native Development Kit (NDK). The NDK uses the Clang compiler to compile C/C++.

How does Android APK execute compiled native code using the Java native interface?

Using Android Studio 2.2 and higher, you can use the NDK to compile C and C++ code into a native library and package it into your APK using Gradle, the IDE's integrated build system. Your Java code can then call functions in your native library through the Java Native Interface (JNI) framework.

What is the use of NDK in Android?

The Android NDK is a toolset that lets you implement parts of your app in native code, using languages such as C and C++. For certain types of apps, this can help you reuse code libraries written in those languages.


1 Answers

UPDATE: with AGP 4.0 (still not released), you can consume OpenSSL from maven.

dependencies {
    implementation "com.android.ndk.thirdparty:openssl:1.1.1d-alpha-1"
}

In Android.mk, you simply add (at the end)

$(call import-module, prefab/curl)

Here are full details: https://android-developers.googleblog.com/2020/02/native-dependencies-in-android-studio-40.html


Original answer:

  • 0. Build openssl libraries for Android; you must choose the ABI (armeabi-v7a and x86 are usually enough). You can find the official tutorial long and boring. In this case, you can find prebuilt binaries on GitHub or elsewhere. Decide whether you want shared libs or static libs.

  • 1. With Android Studio 2.3 you can build your library by the integrated externalNativeBuild gradle task. But you can build it separately, using ndk-build command.

  • 2. If you choose not to use gradle, copy the built shared libs to app/src/main/jniLibs directory of your Android project.

  • 3. You need a JNI wrapper to connect your Java code with C code. On the Java side, declare necessary native methods in one or more classes. These methods must be implemented on the C/C++ side, as described in the Android JNI walkthrough. Your Java code must explicitly load the shared library that includes these native method implementations. Note that you cannot directly call native functions in your library, or in opensssl library, even if these functions are exported.

The working Android.mk:

include $(CLEAR_VARS)

LOCAL_MODULE := vrazovpn
LOCAL_SRC_FILES := src/myc_files.c src/myother_c_files.c
LOCAL_STATIC_LIBRARIES := openssl libcrypto
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := openssl
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_SRC_FILES := libs/$(TARGET_ARCH_ABI)/libssl.a
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := libcrypto
LOCAL_SRC_FILES := libs/$(TARGET_ARCH_ABI)/libcrypto.a
include $(PREBUILT_STATIC_LIBRARY)

few comments to clarify the above:

  • LOCAL_SRC_FILES paths are relative to LOCAL_PATH.
  • LOCAL_C_INCLUDE paths are relative to the working directory, that's why we often prefix them with $(LOCAL_PATH).
  • It's OK to use LOCAL_C_INCLUDES to refer to headers that belong to some library that we use, but it is much safer and cleaner to export these headers with the library, using the power of LOCAL_EXPORT_C_INCLUDES; there are other settings that can be exported similarly.
  • Android.mk should not (and cannot) set the target ABI; it receives the target ABI from ndk-build. You can control which ABIs to include in build either by listing them in your Application.mk file or on the command line, e.g.

    ndk-build APP_ABI="x86 armeabi-v7a"

If you build your library in Android Studio 2.3 or above, using the gradle plugin, the APP_ABI setting is ignored. You must specify the list in abiFilters.

like image 146
Alex Cohn Avatar answered Oct 26 '22 20:10

Alex Cohn