Android NDK - make two native shared libraries calling each other - android

Wasted half a day trying to build two shared libraries, e.g. mod1 and mod2 (which Android NDK compiles to libmod1.so and libmod2.so), from sources in a jni folder and sub-folders, then have mod1 call a function from mod2. Plenty of answers on how to make the build work, but then runtime dynamic linking was not working, the app crashed on startup.
Decided to post this question and immediately answer it, so that Q and A to the whole process are together, and hopefully someone else won't waste a day researching it again.

The correct build procedure was relatively easy, my problem was that making libmod1.so dependent on libmod2.so caused unsatisfied links upon startup - mod1 code could not find mod2 shared library, even though both were present within the same folder in the final APK, under libs/armeabi, libs/x86 etc. However, to make my answer complete:
Put your C or C++ sources and header files under sub-directories of jni dir in your Android project, e.g. folders mod1/ and mod2/
Per NDK instructions, create Application.mk file, e.g. mine is:
NDK_TOOLCHAIN_VERSION=4.7
APP_PLATFORM := android-8
APP_ABI := armeabi armeabi-v7a x86
Create Android.mk following this template:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := mod2 # this makes libmod1.so dependent on libmod2.so
LOCAL_MODULE := mod1
LOCAL_SRC_FILES := mod1/file1.c
LOCAL_SRC_FILES += mod1/file2.cpp
...
include $(BUILD_SHARED_LIBRARY) # this actually builds libmod1.so
include $(CLEAR_VARS)
LOCAL_MODULE := mod2
LOCAL_SRC_FILES := mod2/file1.cc
LOCAL_SRC_FILES += mod2/file2.cc
...
include $(BUILD_SHARED_LIBRARY) # this builds libmod2.so
That's about it, all builds without complains with ndkbuild script. You only need a C wrapper to call some functions from Java. And here was my problem. Since I had functions callable from Java only in libmod1.so, my C wrapper class in Java was like:
public class CWrapper {
static {
System.loadLibrary("mod1");
}
public static native int func1(String aParam);
...
}
This seemed perfectly logical to me - I call to libmod1.so from Java, so I used System.loadLibrary("mod1"), and since libmod1.so knows it depends on libmod2.so, and both files are in the same folder, libmod1 will know how to find and load libmod2, right? Wrong! It was crashing upon app startup with "unsatisfied link". The exact error message was:
java.lang.UnsatisfiedLinkError: Cannot load library: soinfo_link_image(linker.cpp:1635): could not load library "libmod2.so" needed by "libmod1.so"; caused by load_library(linker.cpp:745): library "libmod2.so" not found
I was searching everywhere for some more code to add to Android.mk to resolve this in vain. Finally Eureka! I modified my CWrapper class as follows:
public class CWrapper {
static {
System.loadLibrary("mod2"); // must be first, as mod1 depends on mod2!
System.loadLibrary("mod1");
}
public static native int func1(String aParam);
...
}
and things started working like a charm...
Greg

Related

skip prebuilt-library.mk on gradle sync android studio

My project app depends on a library module alib. both app and alib build a c++ library with ndk. the app c++ lib, called app.so, depends on alib.so, which is the c++ lib for the library module. Inside the android.mk of app, i have:
LOCAL_PATH := $(the_right_alib_path)
include $(CLEAR_VARS)
LOCAL_MODULE := alibsdk
LOCAL_SRC_FILES := libalib.so
include $(PREBUILT_SHARED_LIBRARY)
Gradle sync fails on prebuilt-library.mk, with message Android NDK: Check that /the_right_alib_path/libalib.so exists or that its path is correct.
the file would be there if i build the module with gradle, it's naturally not there when I do gradle sync.
How can I skip the execution of prebuilt-library.mk?
Alternatively there is a way to tell ndk that the alib.so will be built by another gradle module?
Ps. This is more annoying cause in reality it checks all the libraries for different versions/flavor/dimensions, and i don't need to build all those libraries to work on the dev version on the app.
You can avoid the error by small modification of the Android.mk:
include $(CLEAR_VARS)
LOCAL_MODULE := alibsdk
LOCAL_SRC_FILES := libalib.so
ifeq ($(findstring n,$(MAKEFLAGS)),n)
include $(BUILD_SHARED_LIBRARY)
else
include $(PREBUILT_SHARED_LIBRARY)
endif
This takes advantage of the -n flag that is passed to ndk-build during sync. You can create a custom define for that, if you wish. build-shared-library.mk will generate a warning about strange SRC, but won't fail.
Same can be achieved without touching the Android.mk files: in your build.gradle, use
if (project.gradle.startParameter.taskNames.isEmpty()
|| project.gradle.startParameter.taskNames[0].contains(":generate")) {
android.defaultConfig.externalNativeBuild.ndkBuild.arguments += 'PREBUILT_SHARED_LIBRARY=$(BUILD_SHARED_LIBRARY)'
}
Android Studio runs ndk-build many times: with no tasks (the Sync step), within a :generate[flavor][Debug|Release]Sources, and within :externalNativeBuild[flavor][Debug|Release].
To retrieve the current task, I followed https://stackoverflow.com/a/21603707/192373.
Maybe the easiest fix is to rely on an obscure implementation detail of prebuilt-library.mk*). Early in your main Android.mk, add the line
override prebuilt=$(if $(strip $(wildcard $(prebuilt_path))),$(prebuilt_path),.)
This can be done in gradle, if necessary:
android.defaultConfig.externalNativeBuild.ndkBuild {
arguments 'prebuilt=$(if $(strip $(wildcard $(prebuilt_path))),$(prebuilt_path),.)'
}
*) verified for NDK r19b and earlier.

Include prebuilt static library on existing aosp module

I am trying to include my prebuilt library on the existing Android Module art/runtime/. I have followed the official documentation, found here. In detail, these are the changes I 've made:
Added lines in art/Android.mk:
# This makes sure my library's .mk is found
include $(art_path)/mylib/Android.mk
Added lines in art/runtime/Android.mk:
# Added this, after the LOCAL_C_INCLUDES
LOCAL_STATIC_LIBRARIES := mylib-prebuilt
# Also, on the next mention of LOCAL_STATIC_LIBRARIES, I changed the
# assignment operator to '+=', so mylib won't overriden
Include in art/runtime.cc source mylib's header:
#include "mylib.h"
// then at some point use it
I have put mylib sources in art/mylib. I manually build, using a regular Makefile, the archive libmylib.a, which I want to be statically linked to libart.so.
In the Android.mk found at art/mylib I have added the following:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := mylib-prebuilt
LOCAL_SRC_FILES := $(LOCAL_PATH)/libmylib.a
LOCAL_EXPORT_C_INCLUDES := \
$(LOCAL_PATH)/mylib.h \
$(LOCAL_PATH)/another_header.h
include $(PREBUILT_STATIC_LIBRARY)
By exporting the C includes, the dependent modules should have automatically appended them to their local list. However, this is not the case, as I get the error:
fatal error: 'mylib.h' file not found
If I workaround this issue, with a stupid way that I really shouldn't, I stumble upon a link error, as my libmylib.a is never used during linking.
Other information:
I have placed mylib in art/mylib as it will be part of the art namespace and its methods will be calling/being called by art/runtime sources, e.g. runtime.cc. I have chosen to go with prebuilt-libraries, instead of directly including my sources, like Garbage Collector (found at art/runtime/gc`), as I want to pass particular compilation flags to individual source files.
I have already tried, and failed, solutions to other questions, found here and here.
Any thoughts?
EDIT:
When I try to make a full build, I get the following error:
make: *** No rule to make target
'out/host//obj/STATIC_LIBRARIES/mylib-prebuilt_intermediates/export_includes',
needed by
'out/host//obj/EXECUTABLES/dex2oat_intermediates/import_includes'.
Stop.
Thanks!
I finally managed to get this to work.
My library is now placed at art/runtime/mylib.
In art/runtime/Android.mk, I include the sources with:
LOCAL_C_INCLUDES += art/runtime/mylib
This makes mylib.h visible to the runtime source files.
The art/runtime/mylib/Android.mk has changed almost completely to:
LOCAL_MODULE := libmylib
LOCAL_SRC_FILES := libmylib.a
LOCAL_MODULE_SUFFIX := .a
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
include $(BUILD_PREBUILT)
In my previous attempts, LOCAL_EXPORT_C_INCLUDES, in contrary to documentation, it didn't copy files to the dependent modules. Also PREBUILT_STATIC_LIBRARY didn't seem to be working.
Now, I can build my library in an archive of objects, using regular makefiles, and the build system copies it each time it gets changed to the appropriate locations, so at the link stage can be blended with libart.so.

Failure to build a project using NDK

I'm trying out NDK for the first time, and I'm stuck with building this project. I'm attempting to use the libraries used in Android's screencap recording program to read data from my screen. I understand that I need to use LibGUI, which I've pulled from my phone. Whenever I compile, however, I get this as an error message:
C:/Users/Kevin/Desktop/ScreenCapture//jni/main.cpp:2:34: fatal error: gui/ISurfaceComposer.h: No such file or directory
Which is odd, considering that I have libgui.so included.
What my main file looks like:
#include <stdio.h>
#include <gui/ISurfaceComposer.h>
int main() {
printf("Started!");
return 0;
}
What my Android.mk looks like:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libgui-prebuilt
LOCAL_SRC_FILES := libgui.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := screencapture
LOCAL_SRC_FILES := main.cpp
LOCAL_SHARED_LIBRARIES := libgui-prebuilt
include $(BUILD_EXECUTABLE)
And I have both the main file and libgui.so in the same folder.
main.cpp:2:34: fatal error: gui/ISurfaceComposer.h: No such file or directory
Your error message indicates that the compiler is unable to find a header file, but your attempted solution is to provide a shared object (.so) library from the phone.
Libraries might help at link time, but before you can link you must successfully compile, and to do that you are going to have to obtain the missing header (perhaps from AOSP sources, as source code it will not be found on the phone) or else recreate it by deducing its contents from clues you collect. Unfortunately, it often isn't possible to take just one internal header out of AOSP, as it will likely depend on many others.
Linking against private system internals also introduces a risk of your program breaking if the private interface between internal components changes between Android versions.
(Normally I would also mention that you are unlikely to be able to record a screen with the NDK in it's normal usage of making a shared library to link into an application process, however your Android.mk seems to indicate you are building an executable - that might work if you can succesfully build it and you run it as a user with sufficient permission such as adb's shell userid or using a root hack)
Add one line for module screencapture:
LOCAL_C_INCLUDES := $(LOCAL_PATH)/
If you haven't folder gui, you need to copy&paste includes headers *.h into folder jni/gui. Always such a requirement about using like that for ndk...
Or second case - using other location of this source for ldlibs:
LOCAL_LDLIBS := -L/full_path_to_source_gui/gui/

How to build multiple projects in the correct dependency order with Android NDK?

I have a series of existing libraries which I need to re-use in an Android application. The layout is similar to:
\Libraries\libOne
\Libraries\libTwo [Static library]
\Libraries\libThree
\Applications\MyApplication\ [Application]
libTwo depends on libOne, and libThree depends on libTwo. How can I get the build system to build all of the libraries in the correct order? I'm trying to use Eclipse, but if necessary I can use the command line.
All of these libraries will eventually be referenced by a Java application (and use JNI to interact with them). Any clues on how I setup the Android.mk / Application.mk files?
I've tried using BUILD_STATIC_LIBRARY for libTwo, but it doesn't actually output any files! I was expecting a libTwo.a file, but nothing gets compiled or built.
Do I write one Android.mk in the application? Or an Android.mk for each project?
OK, now I see your edit, and this makes it possible to answer the specific question.
You must have at least one Android.mk file for your application if you want to use Android NDK to build your native library/ies. This is not a requirement, though. It is OK to build it though Cmake, or a "standalone toolchain" with "traditional" makefiles, or with a MS Visual Studio plugin, or any other way. It is the result that matters. The result is a shared object built with a compatible compiler for a bionic runtime.
It makes goode sense to put the library in ${project_root}/libs/armeabi/ directory (for ARM v6 compatible devices, other subdirectories for x86, MIPS, arm v7a) to allow the APK builder pack it correctly, to allow app installer to unpack the correct version (compatible with the device processor) into /data/data/${package_name}/lib directory on the device, and finally to be able to use System.loadLibrary(short_name) to use it from Java. But it is also quite possible to pack the so file differently, unpack it manually, and load it from any place on the device file system (provided your app has permission to write and read this file).
But if we filter out exotic cases, it is much more comfortable to have an Android.mk in the ${project_root}/jni directory. In terms of ndk-build command, each library is a separate MODULE, but all three may be defined in one Android.mk file. On the other hand, if your libraries are isolated (e.g. come from separate 3rd parties), you will probably prefer to create three Android.mk files. Luckily, ndk-build is nothing but a wrapper around gnu make, and the simple include statement in Android.mk works as in any other makefiles.
In summary, your case is probably covered by a simple Applications/MyApplication/ [Application]/jni/Android.mk file:
include ../../Libraries/libOne/Android.mk
include ../../Libraries/libTwo/Android.mk
include ../../Libraries/libThree/Android.mk
I don't know what dependency you have between libOne and libTwo, but for libOne the file Libraries/libOne/Android.mk will look like
LOCAL_PATH = $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libOne
LOCAL_SRC_FILES := first.c
include $(BUILD_STATIC_LIBRARY)
and Libraries/libThree/Android.mk
LOCAL_PATH = $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libThree
LOCAL_SRC_FILES := third.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../libOne $(LOCAL_PATH)/../libTwo
LOCAL_STATIC_LIBRARIES := libOne libTwo
include $(BUILD_SHARED_LIBRARY)
You should run ndk-build from Applications/MyApplication/ [Application] directory - either from command prompt, or through Eclipse ADT plugin.
update the same may be expressed by one Android.mk file in jni directory:
LOCAL_PATH = ../../Libraries/libOne
include $(CLEAR_VARS)
LOCAL_MODULE := libOne
LOCAL_SRC_FILES := first.c
include $(BUILD_STATIC_LIBRARY)
LOCAL_PATH = ../../Libraries/libThree
include $(CLEAR_VARS)
LOCAL_MODULE := libThree
LOCAL_SRC_FILES := third.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../libOne $(LOCAL_PATH)/../libTwo
LOCAL_STATIC_LIBRARIES := libOne libTwo
include $(BUILD_SHARED_LIBRARY)
There is an android section in the projects' properties, where you can edit the library dependencies. It can only be used, if libOne libTwo and libThree are marked as libraries, in their properties panel.

android ndk UnsatisfiedLinkError when using a prebuilt shared library

I'm trying to create a shared library that links to another shared library.
Here is my main module Android.mk:
TOP_LOCAL_PATH := $(call my-dir)
include $(call all-subdir-makefiles)
LOCAL_PATH := $(TOP_LOCAL_PATH)
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := cpp
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include/ $(LOCAL_PATH)/lib/include
LOCAL_MODULE := SightCore-jni
LOCAL_SRC_FILES := SightDemo.cpp SightCore-jni.cpp
LOCAL_SHARED_LIBRARIES := SightAPI
LOCAL_LDLIBS = -llog
include $(BUILD_SHARED_LIBRARY)
I also have the prebuilt shared library in ./lib directory with its own Android.mk file:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := SightAPI
LOCAL_SRC_FILES := libSightAPI.so
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_SHARED_LIBRARY)
The SightCore-jni.cpp source file is the jni interface to the shared library and is loaded using the command
System.loadLibrary("SightCore-jni");
During the ndk-build process I get no compilation or linkage errors.
When I try to run the application and access one of the native methods I get the UnsatsfiedLinkError.
I noticed that if disable the references to the SightAPI in my jni code and put a typo to the LOCAL_STATIC_LIBRARIES := SightAPI line, The build is successful and there is no UnsatisfiedLinkError.
This mean that the jni code I have is good (I'm actually sure it is ok...)
So the observation is as follows:
If I compile the shared library with the prebuilt shared library I get a corrupted .so file.
If I compile the same ndk project without linking to the prebuilt shared library there is no problem loading the shared library from the java side.
Please help me out if you can.
Thanks in advance,
Ita
Found the issue.
Apparently the ndk build system doesn't automatically load referenced shared libraries, even if they are declared in your Android.mk.
I had to call on System.loadLibrary(SightAPI) & System.loadLibrary("SightCore-jni") in order to solve this issue. I would have expected that the only library to load would have been the main library SightCore-jni.
Well..I guess the moral is If you want something done, do it yourself :)
+1 to Roy Samuel for his effort and correct instincts.
I hope this helps anyone.
Cheers
Have you made sure that the cpp function name, that you'd like to use over JNI, is corresponding to the package name of the Java wrapper class where System.loadLibrary("SightCore-jni"); is present?
e.g. If you would like to use the C function, myFunction in the java layer, and suppose your JNI wrapper class is in the package com.my.package.sightcore,
then your C code function name should be like this :
JNIEXPORT JNICALL Java_com_my_package_sightcore_myFunction(JNIEnv * env, jobject thiz, ...)
If you are running your app on your device,
See if the API levels, and hence, the sdk release matches to your device's android version (API level).
Hope this helps. Let me know if you need more clarifications...
This happened to me, and the solution for me was to make sure I was including the proper file libgnustl_shared.so and not libc++_shared.so after I switched compilers. So, just making sure either one of these files is the correct one for your build, and making sure it's been updated with the latest, you shouldn't get this issue any longer.

Categories

Resources