Load a different version of system shared library on Android by NDK? - android

I am trying to develop an android application by using some native libraries.However, the system shared libraries on android 4.0 and android 4.1.2 are different. To ensure the compatibility, I get the libskia.so file from platform 4.0, and import it into my project. I wish to load this shared library just like using a 3rd party shared library. Unfortunately, while running on android 4.1.2, my application seems still call the system skia library. I have no idea about this and the followings are my Android.mk file
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := sample
LOCAL_SRC_FILES := sample-jni.cpp
LOCAL_CFLAGS := -I /home/WORKING_DIRECTORY/external/skia/include \
-I /home/WORKING_DIRECTORY/external/skia/include/core \
-I /home/WORKING_DIRECTORY/frameworks/base/core/jni/android/graphics \
-I /home/WORKING_DIRECTORY/frameworks/base/include \
-I /home/WORKING_DIRECTORY/frameworks/base/native/include/android \
-I /home/WORKING_DIRECTORY/system/core/include \
-I /home/WORKING_DIRECTORY/external/skia/include/xml \
-I /home/WORKING_DIRECTORY/external/skia/include/images \
-I /home/WORKING_DIRECTORY/external/skia/include/views \
LOCAL_SHARED_LIBRARIES :=skia jnigraphics
include $(BUILD_SHARED_LIBRARY)
include $(LOCAL_PATH)/prebuilt/Android.mk
and the prebuilt makefile
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := skia
LOCAL_SRC_FILES := libskia.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := jnigraphics
LOCAL_SRC_FILES := libjnigraphics.so
include $(PREBUILT_SHARED_LIBRARY)
Any ideas? Or, are there any other methods to ensure compatibility?

I hope you understand that it is not recommended to have any dependencies on the system libraries beyond the stable API. But the open nature of Android allows (both technically and in terms of licensing) to introduce such dependencies. Essentially it means sailing in the non-chartered waters, and being prepared to API changes not only in the next version of the platform, but also in a vendor-provided (i.e. non-AOSP) setups of the same platform level.
Theoretically, you can put a copy of your variation of a system lib into the libs/armeabi-v7a folder of your app, and load it with System.load(fullPath) instead of loadLibrary().
But in practice I believe that system/lib/libskia.so will be loaded in your process before you have a chance to execute your code, and you cannot load two version of the same lib in the same process. Also, an older version of libskia will most likely fail to load on the system because it depends on other system libraries.
The safest way to ensure forward (and vendor) compatibility is to use dynamic linking for the undocumented system features, and perform careful error checking on the way.
But in many cases, the system undocumented APIs are actually quite stable, and the nice people in the Android team of Google do not make breaking changes too often. Therefore, if you link against the 4.0 version of skia your code will most likely simply work on 4.1.2 and on...
Update: In your particular case, when an extra field fTextLocale was added to an old class, you should first of all bless the developers who did not insert this field in the middle of the class declaration. Because now you have a reasonable strategy: use the 4.1.2 headers (with the extra field), link against the 4.0 library (that does not introduce the accessor methods to the new field), and your code will hopefully just work.

You can use a different version of libskia by using dlopen in your native code with parameter RTLD_DEEPBIND. This will overwrite the global symbol table.

Related

Shared library using static gnutls library has text relocations

Problem: I need to port gnutls to Android to be used in a shared library (say library A) I'm using in my Android application.
What I've tried: I've modified the make file for openconnect to generate a .a static library file for gnutls and its dependencies (libgmp, libnettle and libhogweed), I used them to build static libraries in my Android project and referenced them in the shared library A. Code builds and installs fine but on M+ devices I get the following error at runtime:
java.lang.UnsatisfiedLinkError: dlopen failed: libA.so: has text relocations
I've tried to pass the -fPIC flag when building the static libraries (.a files) and when building the libA.so file with no luck, I can always see TEXTREL entries in libA.so file. I'm sure it is due to the those new static libraries since I was using libA before with no issues.
Other thing I tried: tried building gnutls as a shared library, the generated libA.so now had no text relocations but would still fail to load at runtime because the gnutls so files have a version (e.g libgnutls.so.3.0) and Android does not support versioned libraries.
Specific question: How can I either: 1.Build gnutls as a static library without text relocations or 2. Build it as a shared library with no soname?
Edit: I see the same question asked on the openconnect mailing list but no clear way on how to "fix the TEXTRELs in the native code first".
I have seen other answers for problems with text relocations like this question and this question but that didn't help since I'm using the latest NDK build and passing the PIC flag already
You can't load a library that requires Text Relocations:
Starting with API 23, shared objects must not contain text relocations. That is, the code must be loaded as is and must not be modified.
(source)
Answers:
How can I build gnutls as a static library without text relocations?
-fPIC can't prevent all text relocation. In some cases, if your library uses inline asm, the compiler will unable to make it Position Independent (PIC). However, if you sure your library can be position independent, the problem may be a somewhere in your build config.
If not, you should prevent your library from using text Relocations. Fortunately, there is a great wiki page that explains how to do that in the Gentoo Wiki.
How can I build it as a shared library with no soname?
You can set your soname with: gcc -shared -Wl,-soname,your_soname.
I finally figured it out. Since gnutls depends on nettle and gmp while nettle depends on gmp as well I had to build gmp as a shared library and the rest as static. Since libgmp was the only one building without sonames I had no problem to build it this way. So this is my final Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libgmp
LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libgmp.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libhogweed
LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libhogweed.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libnettle
LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libnettle.a
LOCAL_SHARED_LIBRARIES := libgmp
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libgnutls
LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libgnutls.a
LOCAL_SHARED_LIBRARIES := libgmp
LOCAL_STATIC_LIBRARIES := libhogweed libnettle
include $(PREBUILT_STATIC_LIBRARY)

Include the project tango SDK in an already existing project

I am new to Android development and I have no idea how to include the library that comes with the Google Tango SDK.
The app, as it is, is a small java wrapper around a c++ core that is basically a lightweight render engine. It can render one model and handle input. It is all done in C++ using Android NDK.
The problem is that I now want to use functions like onXyzIjAvailable(). How do I include and use the library? I know of this, but I need to include the library and get access to the TangoService_connectOnXYZijAvailable() function.
I want to stress that I am new to android development and I have never included anything. I have only written the code myself or used Android Studio to download and include the SDKs, generate the GRADLE files and take care of the compilation/makefles. I found this SO post talking about adding a library, but I did not understand the answer. How do I import it to this project and build it?
Thank you so much for the help.
You must download the current tango api and service sdk for C here
Unzip and place the folders (I named them tango_client_api and tango_service_sdk) you want to. I prefer a structure like that:
ProjectFolder/app/
ProjectFolder/build/...
...
tango_client_api/
tango_service_sdk/
third-party/...
...
Now you have to include the lib paths into your Android.mk makefile (located in path like ProjectFolder/app/src/main/jni/Android.mk) as followed:
LOCAL_PATH := $(call my-dir)
PROJECT_ROOT_FROM_JNI:= ../../../../..
PROJECT_ROOT:= $(call my-dir)/../../../../..
include $(CLEAR_VARS)
LOCAL_MODULE := lib_your_project_name
LOCAL_SHARED_LIBRARIES := tango_client_api
LOCAL_CFLAGS := -std=c++11
LOCAL_C_INCLUDES := $(PROJECT_ROOT)/tango_service_sdk/include/ \
LOCAL_SRC_FILES := your-project-file1.cc \
your-project-file2.cc \
your-project-file3.cc
LOCAL_LDLIBS := -llog -lGLESv2 -L$(SYSROOT)/usr/lib
include $(BUILD_SHARED_LIBRARY)
$(call import-add-path, $(PROJECT_ROOT))
$(call import-module,tango_client_api)
In your .h files you can use for example: #include <tango_client_api.h>
to get access to all TangoService_functions
And that's it. I really recommend you to look into the tango C examples on github https://github.com/googlesamples/tango-examples-c

Shared library and Android NDK

I recently started to use Android NDK and I’m facing some problems with shared libraries.
I created a project with Eclipse that allows me to use NDK and natives functions. But now, I would like to use another shared library that I created with QtCreator.
So here is my question. Is that possible to include a shared library to my NDK project, even if I didn’t invoke ndk-build to create it?
Let’s take an example. If I create a simple shared library like that:
g++ -Wall -shared -fPIC -o libapi.so MyDLL.cpp
Could I be able to use it on my NDK project like this (Android.mk):
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := api
LOCAL_SRC_FILES := ../sharedLibs/libapi.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := dbapi
LOCAL_SRC_FILES := dbapi.cpp
LOCAL_C_INCLUDES += ../includes
LOCAL_SHARED_LIBRARIES := api
include $(BUILD_SHARED_LIBRARY)
And of course, could I be able to use its functions on my native NDK side?
Using this method, I'm facing this error when I try to build my Eclipse project:
/libs/armeabi-v7a/libapi.so: File format not recognized
I thought my shared lib was not compatible with ndk-build (true, false?).
Thank you for your answers.
How did you build your shared lib? Is it compiled for arm, with NDK?
If not, it cannot work.
The people in the comments are indicating that you have almost certainly compiled for x86 instead of arm. (You say you are using cygwin with Windows, which will compile to x86 by default unless your windows box is an arm box, which I am guessing is not the case.)
Inside the Android ndk is a script called make-standalone-toolchain.sh. If you are in the current directory where your ndk is installed, you can use this by typing:
./android-ndk-r9/build/tools/make-standalone-toolchain.sh --platform=<your desired android platform> --install-dir=<location where you want android toolchain>
This will create a set of build tools which you can use to build arm binaries to run on your device. These tools will be named things like, e.g.
arm-linux-androideabi-gcc
Put those tools in your path and use them to build your library.
I don't know whether cygwin includes a "file" command, but you may be able to determine the architecture of your shared library by typing:
file libapi.so
If it says it's arm, you're good. If it says x86, it won't work (except on an x86 android device, that is).

How to build libvpx for android arm as well as x86?

I have been building our Android app for armeabi-v7a. Now, to add support for x86 as well, I extended Application.mk:
APP_ABI := armeabi-v7a x86
The first problem I am running into is that libvpx can be configured either for arm or for x86:
$ ./libvpx/configure --target=armv7-android-gcc ...
OR
$ ./libvpx/configure --target=x86-android-gcc
Wondering how to deal with multiple platforms. Does one create two different libvpx directories for two platforms and use if-then-else logic in Android.mk to pick the right directory? Is there a better way?
Yes, pretty much. If you store the build product (as produced by make install) in parallel directories named according to the android ABIs, you can simplify using it from an Android.mk file like this:
include $(CLEAR_VARS)
LOCAL_MODULE := libvpx
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/libvpx/$(TARGET_ARCH_ABI)/include
LOCAL_SRC_FILES := libvpx/$(TARGET_ARCH_ABI)/lib/libvpx.a
include $(PREBUILT_STATIC_LIBRARY)
Since the public headers probably are (should be) free of anything arch specific, you should also be able to share one copy of them instead of having one copy per arch.

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.

Categories

Resources