2010年7月5日星期一

How to use NDK and Eclipse to setup and build a Android JNI project under Ubuntu 10.04

How to use NDK and Eclipse to setup and build a Android JNI project under Ubuntu 10.04

Assume the following software are installed:
- Eclipse 3.5.1
- Android SDK (version 8)
- ADT 0.9.7
- Android NDK r4

1. Use Eclipse and ADT to create a Android project. Remember to set a right min. version for your project.
More information can be found from Android SDK document, Android NDK section.

2. Declare your native functions and load library functions.

Here is a simple example:

In ActivityMain.java file, I wrote:

package com.xxx.TestJNI;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class ActivityMain extends Activity {
private static final String TAG = "TestJNI:ActivityMain";

// Declare native functions
public native String getString();

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "OnCreate >>>>>");

super.onCreate(savedInstanceState);
setContentView(R.layout.main);

Log.d(TAG, "String from native library = " + getString());

Log.d(TAG, "OnCreate <<<<<");
}
// load native library
static {
System.loadLibrary("jnitest");
}
}

If "Project-->Build Automatically" option is enabled, Eclipse will generate the corresponding class file in the {bin} folder.
You can find the class file in {project}/bin/{package}/

3. Create {jni} folder under your project folder. I.e. mkdir {project}/jni

4. Create JNI header file:
Type the following command:

javah -classpath {project}/bin -d {project}/jni/ {full package name}.{class name}

For example:
javah -classpath workspace/TestJNI/bin -d workspace/TestJNI/jni/ com.xxx.TestJNI.ActivityMain

where "com.xxx.TestJNI" is the full package name and "ActivityMain" is the class name.

Then you should find a header file in the {jni} folder.

5. Create the corresponding C/C++ file in the {jni} folder and write your codes.
For example:

In the C++ file (com_xxx_TestJNI_ActivityMain.cpp), I wrote:

#include "com_xxx_TestJNI_ActivityMain.h"
// we want to use android logging functions such that we can use Logcat for debugging
#include <android/log.h>

#define TAG "TestJNI:jnitest"

// this function will be called when the library is loaded
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
// we want to use JDK/JRE 1.4 functions so we will return JNI 1.4 version to JAVA VM
// Otherwise, JAVA VM assumes we want to use JDK/JRE 1.1 functions
// More information can be found from JAVA Native Interface Specification (In the JDK documentation)

JNIEnv *env = 0;
jint result = -1;

__android_log_print(ANDROID_LOG_DEBUG, TAG, "On Load...");

if(vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
__android_log_print(ANDROID_LOG_WARN, TAG, "JNI 1.4 is not supported!");
return result;
}

result = JNI_VERSION_1_4;

return result;
}

JNIEXPORT jstring JNICALL Java_com_xxx_TestJNI_ActivityMain_getString(JNIEnv *env, jobject obj) {

__android_log_print(ANDROID_LOG_DEBUG, TAG, "Create and return String object");

return env->NewStringUTF("Hello native world");
}

Here, we can see that I use "env->NewStringUTF("Hello native world")" to create a String object.
However, if your source file is C, then you have to change the line to "(*env)->NewStringUTF(env, "Hello native world")".

You can find more information from {NDK}/build/platforms/android-(X)/arch-arm/usr/include/jni.h.
(X) = the selected android system version.

In fact, you can search all functions which can be used from {NDK}/build/platforms/android-(X)/arch-arm/usr/include/.
Also, you can read {NDK}/docs/STABLE-APIS.TXT for a quick reference.
For JNI programming, please read JDK 6 Documentation, Java Native Interface section.

6. Create a Make file (Android.mk) in {project}/jni:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := jnitest
LOCAL_SRC_FILES := com_xxx_TestJNI_ActivityMain.cpp
LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)

For the module name (LOCAL_MODULE), it should be as same as what you use for the System.loadLibrary() call in your JAVA source file.

For more information about Make file, please read {NDK}/docs/OVERVIEW.TXT and {NDK}/docs/ANDROID-MK.TXT

7. Build the native shared library.
Type the following commands:

cd {project}
{NDK}/ndk-build -B V=1

For more information about build process, please read {NDK}/docs/OVERVIEW.TXT and {NDK}/docs/NDK-BUILD.TXT

8. Rebuild your android project

Suppose the native library was built successfully, in Eclipse, select "Project-->Clean..." and then Eclipse will rebuild the project again.

Now, you can use DDMS, Logcat and software simulator/hardware to test your application.

2010年7月4日星期日

Setup QEMU under Ubuntu 8.04

1. Install QEMU package

2. Setup network bridge

2.1 install uml-utilities and bridge-utils package
2.2 setup bridge configuration:

In /etc/network/interfaces file, add the following lines:

iface br0 inet dhcp
bridge_ports eth0
bridge_fd 9
bridge_hello 2
bridge_maxage 12
bridge_stp off

auto br0

2.3 Disable Ubuntu roaming function for eth0

In the terminal, type network-admin. And then set eth0 to use "Static IP address".
Assign IP, subnet mask and gateway to eth0. After that, you should see the setting in /etc/network/interfaces file.
For example:

iface eth0 inet static
address 192.168.1.100
netmask 255.255.0.0
gateway 192.168.1.254

auto eth0

Then edit /etc/network/interfaces file to comment the line "auto eth0":

#auto eth0

2.4
Since we want to add default gateway to the bridge interface,
we can create a shell script in the /etc/network/if-up.d folder:

#! /bin/sh

#Not for loopback!
if [ "$IFACE" != "lo" ]; then
/sbin/route add -host 192.168.251.1 gw 192.168.252.254 dev $IFACE
fi

In the above script, route will be ran for each interface when network service is started.
You can adjust it to fit your situation.

3. Prepare qemu script for network bridge

In /etc/qemu-ifup file, add the following lines:

echo "Executing /etc/qemu-ifup"
echo "Bringing up $1 for bridged mode..."
sudo /sbin/ifconfig $1 0.0.0.0 promisc up
echo "Adding $1 to br0..."
sudo /usr/sbin/brctl addif br0 $1
sleep 2

4. Restart network service

sudo /etc/init.d/network restart

Normally, there is no IP for eth0 and br0 has a IP address.

5. Prepare system disk image

6. run Qemu

use qemu command to start the simulation. For example:

sudo qemu -hda sysimage.img -net nic,model=rtl8139 -net tap -serial /dev/ttyS0

-hda sysimage.img = the disk image (sysimage.img) is mapped to hda
-net,model=rtl8139 = use rtl8139 network card for the simulator (input)
-net tap = use TAP/TUN dirver (output), this dirver is already connected to the network bridge
-serial /dev/ttyS0 = enable serial port emulation and map guest's serial port to the host's port (/dev/ttyS0)

7. You can use "ping" command to check the network connection.

If the guest Linux system cannot detect the serial port, we can use "modprobe 8250" command and then you see the following output:

Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
serial8250: ttyS0 at I/O 0x3f8 (irq = 4) is a 16450

That means qemu will use 16450 chip set to emulate serial port for the guest linux system.

2010年7月3日星期六

Netbeans 有關 PHP Debug 設定

如果不想在 Debug PHP 的過程中每次進入新的 PHP 頁時停下來,我們可以在 Options-->PHP-->General 中不選擇 Stop at First Line 就可以了.

2009年9月13日星期日

How to setup Netbeans 6.7 PHP debug environment in Ubuntu 8.04

1. Enable personal HTTP directory

1.1 use sudo command to create two symbolic links in /etc/apache2/mod-enabled directory:
sudo ln -s ../mods-available/userdir.conf userdir.conf
sudo ln -s ../mods-available/userdir.load userdir.load

1.2 create HTTP directory in the user's home directory:
makedir public_html

1.3 restart apache web server
sudo /etc/init.d/apache2 restart

1.4 test personal HTTP directory
use web browser to browse http://localhost/~[username]/

2. Enable xdebug

2.1 install xdebug DEB package

2.2 add the following lines in /etc/php5/apache2/php.ini:
zend_extension=xdebug.so
xdebug.remote_enable=on

3. Setup Netbeans PHP project

3.1 enable "copy files from sources folder to another location" and set the folder to user HTTP directory

3.2 in "run Configuration" page, set "Project URL" to user HTTP directory

2009年9月12日星期六

/etc/network/if-up.d 目錄下的 shell script

當 Ubuntu 啟動所有網絡介面時 (/etc/init.d/network 呼叫 ifup -a), 如果網絡介面成功啟動,那麼 /etc/network/if-up.d/ 目錄下的 shell script 會被執行. 例如有個 shell script 是這樣的:

#!/bin/sh

if [ $IFACE != "lo" ]; then
/sbin/route add default gw 192.168.1.254 $IFACE
fi

那麼只要網路介面不是 lo, route 就會執行, 即將 192.168.1.254 設為該介面的 gateway.
更多資料可以在 interfaces 的 man page 找到: man interfaces.

2008年10月11日星期六

找出執行程式的檔案路徑 (Linux 和 Windows 版本)

#include "string.h"
#include "stdio.h"

#ifndef __WIN32__
#include "unistd.h"
#include "sys/types.h"
#endif

char getApplicationPath(char *pPath, int nMaxPathSize)
{
char proc_path[20], buf[256], *lastchar;
int len;

#ifdef __WIN32__
len = GetModuleFileName(NULL, buf, 256);

if(len == 0) {
// GetModuleFileName error
return -1;
}

if((len >= 256) || (len >= nMaxPathSize)) {
//insufficient size
return -2;
}

// terminate the returned string first
buf[len] = 0;

// then remove the application name
lastchar = strrchr(buf, '\\');
#else
sprintf(proc_path, "/proc/%d/exe", getpid());
len = readlink(proc_path, buf, 256);

if(ret < 0) {
// readlink error
perror("readlink");
return -1;
}

if((len >= 256) || (len >= nMaxPathSize)) {
// insufficient size
return -2;
}

// terminate the returned string first
buf[len] = 0;

// then remove the application name
lastchar = strrchr(buf, '/');
#endif

if(lastchar != NULL) {
*lastchar = 0;
} else {
// application name not found
return -3;
}

strcpy(pPath, buf);

// success
return 0;
}

2008年10月10日星期五

wxWidgets 的 StretchBlt 方案

如果用過 Win32 SDK 的人都知道有一個 StretchBlt() function 可以將原有的 bitmap 放大或縮小再顯示出來. 可是在 wxWidgets 卻沒有此功能. 如果要在 wxWidgets 做出類似功能, 我們可以:

1. 將原來的 wxBitmap object 用 wxBitmap::ConvertToImage() 轉為 wxImage object
2. 用 wxImage::Rescale() 改變大小
3. 再用新的 wxImage object 產生新的 wxBitmap object
4. 用 wxDC::DrawBitmap() 顯示新的 wxBitmap object