8

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.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
Nathan F.
  • 3,250
  • 3
  • 35
  • 69
  • You must build your library as shared, so replace the last line with **include $(BUILD_SHARED_LIBRARY)**. Other fixes are in my updated answer. – Alex Cohn Jun 04 '17 at 08:22
  • Why do you add C tag ? – Stargateur Jun 08 '17 at 06:55
  • 1
    @Stargateur the library I'm trying to compile and link is written in C, the compiler that would end up being used was derivative of GCC. The libraries I'm linking to my library are written in C. My JNI wrapper would be in C. Why wouldn't i add the C tag? – Nathan F. Jun 08 '17 at 17:40
  • 1
    @NathanFiscaletti C is about the language. If you have problem about linker this has nothing to do with C. With your logic we should add C tag for everything because linux, windows, etc... are written in C. So all is about C right ? This is stupid. – Stargateur Jun 08 '17 at 23:13
  • You are probably going to be very disappointed with your `libcrypto.so` and `libssl.so`. Zygote, which is Android's `init`, is going to preload an old, downlevel version of the OpenSSL library packaged with the OS. The updated version you package is never going to load. You should use the [Wrapper Shared Object](https://wiki.openssl.org/index.php/Android#Wrapper_Shared_Objects) technique discussed on the OpenSSL wiki. – jww Jun 09 '17 at 13:46
  • I suggest that we split the question of building latest OpenSSL for all Android architectures. It's a separate issue, and takes different expertise. And @jww comment about wrappers requires extra research. But this has little to do with *Compile native library that relies on other native libraries*. – Alex Cohn Jun 09 '17 at 19:55
  • @AlexCohn Is there no sample on the web where a static `prefab` library is consumed? E.g. [This](https://github.com/android/ndk-samples/blob/master/prefab/curl-ssl/app/src/main/cpp/Android.mk#L22) would instead use `LOCAL_STATIC_LIBRARIES`... – l33t Jan 20 '23 at 23:42
  • @I33t sorry, haven't checked this for the last ~4 years. There may be none these days. – Alex Cohn Jan 22 '23 at 11:04

3 Answers3

7

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.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • I've made the changes you suggested, however now i'm getting an error when i run `ndk-build`: `libcrypto.a(ui_openssl.o):ui_openssl.c:function read_string_inner: error: undefined reference to 'signal'`. I have a feeling that this has something to do with my build of openssl. But I'm at a loss trying to figure out how to compile it properly, thought I already had. – Nathan F. Jun 04 '17 at 17:53
  • Did you follow the Android tutorial from my answer above? Or you downloaded prebuilt libcrypto from internet? – Alex Cohn Jun 04 '17 at 18:56
  • I didn't download a prebuilt version. However, that script you supplied in the official Android Tutorial is outdated. I can't compile for all 7 active architectures that android currently supports using it. – Nathan F. Jun 04 '17 at 20:23
  • And on top of that it doesn't seem to work even for arm archs with the latest version of openssl. – Nathan F. Jun 05 '17 at 00:08
  • Fair enough. But if you want help in finding out what went wrong with your build, please explain how you did build these libraries. – Alex Cohn Jun 05 '17 at 03:45
  • The most likely mistake could be that OpenSSL was built for Android platform 21 or higher. BTW, what is your NDK version? – Alex Cohn Jun 05 '17 at 03:53
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/145902/discussion-between-nathan-fiscaletti-and-alex-cohn). – Nathan F. Jun 05 '17 at 16:47
  • Here I discuss the missing **signal**: https://stackoverflow.com/a/37123859/192373 – Alex Cohn Jun 05 '17 at 19:14
  • Sorry, this discussion does not notify me when you write something. In the nutshell, if you use static libraries (libssl.a and libcrypto.a) to link your libmyclib.so, you don't need to touch the Makefile. – Alex Cohn Jun 09 '17 at 19:12
  • @AlexCohn I am new in openssl. I have successfully got the libssl.so and libcrypto.so files, but unable to use in cmakelist.txt. please help me to get out from this issues. I have a search alot of things but didn't get success yet. – Peter Mar 03 '20 at 05:02
4

There are thousands of operations you can do with native development (I mean C/C++). These languages are so light and you can carry some heavy operations from Java to C/C++. For example, Camera hardware is one of the heaviest and baddest hardware in Android devices. So, it really sucks, when you try to handle this and add your own operations. When it comes to native development, this operations working on CPU and don't need translating via Dalvik or something else. That's why you need all CPU architecture of android devices. Because, each arch has its own calculation speed or technology. If there is any Co-Processors, you should take into consideration this also. This was a short overview about NDK.

For building or rebuilding native libraries you have two ways, CMake and NDK build. There are not lots of differences between these two building system, so i'd like to explain my answer with NDK building.

For native development, you should have main/jni folder which is you should put your libraries and native sources inside it and build them in this folder. Before you should create two files, Android.mk and Application.mk. Android.mk is for building native libs (modules), adding flags and etc. You can build static and shared libraries in Android.mk. When you build your C sources, it creates static library (.a). But these libraries not for using in Java side. You can only use them for creating shared libraries (.so). For this, you should build your C++ sources, and if you want, you can add your static libraries to this.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

ifneq (,$(filter $(TARGET_ARCH_ABI), armeabi-v7a x86 arm64-v8a x86_64))

LOCAL_MODULE := myLibrary

LOCAL_SHARED_LIBRARIES := cpufeatures opencv_imgproc opencv_core 

LOCAL_C_INCLUDES := $(LOCAL_PATH)/c-files

LOCAL_SRC_FILES := $(LOCAL_PATH)/myJni.cpp

include $(BUILD_SHARED_LIBRARY)

--- 1st line returns main/jni path.

--- 2nd line clear all old LOCAL_*** variables.

--- 3th line build modules for each arch's which are declared there.

--- 4th line shows module name, when you build it, NDK automatically add lib and .so extensions to it (libmyLibrary.so).

--- 5th line adds other shared libraries, which you need their sources in your native sources.

--- 6th line adds C files to your module.

--- 7th line shows your C++ sources which you try to build.

--- 8th line is building command.

In Application.mk you can give commands something like, your project in release mode or debug mode. And you should give arch's in this file. For example APP_ABI := armeabi-v7a x86 arm64-v8a x86_64 and etc.

When you try to build libraries and use them in java side, you should do some operations.

  1. Check your ndk path in local.properties file in project folder. It shows ndk-bundle path for building makefiles.

    ndk.dir=/Users/../Library/Android/sdk/ndk-bundle

  2. Check build scripts in gradle file. This script shows where shared libs should be located:

    sourceSets.main {
        jni.srcDirs = []
        jniLibs.srcDir 'src/main/libs'
    }
    
  3. And show your makefile path to gradle.

    externalNativeBuild {
       ndkBuild {
          path 'src/main/jni/Android.mk'
       }
    }
    

I think my answer will help you for understanding some details. For addition, you can take a look these answers also.

  1. For calling native methods: Enter

  2. For linking shared libraries: Enter

Enjoy!

JavadKhan
  • 625
  • 6
  • 18
0

First, you need to have already cross-compiled (for your Android platform and ABI) OpenSSL lib, or to compile it in your AS Project. Then, the easiest way if you are familiarized with cmake and gradle, you can find a complete example of what you want at https://github.com/googlesamples/android-ndk/tree/master/hello-libs. Pay special attention at the cmake script CMakeLists.txt, and the gradle scripts app/build.gradle and gen-libs/build.gradle. I hope this can help you

Eligio Mariño
  • 318
  • 4
  • 13