Monday, April 27, 2009

A closer look at the Android project build system part II

This is the second post on the build system where we will take a closer look at the Android.mk file and what options are available. An Android.mk file describes the build for any native module that should go in the platform. We will start by looking at the makefile for the external ping facility found in external/ping.


ifneq ($(TARGET_SIMULATOR),true)

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_SRC_FILES:= ping.c
LOCAL_MODULE := ping
LOCAL_STATIC_LIBRARIES := libcutils libc
include $(BUILD_EXECUTABLE)

endif # TARGET_SIMULATOR != true


The first line is just a check if we are building for the (obsolete?) simulator. The next line sets the local path to the current directory (the directory of this Android.mk file). This is handled by a function called mydir that can be found in the definitions.mk file in the build system.

The next step is to include a makefile that will clear all local variables with the line include $(CLEAR_VARS). CLEAR_VARS is set in the build configuration in config.mk and will include the clear_vars.mk file at this point. The main purpose of this include is to make sure that we do not use any local variables set by another module.

After the two initial lines that basically prepares the build system to start with this module we set the build variables. These are


LOCAL_SRC_FILES - the source files that make up this module
LOCAL_MODULE - the name of the module
LOCAL_STATIC_LIBRARIES - libraries to statically link to this module

Since ping will be an executable command placed in /system/bin in the Android file system the make file for building an executable should be used. This done with the line:

include $(BUILD_EXECUTABLE)

There are makefiles available in the build system that can be used to build a number of different types of module. To include them you may use one of the variables set in the config.mk file. A few important ones are:


BUILD_EXECUTABLE - Build an executable module
BUILD_SHARED_LIBRARY - Build a shared library
BUILD_STATIC_LIBRARY - Build a static library
BUILD_PREBUILT - Add prebuilt components


There is also a number of local variables that are recognized by the build system and that needs to be set in order to build your module.


LOCAL_C_INCLUDES - path to include files needed by your module, e.g. ($KERNEL_HEADERS)
LOCAL_CFLAGS - Any additional flags to pass to the compiler
LOCAL_LDFLAGS - Any additional flags to pass to the linker
LOCAL_SHARED_LIBRARIES - Shared libraries that the module directly links against
LOCAL_SRC_FILES - The source files to compile
LOCAL_STATIC_LIBRARIES - Static libraries to include in the module


You may use one Android.mk to build several items. It is possible to build both a library and an executable using the same makefile. To illustrate how to write a makefile for some native functionality we will look at another example. This makefile will build a shared library and an executable. It links to a couple of shared libraries in the system.



LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
#Name of target to build
LOCAL_MODULE:= libmylibrary
#Source files to compile
LOCAL_SRC_FILES:= mysrcfile.c mysothersrcfile.c
#The shared libraries to link against
LOCAL_SHARED_LIBRARIES := libcutils
#No special headers needed
LOCAL_C_INCLUDES +=
#Prelink this library, also need to add it to the prelink map
LOCAL_PRELINK_MODULE := true
include $(BUILD_SHARED_LIBRARY)

#Clear variables and build the executable
include $(CLEAR_VARS)
LOCAL_MODULE:= myinfocmd
LOCAL_SRC_FILES:= mycmdsrcfile.c
include $(BUILD_EXECUTABLE)


The output of the build system is put in the out folder in the Android project. Build output is directed to a subfolder based on the product. A default build is for the generic product and will output everything target related to that folder. A shared library ends up in /out/target/product/generic/system/lib. In the same way an executable will end up in /out/target/product/generic/system/bin. The object files and intermediates are kept in the obj folder. The root, system, and data folders will be used to generate the file system images needed for Android in the last build step.

/Mattias

Friday, April 24, 2009

A closer look at the Android project build system part I

In this two part post we will take a closer look at the structure of the build system for native functionality. The build system is based on make and resides in the build folder in the project. The first part will look at what is found in this folder while the second part discusses the Android.mk file and options.

envsetup.sh
There is one important file in build directory itself. That is the envsetup.sh file. This shell script contains a lot of functionality that is nice to have when working with Android. To invoke it type

. build/envsetup.sh

in the android root directory. This will allow you to use a number of new commands including:

m - make from the top of the tree
mm - builds all modules in the current directory
cgrep - grep in all c-files
jgrep - grep in all java-files
mgrep - grep in all makefiles
lunch - choose build target

These shell commands are very useful when working with the platform. Whether you are looking for something specific in a source file or you just want to build the current module for a test.

build/target
The target directory contains make configurations for different boards and products. The target directory is where new products can be added as needed by a hardware manufacturer. In the current release there is a generic product description, and sdk product and some other files. The available boards are the emulator, a generic board and the simulator.

The board makefiles describe the hardware platform level and can be shared among a number of products. For example you can build both the generic product and the generic_with_google product to run on in the emulator.

The makefiles for different products are listed in the file AndroidProducts.h. To add a new product put the makefile in the build/target/product folder and add the product to AndroidProduct.h. It is possible for product makefiles to inherit from each other to reduce the need for copying. The generic_with_google products inherits the generic product that inherits the core product to make up a three level product specification.

build/core
The core folder is the location of the build system files. These files are used to set up the build system and to build all the different modules as configured in the individual Android.mk files. The main.mk file is the entry point to the build system and it is this files that is included in the top level makefile in the project root directory. The build system can also be invoked by the shell command mm with at subset of the makefiles

The include order and hierarchy of the makefiles is rather complicated but an attempt of illustration can be found in this figure.


Most of the build configuration is set in the config.mk file and the definitions.mk file contains the functions to be invoked for the different source types. In the above figure the build system is invoked to build a shared library. The Android makefile uses the BUILD_SHARED_LIBRARY variable set in the config.mk file. This will set the build chain for building a shared library in a number of steps using several other files.

Wednesday, April 1, 2009

JNI hands on

In the demo setup at the seminar we hijacked the SMS application to send a LINX signal using JNI. In this post I'll describe more in detail how we implemented it.

First you need to create a Java-class that loads the C-library and declares the method (send(...))
public class NativeLib {
static {
try {
Log.i("JNI", "Trying to load libNativeLib.so");
System.loadLibrary("NativeLib");
}
catch (UnsatisfiedLinkError ule) {
Log.e("JNI", "WARNING: Could not load libNativeLib.so");
}
}
public static native long send(String number, String message);
}

Call the method somewhere appropriate from your activity:
long sum = NativeLib.send(...);

Next, you need to generate C-headers from the NativeLib Java-class:
cd <path to eclipse project dir>/bin
javah <class name>

Create a directory nativelib in the <android root>/external directory and move the header file to here.

Now, implement the C-file (and remember to include your recently generated header-file)
#include "com_android_mms_transaction_NativeSms.h"

JNIEXPORT jlong JNICALL Java_com_android_mms_transaction_NativeSms_send
(JNIEnv *env, jclass class, jstring number, jstring message)
{
//Get the native string from javaString
const char *numString;
const char *messageString;

numString = (*env)->GetStringUTFChars(env, number, 0);
messageString = (*env)->GetStringUTFChars(env, message, 0);

/*code to handle the sms info in native context goes here*/

/*release resources when done*/
(*env)->ReleaseStringUTFChars(env, number, numString); (*env)->ReleaseStringUTFChars(env, message, messageString);

return 0;
}

Next, we will construct a make-file: name it Android.mk and put it in <android root>/external/nativelib. Fill it up with the following:
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES := %lt;name of C-file%gt;

LOCAL_SHARED_LIBRARIES := libcutils

LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)

LOCAL_MODULE := libNativeLib

include $(BUILD_SHARED_LIBRARY)

Open file prelink-linux-arm.map in <android root>/build/core and add the row:

libNativeLib.so 0x9F400000

under comment:

# libraries for specific apps or temporary libraries

This will add your library to the prelink map. Make sure that the address is not used by anything else.
Now type make in Android root dir to build. Hopefully everything goes well and voila! You have implemented your native C-library

/Sebastian