There are two ways to run your own code on your device: either using the NDK, or by embedding your application in the framework. As far as I understand, the first approach is not considered, therefore, I think you can take a look at the second. Here is an example implementation of the second approach.
Example of transferring existing code to an Android user device
It's time for the next tech post here on the blog. This post will be dedicated to porting existing c-libraries to Android, which I did as part of the demo demos we are doing here in Enea.
Adding a platform or NDK
There are two ways to transfer your own code to an Android device, either add it to the platform, or integrate with the framework, or include the application package in it. The latter method has changed a lot, and with the release of NDK version 5 it even allows you to directly connect to the application life cycle http://developer.android.com/reference/android/app/NativeActivity.html from NDK, NDK is useful for any application in which you you need your own performance, there are portable C libraries that you want to reuse, or just some native code of your own that can be included in your application. NDK integrates well with the Android SDK and is a great way to incorporate native features into your application. This should be the preferred way for any application that needs to be reused on many Android devices.
Another option is to enable your functions, it can be native or Java, as an extension of the API for all applications. This will only work on devices that implement these extensions, and it may be a suitable option for device developers. This is the option we are aiming for here.
Analysis of an existing project
Porting native code to Android is not always straightforward, especially if we are talking about C ++ code because Android uses its own c-runtime with limited exception support, by the way. If you want to know more about the details of bionics, there is a review in the NDK docs.
The code I wanted to use for this project was the Enea LINX framework for Linux, which is a fast IPC framework. My goal was to be able to interact with management systems that run our real-time operating system OSE, which also implements this type of IPC. LINX consists of several kernel driver modules, a user space library, and some configuration and management utilities. This is written in C. I created a small demo with LINX on Android before where I compiled it separately and used static links, but for this project I need a full port for the Android build system. It had no problems with bionic compatibility, so the port should be direct.
I just want to add a short rejection of LINX. I use it here, as this is a good example of integrating a solution in Android with kernel drivers up to the API level. This particular piece of code adds additional IPC mechanisms to systems that more or less confuse the security model, so do not use it if you are not aware of the consequences. The steps required for the port code for Android described in this post are applicable, however, for any type of driver / framework / library that you can include in your product.
Adding kernel driver modules
The first step was to add kernel modules to the Android assembly. One way would be to create a new kernel and enable them directly, but for this project I decided to save them as separate modules. For the kernel, the kernel is not processed by the Android build system, which means that we build them the same way as with any Linux system. The goal is an Atmel-based development platform, and in the LINX module I provide headers and cross-compilation for this kernel and architecture.
Now for specific parts of Android. We need to somehow add the compiled kernel modules to the platform build system and create the Android.mk file, which will include them in the system image when creating. Add the folder to the source tree where your project, device or appearance will go. I created a linx folder that will contain the entire linx port, and in this I added a subfolder called modules, where I place ready-made kernel modules. Now we need a make file for Android to copy them to a suitable place in the out folder to generate a system image. It will look like this:
LOCAL_PATH := $(my-dir) include $(CLEAR_VARS) LOCAL_MODULE := linx.ko LOCAL_MODULE_CLASS := SHARED_LIBRARY
The standard location of the modules in the Android system image is System / lib / modules, so we copy them. If we build the platform, the build system will copy our pre-compiled linx.ko module to the system image that we use for our device. The next step is to make sure that we have a module installed in the system when we start it. This can be done manually through the shell or through a script that we run during init.
In this case, I created a shell script to run from init.rc with the following contents:
This includes installing modules and setting up a network and LINX-link. We started this from init.rc, adding:
...
Customizing the script image is added to the system image in the same way, including it as a finished target.
LOCAL_PATH := $(my-dir) include $(CLEAR_VARS) LOCAL_MODULE := linx_setup.sh LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH := $(TARGET_OUT)/etc LOCAL_SRC_FILES := $(LOCAL_MODULE) include $(BUILD_PREBUILT)
Creating Android for files with user code
Now that we have the drivers, the next step is to look at migrating custom spatial libraries. The LINX system by default uses the standard GNU make files, but we need to create new ones adapted to the Android build system. Start by adding the source files needed for the linx directory created in the Android source tree. This gives the following structure:
Android.mk include liblinx linx_basic linxcfg linx_setup.sh modules
I have a linx script installation and the main Android.mk file in the top directory, and then we have the source files in separate folders and the included files in the include folder. To illustrate how Android files are created for each source component, we can use liblinx as an example. The Android.mk file looks like this:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := linx.c LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include LOCAL_MODULE := liblinx LOCAL_PRELINK_MODULE := false include $(BUILD_SHARED_LIBRARY)
We set our sources by specifying LOCAL_SRC_FILES and the name of the library by specifying LOCAL_MODULE. We also need to provide header files in the include directory by specifying LOCAL_C_INCLUDES. Finally, this is a shared library that we are porting, so use the BUILD_SHARED_LIBRARY template. This will create a library with the Android build system and add it to the system image as a shared library named liblinx.so.
The rest of the code is moved to the Android build system in the same way, creating Android.mk files and specifying the type and any dependencies. As another example, we can look at the syntax for building the mktcpcon configuration program. It depends on the library we just created, and therefore the entry in the makefile looks like this:
LOCAL_SRC_FILES := mktcpcon.c LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include LOCAL_STATIC_LIBRARIES += liblinxcfg LOCAL_SHARED_LIBRARIES += liblinx LOCAL_MODULE := mktcpcon include $(BUILD_EXECUTABLE)
Here we use the BUILD_EXECUTABLE template, and we also need to specify the static and shared libraries that we reference.
Summary
Hopefully this will give you an idea of how you set up the build for an existing linux project to work on Android. Next steps:
- Create all the kernel related stuff using the correct kernel build system and configuration for your device.
- Add kernel modules (and / or kernels) to the platform’s build system and create Android.mk files for them using a pre-created template.
- Create configuration and installation services for your drivers, if necessary, and add them to init.
- Move the rest of the code (user space) into the Android source tree and create Android.mk files for them.
- If you encounter build errors, make them in the source code and see what inconvenience your code has with the C C. Android runtime specification.
This concludes my post for today. Having done this, we can now use our added drivers and API: s from our own programs running in the shell. The next step is to create a JNI layer and java library so that ordinary Android applications can use our platform add-ons.
I was absent from my father’s vacation for six months (a good Swedish benefit), but now it’s a full-time hack of Android and pushing the team to publish things. Hopefully you will see more activity here, including the next steps in this post discussing application APIs.