How to load a .so based on the version of Android?

720 views
Skip to first unread message

folecr

unread,
Aug 5, 2010, 1:58:59 PM8/5/10
to android-ndk
Hello,

I'm trying to load .so files based on the version of Android I am
running.

My app has some native functions to do with rendering that are
dependent on the version of Android. I have 2 .so files to load at
runtime : The first file is libAppJNI.so, the second is
libAppRenderer.so

When running on a single version of Android (say Android 2.) I've
successfully set up my code like this :
* Build two .so's
* Let the android build system build and package libAppJNI.so and
libAppRenderer2.2.so
* libAppJNI.so has a dependency on libAppRenderer2.2.so
* Call System.load("libAppRenderer2.2.so") before
System.load("libAppJNI.so")
My app runs successfully on Android 2.2

Now, my questions that I'm hoping someone has answers for :

*How do I package multiple versions of the libAppRenderer?
* Do I name these differently like libAppRenderer16,
libAppRenderer21, libAppRenderer22?
* But then, what is libAppJNI built with?
* Do I name them the same name and built libAppJNI with
libAppRenderer?
* How do I package these? Store it with these paths in the
archive? : libs/libAppJNI.so, libs/android22/libAppRenderer.so, libs/
android21/libAppRenderer.so?

Is there an example of a JNI app that does this : load a version of a
library and then a common library?
Thanks!

Onur Cinar

unread,
Aug 5, 2010, 2:29:42 PM8/5/10
to andro...@googlegroups.com

Hi,

Since you are building libraries for different versions of Android, I'm guessing that you are using some of the private API?  In that case, you won't really find an "official" answer for your questions, but here is what I'm usually doing:

I usually have my libraries setup the other way, so just loading the head one for the platform works fine for me.  I check the version in Java through android.os.Build.VERSION, and then I load the library accordingly.  But in your case, as you described, if you first load the platform library, then the common library things should work. I guess dalvik is using RTLD_GLOBAL with dlopen.

Regards,

-onur



--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To post to this group, send email to andro...@googlegroups.com.
To unsubscribe from this group, send email to android-ndk...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/android-ndk?hl=en.


folecr

unread,
Aug 5, 2010, 2:35:27 PM8/5/10
to android-ndk
The problem is :

libcommon.so depends on libspecific<version>.so

* What do I make libcommon.so depend on at build time to satisfy
depdencies?
* Are there any checks done at runtime? If I build libcommon.so with
libspecific2.2.so and then load libspecific1.6.so first at runtime -
what are the issues?

If you could share details, how did you set up your build/packaging
system to package multiple libraries in the apk?
Thanks!
> > android-ndk...@googlegroups.com<android-ndk%2Bunsu...@googlegroups.com>
> > .

Onur Cinar

unread,
Aug 5, 2010, 2:47:39 PM8/5/10
to andro...@googlegroups.com

Hi,

My setup is a little different then yours, so I have head libraries for each OS revision, and have common libraries for other portions of the code. It is upside-down of what you have. So in my case, I'm only loading this head library based on the OS revision.

However in your case, since all flavors of libspecific<version>.so will be exposing the same interface to libcommon.so, it won't make any difference during the link time, so you can actually link libcommon.so with any one of them. 

During the runtime, you will need to first load the libspecific<version>.so, then the libcommon.so, so that while the libcommon.so is getting loaded, the functions will be mapped to the implementations in libspecific<version>.so that is already in memory.

If I get a chance Today, I'll try to e-mail a quick test project.

Regards,

-onur


To unsubscribe from this group, send email to android-ndk...@googlegroups.com.

folecr

unread,
Aug 5, 2010, 3:43:49 PM8/5/10
to android-ndk
> During the runtime, you will need to first load the libspecific<version>.so,
> then the libcommon.so, so that while the libcommon.so is getting loaded, the
> functions will be mapped to the implementations in libspecific<version>.so
> that is already in memory.

OK... that's what my current setup is. Until now I was building
two .so's in one project.
I have to figure out how to modify the makefiles so that I can build
the .so's for all versions in one project.

If not... more makefile fun! :p
> > <android-ndk%2Bunsu...@googlegroups.com<android-ndk%252Buns...@googlegroups.com>

Onur Cinar

unread,
Aug 5, 2010, 4:03:17 PM8/5/10
to andro...@googlegroups.com

Hi,

I would do the following in the Android.mk file:

include $(CLEAR_VARS)

LOCAL_MODULE := specific16
LOCAL_CFLAGS := -DANDROID_16

LOCAL_C_INCLUDE := /android/1.6/platform/base/include ...

LOCAL_SRC_FILES := specific.c

LOCAL_LDLIBS := /android/1.6/libabc...

include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE := specific22
LOCAL_CFLAGS := -DANDROID_22

LOCAL_C_INCLUDE := /android/2.2/platform/base/include ...

LOCAL_SRC_FILES := specific.c

LOCAL_LDLIBS := /android/2.2/libabc...

include $(BUILD_SHARED_LIBRARY)

Something like that should build different shared libraries for each platform.  Will this work for you?

Regards,

-onur


To unsubscribe from this group, send email to android-ndk...@googlegroups.com.

folecr

unread,
Aug 6, 2010, 5:26:43 AM8/6/10
to android-ndk
Thanks. My makefiles are a minor variation on the above theme :

* Save the LOCAL_PATH in a temporary variable
* Use this temporary variable to reference the other .mk's
* Define individual make files for libspecific16.so,
libspecific21.so, ...
* "include" these makefiles in the project root's Android.mk

So, my makefile snippet looks like :

==============
...
...
...

LOCAL_STATIC_LIBRARIES := libappcomponents

LOCAL_SHARED_LIBRARIES := libapprendererdummy
LOCAL_LDLIBS := -ldl -llog
include $(BUILD_SHARED_LIBRARY)

# Save LOCAL_PATH because it will be modified by future .mk's
THIS_MAKEFILE_PATH := $(LOCAL_PATH)

include $(THIS_MAKEFILE_PATH)/renderer/apprendererdummy/Android.mk
include $(THIS_MAKEFILE_PATH)/renderer/apprenderer16/Android.mk
include $(THIS_MAKEFILE_PATH)/renderer/apprenderer21/Android.mk
include $(THIS_MAKEFILE_PATH)/renderer/apprenderer22/Android.mk

==============

The individual library's makefile contains the make rules for that .so
> > > > <android-ndk%2Bunsu...@googlegroups.com<android-ndk%252Buns...@googlegroups.com>
> > <android-ndk%252Buns...@googlegroups.com<android-ndk%25252Bun...@googlegroups.com>

folecr

unread,
Aug 6, 2010, 5:28:39 AM8/6/10
to android-ndk
> During the runtime, you will need to first load the libspecific<version>.so,
> then the libcommon.so, so that while the libcommon.so is getting loaded, the
> functions will be mapped to the implementations in libspecific<version>.so
> that is already in memory.

Hmmm... I'm seeing behaviour that is different by Android version.

On Android 1.6 it looks like the loader works as you described.
On Android 2.1, 2.2 the loader seems to be checking at runtime that
the library loaded at run time is the same one defined at build time.

I'll look into this more tomorrow.

Dianne Hackborn

unread,
Aug 6, 2010, 8:08:43 AM8/6/10
to andro...@googlegroups.com
You are talking about using private APIs.  Please take this discussion off this list.

As always: expect this kind of code to break randomly on random devices, because manufacturers can and will change the private APIs in arbitrary ways for individual devices.  Just targeting platform versions is not enough.
--
Dianne Hackborn
Android framework engineer
hac...@android.com

Note: please don't send private questions to me, as I don't have time to provide private support, and so won't reply to such e-mails.  All such questions should be posted on public forums, where I and others can see and answer them.

Angus Lees

unread,
Aug 6, 2010, 9:44:56 AM8/6/10
to andro...@googlegroups.com
I don't think we are necessarily talking about private APIs - or at least, the exact same situation could arise with eg GLES2.0 and GLES1.0 versions of some program (or armeabi-v7a vs armeabi, or neon vs non-neon, etc), where there is a common library that calls out to one of these.

A few comments on the discussion so far based on my experiences with ScummVM dynamic plugins:
- The run-time linker changed in Android 2.0.  Before 2.0, it was effectively always dlopen(RTLD_GLOBAL).  After 2.0, it is effectively always dlopen(RTLD_LOCAL).  In both cases, the exact flag you specify gets ignored (afaics from a quick read of the source).

The post-2.0 behaviour is to look for symbols in all dependencies of the current library (ie: in DT_NEEDED libraries). This lets us workaround this linker deficiency provided we can tell Android that the common library depends on "the" specific library.  I don't think Android supports sonames (at least I didn't find anything hopeful during some simple greps for the relevant elf constants) - but it looks like it only uses the basename of the library as the key so we have to use this to tell Android that your alternative libraries are interchangeable.

So:
Let's say you have GLES1 and GLES2 versions of some of your functions, and then a common library that uses symbols from either of these to do its work.  You now need to make this happen:
- build gles1/libspecific.so and gles2/libspecific.so  <- note the same library basenames
- build libcommon.so with -lspecific and -Lgles1 (or -Lgles2, it doesn't matter since the exported symbols are identical in this example)
- in Java, work out which library alternative you want and System.loadLibrary it - you might need to specify a full path.
- in Java, now System.loadLibrary(libcommon.so).

It should work on all Android versions: The (2.0+) run-time linker should see the DT_NEEDED entry for "libspecific.so", see that something called libspecific.so is already loaded and then reuse those symbols.  A pre-2.0 system will just find the required symbols already in the global list and use them, with no further regard for where they came from.

Untested.  Let us know how it goes.  The fallback if we can't get the run-time linker to do what you need is to use function pointers and dlopen()/dlsym() yourself - essentially doing your own linking in whatever manner you wish.

 - Gus

Dianne Hackborn

unread,
Aug 6, 2010, 6:51:49 PM8/6/10
to andro...@googlegroups.com
The code being posted is most definitely talking about using private APIs.  See these lines:

LOCAL_C_INCLUDE := /android/1.6/platform/base/include ...

...

LOCAL_C_INCLUDE := /android/2.2/platform/base/include ...

Absolutely not okay.

Onur Cinar

unread,
Aug 6, 2010, 7:05:01 PM8/6/10
to andro...@googlegroups.com

Dianne,   that line is just an example. If you read the actual e-mail, you will see that we are not talking about private API here.....    The original question is about dynamically loading different shared libraries on different versions of Android OS from the Android application.   The concept is very useful for developers who would like to use the latest NDK features (like bitmap.h, GLES2.0) and still supporting the older versions of Android OS...  

If you have an idea/response for the actual question please respond to the thread.  However,  please stop sending those irrelevant e-mails...


-onur

Dianne Hackborn

unread,
Aug 7, 2010, 11:44:49 PM8/7/10
to andro...@googlegroups.com
Then please don't post sample code that is clearly making use of private APIs.  We have lots of problems with people getting it in their head this is fine, getting in trouble, and having that trouble come back to us, and I am going to respond strongly to anything posted in this list that implies that this is okay to do.
Reply all
Reply to author
Forward
0 new messages